dsh-plugin-local-infer 0.1.1 → 0.1.2
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 +4 -1
- package/docs/zh/README.md +4 -1
- package/dsh/client.js +627 -133
- package/package.json +11 -10
- package/packages/core/lib/adapter.d.ts +75 -4
- package/packages/core/lib/index.d.ts +2 -1
- package/packages/core/lib/index.js +1 -0
- package/packages/core/lib/launch-command.d.ts +13 -0
- package/packages/core/lib/launch-command.js +64 -0
- package/packages/core/lib/models.d.ts +2 -1
- package/packages/core/lib/models.js +10 -1
- package/packages/core/lib/status.d.ts +9 -1
- package/packages/core/lib/status.js +6 -1
- package/packages/providers/lib/acquire.d.ts +125 -0
- package/packages/providers/lib/acquire.js +325 -0
- package/packages/providers/lib/adapters.d.ts +42 -5
- package/packages/providers/lib/adapters.js +385 -57
- package/packages/providers/lib/deps.d.ts +4 -2
- package/packages/providers/lib/deps.js +16 -2
- package/packages/providers/lib/engines.d.ts +4 -3
- package/packages/providers/lib/engines.js +11 -3
- package/packages/providers/lib/guidelines.js +16 -0
- package/packages/providers/lib/index.d.ts +9 -3
- package/packages/providers/lib/index.js +8 -4
- package/packages/providers/lib/infernix.d.ts +170 -0
- package/packages/providers/lib/infernix.js +671 -0
- package/packages/providers/lib/model-defaults.d.ts +5 -3
- package/packages/providers/lib/model-defaults.js +27 -3
- package/packages/providers/lib/ninfer.d.ts +7 -0
- package/packages/providers/lib/ninfer.js +9 -2
- package/packages/providers/lib/platform.d.ts +31 -0
- package/packages/providers/lib/platform.js +58 -0
- package/packages/service/lib/index.d.ts +61 -18
- package/packages/service/lib/index.js +278 -42
- package/packages/service/lib/types.d.ts +44 -2
- package/packages/ui/lib/route.d.ts +83 -12
- package/packages/ui/lib/route.js +495 -55
- package/packages/ui/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-local-infer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"type": "module",
|
|
25
|
+
"packageManager": "pnpm@12.11.2",
|
|
25
26
|
"engines": {
|
|
26
27
|
"node": "^22.19.0 || >=24.0.0"
|
|
27
28
|
},
|
|
@@ -85,6 +86,14 @@
|
|
|
85
86
|
"peerDependencies": {
|
|
86
87
|
"@deepseek-ai/cordis": ">=4.0.5-alpha.2"
|
|
87
88
|
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"build": "tsc -b",
|
|
91
|
+
"typecheck": "tsc -b",
|
|
92
|
+
"test": "vitest run",
|
|
93
|
+
"test:coverage": "vitest run --coverage",
|
|
94
|
+
"lint": "eslint .",
|
|
95
|
+
"clean": "tsc -b --clean"
|
|
96
|
+
},
|
|
88
97
|
"devDependencies": {
|
|
89
98
|
"@eslint/js": "^10.0.1",
|
|
90
99
|
"@types/node": "^22.10.0",
|
|
@@ -94,13 +103,5 @@
|
|
|
94
103
|
"typescript-eslint": "^8.71.2",
|
|
95
104
|
"vite-tsconfig-paths": "^5.1.4",
|
|
96
105
|
"vitest": "^5.0.4"
|
|
97
|
-
},
|
|
98
|
-
"scripts": {
|
|
99
|
-
"build": "tsc -b",
|
|
100
|
-
"typecheck": "tsc -b",
|
|
101
|
-
"test": "vitest run",
|
|
102
|
-
"test:coverage": "vitest run --coverage",
|
|
103
|
-
"lint": "eslint .",
|
|
104
|
-
"clean": "tsc -b --clean"
|
|
105
106
|
}
|
|
106
|
-
}
|
|
107
|
+
}
|
|
@@ -21,6 +21,7 @@ export interface LaunchPlan {
|
|
|
21
21
|
env?: Record<string, string>;
|
|
22
22
|
composeFile?: string;
|
|
23
23
|
profile?: string;
|
|
24
|
+
composeArgs?: string;
|
|
24
25
|
containerName?: string;
|
|
25
26
|
modelId?: string;
|
|
26
27
|
modelDirectory?: string;
|
|
@@ -32,12 +33,31 @@ export interface LaunchPlan {
|
|
|
32
33
|
* specific engine: where the engine platform lives locally (the checkout and
|
|
33
34
|
* build location) and the git repo that overrides the engine's default
|
|
34
35
|
* upstream for this config. The deploy step consumes both: it clones the fork
|
|
35
|
-
* (or the default upstream) into the engine directory.
|
|
36
|
+
* (or the default upstream) into the engine directory. `buildCommands`
|
|
37
|
+
* overrides the engine's local build command (the local build mode) and
|
|
38
|
+
* `dockerTag` overrides the docker image tag the docker deploy pulls (the
|
|
39
|
+
* docker build mode); each rides the platform map, so an engine switch keeps
|
|
40
|
+
* them.
|
|
36
41
|
*/
|
|
37
42
|
export interface EnginePlatformConfig {
|
|
38
43
|
engineId: string;
|
|
39
44
|
engineDirectory?: string;
|
|
40
45
|
engineFork?: string;
|
|
46
|
+
buildCommands?: string;
|
|
47
|
+
dockerTag?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* ProcessExitInfo is the one-shot answer to a spawned process's lifetime end:
|
|
51
|
+
* the exit code (null when a signal or a spawn failure ended it), the signal
|
|
52
|
+
* name (null when the process exited on its own), and the spawn error message
|
|
53
|
+
* when the child never ran (a missing binary). The host emits it exactly once
|
|
54
|
+
* per child, from the close event or the error event, so a crash or a pkill
|
|
55
|
+
* reaches the plugin's state through one seam instead of an uncaughtException.
|
|
56
|
+
*/
|
|
57
|
+
export interface ProcessExitInfo {
|
|
58
|
+
code: number | null;
|
|
59
|
+
signal: string | null;
|
|
60
|
+
error?: string;
|
|
41
61
|
}
|
|
42
62
|
/**
|
|
43
63
|
* ProcessHandle is the handle to a spawned engine process: its pid and a kill
|
|
@@ -46,12 +66,17 @@ export interface EnginePlatformConfig {
|
|
|
46
66
|
* the host captures the child's stdout and stderr, is the accumulated output
|
|
47
67
|
* the launch step surfaces in the card's Logs section and writes to the log
|
|
48
68
|
* file. A host that does not capture streams (a container it cannot read)
|
|
49
|
-
* omits it.
|
|
69
|
+
* omits it. `onExit`, when the host watches the child, registers a listener
|
|
70
|
+
* fired once with the ProcessExitInfo when the child ends (a crash, a signal
|
|
71
|
+
* like a pkill, or a clean stop); a listener registered after the child has
|
|
72
|
+
* already ended fires immediately. A host that cannot watch (a stub, or a
|
|
73
|
+
* container it cannot read) omits it.
|
|
50
74
|
*/
|
|
51
75
|
export interface ProcessHandle {
|
|
52
76
|
pid: number;
|
|
53
77
|
kill(): void;
|
|
54
78
|
logs?: string;
|
|
79
|
+
onExit?(listener: (exit: ProcessExitInfo) => void): void;
|
|
55
80
|
}
|
|
56
81
|
/**
|
|
57
82
|
* FetchResult is the answer to a health poll: the HTTP status and whether it
|
|
@@ -71,6 +96,15 @@ export interface FetchResult {
|
|
|
71
96
|
*/
|
|
72
97
|
export interface LifecycleHost {
|
|
73
98
|
run(command: string): Promise<CommandOutcome>;
|
|
99
|
+
/**
|
|
100
|
+
* Optional: run a command line and stream each stdout/stderr chunk to
|
|
101
|
+
* onOutput while it runs, so the Get step can append build output to the
|
|
102
|
+
* card's Logs section as it lands. A host that cannot stream omits it,
|
|
103
|
+
* and the caller falls back to run. No overall timeout: a full CUDA
|
|
104
|
+
* build legitimately runs past any fixed cap, and the card prices a
|
|
105
|
+
* stalled step with its own output-idle watchdog.
|
|
106
|
+
*/
|
|
107
|
+
runStream?(command: string, onOutput: (chunk: string) => void): Promise<CommandOutcome>;
|
|
74
108
|
spawn(argv: readonly string[], env: Record<string, string>, cwd?: string): ProcessHandle;
|
|
75
109
|
fetch(url: string): Promise<FetchResult>;
|
|
76
110
|
debug?: DebugLogger;
|
|
@@ -84,6 +118,23 @@ export interface HealthCheckOptions {
|
|
|
84
118
|
pollMs?: number;
|
|
85
119
|
maxAttempts?: number;
|
|
86
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* LaunchStep is the phase of a launch the card's Launch control prices: the
|
|
123
|
+
* system-dependency install (prereqs), the model and git checkout download
|
|
124
|
+
* (download), the source build or image pull (build), and the health poll that
|
|
125
|
+
* waits for the engine to come ready (launch). The service and the adapters
|
|
126
|
+
* report the step as each phase begins, so the card's button reads the phase
|
|
127
|
+
* that is actually running instead of a single "Launching..." for the whole
|
|
128
|
+
* rebuild. The card times out only the launch step; the rebuild steps (prereqs,
|
|
129
|
+
* download, build) run as long as they need to.
|
|
130
|
+
*/
|
|
131
|
+
export type LaunchStep = 'prereqs' | 'download' | 'build' | 'launch';
|
|
132
|
+
/**
|
|
133
|
+
* LaunchProgress is the callback the launch flow calls as each phase begins.
|
|
134
|
+
* The route threads it from the streamed launch response through the service
|
|
135
|
+
* and the adapter's deploy step; a launch with no progress sink omits it.
|
|
136
|
+
*/
|
|
137
|
+
export type LaunchProgress = (step: LaunchStep) => void;
|
|
87
138
|
/**
|
|
88
139
|
* HealthResult is the answer to a /health poll: whether the engine is up, the
|
|
89
140
|
* HTTP status, and a detail string for the UI.
|
|
@@ -93,17 +144,37 @@ export interface HealthResult {
|
|
|
93
144
|
status?: number;
|
|
94
145
|
detail?: string;
|
|
95
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* DeployOptions tunes a deploy run: `allowRunDeps` is the Play/Run trust gate
|
|
149
|
+
* (the card's "Allow one-click install (sudo)" checkbox). A local deploy that
|
|
150
|
+
* rebuilds the engine and leaves its binary out of the path links it into
|
|
151
|
+
* /usr/local/bin; the gate decides whether that sudo link runs (on) or is
|
|
152
|
+
* written to the card's Logs section for the user to run (off). `autoRebuild`
|
|
153
|
+
* is the card's "Auto Rebuild" checkbox: when false, the git sync (fetch,
|
|
154
|
+
* rebase, and the conditional rebuild) is skipped and the checkout launches
|
|
155
|
+
* as-is. Absent reads as true (the rebuild runs).
|
|
156
|
+
*/
|
|
157
|
+
export interface DeployOptions {
|
|
158
|
+
allowRunDeps?: boolean;
|
|
159
|
+
autoRebuild?: boolean;
|
|
160
|
+
}
|
|
96
161
|
/**
|
|
97
162
|
* DeployResult reports whether a build-or-pull succeeded, with a detail for the
|
|
98
163
|
* UI. `handle` is the spawned engine process when the deploy step started one
|
|
99
164
|
* locally: the service reads its accumulated `logs` after the health poll, so
|
|
100
165
|
* the launch result carries the engine's output for the card's Logs section and
|
|
101
|
-
* the log file
|
|
166
|
+
* the log file, and attaches the handle's `onExit` listener to learn of a crash
|
|
167
|
+
* or a pkill. A deploy that starts no process (a compose up) omits it. `logs`
|
|
168
|
+
* is the deploy step's own captured output (the rebuild decision, the build
|
|
169
|
+
* command, and its output) the service prepends to the engine's output, so the
|
|
170
|
+
* card's Logs section shows the build a launch ran. A deploy that runs no
|
|
171
|
+
* rebuild (a docker pull, a compose up) omits it.
|
|
102
172
|
*/
|
|
103
173
|
export interface DeployResult {
|
|
104
174
|
ok: boolean;
|
|
105
175
|
detail?: string;
|
|
106
176
|
handle?: ProcessHandle;
|
|
177
|
+
logs?: string;
|
|
107
178
|
}
|
|
108
179
|
/**
|
|
109
180
|
* EngineAdapter is the abstract base one subclass implements per engine. It
|
|
@@ -123,7 +194,7 @@ export declare abstract class EngineAdapter {
|
|
|
123
194
|
abstract parameters(): readonly ParameterSpec[];
|
|
124
195
|
abstract renderLaunch(config: Config, hw: HardwareProfile): LaunchPlan;
|
|
125
196
|
abstract healthCheck(plan: LaunchPlan, options?: HealthCheckOptions): Promise<HealthResult>;
|
|
126
|
-
abstract deploy(plan: LaunchPlan, platform?: EnginePlatformConfig): Promise<DeployResult>;
|
|
197
|
+
abstract deploy(plan: LaunchPlan, platform?: EnginePlatformConfig, onProgress?: LaunchProgress, options?: DeployOptions): Promise<DeployResult>;
|
|
127
198
|
abstract stop(plan: LaunchPlan): Promise<void>;
|
|
128
199
|
/**
|
|
129
200
|
* Optional: the host deps this engine's deploy step needs for a build mode,
|
|
@@ -13,13 +13,14 @@ export { DEFAULT_OS_RESERVE_MIB, estimateVram, KV_PAGE_TOKENS, pageRoundUp, TIGH
|
|
|
13
13
|
export type { EstimateContext, EstimateOptions, SpeculationTerms, VramBreakdown, VramContribution, VramEstimate, VramModel, VramVerdict, } from './estimator.ts';
|
|
14
14
|
export { GPU_USAGE_BUSY_THRESHOLD, matchLaunchSignature, parseNvidiaSmiGpuUsage, resolveOsReserve, } from './reserve.ts';
|
|
15
15
|
export { commandMatchesArgv, launchRecordFromPlan, reLinkRecord, } from './launch-record.ts';
|
|
16
|
+
export { formatLaunchCommand } from './launch-command.ts';
|
|
16
17
|
export type { ProviderLaunchRecord, ReLinkInput, ReLinkOutcome, } from './launch-record.ts';
|
|
17
18
|
export type { EngineLaunchSignature, GpuUsageProbe, ProcessLine, ReserveResolution, ReserveSource, } from './reserve.ts';
|
|
18
19
|
export { computeProviderStatuses, isGpuBusy, } from './status.ts';
|
|
19
20
|
export type { ProviderRef, ProviderStatus, ProviderStatusInput, RunningEngineRef, } from './status.ts';
|
|
20
21
|
export { DEFAULT_LOG_PATH_TEMPLATE, LOG_FILE_NAME_TEMPLATE, LOG_TEMPLATE_ENGINE, LOG_TEMPLATE_HOME, LOG_TEMPLATE_MODEL, LOG_TEMPLATE_TIMESTAMP, formatLogTimestamp, resolveLogPath, slugifyModelName, } from './logs.ts';
|
|
21
22
|
export { EngineAdapter } from './adapter.ts';
|
|
22
|
-
export type { DeployResult, EnginePlatformConfig, FetchResult, HealthCheckOptions, HealthResult, LaunchPlan, LifecycleHost, ProcessHandle, } from './adapter.ts';
|
|
23
|
+
export type { DeployOptions, DeployResult, EnginePlatformConfig, FetchResult, HealthCheckOptions, HealthResult, LaunchPlan, LaunchProgress, LaunchStep, LifecycleHost, ProcessExitInfo, ProcessHandle, } from './adapter.ts';
|
|
23
24
|
export { NOOP_DEBUG_LOGGER, createCommandDebugLogger, isHarnessLogDebug, } from './debug.ts';
|
|
24
25
|
export type { CreateCommandDebugLoggerArgs, DebugLogger, } from './debug.ts';
|
|
25
26
|
export { constraintId, evaluateGuidelines, modelGuidelines } from './guideline.ts';
|
|
@@ -9,6 +9,7 @@ export { detectHardware, parseNvidiaSmiGpu, } from "./hardware.js";
|
|
|
9
9
|
export { DEFAULT_OS_RESERVE_MIB, estimateVram, KV_PAGE_TOKENS, pageRoundUp, TIGHT_HEADROOM_BYTES, } from "./estimator.js";
|
|
10
10
|
export { GPU_USAGE_BUSY_THRESHOLD, matchLaunchSignature, parseNvidiaSmiGpuUsage, resolveOsReserve, } from "./reserve.js";
|
|
11
11
|
export { commandMatchesArgv, launchRecordFromPlan, reLinkRecord, } from "./launch-record.js";
|
|
12
|
+
export { formatLaunchCommand } from "./launch-command.js";
|
|
12
13
|
export { computeProviderStatuses, isGpuBusy, } from "./status.js";
|
|
13
14
|
export { DEFAULT_LOG_PATH_TEMPLATE, LOG_FILE_NAME_TEMPLATE, LOG_TEMPLATE_ENGINE, LOG_TEMPLATE_HOME, LOG_TEMPLATE_MODEL, LOG_TEMPLATE_TIMESTAMP, formatLogTimestamp, resolveLogPath, slugifyModelName, } from "./logs.js";
|
|
14
15
|
export { EngineAdapter } from "./adapter.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LaunchPlan } from './adapter.ts';
|
|
2
|
+
/**
|
|
3
|
+
* formatLaunchCommand renders a launch plan as the shell command string the
|
|
4
|
+
* deploy step runs to start the engine: the quoted argv joined for a process
|
|
5
|
+
* or docker plan, or the `docker compose up` line for a compose plan. The
|
|
6
|
+
* card's Logs section shows the line above the engine's own output, so a
|
|
7
|
+
* user can read and copy the exact binary and arguments a launch started
|
|
8
|
+
* with. The Hyper-Qwen deploy runs this same string, so the printed line and
|
|
9
|
+
* the host command cannot drift. Returns undefined when the plan names no
|
|
10
|
+
* command (a non-compose plan with no argv).
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatLaunchCommand(plan: LaunchPlan): string | undefined;
|
|
13
|
+
//# sourceMappingURL=launch-command.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* formatLaunchCommand renders a launch plan as the shell command string the
|
|
3
|
+
* deploy step runs to start the engine: the quoted argv joined for a process
|
|
4
|
+
* or docker plan, or the `docker compose up` line for a compose plan. The
|
|
5
|
+
* card's Logs section shows the line above the engine's own output, so a
|
|
6
|
+
* user can read and copy the exact binary and arguments a launch started
|
|
7
|
+
* with. The Hyper-Qwen deploy runs this same string, so the printed line and
|
|
8
|
+
* the host command cannot drift. Returns undefined when the plan names no
|
|
9
|
+
* command (a non-compose plan with no argv).
|
|
10
|
+
*/
|
|
11
|
+
export function formatLaunchCommand(plan) {
|
|
12
|
+
if (plan.kind === 'compose') {
|
|
13
|
+
// The user's compose arguments (the card's docker-tag field) override the
|
|
14
|
+
// default profile: the flags the up step runs before the required `up -d`.
|
|
15
|
+
// An empty field reads as "use the default profile", so the up step falls
|
|
16
|
+
// back to the engine's own default.
|
|
17
|
+
const composeArgs = plan.composeArgs?.split(/\s+/).filter((token) => token !== '');
|
|
18
|
+
if (composeArgs !== undefined && composeArgs.length > 0) {
|
|
19
|
+
return [
|
|
20
|
+
'docker',
|
|
21
|
+
'compose',
|
|
22
|
+
'-f',
|
|
23
|
+
plan.composeFile ?? 'docker-compose.yml',
|
|
24
|
+
...composeArgs,
|
|
25
|
+
'up',
|
|
26
|
+
'-d',
|
|
27
|
+
]
|
|
28
|
+
.map(quoteArg)
|
|
29
|
+
.join(' ');
|
|
30
|
+
}
|
|
31
|
+
// The compose defaults match the adapter's renderLaunch fallbacks, so a
|
|
32
|
+
// plan built from a bare config still spells a runnable command.
|
|
33
|
+
return [
|
|
34
|
+
'docker',
|
|
35
|
+
'compose',
|
|
36
|
+
'-f',
|
|
37
|
+
plan.composeFile ?? 'docker-compose.yml',
|
|
38
|
+
'--profile',
|
|
39
|
+
plan.profile ?? 'single',
|
|
40
|
+
'up',
|
|
41
|
+
'--pull',
|
|
42
|
+
'always',
|
|
43
|
+
'-d',
|
|
44
|
+
]
|
|
45
|
+
.map(quoteArg)
|
|
46
|
+
.join(' ');
|
|
47
|
+
}
|
|
48
|
+
const argv = plan.argv;
|
|
49
|
+
if (argv === undefined || argv.length === 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
return argv.map(quoteArg).join(' ');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* quoteArg renders one argv token for display: a token made only of shell-
|
|
55
|
+
* safe characters rides bare, anything else lands in single quotes with
|
|
56
|
+
* embedded quotes escaped, so the printed line pastes back into a shell
|
|
57
|
+
* unchanged.
|
|
58
|
+
*/
|
|
59
|
+
function quoteArg(token) {
|
|
60
|
+
if (token !== '' && /^[A-Za-z0-9@%+=:,./_-]+$/.test(token))
|
|
61
|
+
return token;
|
|
62
|
+
return `'${token.replaceAll("'", "'\\''")}'`;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=launch-command.js.map
|
|
@@ -67,7 +67,8 @@ export interface ModelDirectoryEntry {
|
|
|
67
67
|
/**
|
|
68
68
|
* modelEntriesForEngine filters a model directory listing down to the model
|
|
69
69
|
* entries the engine's Model dropdown offers: NInfer takes `.ninfer`
|
|
70
|
-
* artifacts,
|
|
70
|
+
* artifacts, Infernix takes `.ninfer` artifacts and its own `.infernix`
|
|
71
|
+
* files, llama.cpp takes `.gguf` files, and Hyper-Qwen takes the repo
|
|
71
72
|
* directories that hold a `.safetensors` weight file. Every other engine
|
|
72
73
|
* (Strata) offers nothing, matching its empty default list. The result is
|
|
73
74
|
* sorted, so the dropdown order is stable across summary fetches.
|
|
@@ -61,7 +61,8 @@ export function hfChatTemplateCommand(repo, directory) {
|
|
|
61
61
|
/**
|
|
62
62
|
* modelEntriesForEngine filters a model directory listing down to the model
|
|
63
63
|
* entries the engine's Model dropdown offers: NInfer takes `.ninfer`
|
|
64
|
-
* artifacts,
|
|
64
|
+
* artifacts, Infernix takes `.ninfer` artifacts and its own `.infernix`
|
|
65
|
+
* files, llama.cpp takes `.gguf` files, and Hyper-Qwen takes the repo
|
|
65
66
|
* directories that hold a `.safetensors` weight file. Every other engine
|
|
66
67
|
* (Strata) offers nothing, matching its empty default list. The result is
|
|
67
68
|
* sorted, so the dropdown order is stable across summary fetches.
|
|
@@ -74,6 +75,14 @@ export function modelEntriesForEngine(entries, engineId) {
|
|
|
74
75
|
.filter((entry) => !entry.isDirectory && entry.name.endsWith('.ninfer'))
|
|
75
76
|
.map((entry) => entry.name);
|
|
76
77
|
break;
|
|
78
|
+
case 'infernix':
|
|
79
|
+
// Infernix loads NInfer artifacts unchanged plus its own `.infernix`
|
|
80
|
+
// files, so the dropdown offers both extensions.
|
|
81
|
+
names = entries
|
|
82
|
+
.filter((entry) => !entry.isDirectory &&
|
|
83
|
+
(entry.name.endsWith('.ninfer') || entry.name.endsWith('.infernix')))
|
|
84
|
+
.map((entry) => entry.name);
|
|
85
|
+
break;
|
|
77
86
|
case 'llamacpp':
|
|
78
87
|
names = entries
|
|
79
88
|
.filter((entry) => !entry.isDirectory && entry.name.endsWith('.gguf'))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GpuUsageProbe } from './reserve.ts';
|
|
2
|
+
import type { Config } from './config.ts';
|
|
2
3
|
/**
|
|
3
4
|
* ProviderStatus is the running-state the plugin card paints each saved
|
|
4
5
|
* provider's status dot: green while the engine is confirmed serving the
|
|
@@ -10,12 +11,19 @@ export type ProviderStatus = 'running' | 'stopped' | 'unsure';
|
|
|
10
11
|
* ProviderRef is the identity side of one saved provider config: the stable
|
|
11
12
|
* id the card renders and the engine and model it serves. The model is
|
|
12
13
|
* optional because a config that names no model cannot be confirmed against
|
|
13
|
-
* a running engine, and reads as unsure rather than a guess.
|
|
14
|
+
* a running engine, and reads as unsure rather than a guess. The config is
|
|
15
|
+
* optional and lets the detector price the provider's own launch signature
|
|
16
|
+
* (the engine binary the build mode runs, the port it binds) rather than the
|
|
17
|
+
* adapter's default-port signature; the port is the resolved bind port the
|
|
18
|
+
* detector read from that signature, and the resolver ties a running engine
|
|
19
|
+
* to the provider only when the ports agree.
|
|
14
20
|
*/
|
|
15
21
|
export interface ProviderRef {
|
|
16
22
|
id: string;
|
|
17
23
|
engine: string;
|
|
18
24
|
model?: string;
|
|
25
|
+
config?: Config;
|
|
26
|
+
port?: number;
|
|
19
27
|
}
|
|
20
28
|
/**
|
|
21
29
|
* RunningEngineRef is one engine the detector confirmed live: the engine id,
|
|
@@ -28,7 +28,12 @@ export function isGpuBusy(usage) {
|
|
|
28
28
|
export function computeProviderStatuses(input) {
|
|
29
29
|
const statuses = {};
|
|
30
30
|
for (const provider of input.providers) {
|
|
31
|
-
|
|
31
|
+
// A provider that names a port only counts an engine running on that
|
|
32
|
+
// port: two configs of one engine on different ports are distinct
|
|
33
|
+
// engines, and a match on the other port must not confirm (or deny) this
|
|
34
|
+
// config's model. An unnamed port keeps the engine-wide match.
|
|
35
|
+
const matches = input.running.filter((ref) => ref.engineId === provider.engine &&
|
|
36
|
+
(provider.port === undefined || ref.port === undefined || ref.port === provider.port));
|
|
32
37
|
if (matches.length > 0) {
|
|
33
38
|
// The engine is running. The question is which model it serves: the
|
|
34
39
|
// detector's identified model, if it tied one, else unknown.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-engine acquisition data and the Get step. Each engine declares its
|
|
3
|
+
* git source (the upstream repo, the branch the rebase targets, and the build
|
|
4
|
+
* steps the checkout runs) as data here; the adapters' deploy steps and the
|
|
5
|
+
* /local-infer/get route both consume the same constants, so a build command
|
|
6
|
+
* has one home. The sync step clones a missing checkout, and a clean existing
|
|
7
|
+
* checkout is fetched and rebased onto the upstream branch; a checkout with
|
|
8
|
+
* local changes aborts, because a rebase would eat the user's work.
|
|
9
|
+
* @module dsh-plugin-local-infer/providers/acquire
|
|
10
|
+
*/
|
|
11
|
+
import type { LifecycleHost } from '../../core/lib/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* EngineSource is the per-engine acquisition data: the upstream git repo the
|
|
14
|
+
* clone step uses when no fork is provided, the branch the fetch/rebase step
|
|
15
|
+
* rebases onto, and the build steps run in the checkout after a clone or a
|
|
16
|
+
* rebase. A build step is a shell command line; the `{dir}` placeholder is
|
|
17
|
+
* replaced with the checkout directory before the step runs.
|
|
18
|
+
*/
|
|
19
|
+
export interface EngineSource {
|
|
20
|
+
repo: string;
|
|
21
|
+
branch: string;
|
|
22
|
+
buildSteps: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The remote name a user-provided fork is added under, so the checkout's own
|
|
26
|
+
* origin (the upstream it was cloned from) survives the fork override.
|
|
27
|
+
*/
|
|
28
|
+
export declare const FORK_REMOTE = "dsh-fork";
|
|
29
|
+
/**
|
|
30
|
+
* The per-engine git sources, keyed by engine id. NInfer and Infernix build
|
|
31
|
+
* through cmake (Infernix through its own preset); llama.cpp builds through
|
|
32
|
+
* the same cmake shape as NInfer; Hyper-Qwen is the compose-driven vLLM
|
|
33
|
+
* engine, so it carries no local build steps (the compose up pulls the
|
|
34
|
+
* image); Strata is the placeholder engine, and its repo and pull step land
|
|
35
|
+
* with the engine, the same way its docker image constant did.
|
|
36
|
+
*/
|
|
37
|
+
export declare const ENGINE_SOURCES: Record<string, EngineSource>;
|
|
38
|
+
/**
|
|
39
|
+
* AcquireLog receives one line (or chunk) of Get output as it lands, so the
|
|
40
|
+
* route can stream it to the card's Logs section.
|
|
41
|
+
*/
|
|
42
|
+
export type AcquireLog = (chunk: string) => void;
|
|
43
|
+
/**
|
|
44
|
+
* EngineStepResult reports one Get step (sync or build): whether it passed,
|
|
45
|
+
* whether it aborted on a rule (a dirty checkout), and a detail the Logs
|
|
46
|
+
* section shows when it did not pass.
|
|
47
|
+
*/
|
|
48
|
+
export interface EngineStepResult {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
aborted?: boolean;
|
|
51
|
+
detail?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* engineSource reads the registered acquisition data for an engine id, or
|
|
55
|
+
* undefined when none is registered.
|
|
56
|
+
*/
|
|
57
|
+
export declare function engineSource(engineId: string): EngineSource | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* engineBuildSteps reads the build steps an engine runs in its checkout, so
|
|
60
|
+
* the adapter's deploy step and the Get build step share one list.
|
|
61
|
+
*/
|
|
62
|
+
export declare function engineBuildSteps(engineId: string): readonly string[];
|
|
63
|
+
/**
|
|
64
|
+
* syncEngineRepo brings the engine checkout to the listed source. A missing
|
|
65
|
+
* checkout is a fresh clone of the fork (or the engine's upstream repo when
|
|
66
|
+
* no fork is provided). An existing checkout with local changes aborts, so
|
|
67
|
+
* the user's work is never rebased away; a clean checkout fetches the listed
|
|
68
|
+
* fork (added as the named fork remote when provided, origin when not) and
|
|
69
|
+
* rebases onto its branch. Every command echoes into log as it runs.
|
|
70
|
+
*/
|
|
71
|
+
export declare function syncEngineRepo(host: LifecycleHost, request: {
|
|
72
|
+
engineId: string;
|
|
73
|
+
directory: string;
|
|
74
|
+
fork?: string;
|
|
75
|
+
}, log: AcquireLog): Promise<EngineStepResult>;
|
|
76
|
+
/**
|
|
77
|
+
* SyncRebuildRequest names the checkout the conditional rebuild prices: the
|
|
78
|
+
* engine, its directory, the optional fork remote, the engine binary the
|
|
79
|
+
* symlink step links when a rebuild leaves it out of the path, and the
|
|
80
|
+
* Play/Run trust gate that decides whether that symlink runs (on) or is
|
|
81
|
+
* written to the logs (off).
|
|
82
|
+
*/
|
|
83
|
+
export interface SyncRebuildRequest {
|
|
84
|
+
engineId: string;
|
|
85
|
+
directory: string;
|
|
86
|
+
fork?: string;
|
|
87
|
+
binary?: string;
|
|
88
|
+
allowRunDeps?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* SyncRebuildResult reports the conditional rebuild: whether it passed,
|
|
92
|
+
* whether a build actually ran (a fresh clone, or a rebase of a checkout that
|
|
93
|
+
* was behind), a detail for a failure, and the captured log text (the decision,
|
|
94
|
+
* the build command, and its output) the deploy step threads to the card's
|
|
95
|
+
* Logs section.
|
|
96
|
+
*/
|
|
97
|
+
export interface SyncRebuildResult {
|
|
98
|
+
ok: boolean;
|
|
99
|
+
rebuilt: boolean;
|
|
100
|
+
detail?: string;
|
|
101
|
+
logs: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* syncAndRebuildEngine brings the engine checkout to the listed source and
|
|
105
|
+
* rebuilds it only when it needs to. A missing checkout is a fresh clone of
|
|
106
|
+
* the fork (or the upstream repo) and always builds. An existing checkout is
|
|
107
|
+
* fetched and priced against the remote branch: when it is behind and clean,
|
|
108
|
+
* the primary branch is checked out, rebased onto the remote branch, and the
|
|
109
|
+
* working tree is cleaned (git clean -fxd) before the build; when it is not
|
|
110
|
+
* behind, or it carries local changes, the build is skipped so a launch does
|
|
111
|
+
* not rebuild a checkout that is current. The decision, the build command,
|
|
112
|
+
* and its output stream through log and are returned as the result's logs.
|
|
113
|
+
* After a rebuild, the engine binary is checked against the path; when a build
|
|
114
|
+
* that did not install it leaves it out, a symlink into /usr/local/bin is
|
|
115
|
+
* added (run when the trust gate is on, written to the logs when it is off).
|
|
116
|
+
*/
|
|
117
|
+
export declare function syncAndRebuildEngine(host: LifecycleHost, request: SyncRebuildRequest, log: AcquireLog): Promise<SyncRebuildResult>;
|
|
118
|
+
/**
|
|
119
|
+
* buildEngineRepo runs the engine's build steps in the checkout, streaming
|
|
120
|
+
* each step's output through log. An engine with no build steps (the compose
|
|
121
|
+
* engines) passes through with a note; a failing step stops the build with
|
|
122
|
+
* the step's detail.
|
|
123
|
+
*/
|
|
124
|
+
export declare function buildEngineRepo(host: LifecycleHost, engineId: string, directory: string, log: AcquireLog): Promise<EngineStepResult>;
|
|
125
|
+
//# sourceMappingURL=acquire.d.ts.map
|