dsh-plugin-local-infer 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +10 -2
  2. package/docs/zh/README.md +10 -2
  3. package/dsh/client.js +669 -191
  4. package/package.json +6 -1
  5. package/packages/core/lib/adapter.d.ts +44 -2
  6. package/packages/core/lib/deps.d.ts +4 -3
  7. package/packages/core/lib/index.d.ts +1 -1
  8. package/packages/core/lib/registrar.d.ts +5 -5
  9. package/packages/core/lib/registrar.js +3 -3
  10. package/packages/core/package.json +1 -1
  11. package/packages/providers/lib/acquire.d.ts +5 -3
  12. package/packages/providers/lib/acquire.js +30 -9
  13. package/packages/providers/lib/adapters.d.ts +19 -209
  14. package/packages/providers/lib/adapters.js +13 -886
  15. package/packages/providers/lib/deps.d.ts +24 -2
  16. package/packages/providers/lib/deps.js +23 -4
  17. package/packages/providers/lib/engines/hyperqwenAdapter.d.ts +38 -0
  18. package/packages/providers/lib/engines/hyperqwenAdapter.js +105 -0
  19. package/packages/providers/lib/{infernix.js → engines/infernix.js} +1 -1
  20. package/packages/providers/lib/engines/infernixAdapter.d.ts +53 -0
  21. package/packages/providers/lib/engines/infernixAdapter.js +202 -0
  22. package/packages/providers/lib/engines/llamacppAdapter.d.ts +51 -0
  23. package/packages/providers/lib/engines/llamacppAdapter.js +232 -0
  24. package/packages/providers/lib/{ninfer.js → engines/ninfer.js} +1 -1
  25. package/packages/providers/lib/engines/ninferAdapter.d.ts +82 -0
  26. package/packages/providers/lib/engines/ninferAdapter.js +324 -0
  27. package/packages/providers/lib/engines/strata.d.ts +53 -0
  28. package/packages/providers/lib/engines/strata.js +197 -0
  29. package/packages/providers/lib/engines/strataAdapter.d.ts +98 -0
  30. package/packages/providers/lib/engines/strataAdapter.js +427 -0
  31. package/packages/providers/lib/engines.d.ts +5 -4
  32. package/packages/providers/lib/engines.js +10 -10
  33. package/packages/providers/lib/guidelines.js +1 -1
  34. package/packages/providers/lib/index.d.ts +14 -9
  35. package/packages/providers/lib/index.js +17 -7
  36. package/packages/providers/lib/model-defaults.d.ts +5 -4
  37. package/packages/providers/lib/model-defaults.js +22 -4
  38. package/packages/providers/lib/platform.js +7 -1
  39. package/packages/providers/package.json +1 -1
  40. package/packages/service/lib/index.d.ts +13 -4
  41. package/packages/service/lib/index.js +79 -5
  42. package/packages/service/lib/types.d.ts +5 -4
  43. package/packages/service/package.json +1 -1
  44. package/packages/tool/package.json +1 -1
  45. package/packages/ui/lib/config.d.ts +79 -0
  46. package/packages/ui/lib/config.js +235 -0
  47. package/packages/ui/lib/deps.d.ts +3 -3
  48. package/packages/ui/lib/ops/engine.d.ts +108 -0
  49. package/packages/ui/lib/ops/engine.js +493 -0
  50. package/packages/ui/lib/ops/hf.d.ts +27 -0
  51. package/packages/ui/lib/ops/hf.js +60 -0
  52. package/packages/ui/lib/ops/logs.d.ts +23 -0
  53. package/packages/ui/lib/ops/logs.js +58 -0
  54. package/packages/ui/lib/ops/model.d.ts +79 -0
  55. package/packages/ui/lib/ops/model.js +130 -0
  56. package/packages/ui/lib/request.d.ts +106 -0
  57. package/packages/ui/lib/request.js +80 -0
  58. package/packages/ui/lib/route.d.ts +88 -420
  59. package/packages/ui/lib/route.js +111 -1843
  60. package/packages/ui/lib/routes/chat-template.d.ts +14 -0
  61. package/packages/ui/lib/routes/chat-template.js +37 -0
  62. package/packages/ui/lib/routes/deps.d.ts +16 -0
  63. package/packages/ui/lib/routes/deps.js +27 -0
  64. package/packages/ui/lib/routes/download.d.ts +16 -0
  65. package/packages/ui/lib/routes/download.js +48 -0
  66. package/packages/ui/lib/routes/get.d.ts +18 -0
  67. package/packages/ui/lib/routes/get.js +57 -0
  68. package/packages/ui/lib/routes/install.d.ts +16 -0
  69. package/packages/ui/lib/routes/install.js +36 -0
  70. package/packages/ui/lib/routes/launch.d.ts +18 -0
  71. package/packages/ui/lib/routes/launch.js +50 -0
  72. package/packages/ui/lib/routes/provider-delete.d.ts +15 -0
  73. package/packages/ui/lib/routes/provider-delete.js +37 -0
  74. package/packages/ui/lib/routes/register.d.ts +16 -0
  75. package/packages/ui/lib/routes/register.js +28 -0
  76. package/packages/ui/lib/routes/save.d.ts +18 -0
  77. package/packages/ui/lib/routes/save.js +139 -0
  78. package/packages/ui/lib/routes/stop.d.ts +16 -0
  79. package/packages/ui/lib/routes/stop.js +29 -0
  80. package/packages/ui/lib/routes/summary.d.ts +15 -0
  81. package/packages/ui/lib/routes/summary.js +26 -0
  82. package/packages/ui/lib/routes/unregister.d.ts +14 -0
  83. package/packages/ui/lib/routes/unregister.js +26 -0
  84. package/packages/ui/lib/viewmodels/deps.d.ts +31 -0
  85. package/packages/ui/lib/viewmodels/deps.js +89 -0
  86. package/packages/ui/lib/viewmodels/depsHelpers.d.ts +28 -0
  87. package/packages/ui/lib/viewmodels/depsHelpers.js +33 -0
  88. package/packages/ui/lib/viewmodels/summary.d.ts +140 -0
  89. package/packages/ui/lib/viewmodels/summary.js +225 -0
  90. package/packages/ui/lib/viewmodels/summaryHelpers.d.ts +30 -0
  91. package/packages/ui/lib/viewmodels/summaryHelpers.js +42 -0
  92. package/packages/ui/package.json +1 -1
  93. package/packages/providers/lib/strata.d.ts +0 -15
  94. package/packages/providers/lib/strata.js +0 -14
  95. /package/packages/providers/lib/{hyperqwen.d.ts → engines/hyperqwen.d.ts} +0 -0
  96. /package/packages/providers/lib/{hyperqwen.js → engines/hyperqwen.js} +0 -0
  97. /package/packages/providers/lib/{infernix.d.ts → engines/infernix.d.ts} +0 -0
  98. /package/packages/providers/lib/{llamacpp.d.ts → engines/llamacpp.d.ts} +0 -0
  99. /package/packages/providers/lib/{llamacpp.js → engines/llamacpp.js} +0 -0
  100. /package/packages/providers/lib/{ninfer.d.ts → engines/ninfer.d.ts} +0 -0
@@ -0,0 +1,427 @@
1
+ /**
2
+ * The Strata engine adapter. StrataAdapter is the Strata engine, three build
3
+ * modes. `renderLaunch` renders a `docker run` of the ghcr image (docker
4
+ * mode), a `strata launch` process (binary mode), or a `setup.sh` run with the
5
+ * model (local mode); `deploy` pulls the image, installs the Terminal, or
6
+ * git-syncs the checkout and resolves `setup.sh` to an absolute path in it
7
+ * before spawning (spawn resolves a bare name against PATH, not the cwd);
8
+ * `healthCheck` polls the health URL; `stop` stops the named container, runs
9
+ * `strata launch --stop`, or signals the tracked process. The model the
10
+ * engine serves is the config model (or the engine's own pick when none is
11
+ * named), and the engine downloads it itself, so the deploy step runs no model
12
+ * download of its own. When the config names no model, `discoverModel`
13
+ * queries the running engine's model endpoint through the terminal for the id
14
+ * it actually loaded (family and size sized to the host), so the registrar
15
+ * publishes the served model, not a placeholder.
16
+ * @module dsh-plugin-local-infer/providers/engines/strataAdapter
17
+ */
18
+ import { join } from 'node:path';
19
+ import { EngineAdapter } from '../../../core/lib/index.js';
20
+ import { customArgEnv, customArgFlags, delay, envFromSpecs, flagsFromSpecs, nodeLifecycleHost, trackChild, } from "../adapters.js";
21
+ import { syncAndRebuildEngine } from "../acquire.js";
22
+ import { STRATA_PARAMETERS, strataModelKey } from "./strata.js";
23
+ /**
24
+ * The Strata docker image the adapter pulls and runs. The image is the
25
+ * upstream GitHub Container Registry artifact for the newest release's CUDA 13
26
+ * flavor (NVIDIA RTX 20/30/40/50 series, driver 580 or newer); the README pins
27
+ * a version tag when updates should only land on purpose.
28
+ */
29
+ export const STRATA_DOCKER_IMAGE = 'ghcr.io/niko1221/strata:latest-cuda13';
30
+ /**
31
+ * The one-line install the Strata binary build mode runs: it downloads the
32
+ * `strata` Terminal from the latest release, checks its SHA-256, unpacks it into
33
+ * the user's home folder, and puts `strata` on the PATH. Sourced from
34
+ * docs/AI_SETUP.md step 2 (Linux and Mac; Windows uses the PowerShell twin).
35
+ */
36
+ export const STRATA_BINARY_INSTALL = 'curl -fsSL https://github.com/Niko1221/Strata/releases/latest/download/install.sh -o install.sh && sh install.sh';
37
+ /**
38
+ * strataPreReadyState reads the phase the strata process is in from the tail of
39
+ * its accumulated output. The Terminal downloads the engine and the model files
40
+ * before it can serve, and prints a progress line for each (Downloading,
41
+ * Unpacking, Checking the package signature, Getting the model files, ...), so
42
+ * a tail that carries one of those reads as downloading. A tail past the
43
+ * download (the engine starting, the API binding) reads as starting. The tail
44
+ * is the most recent output, so a finished download no longer reads as
45
+ * downloading even though its progress lines are still in the buffer.
46
+ */
47
+ export function strataPreReadyState(logs) {
48
+ const tail = logs.slice(-2000);
49
+ const downloading = /Downloading|Unpacking|Checking the package signature|Test-starting|Getting the model files|Getting your model ready|Getting the helper packages/i.test(tail);
50
+ return downloading ? 'downloading' : 'starting';
51
+ }
52
+ /**
53
+ * strataServedModelId reads the served model id out of a /v1/models answer:
54
+ * the id of the first entry in the JSON `data` list. The engine names the
55
+ * model it auto-selected with family and size (qwen3.8-flash-next-iq3_s), so
56
+ * the registrar can publish the id the engine actually serves. A body that is
57
+ * not JSON, or carries no usable id (an error page, a truncated answer), reads
58
+ * as no discovery.
59
+ */
60
+ export function strataServedModelId(body) {
61
+ const start = body.indexOf('{');
62
+ const end = body.lastIndexOf('}');
63
+ if (start < 0 || end <= start) {
64
+ return undefined;
65
+ }
66
+ try {
67
+ const parsed = JSON.parse(body.slice(start, end + 1));
68
+ const list = Array.isArray(parsed.data) ? parsed.data : [];
69
+ for (const item of list) {
70
+ const id = typeof item === 'object' && item !== null ? item.id : undefined;
71
+ if (typeof id === 'string' && id !== '') {
72
+ return id;
73
+ }
74
+ }
75
+ }
76
+ catch {
77
+ // A partial or non-JSON answer (an error page, a truncated body) carries
78
+ // no served id.
79
+ }
80
+ return undefined;
81
+ }
82
+ /**
83
+ * StrataAdapter is the Strata engine, three build modes. `renderLaunch` renders
84
+ * a `docker run` of the ghcr image (docker mode), a `strata launch` process
85
+ * (binary mode), or a `setup.sh` run with the model (local mode); `deploy` pulls
86
+ * the image, installs the Terminal, or git-syncs the checkout and resolves
87
+ * `setup.sh` to an absolute path in it before spawning (spawn resolves a bare
88
+ * name against PATH, not the cwd); `healthCheck` polls the health URL; `stop`
89
+ * stops the named container, runs `strata launch --stop`, or signals the
90
+ * tracked process. The model the engine serves is the config model (or the
91
+ * engine's own pick when none is named), and the engine downloads it itself, so
92
+ * the deploy step runs no model download of its own. When the config names no
93
+ * model, `discoverModel` queries the running engine's model endpoint through
94
+ * the terminal for the id it actually loaded (family and size sized to the
95
+ * host), so the registrar publishes the served model, not a placeholder.
96
+ */
97
+ export class StrataAdapter extends EngineAdapter {
98
+ id = 'strata';
99
+ supports = { docker: true, local: true, binary: true };
100
+ host;
101
+ children = new Map();
102
+ constructor(host = nodeLifecycleHost) {
103
+ super();
104
+ this.host = host;
105
+ }
106
+ parameters() {
107
+ return STRATA_PARAMETERS;
108
+ }
109
+ modelRef(config) {
110
+ // Strata serves the model under the name the config names (or the engine
111
+ // default when none), so the registrar publishes that name and a chat turn
112
+ // routes to it.
113
+ const model = strataModelKey(config);
114
+ return model !== '' ? { id: model, name: model } : { id: 'strata', name: 'Strata' };
115
+ }
116
+ async discoverModel(config) {
117
+ // A config that names a model already routes through modelRef; discovery
118
+ // answers only for the model the engine auto-selected for the host.
119
+ if (strataModelKey(config) !== '') {
120
+ return undefined;
121
+ }
122
+ // The terminal query: curl the running engine's model list, capped at a
123
+ // few seconds so a down engine fails fast rather than parking the
124
+ // register route. A host without output capture cannot read the answer,
125
+ // so it discovers nothing and the route falls back to modelRef.
126
+ const runStream = this.host.runStream;
127
+ if (runStream === undefined) {
128
+ return undefined;
129
+ }
130
+ const port = this.port(config);
131
+ const chunks = [];
132
+ const outcome = await runStream.call(this.host, `curl -fsS --max-time 3 http://127.0.0.1:${port}/v1/models`, (chunk) => {
133
+ chunks.push(chunk);
134
+ });
135
+ if (!outcome.ok) {
136
+ return undefined;
137
+ }
138
+ const id = strataServedModelId(chunks.join(''));
139
+ return id === undefined ? undefined : { id, name: id };
140
+ }
141
+ modelInput(config) {
142
+ // Strata's models read pictures and the vision control is on by default
143
+ // (on, gpu, and cpu all enable a vision runner), so only 'off' withholds
144
+ // the Image input on the DSH model row.
145
+ return config.vision === 'off' ? ['text'] : ['text', 'image'];
146
+ }
147
+ launchSignature(config = {}) {
148
+ // The docker plan's host process is docker run of the ghcr image, so the
149
+ // image name is the command-line marker; the binary and source-build plans
150
+ // run the strata command, so the binary name is the marker.
151
+ const port = this.port(config);
152
+ if (this.buildMode(config) === 'docker') {
153
+ return { binary: STRATA_DOCKER_IMAGE, port };
154
+ }
155
+ return { binary: 'strata', port };
156
+ }
157
+ renderLaunch(config, _hw) {
158
+ const port = this.port(config);
159
+ const model = strataModelKey(config);
160
+ const modelId = model !== '' ? model : undefined;
161
+ const customArgs = customArgFlags(config);
162
+ const mode = this.buildMode(config);
163
+ if (mode === 'docker') {
164
+ // The container env is the spec surface's toEnv spellings (FAMILY,
165
+ // CONTEXT, VISION, ...) plus the card's Custom Arguments, plus the model
166
+ // the container serves. The model download happens inside the container
167
+ // on first start into the strata-data volume, so no host download.
168
+ const env = { ...envFromSpecs(STRATA_PARAMETERS, config), ...customArgEnv(config) };
169
+ if (model !== '') {
170
+ env.MODEL = model;
171
+ }
172
+ const envFlags = Object.entries(env).flatMap(([key, value]) => ['-e', `${key}=${value}`]);
173
+ const containerName = `strata-${port}`;
174
+ return {
175
+ engineId: this.id,
176
+ kind: 'docker',
177
+ argv: [
178
+ 'docker',
179
+ 'run',
180
+ '-d',
181
+ '--name',
182
+ containerName,
183
+ '--gpus',
184
+ 'all',
185
+ '-p',
186
+ `127.0.0.1:${port}:8080`,
187
+ '--ulimit',
188
+ 'memlock=-1:-1',
189
+ '--shm-size',
190
+ '2g',
191
+ '--stop-timeout',
192
+ '120',
193
+ '-v',
194
+ 'strata-data:/data',
195
+ ...envFlags,
196
+ STRATA_DOCKER_IMAGE,
197
+ ...customArgs,
198
+ ],
199
+ env: {},
200
+ containerName,
201
+ ...(modelId !== undefined ? { modelId } : {}),
202
+ port,
203
+ healthUrl: `http://127.0.0.1:${port}/health`,
204
+ };
205
+ }
206
+ if (mode === 'binary') {
207
+ // The strata Terminal the install script places on the PATH: a
208
+ // non-interactive launch that downloads what it needs and serves.
209
+ const flags = flagsFromSpecs(STRATA_PARAMETERS, config);
210
+ return {
211
+ engineId: this.id,
212
+ kind: 'process',
213
+ argv: [
214
+ 'strata',
215
+ 'launch',
216
+ ...(model !== '' ? ['--model', model] : []),
217
+ '--yes',
218
+ '--no-tui',
219
+ '--port',
220
+ String(port),
221
+ ...flags,
222
+ ...customArgs,
223
+ ],
224
+ env: {},
225
+ ...(modelId !== undefined ? { modelId } : {}),
226
+ port,
227
+ healthUrl: `http://127.0.0.1:${port}/health`,
228
+ };
229
+ }
230
+ // The source build: run setup.sh with the model. It reuses the engine the
231
+ // Get step built (the acquisition build step), downloads the named model,
232
+ // and starts the engine. Running setup.sh (rather than a pre-written
233
+ // run-<model>.sh start script) is model-aware and self-contained, so the
234
+ // launch does not depend on the build step having written a script for this
235
+ // exact model. The deploy step resolves setup.sh to an absolute path in the
236
+ // checkout before spawning.
237
+ const family = typeof config.family === 'string' && config.family !== '' ? config.family : 'qwen';
238
+ const vision = typeof config.vision === 'string' && config.vision !== '' ? config.vision : 'on';
239
+ const context = typeof config.context === 'string' && config.context !== '' ? config.context : undefined;
240
+ // setup.sh's --vision takes yes|no|gpu|cpu; the surface's on|off maps onto
241
+ // it (gpu and cpu pass through).
242
+ const visionFlag = vision === 'on' ? 'yes' : vision === 'off' ? 'no' : vision;
243
+ return {
244
+ engineId: this.id,
245
+ kind: 'process',
246
+ argv: [
247
+ 'setup.sh',
248
+ '--yes',
249
+ ...(model !== '' ? ['--model', model] : []),
250
+ ...(family !== 'qwen' ? ['--family', family] : []),
251
+ '--vision',
252
+ visionFlag,
253
+ ...(context !== undefined ? ['--context', context] : []),
254
+ '--port',
255
+ String(port),
256
+ ...customArgs,
257
+ ],
258
+ env: {},
259
+ ...(modelId !== undefined ? { modelId } : {}),
260
+ port,
261
+ healthUrl: `http://127.0.0.1:${port}/health`,
262
+ };
263
+ }
264
+ async deploy(plan, platform, onProgress, options) {
265
+ if (plan.kind === 'docker') {
266
+ // The image pull is the docker build step: the card's button reads
267
+ // "Building..." for it.
268
+ onProgress?.('build');
269
+ const pull = await this.host.run(`docker pull ${STRATA_DOCKER_IMAGE}`);
270
+ if (!pull.ok) {
271
+ return { ok: false, detail: `docker pull failed: ${pull.detail ?? 'unknown'}` };
272
+ }
273
+ const handle = this.host.spawn(plan.argv ?? [], plan.env ?? {});
274
+ trackChild(this.children, this.planKey(plan), handle);
275
+ return { ok: true, handle };
276
+ }
277
+ if (plan.kind === 'process') {
278
+ const engineDir = platform?.engineDirectory;
279
+ // The binary plan runs the strata command (on the PATH after the install
280
+ // script), so its build step is the one-line install. The source-build
281
+ // plan runs setup.sh in the checkout, so its build step is the git sync
282
+ // plus the engine's own setup.sh (the acquisition build steps), and the
283
+ // launch resolves setup.sh to an absolute path in that checkout.
284
+ const isBinary = plan.argv?.[0] === 'strata';
285
+ let buildLogs = '';
286
+ if (isBinary) {
287
+ // The install script downloads the Terminal: the card's button reads
288
+ // "Downloading..." for it.
289
+ onProgress?.('download');
290
+ const install = await this.host.run(STRATA_BINARY_INSTALL);
291
+ if (!install.ok) {
292
+ return { ok: false, detail: `strata install failed: ${install.detail ?? 'unknown'}` };
293
+ }
294
+ }
295
+ else {
296
+ // The source build needs the checkout directory to resolve setup.sh;
297
+ // without it the launch cannot find the engine.
298
+ if (engineDir === undefined) {
299
+ return {
300
+ ok: false,
301
+ detail: 'the source build needs an engine directory (the Get step checkout) to run setup.sh',
302
+ };
303
+ }
304
+ if (options?.autoRebuild !== false) {
305
+ onProgress?.('download');
306
+ const sync = await syncAndRebuildEngine(this.host, {
307
+ engineId: this.id,
308
+ directory: engineDir,
309
+ ...(platform?.engineFork !== undefined ? { fork: platform.engineFork } : {}),
310
+ ...(options?.allowRunDeps !== undefined ? { allowRunDeps: options.allowRunDeps } : {}),
311
+ ...(platform?.buildCommands !== undefined
312
+ ? { buildCommands: platform.buildCommands }
313
+ : {}),
314
+ // Report the build phase the moment the build steps start, so the
315
+ // card's button reads "Building..." while the build is in flight.
316
+ onBuildStart: () => onProgress?.('build'),
317
+ }, () => { });
318
+ buildLogs = sync.logs;
319
+ if (!sync.ok) {
320
+ return { ok: false, detail: sync.detail ?? 'unknown', logs: buildLogs };
321
+ }
322
+ // The build output reads as a fresh Logs section: a "Building..."
323
+ // separator leads the build decision and output, the same line the
324
+ // card writes when the build step event lands.
325
+ if (sync.rebuilt && buildLogs !== '') {
326
+ buildLogs = `--- Building...\n${buildLogs}`;
327
+ }
328
+ }
329
+ }
330
+ const withLogs = (result) => buildLogs !== '' ? { ...result, logs: buildLogs } : result;
331
+ // The source-build plan names setup.sh relative to the checkout; resolve
332
+ // it to an absolute path so spawn finds it (spawn resolves a bare name
333
+ // against PATH, not the cwd). The binary plan's strata is on the PATH, so
334
+ // it spawns as-is.
335
+ const argv = plan.argv ?? [];
336
+ const spawnArgv = !isBinary && engineDir !== undefined
337
+ ? [join(engineDir, argv[0] ?? 'setup.sh'), ...argv.slice(1)]
338
+ : argv;
339
+ const handle = this.host.spawn(spawnArgv, plan.env ?? {}, engineDir);
340
+ trackChild(this.children, this.planKey(plan), handle);
341
+ return withLogs({ ok: true, handle });
342
+ }
343
+ // A compose plan is a no-op for Strata.
344
+ return { ok: true };
345
+ }
346
+ async healthCheck(plan, options) {
347
+ // The Strata Terminal downloads its engine and the model files before it
348
+ // can serve, so the health endpoint is not up for a long time (the model
349
+ // download can be tens of gigabytes). A plain poll times out and reads the
350
+ // start as broken, so this poll reads the tracked process's output and
351
+ // reports the start phase: a 200 is ready, a process that ended before
352
+ // readiness is failed, and a process still fetching its files after the
353
+ // attempt cap is downloading (in progress, not a failure) so the card
354
+ // re-polls instead of showing a broken launch.
355
+ const pollMs = options?.pollMs ?? 1000;
356
+ const maxAttempts = options?.maxAttempts ?? 120;
357
+ let last = { status: 0, ok: false };
358
+ let lastPreReady = null;
359
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
360
+ last = await this.host.fetch(plan.healthUrl);
361
+ if (last.status === 200) {
362
+ return { healthy: true, status: 200, state: 'ready' };
363
+ }
364
+ // A running tracked process reports its start phase from its output. A
365
+ // missing handle (no deploy tracked one, or the process already ended)
366
+ // falls through to the plain poll: the health URL stays down, so the
367
+ // loop times out and reads the start as failed.
368
+ const handle = this.children.get(this.planKey(plan));
369
+ if (handle !== undefined) {
370
+ lastPreReady = strataPreReadyState(handle.logs ?? '');
371
+ }
372
+ if (pollMs > 0 && attempt < maxAttempts) {
373
+ await delay(pollMs);
374
+ }
375
+ }
376
+ // The engine did not report ready within the attempt cap. If it was still
377
+ // downloading its files, read the start as in progress (downloading): the
378
+ // download is not a broken launch, and the card re-polls until the engine
379
+ // reports ready. If the download was done and the engine was starting,
380
+ // read the start as failed (a stuck start timed out).
381
+ if (lastPreReady === 'downloading') {
382
+ return {
383
+ healthy: false,
384
+ status: last.status,
385
+ state: 'downloading',
386
+ detail: 'the engine is still downloading its model files',
387
+ };
388
+ }
389
+ return {
390
+ healthy: false,
391
+ status: last.status,
392
+ state: 'failed',
393
+ detail: `health check did not return 200 within ${maxAttempts} attempts`,
394
+ };
395
+ }
396
+ async stop(plan) {
397
+ if (plan.kind === 'docker' && plan.containerName !== undefined) {
398
+ await this.host.run(`docker stop ${plan.containerName}`);
399
+ }
400
+ // The binary plan stops through the strata command; the source-build plan
401
+ // has no strata command, so it stops by signalling the tracked process.
402
+ if (plan.kind === 'process' && plan.argv?.[0] === 'strata') {
403
+ await this.host.run('strata launch --stop');
404
+ }
405
+ const handle = this.children.get(this.planKey(plan));
406
+ if (handle !== undefined) {
407
+ handle.kill();
408
+ this.children.delete(this.planKey(plan));
409
+ }
410
+ }
411
+ buildMode(config) {
412
+ const mode = config.buildMode;
413
+ if (mode === 'docker' || mode === 'local' || mode === 'binary') {
414
+ return mode;
415
+ }
416
+ // The binary install is the README's one-line path (the one docs/AI_SETUP.md
417
+ // walks an agent through), so a bare config renders the binary plan.
418
+ return 'binary';
419
+ }
420
+ port(config) {
421
+ return typeof config.port === 'number' ? config.port : 8080;
422
+ }
423
+ planKey(plan) {
424
+ return `${plan.engineId}:${plan.port}`;
425
+ }
426
+ }
427
+ //# sourceMappingURL=strataAdapter.js.map
@@ -14,15 +14,16 @@ export interface EngineInfo {
14
14
  supports: {
15
15
  docker: boolean;
16
16
  local: boolean;
17
+ binary?: boolean;
17
18
  };
18
19
  parameters: readonly ParameterSpec[];
19
20
  }
20
21
  /**
21
22
  * The engines the Web card offers, in the order the card lists them. NInfer,
22
- * Hyper-Qwen, Infernix, and llama.cpp carry parameter surfaces (llama.cpp's
23
- * is the specified set the plan names, verified when the binary lands);
24
- * Strata is the placeholder engine, its surface empty until the engine's
25
- * flag set lands.
23
+ * Hyper-Qwen, Infernix, llama.cpp, and Strata all carry parameter surfaces
24
+ * (llama.cpp's is the specified set the plan names, verified when the binary
25
+ * lands); Strata's surface is the `strata launch` flags and it supports all
26
+ * three build modes (docker, source build, and the binary install).
26
27
  */
27
28
  export declare const ENGINES: readonly EngineInfo[];
28
29
  //# sourceMappingURL=engines.d.ts.map
@@ -7,17 +7,17 @@
7
7
  * here, never a rewire of the card.
8
8
  * @module dsh-plugin-local-infer/providers/engines
9
9
  */
10
- import { HYPERQWEN_PARAMETERS } from "./hyperqwen.js";
11
- import { INFERNIX_PARAMETERS } from "./infernix.js";
12
- import { LLAMACPP_PARAMETERS } from "./llamacpp.js";
13
- import { NINFER_PARAMETERS } from "./ninfer.js";
14
- import { STRATA_PARAMETERS } from "./strata.js";
10
+ import { HYPERQWEN_PARAMETERS } from "./engines/hyperqwen.js";
11
+ import { INFERNIX_PARAMETERS } from "./engines/infernix.js";
12
+ import { LLAMACPP_PARAMETERS } from "./engines/llamacpp.js";
13
+ import { NINFER_PARAMETERS } from "./engines/ninfer.js";
14
+ import { STRATA_PARAMETERS } from "./engines/strata.js";
15
15
  /**
16
16
  * The engines the Web card offers, in the order the card lists them. NInfer,
17
- * Hyper-Qwen, Infernix, and llama.cpp carry parameter surfaces (llama.cpp's
18
- * is the specified set the plan names, verified when the binary lands);
19
- * Strata is the placeholder engine, its surface empty until the engine's
20
- * flag set lands.
17
+ * Hyper-Qwen, Infernix, llama.cpp, and Strata all carry parameter surfaces
18
+ * (llama.cpp's is the specified set the plan names, verified when the binary
19
+ * lands); Strata's surface is the `strata launch` flags and it supports all
20
+ * three build modes (docker, source build, and the binary install).
21
21
  */
22
22
  export const ENGINES = [
23
23
  {
@@ -47,7 +47,7 @@ export const ENGINES = [
47
47
  {
48
48
  id: 'strata',
49
49
  label: 'Strata',
50
- supports: { docker: true, local: false },
50
+ supports: { docker: true, local: true, binary: true },
51
51
  parameters: STRATA_PARAMETERS,
52
52
  },
53
53
  ];
@@ -11,7 +11,7 @@
11
11
  * a model, not against the hardware alone.
12
12
  * @module dsh-plugin-local-infer/providers/guidelines
13
13
  */
14
- import { infernixDraftModelPath } from "./infernix.js";
14
+ import { infernixDraftModelPath } from "./engines/infernix.js";
15
15
  /**
16
16
  * One pinned Host StateImage, in bytes. The NInfer measured figure for the
17
17
  * 27B artifact (195,897,344 B, the README's Host StateImage size), used as
@@ -18,8 +18,8 @@ export declare const inject: string[];
18
18
  * Phase 5 adapters. NInfer's surface is CLI flags (`toArgv`); Hyper-Qwen's is
19
19
  * `.env` keys (`toEnv`).
20
20
  */
21
- export { CTX_OPTIONS, HYPERQWEN_PARAMETERS, KV_OPTIONS, SPEC_OPTIONS as HYPERQWEN_SPEC_OPTIONS, } from './hyperqwen.ts';
22
- export { CHAT_TEMPLATE_OPTIONS, DEVICE_PROFILE_OPTIONS, KV_DTYPE_OPTIONS, NINFER_PARAMETERS, SPEC_OPTIONS as NINFER_SPEC_OPTIONS, } from './ninfer.ts';
21
+ export { CTX_OPTIONS, HYPERQWEN_PARAMETERS, KV_OPTIONS, SPEC_OPTIONS as HYPERQWEN_SPEC_OPTIONS, } from './engines/hyperqwen.ts';
22
+ export { CHAT_TEMPLATE_OPTIONS, DEVICE_PROFILE_OPTIONS, KV_DTYPE_OPTIONS, NINFER_PARAMETERS, SPEC_OPTIONS as NINFER_SPEC_OPTIONS, } from './engines/ninfer.ts';
23
23
  export { NINFER_VRAM_MODELS, ninferVramModel } from './vram.ts';
24
24
  export { BUILTIN_GUIDELINES, HOST_STATE_IMAGE_BYTES } from './guidelines.ts';
25
25
  export { ENGINES } from './engines.ts';
@@ -31,13 +31,18 @@ export { ENGINE_PLATFORM_DEFAULTS, enginePlatformDefaults } from './platform.ts'
31
31
  export type { EnginePlatformDefaults } from './platform.ts';
32
32
  export { BUILTIN_PRESETS, PRESET_SOURCES, extractJsonBlock } from './presets.ts';
33
33
  export { DEFAULT_MODELS, defaultModelFor, defaultModelsForEngine } from './model-defaults.ts';
34
- export { HyperQwenAdapter, LLAMACPP_DOCKER_IMAGE, NINFER_DOCKER_IMAGE, NINFER_MODEL_ARTIFACTS, NInferAdapter, STRATA_DOCKER_IMAGE, StrataAdapter, LlamaCppAdapter, customArgEnv, customArgFlags, envFromSpecs, flagsFromSpecs, nodeLifecycleHost, pollHealth, } from './adapters.ts';
35
- export type { NInferModelArtifact } from './adapters.ts';
36
- export { KV_CACHE_TYPE_OPTIONS, LLAMACPP_MODEL_ARTIFACTS, LLAMACPP_PARAMETERS, llamaCppModelKey, } from './llamacpp.ts';
37
- export type { LlamaCppModelArtifact } from './llamacpp.ts';
38
- export { DFLASH2_CUSTOM_VALUE, DFLASH2_GATE_MESSAGE, INFERNIX_DFLASH2_MODELS, INFERNIX_KV_DTYPE_OPTIONS, INFERNIX_MODEL_ARTIFACTS, INFERNIX_NGRAM_RESIDENCY_OPTIONS, INFERNIX_NGRAM_VOLUMES, INFERNIX_PARAMETERS, INFERNIX_REASONING_LOOP_OPTIONS, INFERNIX_SPEC_OPTIONS, SPEC_GATE_MESSAGE as INFERNIX_SPEC_GATE_MESSAGE, dflash2PresetOptions, infernixArtifactFor, infernixDraftModelPath, infernixGetDownloads, infernixModelKey, } from './infernix.ts';
39
- export type { InfernixDFlash2Model, InfernixModelArtifact, InfernixNgramVolume, } from './infernix.ts';
40
- export { STRATA_PARAMETERS } from './strata.ts';
34
+ export { customArgEnv, customArgFlags, envFromSpecs, flagsFromSpecs, nodeLifecycleHost, pollHealth, } from './adapters.ts';
35
+ export { HyperQwenAdapter } from './engines/hyperqwenAdapter.ts';
36
+ export { InfernixAdapter } from './engines/infernixAdapter.ts';
37
+ export { LLAMACPP_DOCKER_IMAGE, LlamaCppAdapter } from './engines/llamacppAdapter.ts';
38
+ export { NINFER_DOCKER_IMAGE, NINFER_MODEL_ARTIFACTS, NInferAdapter } from './engines/ninferAdapter.ts';
39
+ export { STRATA_DOCKER_IMAGE, StrataAdapter } from './engines/strataAdapter.ts';
40
+ export type { NInferModelArtifact } from './engines/ninferAdapter.ts';
41
+ export { KV_CACHE_TYPE_OPTIONS, LLAMACPP_MODEL_ARTIFACTS, LLAMACPP_PARAMETERS, llamaCppModelKey, } from './engines/llamacpp.ts';
42
+ export type { LlamaCppModelArtifact } from './engines/llamacpp.ts';
43
+ export { DFLASH2_CUSTOM_VALUE, DFLASH2_GATE_MESSAGE, INFERNIX_DFLASH2_MODELS, INFERNIX_KV_DTYPE_OPTIONS, INFERNIX_MODEL_ARTIFACTS, INFERNIX_NGRAM_RESIDENCY_OPTIONS, INFERNIX_NGRAM_VOLUMES, INFERNIX_PARAMETERS, INFERNIX_REASONING_LOOP_OPTIONS, INFERNIX_SPEC_OPTIONS, SPEC_GATE_MESSAGE as INFERNIX_SPEC_GATE_MESSAGE, dflash2PresetOptions, infernixArtifactFor, infernixDraftModelPath, infernixGetDownloads, infernixModelKey, } from './engines/infernix.ts';
44
+ export type { InfernixDFlash2Model, InfernixModelArtifact, InfernixNgramVolume, } from './engines/infernix.ts';
45
+ export { STRATA_PARAMETERS } from './engines/strata.ts';
41
46
  /**
42
47
  * Mount the providers. The NInfer measured VramModels register here so the
43
48
  * service's estimate prices a named model; the built-in hardware guidelines
@@ -10,7 +10,12 @@
10
10
  * that the defaults resolver resolves against.
11
11
  * @module dsh-plugin-local-infer/providers
12
12
  */
13
- import { createNodeLifecycleHost, HyperQwenAdapter, InfernixAdapter, LlamaCppAdapter, NInferAdapter, StrataAdapter, } from "./adapters.js";
13
+ import { createNodeLifecycleHost } from "./adapters.js";
14
+ import { HyperQwenAdapter } from "./engines/hyperqwenAdapter.js";
15
+ import { InfernixAdapter } from "./engines/infernixAdapter.js";
16
+ import { LlamaCppAdapter } from "./engines/llamacppAdapter.js";
17
+ import { NInferAdapter } from "./engines/ninferAdapter.js";
18
+ import { StrataAdapter } from "./engines/strataAdapter.js";
14
19
  import { createDshDebugLogger } from "./debug.js";
15
20
  import { ENGINE_DEP_PLANS } from "./deps.js";
16
21
  import { BUILTIN_GUIDELINES } from "./guidelines.js";
@@ -23,8 +28,8 @@ export const inject = ['localInfer'];
23
28
  * Phase 5 adapters. NInfer's surface is CLI flags (`toArgv`); Hyper-Qwen's is
24
29
  * `.env` keys (`toEnv`).
25
30
  */
26
- export { CTX_OPTIONS, HYPERQWEN_PARAMETERS, KV_OPTIONS, SPEC_OPTIONS as HYPERQWEN_SPEC_OPTIONS, } from "./hyperqwen.js";
27
- export { CHAT_TEMPLATE_OPTIONS, DEVICE_PROFILE_OPTIONS, KV_DTYPE_OPTIONS, NINFER_PARAMETERS, SPEC_OPTIONS as NINFER_SPEC_OPTIONS, } from "./ninfer.js";
31
+ export { CTX_OPTIONS, HYPERQWEN_PARAMETERS, KV_OPTIONS, SPEC_OPTIONS as HYPERQWEN_SPEC_OPTIONS, } from "./engines/hyperqwen.js";
32
+ export { CHAT_TEMPLATE_OPTIONS, DEVICE_PROFILE_OPTIONS, KV_DTYPE_OPTIONS, NINFER_PARAMETERS, SPEC_OPTIONS as NINFER_SPEC_OPTIONS, } from "./engines/ninfer.js";
28
33
  export { NINFER_VRAM_MODELS, ninferVramModel } from "./vram.js";
29
34
  export { BUILTIN_GUIDELINES, HOST_STATE_IMAGE_BYTES } from "./guidelines.js";
30
35
  export { ENGINES } from "./engines.js";
@@ -33,10 +38,15 @@ export { ENGINE_DEP_PLANS, SYSTEM_DEPS } from "./deps.js";
33
38
  export { ENGINE_PLATFORM_DEFAULTS, enginePlatformDefaults } from "./platform.js";
34
39
  export { BUILTIN_PRESETS, PRESET_SOURCES, extractJsonBlock } from "./presets.js";
35
40
  export { DEFAULT_MODELS, defaultModelFor, defaultModelsForEngine } from "./model-defaults.js";
36
- export { HyperQwenAdapter, LLAMACPP_DOCKER_IMAGE, NINFER_DOCKER_IMAGE, NINFER_MODEL_ARTIFACTS, NInferAdapter, STRATA_DOCKER_IMAGE, StrataAdapter, LlamaCppAdapter, customArgEnv, customArgFlags, envFromSpecs, flagsFromSpecs, nodeLifecycleHost, pollHealth, } from "./adapters.js";
37
- export { KV_CACHE_TYPE_OPTIONS, LLAMACPP_MODEL_ARTIFACTS, LLAMACPP_PARAMETERS, llamaCppModelKey, } from "./llamacpp.js";
38
- export { DFLASH2_CUSTOM_VALUE, DFLASH2_GATE_MESSAGE, INFERNIX_DFLASH2_MODELS, INFERNIX_KV_DTYPE_OPTIONS, INFERNIX_MODEL_ARTIFACTS, INFERNIX_NGRAM_RESIDENCY_OPTIONS, INFERNIX_NGRAM_VOLUMES, INFERNIX_PARAMETERS, INFERNIX_REASONING_LOOP_OPTIONS, INFERNIX_SPEC_OPTIONS, SPEC_GATE_MESSAGE as INFERNIX_SPEC_GATE_MESSAGE, dflash2PresetOptions, infernixArtifactFor, infernixDraftModelPath, infernixGetDownloads, infernixModelKey, } from "./infernix.js";
39
- export { STRATA_PARAMETERS } from "./strata.js";
41
+ export { customArgEnv, customArgFlags, envFromSpecs, flagsFromSpecs, nodeLifecycleHost, pollHealth, } from "./adapters.js";
42
+ export { HyperQwenAdapter } from "./engines/hyperqwenAdapter.js";
43
+ export { InfernixAdapter } from "./engines/infernixAdapter.js";
44
+ export { LLAMACPP_DOCKER_IMAGE, LlamaCppAdapter } from "./engines/llamacppAdapter.js";
45
+ export { NINFER_DOCKER_IMAGE, NINFER_MODEL_ARTIFACTS, NInferAdapter } from "./engines/ninferAdapter.js";
46
+ export { STRATA_DOCKER_IMAGE, StrataAdapter } from "./engines/strataAdapter.js";
47
+ export { KV_CACHE_TYPE_OPTIONS, LLAMACPP_MODEL_ARTIFACTS, LLAMACPP_PARAMETERS, llamaCppModelKey, } from "./engines/llamacpp.js";
48
+ export { DFLASH2_CUSTOM_VALUE, DFLASH2_GATE_MESSAGE, INFERNIX_DFLASH2_MODELS, INFERNIX_KV_DTYPE_OPTIONS, INFERNIX_MODEL_ARTIFACTS, INFERNIX_NGRAM_RESIDENCY_OPTIONS, INFERNIX_NGRAM_VOLUMES, INFERNIX_PARAMETERS, INFERNIX_REASONING_LOOP_OPTIONS, INFERNIX_SPEC_OPTIONS, SPEC_GATE_MESSAGE as INFERNIX_SPEC_GATE_MESSAGE, dflash2PresetOptions, infernixArtifactFor, infernixDraftModelPath, infernixGetDownloads, infernixModelKey, } from "./engines/infernix.js";
49
+ export { STRATA_PARAMETERS } from "./engines/strata.js";
40
50
  /**
41
51
  * Mount the providers. The NInfer measured VramModels register here so the
42
52
  * service's estimate prices a named model; the built-in hardware guidelines
@@ -4,8 +4,9 @@
4
4
  * Hyper-Qwen W4A16 27B and AWQ 35B-A3B artifacts, the llama.cpp GGUF 27B and
5
5
  * 35B-A3B artifacts, and the Infernix 27B NVIDIA NVFP4 and the Qwen3.8-
6
6
  * Flash-Next Dense8 conversion (whose whole-repo download carries the FP8
7
- * n-gram volume the `--ngram-volume` flag reads). Strata does not focus on
8
- * the dense Qwen models, so it carries no entry.
7
+ * n-gram volume the `--ngram-volume` flag reads). Strata carries the two
8
+ * models the README's Models section links (Gyro and DeepSeek V4.1-Flash),
9
+ * each named by the model the engine accepts and the repo it downloads.
9
10
  *
10
11
  * The list is the single source the card's Model dropdown, the model
11
12
  * presence check, and the hf CLI download route read. Adding a model is a
@@ -22,8 +23,8 @@ import type { DefaultModel } from 'dsh-plugin-local-infer/core';
22
23
  export declare const DEFAULT_MODELS: readonly DefaultModel[];
23
24
  /**
24
25
  * defaultModelsForEngine reads the default list for one engine id, in list
25
- * order. An engine with no entry (Strata) reads as an empty list, which the
26
- * card renders as no Model row.
26
+ * order. An engine with no entry reads as an empty list, which the card
27
+ * renders as no Model row.
27
28
  */
28
29
  export declare function defaultModelsForEngine(engineId: string): readonly DefaultModel[];
29
30
  /**
@@ -4,8 +4,9 @@
4
4
  * Hyper-Qwen W4A16 27B and AWQ 35B-A3B artifacts, the llama.cpp GGUF 27B and
5
5
  * 35B-A3B artifacts, and the Infernix 27B NVIDIA NVFP4 and the Qwen3.8-
6
6
  * Flash-Next Dense8 conversion (whose whole-repo download carries the FP8
7
- * n-gram volume the `--ngram-volume` flag reads). Strata does not focus on
8
- * the dense Qwen models, so it carries no entry.
7
+ * n-gram volume the `--ngram-volume` flag reads). Strata carries the two
8
+ * models the README's Models section links (Gyro and DeepSeek V4.1-Flash),
9
+ * each named by the model the engine accepts and the repo it downloads.
9
10
  *
10
11
  * The list is the single source the card's Model dropdown, the model
11
12
  * presence check, and the hf CLI download route read. Adding a model is a
@@ -88,11 +89,28 @@ export const DEFAULT_MODELS = [
88
89
  url: 'https://huggingface.co/neroued/Qwen3.6-35B-A3B-NInfer',
89
90
  file: 'qwen3_6_35b_a3b.ninfer',
90
91
  },
92
+ // The two models the Strata README's Models section links: Gyro (the
93
+ // smallest way to run Qwen3.8-Flash-Next) and DeepSeek V4.1-Flash (the
94
+ // bigger model that runs on one 12 GB card). No single file: the engine
95
+ // downloads the model itself (into the container volume or its data dir),
96
+ // so the entry names the repo only.
97
+ {
98
+ engineId: 'strata',
99
+ modelId: 'gyro-s',
100
+ label: 'Strata Gyro-S (Qwen3.8-Flash-Next)',
101
+ url: 'https://huggingface.co/agentionai/Qwen3.8-Flash-Next-Gyro-GGUF',
102
+ },
103
+ {
104
+ engineId: 'strata',
105
+ modelId: 'ist-das-2.00bpw',
106
+ label: 'Strata DeepSeek V4.1-Flash (2.00 bpw)',
107
+ url: 'https://huggingface.co/ISTA-DASLab/DeepSeek-V4.1-Flash-1017-GSQ-RCO-GGUF',
108
+ },
91
109
  ];
92
110
  /**
93
111
  * defaultModelsForEngine reads the default list for one engine id, in list
94
- * order. An engine with no entry (Strata) reads as an empty list, which the
95
- * card renders as no Model row.
112
+ * order. An engine with no entry reads as an empty list, which the card
113
+ * renders as no Model row.
96
114
  */
97
115
  export function defaultModelsForEngine(engineId) {
98
116
  return DEFAULT_MODELS.filter((entry) => entry.engineId === engineId);