@otto-code/brain 0.8.7 → 0.8.9
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/dist/cli.js +2 -1
- package/dist/commands/bench.js +19 -5
- package/dist/commands/catalog.d.ts +3 -0
- package/dist/commands/catalog.js +2 -0
- package/dist/commands/pull.d.ts +1 -0
- package/dist/commands/pull.js +47 -14
- package/dist/commands/repo-download.d.ts +14 -0
- package/dist/commands/repo-download.js +29 -0
- package/dist/commands/runtime.d.ts +3 -0
- package/dist/commands/runtime.js +65 -18
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.js +38 -17
- package/dist/config/builtin-hosting-profiles.d.ts +8 -0
- package/dist/config/builtin-hosting-profiles.js +32 -0
- package/dist/config/hosting-profiles.d.ts +33 -0
- package/dist/config/hosting-profiles.js +71 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/config/paths.d.ts +2 -0
- package/dist/config/paths.js +1 -0
- package/dist/config/profile-edit.d.ts +5 -3
- package/dist/config/profile-edit.js +134 -14
- package/dist/config/profiles.js +66 -3
- package/dist/config/schema.d.ts +998 -0
- package/dist/config/schema.js +79 -0
- package/dist/config/store.js +28 -16
- package/dist/gguf.d.ts +1 -0
- package/dist/gguf.js +1 -0
- package/dist/models/download.d.ts +7 -0
- package/dist/models/download.js +165 -17
- package/dist/models/enrich.d.ts +6 -19
- package/dist/models/enrich.js +138 -4
- package/dist/models/hf.d.ts +14 -1
- package/dist/models/hf.js +239 -6
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.js +8 -5
- package/dist/models/manage.d.ts +4 -0
- package/dist/models/manage.js +34 -4
- package/dist/models/scan.js +8 -42
- package/dist/ops/calibrate.d.ts +4 -1
- package/dist/ops/calibrate.js +10 -7
- package/dist/ops/sweep.d.ts +3 -1
- package/dist/ops/sweep.js +3 -3
- package/dist/runtime/args.d.ts +2 -2
- package/dist/runtime/args.js +18 -1
- package/dist/runtime/index.d.ts +8 -1
- package/dist/runtime/index.js +11 -1
- package/dist/runtime/managed.d.ts +40 -0
- package/dist/runtime/managed.js +146 -7
- package/dist/service/host-api.d.ts +20 -3
- package/dist/service/host-api.js +313 -20
- package/dist/service/router.d.ts +18 -0
- package/dist/service/router.js +89 -4
- package/dist/service/serve.js +221 -22
- package/dist/service/supervisor.d.ts +32 -4
- package/dist/service/supervisor.js +30 -6
- package/dist/tui/app.js +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/vram.d.ts +3 -0
- package/dist/vram.js +24 -6
- package/package.json +1 -1
package/dist/runtime/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { listRuntimes as listLmStudioRuntimes, resolveOverride } from "./lmstudi
|
|
|
16
16
|
import { defaultRuntimeSpec, installManagedRuntime, listManagedRuntimes, } from "./managed.js";
|
|
17
17
|
export { BACKENDS_DIR, LMSTUDIO_ROOT, listRuntimes as listLmStudioRuntimes } from "./lmstudio.js";
|
|
18
18
|
export { buildArgs, buildEnv, formatCommand } from "./args.js";
|
|
19
|
-
export { installManagedRuntime, listManagedRuntimes, listRuntimeDevices, verifyRuntimeExecutable, defaultRuntimeSpec, extractArchive, resolveRuntimeVariant, serverExeName, supportedVariants, DEFAULT_LLAMA_BUILD, } from "./managed.js";
|
|
19
|
+
export { installManagedRuntime, removeManagedRuntime, listManagedRuntimes, listRuntimeDevices, verifyRuntimeExecutable, defaultRuntimeSpec, extractArchive, resolveRuntimeVariant, serverExeName, supportedVariants, DEFAULT_LLAMA_BUILD, listRuntimeReleases, latestRuntimeBuild, resolveLatestBuildOrPin, MissingAssetError, } from "./managed.js";
|
|
20
20
|
/** Every runtime available on this machine, managed first then LM Studio. */
|
|
21
21
|
export function listAllRuntimes(env = process.env) {
|
|
22
22
|
const paths = resolveBrainPaths(env);
|
|
@@ -44,6 +44,16 @@ export function resolveRuntime(config, env = process.env) {
|
|
|
44
44
|
return lmstudio[0] ?? null;
|
|
45
45
|
return managed[0] ?? lmstudio[0] ?? null; // auto
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* The numeric llama.cpp build carried by a resolved runtime, when its source
|
|
49
|
+
* identifies one. LM Studio and explicit overrides need not expose their
|
|
50
|
+
* upstream build, so callers must treat null as incompatible with a component
|
|
51
|
+
* that declares a minimum build rather than guessing compatibility.
|
|
52
|
+
*/
|
|
53
|
+
export function runtimeBuild(runtime) {
|
|
54
|
+
const match = /^b(\d+)$/iu.exec(runtime?.version ?? "");
|
|
55
|
+
return match ? Number(match[1]) : null;
|
|
56
|
+
}
|
|
47
57
|
/** Ensure a runtime exists, downloading the default managed build if none does. */
|
|
48
58
|
export async function ensureRuntime(config, env = process.env, onProgress, target = {}) {
|
|
49
59
|
const existing = resolveRuntime(config, env);
|
|
@@ -30,6 +30,33 @@ export interface InstallProgress {
|
|
|
30
30
|
* names (see the module header) - the naming scheme is not stable across tags.
|
|
31
31
|
*/
|
|
32
32
|
export declare const DEFAULT_LLAMA_BUILD = "b10265";
|
|
33
|
+
export interface RuntimeRelease {
|
|
34
|
+
build: string;
|
|
35
|
+
publishedAt: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** Official llama.cpp builds, newest first. Only numbered build releases are actionable. */
|
|
38
|
+
export declare function listRuntimeReleases(limit?: number): Promise<RuntimeRelease[]>;
|
|
39
|
+
/** Resolve the latest official build at the last responsible moment. */
|
|
40
|
+
export declare function latestRuntimeBuild(): Promise<string>;
|
|
41
|
+
export interface ResolvedBuild {
|
|
42
|
+
build: string;
|
|
43
|
+
/** A single line explaining a fallback, or null when "latest" resolved. */
|
|
44
|
+
warning: string | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The build to install for a "latest" request, with the pin as the safety net.
|
|
48
|
+
*
|
|
49
|
+
* Asking upstream is a best effort, never a precondition. `listRuntimeReleases`
|
|
50
|
+
* hits api.github.com unauthenticated, which is rate limited to 60 requests per
|
|
51
|
+
* hour per IP: behind NAT, on a corporate egress or on a CI runner that is a 403
|
|
52
|
+
* on an address that has spent its budget on something else entirely. An install
|
|
53
|
+
* the pinned build can serve must not fail because a version lookup did.
|
|
54
|
+
*
|
|
55
|
+
* The warning is deliberately one line. The daemon's BrainOpsManager keeps the
|
|
56
|
+
* *last* stderr line as the job's message, so a wrapped warning would surface in
|
|
57
|
+
* the GUI as a dangling fragment.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveLatestBuildOrPin(): Promise<ResolvedBuild>;
|
|
33
60
|
/** The binary name llama.cpp ships for a platform. */
|
|
34
61
|
export declare function serverExeName(platform?: NodeJS.Platform): string;
|
|
35
62
|
/**
|
|
@@ -49,6 +76,17 @@ export declare function supportedVariants(platform?: NodeJS.Platform, arch?: str
|
|
|
49
76
|
* best accelerator first and newest build first within an accelerator.
|
|
50
77
|
*/
|
|
51
78
|
export declare function listManagedRuntimes(runtimesDir: string, platform?: NodeJS.Platform): Runtime[];
|
|
79
|
+
/**
|
|
80
|
+
* An asset upstream does not serve at all, which is what a renamed asset or a
|
|
81
|
+
* tag without the expected build looks like from here. Distinguished from every
|
|
82
|
+
* other download failure because it is the one a caller can answer by retrying
|
|
83
|
+
* a build whose asset names are pinned and tested, rather than by retrying the
|
|
84
|
+
* same URL later.
|
|
85
|
+
*/
|
|
86
|
+
export declare class MissingAssetError extends Error {
|
|
87
|
+
readonly url: string;
|
|
88
|
+
constructor(url: string);
|
|
89
|
+
}
|
|
52
90
|
/**
|
|
53
91
|
* The actionable message for a dynamic-loader failure, or null when the output
|
|
54
92
|
* is not one. Pure, so the classification is testable without spawning.
|
|
@@ -87,4 +125,6 @@ export declare function listRuntimeDevices(runtime: Runtime, platform?: NodeJS.P
|
|
|
87
125
|
export declare function extractArchive(archivePath: string, destDir: string, platform?: NodeJS.Platform): Promise<void>;
|
|
88
126
|
/** Download + extract a runtime spec into runtimesDir and return the Runtime. */
|
|
89
127
|
export declare function installManagedRuntime(spec: RuntimeSpec, runtimesDir: string, onProgress?: (progress: InstallProgress) => void): Promise<Runtime>;
|
|
128
|
+
/** Remove one Otto-managed runtime. LM Studio files are outside this root. */
|
|
129
|
+
export declare function removeManagedRuntime(runtimesDir: string, name: string): void;
|
|
90
130
|
//# sourceMappingURL=managed.d.ts.map
|
package/dist/runtime/managed.js
CHANGED
|
@@ -63,8 +63,75 @@ import { buildEnv } from "./args.js";
|
|
|
63
63
|
*/
|
|
64
64
|
export const DEFAULT_LLAMA_BUILD = "b10265";
|
|
65
65
|
const LLAMA_RELEASE_BASE = "https://github.com/ggml-org/llama.cpp/releases/download";
|
|
66
|
+
const LLAMA_RELEASE_API = "https://api.github.com/repos/ggml-org/llama.cpp/releases";
|
|
67
|
+
/**
|
|
68
|
+
* How many releases to read when resolving "latest".
|
|
69
|
+
*
|
|
70
|
+
* Not 1: the newest release is not necessarily a numbered build (upstream also
|
|
71
|
+
* publishes differently tagged and pre-release entries), and a single
|
|
72
|
+
* non-matching entry would otherwise read as "llama.cpp published no release
|
|
73
|
+
* builds" while the very next one qualified.
|
|
74
|
+
*/
|
|
75
|
+
const LATEST_BUILD_SCAN_PAGE = 30;
|
|
76
|
+
/** Official llama.cpp builds, newest first. Only numbered build releases are actionable. */
|
|
77
|
+
export async function listRuntimeReleases(limit = 100) {
|
|
78
|
+
const response = await fetch(`${LLAMA_RELEASE_API}?per_page=${Math.min(Math.max(limit, 1), 100)}`, {
|
|
79
|
+
headers: { Accept: "application/vnd.github+json" },
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok)
|
|
82
|
+
throw new Error(`could not fetch llama.cpp releases (${response.status})`);
|
|
83
|
+
const releases = await response.json();
|
|
84
|
+
if (!Array.isArray(releases))
|
|
85
|
+
throw new Error("llama.cpp releases response was invalid");
|
|
86
|
+
return releases.flatMap((release) => {
|
|
87
|
+
if (!release || typeof release !== "object")
|
|
88
|
+
return [];
|
|
89
|
+
const value = release;
|
|
90
|
+
const build = typeof value.tag_name === "string" ? value.tag_name : "";
|
|
91
|
+
return /^b\d+$/.test(build)
|
|
92
|
+
? [{ build, publishedAt: typeof value.published_at === "string" ? value.published_at : null }]
|
|
93
|
+
: [];
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** Resolve the latest official build at the last responsible moment. */
|
|
97
|
+
export async function latestRuntimeBuild() {
|
|
98
|
+
const [latest] = await listRuntimeReleases(LATEST_BUILD_SCAN_PAGE);
|
|
99
|
+
if (!latest)
|
|
100
|
+
throw new Error("llama.cpp published no release builds");
|
|
101
|
+
return latest.build;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The build to install for a "latest" request, with the pin as the safety net.
|
|
105
|
+
*
|
|
106
|
+
* Asking upstream is a best effort, never a precondition. `listRuntimeReleases`
|
|
107
|
+
* hits api.github.com unauthenticated, which is rate limited to 60 requests per
|
|
108
|
+
* hour per IP: behind NAT, on a corporate egress or on a CI runner that is a 403
|
|
109
|
+
* on an address that has spent its budget on something else entirely. An install
|
|
110
|
+
* the pinned build can serve must not fail because a version lookup did.
|
|
111
|
+
*
|
|
112
|
+
* The warning is deliberately one line. The daemon's BrainOpsManager keeps the
|
|
113
|
+
* *last* stderr line as the job's message, so a wrapped warning would surface in
|
|
114
|
+
* the GUI as a dangling fragment.
|
|
115
|
+
*/
|
|
116
|
+
export async function resolveLatestBuildOrPin() {
|
|
117
|
+
try {
|
|
118
|
+
return { build: await latestRuntimeBuild(), warning: null };
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return {
|
|
122
|
+
build: DEFAULT_LLAMA_BUILD,
|
|
123
|
+
warning: `could not look up the latest llama.cpp build (${oneLine(error)}), so Otto installed` +
|
|
124
|
+
` the pinned build ${DEFAULT_LLAMA_BUILD} instead.`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Flatten a message to one line, so a warning survives the last-line rule. */
|
|
129
|
+
function oneLine(error) {
|
|
130
|
+
return (error instanceof Error ? error.message : String(error)).replace(/\s+/gu, " ").trim();
|
|
131
|
+
}
|
|
66
132
|
/** The CUDA toolkit version whose Windows assets we pin. */
|
|
67
133
|
const WINDOWS_CUDA = "12.4";
|
|
134
|
+
const MANAGED_RUNTIME_METADATA_FILE = ".otto-runtime.json";
|
|
68
135
|
/** The binary name llama.cpp ships for a platform. */
|
|
69
136
|
export function serverExeName(platform = process.platform) {
|
|
70
137
|
return platform === "win32" ? "llama-server.exe" : "llama-server";
|
|
@@ -181,6 +248,34 @@ function slug(spec) {
|
|
|
181
248
|
.replace(/[^a-z0-9]+/g, "-")
|
|
182
249
|
.replace(/(^-|-$)/g, "");
|
|
183
250
|
}
|
|
251
|
+
function displayNameForManagedRuntime(label, version) {
|
|
252
|
+
return `${label.replace(/\s*\(managed\)$/iu, "")} · ${version} (Otto managed)`;
|
|
253
|
+
}
|
|
254
|
+
function legacyManagedDisplayName(dirName, version) {
|
|
255
|
+
const cuda = /^cuda-(\d+)-(\d+)-managed(?:-|$)/iu.exec(dirName);
|
|
256
|
+
if (cuda)
|
|
257
|
+
return `CUDA ${cuda[1]}.${cuda[2]} · ${version} (Otto managed)`;
|
|
258
|
+
if (/^vulkan-managed(?:-|$)/iu.test(dirName))
|
|
259
|
+
return `Vulkan · ${version} (Otto managed)`;
|
|
260
|
+
if (/^metal-managed(?:-|$)/iu.test(dirName))
|
|
261
|
+
return `Metal · ${version} (Otto managed)`;
|
|
262
|
+
if (/^cpu-managed(?:-|$)/iu.test(dirName))
|
|
263
|
+
return `CPU · ${version} (Otto managed)`;
|
|
264
|
+
return `${version} (Otto managed)`;
|
|
265
|
+
}
|
|
266
|
+
function readManagedRuntimeDisplayName(root, dirName, version) {
|
|
267
|
+
try {
|
|
268
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(root, MANAGED_RUNTIME_METADATA_FILE), "utf8"));
|
|
269
|
+
if (typeof parsed.displayName === "string" && parsed.displayName.trim()) {
|
|
270
|
+
return parsed.displayName;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// Existing installations predate metadata; their directory name remains a
|
|
275
|
+
// backend-only migration source, never a UI presentation contract.
|
|
276
|
+
}
|
|
277
|
+
return legacyManagedDisplayName(dirName, version);
|
|
278
|
+
}
|
|
184
279
|
/** Recursively find the first file named `name` under `dir`. */
|
|
185
280
|
function findFile(dir, name) {
|
|
186
281
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -230,9 +325,11 @@ export function listManagedRuntimes(runtimesDir, platform = process.platform) {
|
|
|
230
325
|
const exe = findFile(root, exeName);
|
|
231
326
|
if (!exe)
|
|
232
327
|
continue;
|
|
328
|
+
const version = entry.name.replace(/^.*-/, "");
|
|
233
329
|
found.push({
|
|
234
330
|
label: entry.name,
|
|
235
|
-
|
|
331
|
+
displayName: readManagedRuntimeDisplayName(root, entry.name, version),
|
|
332
|
+
version,
|
|
236
333
|
dir: path.dirname(exe),
|
|
237
334
|
exe,
|
|
238
335
|
vendorDir: null,
|
|
@@ -242,8 +339,24 @@ export function listManagedRuntimes(runtimesDir, platform = process.platform) {
|
|
|
242
339
|
return found.sort((a, b) => managedVariantRank(a.label) - managedVariantRank(b.label) ||
|
|
243
340
|
buildNumber(b.version) - buildNumber(a.version));
|
|
244
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* An asset upstream does not serve at all, which is what a renamed asset or a
|
|
344
|
+
* tag without the expected build looks like from here. Distinguished from every
|
|
345
|
+
* other download failure because it is the one a caller can answer by retrying
|
|
346
|
+
* a build whose asset names are pinned and tested, rather than by retrying the
|
|
347
|
+
* same URL later.
|
|
348
|
+
*/
|
|
349
|
+
export class MissingAssetError extends Error {
|
|
350
|
+
constructor(url) {
|
|
351
|
+
super(`download failed (404) for ${url}`);
|
|
352
|
+
this.url = url;
|
|
353
|
+
this.name = "MissingAssetError";
|
|
354
|
+
}
|
|
355
|
+
}
|
|
245
356
|
async function downloadFile(url, dest, onProgress) {
|
|
246
357
|
const response = await fetch(url);
|
|
358
|
+
if (response.status === 404)
|
|
359
|
+
throw new MissingAssetError(url);
|
|
247
360
|
if (!response.ok || !response.body) {
|
|
248
361
|
throw new Error(`download failed (${response.status}) for ${url}`);
|
|
249
362
|
}
|
|
@@ -480,13 +593,26 @@ export async function extractArchive(archivePath, destDir, platform = process.pl
|
|
|
480
593
|
export async function installManagedRuntime(spec, runtimesDir, onProgress) {
|
|
481
594
|
const platform = spec.platform ?? process.platform;
|
|
482
595
|
const targetDir = path.join(runtimesDir, slug(spec));
|
|
596
|
+
const preexisting = fs.existsSync(targetDir);
|
|
483
597
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
484
|
-
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
598
|
+
try {
|
|
599
|
+
for (const url of spec.assets) {
|
|
600
|
+
const archivePath = path.join(targetDir, path.basename(new URL(url).pathname));
|
|
601
|
+
await downloadFile(url, archivePath, onProgress);
|
|
602
|
+
onProgress?.({ phase: "extracting", asset: url });
|
|
603
|
+
await extractArchive(archivePath, targetDir, platform);
|
|
604
|
+
fs.rmSync(archivePath, { force: true });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
// A half-installed directory is worse than no directory: Windows CUDA needs
|
|
609
|
+
// two archives, so a 404 on the companion leaves a llama-server with no CUDA
|
|
610
|
+
// runtime beside it, and `listManagedRuntimes` ranks by build number - that
|
|
611
|
+
// newer, broken build would then outrank the working runtime it replaced.
|
|
612
|
+
// Only a directory this call created is removed; an existing install stands.
|
|
613
|
+
if (!preexisting)
|
|
614
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
615
|
+
throw error;
|
|
490
616
|
}
|
|
491
617
|
const exeName = serverExeName(platform);
|
|
492
618
|
const exe = findFile(targetDir, exeName);
|
|
@@ -499,6 +625,7 @@ export async function installManagedRuntime(spec, runtimesDir, onProgress) {
|
|
|
499
625
|
}
|
|
500
626
|
const runtime = {
|
|
501
627
|
label: spec.label,
|
|
628
|
+
displayName: displayNameForManagedRuntime(spec.label, spec.version),
|
|
502
629
|
version: spec.version,
|
|
503
630
|
dir: path.dirname(exe),
|
|
504
631
|
exe,
|
|
@@ -508,7 +635,19 @@ export async function installManagedRuntime(spec, runtimesDir, onProgress) {
|
|
|
508
635
|
// Before reporting success. An install that cannot exec is not an install, and
|
|
509
636
|
// the loader error names the cause far better than the later spawn failure does.
|
|
510
637
|
await verifyRuntimeExecutable(runtime, platform);
|
|
638
|
+
fs.writeFileSync(path.join(targetDir, MANAGED_RUNTIME_METADATA_FILE), `${JSON.stringify({ displayName: runtime.displayName })}\n`);
|
|
511
639
|
onProgress?.({ phase: "done" });
|
|
512
640
|
return runtime;
|
|
513
641
|
}
|
|
642
|
+
/** Remove one Otto-managed runtime. LM Studio files are outside this root. */
|
|
643
|
+
export function removeManagedRuntime(runtimesDir, name) {
|
|
644
|
+
if (!/^[a-z0-9][a-z0-9-]*$/i.test(name))
|
|
645
|
+
throw new Error("invalid runtime name");
|
|
646
|
+
const root = path.resolve(runtimesDir);
|
|
647
|
+
const target = path.resolve(root, name);
|
|
648
|
+
if (path.dirname(target) !== root || !fs.existsSync(target)) {
|
|
649
|
+
throw new Error(`managed runtime not found: ${name}`);
|
|
650
|
+
}
|
|
651
|
+
fs.rmSync(target, { recursive: true, force: false });
|
|
652
|
+
}
|
|
514
653
|
//# sourceMappingURL=managed.js.map
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type http from "node:http";
|
|
22
22
|
import { calibrationInfo, profileWarnings } from "../config/profile-edit.js";
|
|
23
|
-
import type
|
|
23
|
+
import { type Profile, type ProfileDefaults, type ProfilesStore } from "../config/schema.js";
|
|
24
24
|
import { planDelete } from "../models/manage.js";
|
|
25
25
|
import type { RankedModel } from "../ops/results.js";
|
|
26
26
|
import type { GpuInfo, Model } from "../types.js";
|
|
@@ -82,11 +82,11 @@ export interface HostCapabilities {
|
|
|
82
82
|
/** A long-running operation owned by this brain host, not its caller. */
|
|
83
83
|
export interface HostJob {
|
|
84
84
|
id: string;
|
|
85
|
-
kind: "pull" | "runtime-install" | "calibrate" | "sweep" | "bench";
|
|
85
|
+
kind: "pull" | "runtime-install" | "runtime-remove" | "calibrate" | "sweep" | "bench";
|
|
86
86
|
label: string;
|
|
87
87
|
target: string | null;
|
|
88
88
|
status: "running" | "succeeded" | "failed" | "canceled";
|
|
89
|
-
percent: null;
|
|
89
|
+
percent: number | null;
|
|
90
90
|
message: string | null;
|
|
91
91
|
error: string | null;
|
|
92
92
|
startedAt: string;
|
|
@@ -130,6 +130,8 @@ export interface HostApiDeps {
|
|
|
130
130
|
export interface InventoryRow {
|
|
131
131
|
id: string;
|
|
132
132
|
displayName: string;
|
|
133
|
+
/** Curated model-family identity for the Otto Brain client glyph. */
|
|
134
|
+
family: string | null;
|
|
133
135
|
publisher: string | null;
|
|
134
136
|
quant: string | null;
|
|
135
137
|
sizeBytes: number;
|
|
@@ -153,7 +155,21 @@ export interface InventoryRow {
|
|
|
153
155
|
score: RankedModel | null;
|
|
154
156
|
state: "loaded" | "loading" | "not-loaded";
|
|
155
157
|
warnings: ReturnType<typeof profileWarnings>;
|
|
158
|
+
components: NonNullable<Model["components"]> | null;
|
|
156
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Apply the hosting-profile half of a profile patch, mutating both `profile`
|
|
162
|
+
* (this model's selection) and `store` (the shared library and family default).
|
|
163
|
+
*
|
|
164
|
+
* Separate from `sanitizeProfilePatch` because these keys are not profile
|
|
165
|
+
* fields: three of the five write to the store rather than the profile.
|
|
166
|
+
* Exported for testing - the ordering and the cross-profile cleanup are the
|
|
167
|
+
* parts worth pinning down, and they are unreachable through the HTTP surface
|
|
168
|
+
* without standing up a service.
|
|
169
|
+
*
|
|
170
|
+
* Throws on any invalid input; the caller turns that into a 400.
|
|
171
|
+
*/
|
|
172
|
+
export declare function applyHostingProfilePatch(store: ProfilesStore, model: Model, profile: Profile, patch: Record<string, unknown>, onDelete?: ((id: string) => void) | undefined): void;
|
|
157
173
|
/**
|
|
158
174
|
* Join one model's scan row, GGUF metadata, saved profile, calibration, VRAM
|
|
159
175
|
* budget and benchmark score into the single shape the Models tab renders.
|
|
@@ -168,6 +184,7 @@ export declare function buildInventoryRow(params: {
|
|
|
168
184
|
gpu: GpuInfo | null;
|
|
169
185
|
ranking: RankedModel[];
|
|
170
186
|
supervisor: Supervisor;
|
|
187
|
+
runtimeBuild?: number | null;
|
|
171
188
|
}): InventoryRow;
|
|
172
189
|
export interface HostApi {
|
|
173
190
|
/** Returns true when it answered the request, false to fall through. */
|