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.
- package/README.md +10 -2
- package/docs/zh/README.md +10 -2
- package/dsh/client.js +669 -191
- package/package.json +6 -1
- package/packages/core/lib/adapter.d.ts +44 -2
- package/packages/core/lib/deps.d.ts +4 -3
- package/packages/core/lib/index.d.ts +1 -1
- package/packages/core/lib/registrar.d.ts +5 -5
- package/packages/core/lib/registrar.js +3 -3
- package/packages/core/package.json +1 -1
- package/packages/providers/lib/acquire.d.ts +5 -3
- package/packages/providers/lib/acquire.js +30 -9
- package/packages/providers/lib/adapters.d.ts +19 -209
- package/packages/providers/lib/adapters.js +13 -886
- package/packages/providers/lib/deps.d.ts +24 -2
- package/packages/providers/lib/deps.js +23 -4
- package/packages/providers/lib/engines/hyperqwenAdapter.d.ts +38 -0
- package/packages/providers/lib/engines/hyperqwenAdapter.js +105 -0
- package/packages/providers/lib/{infernix.js → engines/infernix.js} +1 -1
- package/packages/providers/lib/engines/infernixAdapter.d.ts +53 -0
- package/packages/providers/lib/engines/infernixAdapter.js +202 -0
- package/packages/providers/lib/engines/llamacppAdapter.d.ts +51 -0
- package/packages/providers/lib/engines/llamacppAdapter.js +232 -0
- package/packages/providers/lib/{ninfer.js → engines/ninfer.js} +1 -1
- package/packages/providers/lib/engines/ninferAdapter.d.ts +82 -0
- package/packages/providers/lib/engines/ninferAdapter.js +324 -0
- package/packages/providers/lib/engines/strata.d.ts +53 -0
- package/packages/providers/lib/engines/strata.js +197 -0
- package/packages/providers/lib/engines/strataAdapter.d.ts +98 -0
- package/packages/providers/lib/engines/strataAdapter.js +427 -0
- package/packages/providers/lib/engines.d.ts +5 -4
- package/packages/providers/lib/engines.js +10 -10
- package/packages/providers/lib/guidelines.js +1 -1
- package/packages/providers/lib/index.d.ts +14 -9
- package/packages/providers/lib/index.js +17 -7
- package/packages/providers/lib/model-defaults.d.ts +5 -4
- package/packages/providers/lib/model-defaults.js +22 -4
- package/packages/providers/lib/platform.js +7 -1
- package/packages/providers/package.json +1 -1
- package/packages/service/lib/index.d.ts +13 -4
- package/packages/service/lib/index.js +79 -5
- package/packages/service/lib/types.d.ts +5 -4
- package/packages/service/package.json +1 -1
- package/packages/tool/package.json +1 -1
- package/packages/ui/lib/config.d.ts +79 -0
- package/packages/ui/lib/config.js +235 -0
- package/packages/ui/lib/deps.d.ts +3 -3
- package/packages/ui/lib/ops/engine.d.ts +108 -0
- package/packages/ui/lib/ops/engine.js +493 -0
- package/packages/ui/lib/ops/hf.d.ts +27 -0
- package/packages/ui/lib/ops/hf.js +60 -0
- package/packages/ui/lib/ops/logs.d.ts +23 -0
- package/packages/ui/lib/ops/logs.js +58 -0
- package/packages/ui/lib/ops/model.d.ts +79 -0
- package/packages/ui/lib/ops/model.js +130 -0
- package/packages/ui/lib/request.d.ts +106 -0
- package/packages/ui/lib/request.js +80 -0
- package/packages/ui/lib/route.d.ts +88 -420
- package/packages/ui/lib/route.js +111 -1843
- package/packages/ui/lib/routes/chat-template.d.ts +14 -0
- package/packages/ui/lib/routes/chat-template.js +37 -0
- package/packages/ui/lib/routes/deps.d.ts +16 -0
- package/packages/ui/lib/routes/deps.js +27 -0
- package/packages/ui/lib/routes/download.d.ts +16 -0
- package/packages/ui/lib/routes/download.js +48 -0
- package/packages/ui/lib/routes/get.d.ts +18 -0
- package/packages/ui/lib/routes/get.js +57 -0
- package/packages/ui/lib/routes/install.d.ts +16 -0
- package/packages/ui/lib/routes/install.js +36 -0
- package/packages/ui/lib/routes/launch.d.ts +18 -0
- package/packages/ui/lib/routes/launch.js +50 -0
- package/packages/ui/lib/routes/provider-delete.d.ts +15 -0
- package/packages/ui/lib/routes/provider-delete.js +37 -0
- package/packages/ui/lib/routes/register.d.ts +16 -0
- package/packages/ui/lib/routes/register.js +28 -0
- package/packages/ui/lib/routes/save.d.ts +18 -0
- package/packages/ui/lib/routes/save.js +139 -0
- package/packages/ui/lib/routes/stop.d.ts +16 -0
- package/packages/ui/lib/routes/stop.js +29 -0
- package/packages/ui/lib/routes/summary.d.ts +15 -0
- package/packages/ui/lib/routes/summary.js +26 -0
- package/packages/ui/lib/routes/unregister.d.ts +14 -0
- package/packages/ui/lib/routes/unregister.js +26 -0
- package/packages/ui/lib/viewmodels/deps.d.ts +31 -0
- package/packages/ui/lib/viewmodels/deps.js +89 -0
- package/packages/ui/lib/viewmodels/depsHelpers.d.ts +28 -0
- package/packages/ui/lib/viewmodels/depsHelpers.js +33 -0
- package/packages/ui/lib/viewmodels/summary.d.ts +140 -0
- package/packages/ui/lib/viewmodels/summary.js +225 -0
- package/packages/ui/lib/viewmodels/summaryHelpers.d.ts +30 -0
- package/packages/ui/lib/viewmodels/summaryHelpers.js +42 -0
- package/packages/ui/package.json +1 -1
- package/packages/providers/lib/strata.d.ts +0 -15
- package/packages/providers/lib/strata.js +0 -14
- /package/packages/providers/lib/{hyperqwen.d.ts → engines/hyperqwen.d.ts} +0 -0
- /package/packages/providers/lib/{hyperqwen.js → engines/hyperqwen.js} +0 -0
- /package/packages/providers/lib/{infernix.d.ts → engines/infernix.d.ts} +0 -0
- /package/packages/providers/lib/{llamacpp.d.ts → engines/llamacpp.d.ts} +0 -0
- /package/packages/providers/lib/{llamacpp.js → engines/llamacpp.js} +0 -0
- /package/packages/providers/lib/{ninfer.d.ts → engines/ninfer.d.ts} +0 -0
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* defaults are a data edit here, never a card rewire.
|
|
6
6
|
* @module dsh-plugin-local-infer/providers/platform
|
|
7
7
|
*/
|
|
8
|
-
import { LLAMACPP_DOCKER_IMAGE
|
|
8
|
+
import { LLAMACPP_DOCKER_IMAGE } from "./engines/llamacppAdapter.js";
|
|
9
|
+
import { NINFER_DOCKER_IMAGE } from "./engines/ninferAdapter.js";
|
|
10
|
+
import { STRATA_DOCKER_IMAGE } from "./engines/strataAdapter.js";
|
|
9
11
|
import { engineBuildSteps } from "./acquire.js";
|
|
10
12
|
// The local build command is the engine's first acquisition build step (the
|
|
11
13
|
// cmake build the deploy step runs in the checkout), read through
|
|
@@ -15,6 +17,7 @@ import { engineBuildSteps } from "./acquire.js";
|
|
|
15
17
|
const NINFER_BUILD_COMMAND = engineBuildSteps('ninfer')[0];
|
|
16
18
|
const LLAMACPP_BUILD_COMMAND = engineBuildSteps('llamacpp')[0];
|
|
17
19
|
const INFERNIX_BUILD_COMMAND = engineBuildSteps('infernix')[0];
|
|
20
|
+
const STRATA_BUILD_COMMAND = engineBuildSteps('strata')[0];
|
|
18
21
|
/**
|
|
19
22
|
* The per-engine platform defaults, keyed by engine id. The build command is
|
|
20
23
|
* the engine's first acquisition build step; the docker tag is the upstream
|
|
@@ -45,6 +48,9 @@ export const ENGINE_PLATFORM_DEFAULTS = {
|
|
|
45
48
|
: {}),
|
|
46
49
|
},
|
|
47
50
|
strata: {
|
|
51
|
+
...(STRATA_BUILD_COMMAND !== undefined
|
|
52
|
+
? { defaultBuildCommand: STRATA_BUILD_COMMAND }
|
|
53
|
+
: {}),
|
|
48
54
|
defaultDockerTag: STRATA_DOCKER_IMAGE,
|
|
49
55
|
},
|
|
50
56
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-local-infer-providers",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Concrete providers for the dsh local-inference plugin: the ninfer, hyperqwen, llamacpp, and strata engine adapters, the NInfer VramModel, the built-in presets, the built-in guidelines, and the per-OS SystemDep install command data. Each registers itself with ctx.localInfer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* @module dsh-plugin-local-infer/service
|
|
19
19
|
*/
|
|
20
20
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
21
|
-
import type { Config, DefaultModel, ModelDirectoryEntry, EngineAdapter, EngineDepPlan, EnginePlatformConfig, GpuUsageProbe, Guideline, GuidelineResult, HardwareProfile, HardwareProbes, InstallResult, LaunchPlan, ModelRow, Preset, PresetSource, ProcessLine, ProviderLaunchRecord, ProviderRef, ProviderStatus, ReLinkOutcome, RegisterResult, ReserveResolution, ResolvedDefaults, SystemDep, VramModel } from 'dsh-plugin-local-infer/core';
|
|
21
|
+
import type { BuildMode, Config, DefaultModel, ModelDirectoryEntry, EngineAdapter, EngineDepPlan, EnginePlatformConfig, GpuUsageProbe, Guideline, GuidelineResult, HardwareProfile, HardwareProbes, InstallResult, LaunchPlan, ModelRow, Preset, PresetSource, ProcessLine, ProviderLaunchRecord, ProviderRef, ProviderStatus, ReLinkOutcome, RegisterResult, ReserveResolution, ResolvedDefaults, SystemDep, VramModel } from 'dsh-plugin-local-infer/core';
|
|
22
22
|
import type { DetectOsReserveOptions, DownloadModelOptions, DownloadResult, EngineDeathEvent, EstimateResult, FetchPresetOptions, FetchPresetResult, HfCliOptions, LaunchOptions, LaunchResult, LocalInferService, ProviderStatusOptions, RegisterModelOptions } from './types.ts';
|
|
23
23
|
export type { DetectOsReserveOptions, DownloadModelOptions, DownloadResult, EngineDeathEvent, EstimateResult, FetchPresetOptions, FetchPresetResult, HfCliOptions, LaunchOptions, LaunchResult, LocalInferService, ProviderStatusOptions, RegisterModelOptions, SettingsDescribeResult, SettingsForms, SettingsNamespaceDescriptor, } from './types.ts';
|
|
24
24
|
declare module '@deepseek-ai/cordis' {
|
|
@@ -53,6 +53,8 @@ export declare class LocalInfer extends Service implements LocalInferService {
|
|
|
53
53
|
private readonly launchedPorts;
|
|
54
54
|
private readonly engineWatchers;
|
|
55
55
|
private readonly handledPids;
|
|
56
|
+
private readonly launchingEngines;
|
|
57
|
+
private readonly stoppingEngines;
|
|
56
58
|
private deathObserver;
|
|
57
59
|
constructor(ctx: Context);
|
|
58
60
|
registerEngine(adapter: EngineAdapter): () => void;
|
|
@@ -168,19 +170,19 @@ export declare class LocalInfer extends Service implements LocalInferService {
|
|
|
168
170
|
* registered plan for the engine id is the fallback, and an engine with
|
|
169
171
|
* neither plans an empty list (nothing to install, the gate passes).
|
|
170
172
|
*/
|
|
171
|
-
deps(engineId: string, buildMode:
|
|
173
|
+
deps(engineId: string, buildMode: BuildMode, hw: HardwareProfile): Promise<readonly SystemDep[]>;
|
|
172
174
|
/**
|
|
173
175
|
* installString renders the copy/paste block for the resolved dep plan,
|
|
174
176
|
* keyed to the detected package manager, with the by-hand note for a dep
|
|
175
177
|
* the manager has no commands for.
|
|
176
178
|
*/
|
|
177
|
-
installString(engineId: string, buildMode:
|
|
179
|
+
installString(engineId: string, buildMode: BuildMode, hw: HardwareProfile): Promise<string>;
|
|
178
180
|
/**
|
|
179
181
|
* runInstall is the Play/Run path: it runs the resolved dep plan's commands
|
|
180
182
|
* through the shell runner and re-probes each dep. The runner captures the
|
|
181
183
|
* per-command exit, so a failure names the command that broke.
|
|
182
184
|
*/
|
|
183
|
-
runInstall(engineId: string, buildMode:
|
|
185
|
+
runInstall(engineId: string, buildMode: BuildMode, hw: HardwareProfile): Promise<InstallResult>;
|
|
184
186
|
/**
|
|
185
187
|
* launch is the lifecycle entry point: it resolves the adapter that rendered
|
|
186
188
|
* the plan (by the plan's engineId), runs the adapter's deploy step (build
|
|
@@ -238,6 +240,13 @@ export declare class LocalInfer extends Service implements LocalInferService {
|
|
|
238
240
|
* record dead.
|
|
239
241
|
*/
|
|
240
242
|
private watchAdoptedEngine;
|
|
243
|
+
/**
|
|
244
|
+
* engineKey is the in-flight state machine's key for one engine: the engine
|
|
245
|
+
* id plus its port when the plan names one, so two configs of one engine on
|
|
246
|
+
* different ports are distinct engines. A plan with no port keys on the
|
|
247
|
+
* engine id alone.
|
|
248
|
+
*/
|
|
249
|
+
private engineKey;
|
|
241
250
|
/**
|
|
242
251
|
* stopEngineWatcher cancels one port's adopted-process poll, if it has one.
|
|
243
252
|
*/
|
|
@@ -94,6 +94,20 @@ export class LocalInfer extends Service {
|
|
|
94
94
|
// handle. A summary re-link that adopts this DSH's own launch record must
|
|
95
95
|
// not stack a poll watcher on top of the handle's close event.
|
|
96
96
|
handledPids = new Set();
|
|
97
|
+
// The engine keys (engineId:port) whose launch this service is in flight
|
|
98
|
+
// for: the deploy has started and the health poll has not settled. While a
|
|
99
|
+
// key is here the provider-status detector keeps the provider's linked read
|
|
100
|
+
// (running, not unsure), so an engine that sizes its own model (Strata, no
|
|
101
|
+
// model tie) or a launch mid-poll never flashes the "Unable to link" banner.
|
|
102
|
+
// The key clears when the launch settles (a healthy poll, a failed deploy).
|
|
103
|
+
launchingEngines = new Set();
|
|
104
|
+
// The engine keys (engineId:port) whose stop this service is in flight for:
|
|
105
|
+
// the stop step signalled the engine and the process has not died yet. While
|
|
106
|
+
// a key is here the detector keeps the linked read, so a stop never reads as
|
|
107
|
+
// an unlinked provider for the moment the engine takes to shut down. The key
|
|
108
|
+
// clears when the process dies (the death observer) or the detector sees the
|
|
109
|
+
// process down (a stop of an engine it cannot watch).
|
|
110
|
+
stoppingEngines = new Set();
|
|
97
111
|
// The route-registered hook for engine process deaths (a crash, a pkill, or
|
|
98
112
|
// a poll-detected death of an adopted process). The observer persists the
|
|
99
113
|
// provider entry's cleared state; a headless profile registers none and the
|
|
@@ -352,7 +366,29 @@ export class LocalInfer extends Service {
|
|
|
352
366
|
const port = signatures[index]?.port;
|
|
353
367
|
return port !== undefined ? { ...provider, port } : provider;
|
|
354
368
|
});
|
|
355
|
-
|
|
369
|
+
const statuses = computeProviderStatuses({ providers: priced, running, gpuBusy: isGpuBusy(gpuUsage) });
|
|
370
|
+
// The in-flight launch and stop state machine: a provider the launch or
|
|
371
|
+
// stop step is in flight for keeps its linked read (running, not unsure)
|
|
372
|
+
// while the engine process is still up, so neither a stop (the record is
|
|
373
|
+
// cleared but the process is still shutting down) nor a launch (an engine
|
|
374
|
+
// that sizes its own model carries no model tie) flashes the "Unable to
|
|
375
|
+
// link" banner. A stopped provider whose process is down reads as stopped,
|
|
376
|
+
// and the stopping flag clears with it, so a stop of an engine the service
|
|
377
|
+
// cannot watch (no death event) never leaves a stale flag behind.
|
|
378
|
+
for (const provider of priced) {
|
|
379
|
+
const key = this.engineKey(provider.engine, provider.port);
|
|
380
|
+
const launching = this.launchingEngines.has(key);
|
|
381
|
+
const stopping = this.stoppingEngines.has(key);
|
|
382
|
+
if (!launching && !stopping)
|
|
383
|
+
continue;
|
|
384
|
+
if (statuses[provider.id] === 'unsure') {
|
|
385
|
+
statuses[provider.id] = 'running';
|
|
386
|
+
}
|
|
387
|
+
else if (stopping) {
|
|
388
|
+
this.stoppingEngines.delete(key);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return statuses;
|
|
356
392
|
}
|
|
357
393
|
/**
|
|
358
394
|
* unsureBannerKillCommands returns the bash kill commands for the engine
|
|
@@ -472,8 +508,16 @@ export class LocalInfer extends Service {
|
|
|
472
508
|
...(options?.autoRebuild !== undefined ? { autoRebuild: options.autoRebuild } : {}),
|
|
473
509
|
}
|
|
474
510
|
: undefined;
|
|
511
|
+
// The in-flight launch flag: the provider-status detector keeps this
|
|
512
|
+
// provider's linked read while the deploy and health poll run, so an engine
|
|
513
|
+
// that sizes its own model (no model tie) never flashes "Unable to link".
|
|
514
|
+
const launchKey = this.engineKey(plan.engineId, plan.port);
|
|
515
|
+
this.launchingEngines.add(launchKey);
|
|
475
516
|
const deployResult = await adapter.deploy(plan, platform, onProgress, deployOptions);
|
|
476
517
|
if (!deployResult.ok) {
|
|
518
|
+
// A failed deploy settles the launch: clear the in-flight flag so the
|
|
519
|
+
// provider reads from the process state (stopped), not a stuck launch.
|
|
520
|
+
this.launchingEngines.delete(launchKey);
|
|
477
521
|
// exactOptionalPropertyTypes: only write the detail key when it is present.
|
|
478
522
|
const result = { plan, healthy: false };
|
|
479
523
|
if (deployResult.detail !== undefined) {
|
|
@@ -517,10 +561,20 @@ export class LocalInfer extends Service {
|
|
|
517
561
|
// "Launching..." while it waits for the engine to report ready.
|
|
518
562
|
onProgress?.('launch');
|
|
519
563
|
const health = await adapter.healthCheck(plan, options);
|
|
564
|
+
// The launch has settled (a healthy poll, a failed one, or a download that
|
|
565
|
+
// the card re-polls): clear the in-flight flag so the provider reads from
|
|
566
|
+
// the model tie (a healthy launch) or the process state (a failed one).
|
|
567
|
+
this.launchingEngines.delete(launchKey);
|
|
520
568
|
const launchResult = { plan, healthy: health.healthy };
|
|
521
569
|
if (health.detail !== undefined) {
|
|
522
570
|
launchResult.detail = health.detail;
|
|
523
571
|
}
|
|
572
|
+
// The start phase the poll observed (downloading, starting, ready, failed)
|
|
573
|
+
// rides the result so the card reads a long model download as in progress
|
|
574
|
+
// instead of a broken launch.
|
|
575
|
+
if (health.state !== undefined) {
|
|
576
|
+
launchResult.state = health.state;
|
|
577
|
+
}
|
|
524
578
|
// The launch record the route persists for the provider config entry: the
|
|
525
579
|
// engine's pid and the DSH process pid that launched it. The parent pid is
|
|
526
580
|
// only written when the host process identifies as DSH, so a non-DSH host
|
|
@@ -671,6 +725,15 @@ export class LocalInfer extends Service {
|
|
|
671
725
|
timer.unref();
|
|
672
726
|
this.engineWatchers.set(port, { pid: record.pid, timer });
|
|
673
727
|
}
|
|
728
|
+
/**
|
|
729
|
+
* engineKey is the in-flight state machine's key for one engine: the engine
|
|
730
|
+
* id plus its port when the plan names one, so two configs of one engine on
|
|
731
|
+
* different ports are distinct engines. A plan with no port keys on the
|
|
732
|
+
* engine id alone.
|
|
733
|
+
*/
|
|
734
|
+
engineKey(engineId, port) {
|
|
735
|
+
return port !== undefined ? `${engineId}:${port}` : engineId;
|
|
736
|
+
}
|
|
674
737
|
/**
|
|
675
738
|
* stopEngineWatcher cancels one port's adopted-process poll, if it has one.
|
|
676
739
|
*/
|
|
@@ -693,6 +756,10 @@ export class LocalInfer extends Service {
|
|
|
693
756
|
*/
|
|
694
757
|
observeEngineDeath(event) {
|
|
695
758
|
if (event.port !== undefined) {
|
|
759
|
+
// The process is down: the in-flight stop (if one was in flight) has
|
|
760
|
+
// settled, so drop the flag and the deferred port-to-model tie the stop
|
|
761
|
+
// step kept for the provider's linked read.
|
|
762
|
+
this.stoppingEngines.delete(this.engineKey(event.engineId, event.port));
|
|
696
763
|
const tracked = this.launchedModels.get(event.port);
|
|
697
764
|
if (tracked !== undefined && tracked.engineId === event.engineId) {
|
|
698
765
|
this.launchedModels.delete(event.port);
|
|
@@ -722,12 +789,19 @@ export class LocalInfer extends Service {
|
|
|
722
789
|
if (adapter === undefined) {
|
|
723
790
|
throw new LocalInferError(`no engine adapter is registered for "${plan.engineId}"`, 'LOCAL_INFER_INVALID_ENGINE');
|
|
724
791
|
}
|
|
792
|
+
// The in-flight stop flag: the provider-status detector keeps this
|
|
793
|
+
// provider's linked read while the engine shuts down, so a stop never
|
|
794
|
+
// flashes the "Unable to link" banner for the moment the process takes to
|
|
795
|
+
// die. The flag clears when the process dies (the death observer) or the
|
|
796
|
+
// detector sees the process down (a stop of an engine it cannot watch).
|
|
797
|
+
this.stoppingEngines.add(this.engineKey(plan.engineId, plan.port));
|
|
725
798
|
await adapter.stop(plan);
|
|
726
|
-
// Clear the port
|
|
727
|
-
//
|
|
728
|
-
//
|
|
799
|
+
// Clear the live-port ledger and the plugin-launched flag, and cancel any
|
|
800
|
+
// adopted-process watcher standing on the port. The port-to-model tie is
|
|
801
|
+
// deferred to the death observer, so the provider keeps its linked read
|
|
802
|
+
// (running, not unsure) for the moment the engine is still shutting down;
|
|
803
|
+
// the observer drops it the instant the process is down.
|
|
729
804
|
if (plan.port !== undefined) {
|
|
730
|
-
this.launchedModels.delete(plan.port);
|
|
731
805
|
this.stopEngineWatcher(plan.port);
|
|
732
806
|
const ports = this.launchedPorts.get(plan.engineId);
|
|
733
807
|
if (ports !== undefined) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommandOutcome, Config, DefaultModel, ModelDirectoryEntry, EngineAdapter, EngineDepPlan, EnginePlatformConfig, Guideline, GuidelineResult, GpuUsageProbe, HardwareProfile, InstallResult, LaunchPlan, LaunchProgress, ModelRow, Preset, PresetSource, ProcessLine, ProviderLaunchRecord, ProviderRef, ProviderStatus, ReLinkOutcome, RegisterResult, ReserveResolution, ReserveSource, ResolvedDefaults, SettingsOp, SystemDep, VramBreakdown, VramModel, VramVerdict } from 'dsh-plugin-local-infer/core';
|
|
1
|
+
import type { BuildMode, CommandOutcome, Config, DefaultModel, ModelDirectoryEntry, EngineAdapter, EngineDepPlan, EngineLaunchState, EnginePlatformConfig, Guideline, GuidelineResult, GpuUsageProbe, HardwareProfile, InstallResult, LaunchPlan, LaunchProgress, ModelRow, Preset, PresetSource, ProcessLine, ProviderLaunchRecord, ProviderRef, ProviderStatus, ReLinkOutcome, RegisterResult, ReserveResolution, ReserveSource, ResolvedDefaults, SettingsOp, SystemDep, VramBreakdown, VramModel, VramVerdict } from 'dsh-plugin-local-infer/core';
|
|
2
2
|
/**
|
|
3
3
|
* EstimateResult is the answer to an estimate call: the full VRAM breakdown,
|
|
4
4
|
* the one-word verdict the meter renders, the source the numbers came from,
|
|
@@ -79,6 +79,7 @@ export interface LaunchResult {
|
|
|
79
79
|
buildLogs?: string;
|
|
80
80
|
pid?: number;
|
|
81
81
|
parentPid?: number;
|
|
82
|
+
state?: EngineLaunchState;
|
|
82
83
|
}
|
|
83
84
|
/**
|
|
84
85
|
* LaunchOptions tunes the launch health poll: the interval between attempts
|
|
@@ -239,9 +240,9 @@ export interface LocalInferService {
|
|
|
239
240
|
gpuUsage?: GpuUsageProbe | null;
|
|
240
241
|
}): Promise<string[]>;
|
|
241
242
|
guidelines(config: Config, hw: HardwareProfile, engineId?: string): Promise<GuidelineResult[]>;
|
|
242
|
-
deps(engineId: string, buildMode:
|
|
243
|
-
installString(engineId: string, buildMode:
|
|
244
|
-
runInstall(engineId: string, buildMode:
|
|
243
|
+
deps(engineId: string, buildMode: BuildMode, hw: HardwareProfile): Promise<readonly SystemDep[]>;
|
|
244
|
+
installString(engineId: string, buildMode: BuildMode, hw: HardwareProfile): Promise<string>;
|
|
245
|
+
runInstall(engineId: string, buildMode: BuildMode, hw: HardwareProfile): Promise<InstallResult>;
|
|
245
246
|
launch(plan: LaunchPlan, options?: LaunchOptions): Promise<LaunchResult>;
|
|
246
247
|
stop(plan: LaunchPlan): Promise<void>;
|
|
247
248
|
reLinkProviders(entries: readonly {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-local-infer-service",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Service Definition for the dsh local-inference capability seam (ctx.localInfer): the engine, preset, and guideline registries plus the orchestration entry points (estimate, guidelines, deps, installString, runInstall, launch, registerModel). Carries no store of its own.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-local-infer-tool",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Model-facing tools for the dsh local-inference plugin: local_infer_estimate, local_infer_launch, local_infer_status, and local_infer_register, each on its own timeout budget, plus the system-prompt section that tells the agent when to use them",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stored-config persistence the routes and ops share. The values live in
|
|
3
|
+
* ~/.dsh/local-infer/config.json, shared with the CLI, so the browser never
|
|
4
|
+
* sees a secret it did not store. Every read sanitizes the file field by
|
|
5
|
+
* field, so a by-hand edit never poisons the deploy step or the re-link rule.
|
|
6
|
+
* @module dsh-plugin-local-infer/ui/config
|
|
7
|
+
*/
|
|
8
|
+
import type { Config, ProviderLaunchRecord } from 'dsh-plugin-local-infer/core';
|
|
9
|
+
/**
|
|
10
|
+
* The config file the route owns. It sits under the dsh home so a web user and
|
|
11
|
+
* a CLI user share one file, the way modsearch shares ~/.modsearch.
|
|
12
|
+
*/
|
|
13
|
+
export declare function localInferConfigPath(): string;
|
|
14
|
+
/**
|
|
15
|
+
* StoredPlatformConfig is the per-engine platform config the deploy step
|
|
16
|
+
* consumes (§3.10): where the engine platform lives locally and the git repo
|
|
17
|
+
* that overrides the engine's default upstream. The map is keyed by engine id,
|
|
18
|
+
* so the stored entry carries no id of its own; the route adds it when it
|
|
19
|
+
* builds the EnginePlatformConfig the service registry stores.
|
|
20
|
+
*/
|
|
21
|
+
export interface StoredPlatformConfig {
|
|
22
|
+
engineDirectory?: string;
|
|
23
|
+
engineFork?: string;
|
|
24
|
+
buildCommands?: string;
|
|
25
|
+
dockerTag?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* StoredProviderConfig is one saved provider model+engine config the card
|
|
29
|
+
* lists: a stable id the card edits and deletes against, the engine and the
|
|
30
|
+
* full parameter config it serves, the per-engine platform config the deploy
|
|
31
|
+
* step consumes, and the launch record the launch step persists when it
|
|
32
|
+
* starts the entry's engine (the engine pid, start time, launch arguments,
|
|
33
|
+
* and the DSH process pid that launched it). The record is what a DSH
|
|
34
|
+
* restart re-links a surviving engine to its config; a stop clears it.
|
|
35
|
+
*/
|
|
36
|
+
export interface StoredProviderConfig {
|
|
37
|
+
id: string;
|
|
38
|
+
engine: string;
|
|
39
|
+
config: Config;
|
|
40
|
+
platform: StoredPlatformConfig;
|
|
41
|
+
launch?: ProviderLaunchRecord;
|
|
42
|
+
}
|
|
43
|
+
export interface StoredConfig {
|
|
44
|
+
engine: string;
|
|
45
|
+
config: Config;
|
|
46
|
+
allowRunDeps: boolean;
|
|
47
|
+
platform: Record<string, StoredPlatformConfig>;
|
|
48
|
+
showHelp: boolean;
|
|
49
|
+
providers: StoredProviderConfig[];
|
|
50
|
+
logPath: string;
|
|
51
|
+
lastLogs: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* readConfig reads the stored engine, config, trust gate, and per-engine
|
|
55
|
+
* platform configs. A missing or broken file reads as the scaffold default
|
|
56
|
+
* (NInfer, empty config, gate off, no platform configs) so the card always has
|
|
57
|
+
* a form to draw. A platform entry that is not an object (or whose fields are
|
|
58
|
+
* not strings) is dropped, so a by-hand edit never poisons the deploy step.
|
|
59
|
+
*/
|
|
60
|
+
export declare function readConfig(): StoredConfig;
|
|
61
|
+
/**
|
|
62
|
+
* writeConfig persists the engine, config, trust gate, per-engine platform
|
|
63
|
+
* configs, and the help toggle, creating the directory tree on first write.
|
|
64
|
+
*/
|
|
65
|
+
export declare function writeConfig(engine: string, config: Config, allowRunDeps: boolean, platform?: Record<string, StoredPlatformConfig>, showHelp?: boolean, providers?: StoredProviderConfig[], logPath?: string, lastLogs?: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* parsePlatformConfigs reads the stored platform map, keeping only entries
|
|
68
|
+
* whose fields are strings. A non-object map or a non-object entry reads as
|
|
69
|
+
* absent, so the deploy step never sees a platform config it cannot price.
|
|
70
|
+
*/
|
|
71
|
+
export declare function parsePlatformConfigs(raw: unknown): Record<string, StoredPlatformConfig>;
|
|
72
|
+
/**
|
|
73
|
+
* parseStoredPlatformConfig reads one engine's platform config (the launch
|
|
74
|
+
* request's `platform` key), keeping only string fields. A non-object or an
|
|
75
|
+
* object with no string fields reads as absent, so the launch keeps the stored
|
|
76
|
+
* entry the save route synced.
|
|
77
|
+
*/
|
|
78
|
+
export declare function parseStoredPlatformConfig(raw: unknown): StoredPlatformConfig | undefined;
|
|
79
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stored-config persistence the routes and ops share. The values live in
|
|
3
|
+
* ~/.dsh/local-infer/config.json, shared with the CLI, so the browser never
|
|
4
|
+
* sees a secret it did not store. Every read sanitizes the file field by
|
|
5
|
+
* field, so a by-hand edit never poisons the deploy step or the re-link rule.
|
|
6
|
+
* @module dsh-plugin-local-infer/ui/config
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
// Relative, not the root package subpath: a self-referencing bare specifier
|
|
12
|
+
// only resolves when the package sits inside a node_modules tree, so a
|
|
13
|
+
// local-path or pnpm-linked install of this repo would fail the import.
|
|
14
|
+
import { DEFAULT_LOG_PATH_TEMPLATE } from '../../core/lib/index.js';
|
|
15
|
+
/**
|
|
16
|
+
* The config file the route owns. It sits under the dsh home so a web user and
|
|
17
|
+
* a CLI user share one file, the way modsearch shares ~/.modsearch.
|
|
18
|
+
*/
|
|
19
|
+
export function localInferConfigPath() {
|
|
20
|
+
return join(homedir(), '.dsh', 'local-infer', 'config.json');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* readConfig reads the stored engine, config, trust gate, and per-engine
|
|
24
|
+
* platform configs. A missing or broken file reads as the scaffold default
|
|
25
|
+
* (NInfer, empty config, gate off, no platform configs) so the card always has
|
|
26
|
+
* a form to draw. A platform entry that is not an object (or whose fields are
|
|
27
|
+
* not strings) is dropped, so a by-hand edit never poisons the deploy step.
|
|
28
|
+
*/
|
|
29
|
+
export function readConfig() {
|
|
30
|
+
const path = localInferConfigPath();
|
|
31
|
+
if (!existsSync(path)) {
|
|
32
|
+
return {
|
|
33
|
+
engine: 'ninfer',
|
|
34
|
+
config: {},
|
|
35
|
+
allowRunDeps: false,
|
|
36
|
+
platform: {},
|
|
37
|
+
showHelp: true,
|
|
38
|
+
providers: [],
|
|
39
|
+
logPath: DEFAULT_LOG_PATH_TEMPLATE,
|
|
40
|
+
lastLogs: '',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
45
|
+
return {
|
|
46
|
+
engine: typeof parsed.engine === 'string' ? parsed.engine : 'ninfer',
|
|
47
|
+
config: parsed.config && typeof parsed.config === 'object' ? parsed.config : {},
|
|
48
|
+
allowRunDeps: typeof parsed.allowRunDeps === 'boolean' ? parsed.allowRunDeps : false,
|
|
49
|
+
platform: parsePlatformConfigs(parsed.platform),
|
|
50
|
+
// A file that predates the key (or carries a non-boolean) reads as
|
|
51
|
+
// descriptions visible, the default the card draws on first open.
|
|
52
|
+
showHelp: typeof parsed.showHelp === 'boolean' ? parsed.showHelp : true,
|
|
53
|
+
providers: parseProviders(parsed.providers),
|
|
54
|
+
// A file that predates the key reads as the plugin's default log dir,
|
|
55
|
+
// and the last-run logs start empty until a launch fills them.
|
|
56
|
+
logPath: typeof parsed.logPath === 'string' && parsed.logPath !== ''
|
|
57
|
+
? parsed.logPath
|
|
58
|
+
: DEFAULT_LOG_PATH_TEMPLATE,
|
|
59
|
+
lastLogs: typeof parsed.lastLogs === 'string' ? parsed.lastLogs : '',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return {
|
|
64
|
+
engine: 'ninfer',
|
|
65
|
+
config: {},
|
|
66
|
+
allowRunDeps: false,
|
|
67
|
+
platform: {},
|
|
68
|
+
showHelp: true,
|
|
69
|
+
providers: [],
|
|
70
|
+
logPath: DEFAULT_LOG_PATH_TEMPLATE,
|
|
71
|
+
lastLogs: '',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* parseProviders reads the stored provider list, keeping only entries with a
|
|
77
|
+
* non-blank string id. A non-object entry or an entry with no id is dropped,
|
|
78
|
+
* so a by-hand edit never poisons the card's provider list. The per-engine
|
|
79
|
+
* platform config is read through the same string-field rule the platform map
|
|
80
|
+
* uses, so a provider never carries a platform field it cannot price.
|
|
81
|
+
*/
|
|
82
|
+
function parseProviders(raw) {
|
|
83
|
+
if (raw === null || typeof raw !== 'object' || !Array.isArray(raw)) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const providers = [];
|
|
87
|
+
for (const entry of raw) {
|
|
88
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry))
|
|
89
|
+
continue;
|
|
90
|
+
const fields = entry;
|
|
91
|
+
if (typeof fields.id !== 'string' || fields.id.trim() === '')
|
|
92
|
+
continue;
|
|
93
|
+
const engine = typeof fields.engine === 'string' ? fields.engine : 'ninfer';
|
|
94
|
+
const config = fields.config && typeof fields.config === 'object' ? fields.config : {};
|
|
95
|
+
const platform = parseStoredPlatformConfig(fields.platform) ?? {};
|
|
96
|
+
const provider = { id: fields.id, engine, config, platform };
|
|
97
|
+
const launch = parseLaunchRecord(fields.launch);
|
|
98
|
+
if (launch !== undefined) {
|
|
99
|
+
provider.launch = launch;
|
|
100
|
+
}
|
|
101
|
+
providers.push(provider);
|
|
102
|
+
}
|
|
103
|
+
return providers;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* parseLaunchRecord reads one stored launch record, keeping only the fields
|
|
107
|
+
* the re-link rule prices (the engine pid, the start time, the argv, the
|
|
108
|
+
* parent and previous pids, and the engine/model/port the detector ties the
|
|
109
|
+
* engine to). A record that is not an object, or that names no integer pid
|
|
110
|
+
* or engine id, is dropped, so a by-hand edit never poisons the re-link.
|
|
111
|
+
*/
|
|
112
|
+
function parseLaunchRecord(raw) {
|
|
113
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const fields = raw;
|
|
117
|
+
if (typeof fields.pid !== 'number' || !Number.isInteger(fields.pid)) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
if (typeof fields.startedAt !== 'string' || fields.startedAt === '') {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
if (typeof fields.engineId !== 'string' || fields.engineId.trim() === '') {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
const record = {
|
|
127
|
+
pid: fields.pid,
|
|
128
|
+
startedAt: fields.startedAt,
|
|
129
|
+
argv: Array.isArray(fields.argv)
|
|
130
|
+
? fields.argv.filter((part) => typeof part === 'string')
|
|
131
|
+
: [],
|
|
132
|
+
previousPids: Array.isArray(fields.previousPids)
|
|
133
|
+
? fields.previousPids.filter((part) => typeof part === 'number')
|
|
134
|
+
: [],
|
|
135
|
+
engineId: fields.engineId,
|
|
136
|
+
};
|
|
137
|
+
if (typeof fields.parentPid === 'number') {
|
|
138
|
+
record.parentPid = fields.parentPid;
|
|
139
|
+
}
|
|
140
|
+
if (typeof fields.modelId === 'string' && fields.modelId !== '') {
|
|
141
|
+
record.modelId = fields.modelId;
|
|
142
|
+
}
|
|
143
|
+
if (typeof fields.port === 'number') {
|
|
144
|
+
record.port = fields.port;
|
|
145
|
+
}
|
|
146
|
+
return record;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* writeConfig persists the engine, config, trust gate, per-engine platform
|
|
150
|
+
* configs, and the help toggle, creating the directory tree on first write.
|
|
151
|
+
*/
|
|
152
|
+
export function writeConfig(engine, config, allowRunDeps, platform, showHelp, providers, logPath, lastLogs) {
|
|
153
|
+
const path = localInferConfigPath();
|
|
154
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
155
|
+
writeFileSync(path, JSON.stringify({
|
|
156
|
+
engine,
|
|
157
|
+
config,
|
|
158
|
+
allowRunDeps,
|
|
159
|
+
platform: platform ?? {},
|
|
160
|
+
showHelp: showHelp ?? true,
|
|
161
|
+
providers: providers ?? [],
|
|
162
|
+
logPath: logPath ?? DEFAULT_LOG_PATH_TEMPLATE,
|
|
163
|
+
lastLogs: lastLogs ?? '',
|
|
164
|
+
}, null, 2));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* parsePlatformConfigs reads the stored platform map, keeping only entries
|
|
168
|
+
* whose fields are strings. A non-object map or a non-object entry reads as
|
|
169
|
+
* absent, so the deploy step never sees a platform config it cannot price.
|
|
170
|
+
*/
|
|
171
|
+
export function parsePlatformConfigs(raw) {
|
|
172
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
173
|
+
return {};
|
|
174
|
+
}
|
|
175
|
+
const platform = {};
|
|
176
|
+
for (const [engineId, entry] of Object.entries(raw)) {
|
|
177
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const stored = {};
|
|
181
|
+
const fields = entry;
|
|
182
|
+
if (typeof fields.engineDirectory === 'string') {
|
|
183
|
+
stored.engineDirectory = fields.engineDirectory;
|
|
184
|
+
}
|
|
185
|
+
if (typeof fields.engineFork === 'string') {
|
|
186
|
+
stored.engineFork = fields.engineFork;
|
|
187
|
+
}
|
|
188
|
+
if (typeof fields.buildCommands === 'string') {
|
|
189
|
+
stored.buildCommands = fields.buildCommands;
|
|
190
|
+
}
|
|
191
|
+
if (typeof fields.dockerTag === 'string') {
|
|
192
|
+
stored.dockerTag = fields.dockerTag;
|
|
193
|
+
}
|
|
194
|
+
if (stored.engineDirectory !== undefined ||
|
|
195
|
+
stored.engineFork !== undefined ||
|
|
196
|
+
stored.buildCommands !== undefined ||
|
|
197
|
+
stored.dockerTag !== undefined) {
|
|
198
|
+
platform[engineId] = stored;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return platform;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* parseStoredPlatformConfig reads one engine's platform config (the launch
|
|
205
|
+
* request's `platform` key), keeping only string fields. A non-object or an
|
|
206
|
+
* object with no string fields reads as absent, so the launch keeps the stored
|
|
207
|
+
* entry the save route synced.
|
|
208
|
+
*/
|
|
209
|
+
export function parseStoredPlatformConfig(raw) {
|
|
210
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const fields = raw;
|
|
214
|
+
const stored = {};
|
|
215
|
+
if (typeof fields.engineDirectory === 'string') {
|
|
216
|
+
stored.engineDirectory = fields.engineDirectory;
|
|
217
|
+
}
|
|
218
|
+
if (typeof fields.engineFork === 'string') {
|
|
219
|
+
stored.engineFork = fields.engineFork;
|
|
220
|
+
}
|
|
221
|
+
if (typeof fields.buildCommands === 'string') {
|
|
222
|
+
stored.buildCommands = fields.buildCommands;
|
|
223
|
+
}
|
|
224
|
+
if (typeof fields.dockerTag === 'string') {
|
|
225
|
+
stored.dockerTag = fields.dockerTag;
|
|
226
|
+
}
|
|
227
|
+
if (stored.engineDirectory === undefined &&
|
|
228
|
+
stored.engineFork === undefined &&
|
|
229
|
+
stored.buildCommands === undefined &&
|
|
230
|
+
stored.dockerTag === undefined) {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
return stored;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* here.
|
|
8
8
|
* @module dsh-plugin-local-infer/ui/deps
|
|
9
9
|
*/
|
|
10
|
-
import type { DepCheck, DepStatus } from 'dsh-plugin-local-infer/core';
|
|
10
|
+
import type { BuildMode, DepCheck, DepStatus } from 'dsh-plugin-local-infer/core';
|
|
11
11
|
/**
|
|
12
12
|
* DepItemModel is one row of the panel's dep list: the dep id, the purpose
|
|
13
13
|
* the user reads, the current status, and whether installing it needs sudo.
|
|
@@ -40,7 +40,7 @@ export interface ChatTemplateDepModel {
|
|
|
40
40
|
*/
|
|
41
41
|
export interface DepsModel {
|
|
42
42
|
engineId: string;
|
|
43
|
-
buildMode:
|
|
43
|
+
buildMode: BuildMode;
|
|
44
44
|
packageManager: string | null;
|
|
45
45
|
deps: DepItemModel[];
|
|
46
46
|
allSatisfied: boolean;
|
|
@@ -56,7 +56,7 @@ export interface DepsModel {
|
|
|
56
56
|
*/
|
|
57
57
|
export declare function renderDeps(input: {
|
|
58
58
|
engineId: string;
|
|
59
|
-
buildMode:
|
|
59
|
+
buildMode: BuildMode;
|
|
60
60
|
packageManager: string | null;
|
|
61
61
|
checks: DepCheck[];
|
|
62
62
|
installString: string;
|