@otto-code/brain 0.8.8 → 0.8.10
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/commands/bench.js +19 -5
- package/dist/commands/catalog.d.ts +1 -0
- package/dist/commands/catalog.js +1 -0
- package/dist/commands/pull.js +14 -33
- package/dist/commands/repo-download.d.ts +14 -0
- package/dist/commands/repo-download.js +29 -0
- package/dist/commands/search.js +6 -14
- 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 +4 -2
- package/dist/config/profile-edit.js +94 -4
- package/dist/config/profiles.js +25 -2
- package/dist/config/schema.d.ts +494 -24
- package/dist/config/schema.js +55 -0
- package/dist/config/store.js +12 -2
- package/dist/gguf.d.ts +1 -0
- package/dist/gguf.js +1 -0
- package/dist/models/download.js +5 -0
- package/dist/models/enrich.d.ts +6 -0
- package/dist/models/enrich.js +50 -2
- 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 +13 -1
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +10 -0
- package/dist/service/host-api.d.ts +17 -1
- package/dist/service/host-api.js +196 -13
- package/dist/service/router.d.ts +18 -0
- package/dist/service/router.js +89 -4
- package/dist/service/serve.js +184 -12
- 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 +4 -0
- package/dist/vram.js +3 -3
- package/package.json +1 -1
package/dist/config/schema.js
CHANGED
|
@@ -25,11 +25,54 @@ export const ProfileSchema = z
|
|
|
25
25
|
reasoningBudget: z.number().default(1536),
|
|
26
26
|
reasoningBudgetMessage: z.string().default(DEFAULT_REASONING_MESSAGE),
|
|
27
27
|
parallelSlots: z.number().default(1),
|
|
28
|
+
/** RoPE extension factor; 1 keeps the GGUF-native context window. */
|
|
29
|
+
contextMultiplier: z.number().default(1),
|
|
30
|
+
/** Cleared only by a successful calibration of this saved model profile. */
|
|
31
|
+
calibrationRequired: z.boolean().default(true),
|
|
28
32
|
batchSize: z.number().nullable().default(null),
|
|
29
33
|
ubatchSize: z.number().nullable().default(null),
|
|
30
34
|
extraArgs: z.array(z.string()).default([]),
|
|
35
|
+
/** The selected profile id when `hostingProfileMode` is `custom`. */
|
|
36
|
+
hostingProfileId: z.string().nullable().default(null),
|
|
37
|
+
/**
|
|
38
|
+
* Whether this model inherits its family's profile, disables profiles, or
|
|
39
|
+
* selects one itself. `inherit` is the default because it is what a profile
|
|
40
|
+
* stored before this field existed meant: the family default applied to
|
|
41
|
+
* every model in the family that had not overridden it. Defaulting to `off`
|
|
42
|
+
* would silently drop that default on every profile written by an older
|
|
43
|
+
* Brain. With no family default set, `inherit` resolves to nothing, which
|
|
44
|
+
* is exactly what `off` does, so the safe default costs nothing.
|
|
45
|
+
*/
|
|
46
|
+
hostingProfileMode: z.enum(["inherit", "off", "custom"]).default("inherit"),
|
|
47
|
+
/** Derived at load time from the selected Brain-owned hosting profile. */
|
|
48
|
+
chatTemplateFile: z.string().nullable().default(null),
|
|
49
|
+
/** Derived at load time; passed directly to llama-server's Jinja engine. */
|
|
50
|
+
chatTemplateKwargs: z.record(z.unknown()).default({}),
|
|
51
|
+
/**
|
|
52
|
+
* Derived at load time from the selected hosting profile's system-prompt
|
|
53
|
+
* addendum. The router injects it per request rather than the launcher
|
|
54
|
+
* baking it into a CLI flag; see `hosting-profiles.ts` for why.
|
|
55
|
+
*/
|
|
56
|
+
chatSystemAddendum: z.string().nullable().default(null),
|
|
31
57
|
})
|
|
32
58
|
.passthrough();
|
|
59
|
+
/**
|
|
60
|
+
* A named, Brain-owned inference composition. The template is intentionally
|
|
61
|
+
* text, rather than a user-owned path: remote Brains must be able to apply the
|
|
62
|
+
* same profile and no client is allowed to make llama-server read arbitrary
|
|
63
|
+
* files from its host.
|
|
64
|
+
*/
|
|
65
|
+
export const HostingProfileSchema = z
|
|
66
|
+
.object({
|
|
67
|
+
id: z.string(),
|
|
68
|
+
name: z.string(),
|
|
69
|
+
family: z.string(),
|
|
70
|
+
description: z.string().default(""),
|
|
71
|
+
template: z.string().nullable().default(null),
|
|
72
|
+
systemPromptAddendum: z.string().nullable().default(null),
|
|
73
|
+
templateKwargs: z.record(z.unknown()).default({}),
|
|
74
|
+
})
|
|
75
|
+
.strict();
|
|
33
76
|
export const CalibrationSampleSchema = z
|
|
34
77
|
.object({
|
|
35
78
|
contextSize: z.number(),
|
|
@@ -67,6 +110,16 @@ export const ProfilesStoreSchema = z
|
|
|
67
110
|
profiles: z.record(ProfileSchema).default({}),
|
|
68
111
|
calibrations: z.record(z.record(CalibrationSchema)).default({}),
|
|
69
112
|
geometryCalibrations: z.record(GeometryCalibrationSchema).default({}),
|
|
113
|
+
/** Named reusable profiles, scoped to this Brain rather than any client. */
|
|
114
|
+
hostingProfiles: z.record(HostingProfileSchema).default({}),
|
|
115
|
+
/** Optional family default; individual model profiles may override it. */
|
|
116
|
+
familyHostingProfileIds: z.record(z.string().nullable()).default({}),
|
|
117
|
+
/**
|
|
118
|
+
* A saved edit made while that model was resident. It stays visible when
|
|
119
|
+
* the user leaves and revisits the model settings, and clears only after a
|
|
120
|
+
* successful fresh llama-server load of that model.
|
|
121
|
+
*/
|
|
122
|
+
pendingReloadModelIds: z.record(z.boolean()).default({}),
|
|
70
123
|
lastModelId: z.string().nullable().default(null),
|
|
71
124
|
})
|
|
72
125
|
.passthrough();
|
|
@@ -169,6 +222,8 @@ export const CatalogModelSchema = z
|
|
|
169
222
|
/** Retired Otto-curated ids this canonical catalog entry replaces. */
|
|
170
223
|
replaces: z.array(z.string()).optional(),
|
|
171
224
|
name: z.string(),
|
|
225
|
+
/** Stable UI family identity. Otto clients resolve this to a monochrome glyph. */
|
|
226
|
+
family: z.string().optional(),
|
|
172
227
|
publisher: z.string().optional(),
|
|
173
228
|
hfRepo: z.string(),
|
|
174
229
|
quant: z.string(),
|
package/dist/config/store.js
CHANGED
|
@@ -10,6 +10,7 @@ import { writePrivateFileAtomicSync } from "./private-files.js";
|
|
|
10
10
|
import { packageRoot, resolveBrainPaths } from "./paths.js";
|
|
11
11
|
import { BrainConfigSchema, CatalogSchema, ProfilesStoreSchema, } from "./schema.js";
|
|
12
12
|
import { applyEnvOverrides } from "./env.js";
|
|
13
|
+
import { seedBuiltinHostingProfiles } from "./builtin-hosting-profiles.js";
|
|
13
14
|
function readJson(file, schema) {
|
|
14
15
|
if (!existsSync(file))
|
|
15
16
|
return null;
|
|
@@ -26,15 +27,24 @@ function writeJson(file, data) {
|
|
|
26
27
|
// ------------------------------------------------------------------- profiles
|
|
27
28
|
export function loadProfilesStore(paths = resolveBrainPaths()) {
|
|
28
29
|
const current = readJson(paths.profilesFile, ProfilesStoreSchema);
|
|
29
|
-
if (current)
|
|
30
|
+
if (current) {
|
|
31
|
+
if (seedBuiltinHostingProfiles(current))
|
|
32
|
+
writeJson(paths.profilesFile, current);
|
|
30
33
|
return current;
|
|
34
|
+
}
|
|
31
35
|
const legacy = path.join(packageRoot(), "config", "profiles.json");
|
|
32
36
|
const migrated = readJson(legacy, ProfilesStoreSchema);
|
|
33
37
|
if (migrated) {
|
|
38
|
+
seedBuiltinHostingProfiles(migrated);
|
|
34
39
|
writeJson(paths.profilesFile, migrated);
|
|
35
40
|
return migrated;
|
|
36
41
|
}
|
|
37
|
-
|
|
42
|
+
const seeded = ProfilesStoreSchema.parse({});
|
|
43
|
+
seedBuiltinHostingProfiles(seeded);
|
|
44
|
+
// A read-only command must not create profiles.json. In-memory seeding keeps
|
|
45
|
+
// built-ins available; the first real profile save persists the whole store.
|
|
46
|
+
// This avoids racing a running service that later writes its startup snapshot.
|
|
47
|
+
return seeded;
|
|
38
48
|
}
|
|
39
49
|
export function saveProfilesStore(store, paths = resolveBrainPaths()) {
|
|
40
50
|
writeJson(paths.profilesFile, store);
|
package/dist/gguf.d.ts
CHANGED
package/dist/gguf.js
CHANGED
|
@@ -210,6 +210,7 @@ export function summarize(file) {
|
|
|
210
210
|
tensorCount,
|
|
211
211
|
arch,
|
|
212
212
|
name: meta["general.name"] || null,
|
|
213
|
+
basename: meta["general.basename"] || null,
|
|
213
214
|
sizeLabel: meta["general.size_label"] || null,
|
|
214
215
|
fileType: (meta["general.file_type"] ?? null),
|
|
215
216
|
contextLength: (get("context_length") ?? null),
|
package/dist/models/download.js
CHANGED
|
@@ -98,6 +98,11 @@ async function streamRepoFile(url, destPath, label, token, onProgress, received)
|
|
|
98
98
|
mkdirSync(path.dirname(destPath), { recursive: true });
|
|
99
99
|
if (existsSync(destPath)) {
|
|
100
100
|
clearPartial(tmp);
|
|
101
|
+
// A bundle plan counts every selected artifact. An artifact that is
|
|
102
|
+
// already present is complete work, not zero work, otherwise the progress
|
|
103
|
+
// denominator includes it while the numerator never can.
|
|
104
|
+
received.bytes += statSync(destPath).size;
|
|
105
|
+
onProgress?.({ file: label, receivedBytes: received.bytes });
|
|
101
106
|
return false;
|
|
102
107
|
}
|
|
103
108
|
let partial = resumablePartial(tmp);
|
package/dist/models/enrich.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Catalog, CatalogModel } from "../config/schema.js";
|
|
2
2
|
import type { Model } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Normalize a GGUF identity into the catalog's hosting-profile vocabulary.
|
|
5
|
+
* Architecture is a stable structural field; names are only fallbacks for
|
|
6
|
+
* headers whose architecture is absent or unrecognised.
|
|
7
|
+
*/
|
|
8
|
+
export declare function familyFromGgufMetadata(model: Model): string | undefined;
|
|
3
9
|
/**
|
|
4
10
|
* Find the catalog entry a scanned model belongs to, or null. A model matches
|
|
5
11
|
* when its id path sits directly under the entry's hfRepo directory. When a repo
|
package/dist/models/enrich.js
CHANGED
|
@@ -19,6 +19,50 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import fs from "node:fs";
|
|
21
21
|
import path from "node:path";
|
|
22
|
+
/**
|
|
23
|
+
* Hosting-profile families deliberately use a small, curated vocabulary. It is
|
|
24
|
+
* a mix of publisher and model-line identities, so GGUF architecture names
|
|
25
|
+
* must be folded into the existing buckets rather than exposed directly.
|
|
26
|
+
*/
|
|
27
|
+
const FAMILY_BY_GGUF_IDENTIFIER = {
|
|
28
|
+
chatglm: "chatglm",
|
|
29
|
+
deepseek: "deepseek",
|
|
30
|
+
gemma: "gemma",
|
|
31
|
+
gptoss: "openai",
|
|
32
|
+
llama: "meta",
|
|
33
|
+
meta: "meta",
|
|
34
|
+
microsoft: "microsoft",
|
|
35
|
+
mistral: "mistral",
|
|
36
|
+
mixtral: "mistral",
|
|
37
|
+
nemotron: "nvidia",
|
|
38
|
+
nvidia: "nvidia",
|
|
39
|
+
openai: "openai",
|
|
40
|
+
phi: "microsoft",
|
|
41
|
+
qwen: "qwen",
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a GGUF identity into the catalog's hosting-profile vocabulary.
|
|
45
|
+
* Architecture is a stable structural field; names are only fallbacks for
|
|
46
|
+
* headers whose architecture is absent or unrecognised.
|
|
47
|
+
*/
|
|
48
|
+
export function familyFromGgufMetadata(model) {
|
|
49
|
+
const metadata = model.metadata;
|
|
50
|
+
if (!metadata)
|
|
51
|
+
return undefined;
|
|
52
|
+
for (const value of [metadata.arch, metadata.basename, metadata.name]) {
|
|
53
|
+
if (typeof value !== "string")
|
|
54
|
+
continue;
|
|
55
|
+
const identifier = value
|
|
56
|
+
.trim()
|
|
57
|
+
.toLowerCase()
|
|
58
|
+
.replace(/[^a-z0-9]/gu, "");
|
|
59
|
+
for (const [prefix, family] of Object.entries(FAMILY_BY_GGUF_IDENTIFIER)) {
|
|
60
|
+
if (identifier.startsWith(prefix))
|
|
61
|
+
return family;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
22
66
|
/** Normalize a repo/id path: forward slashes, lowercased, trailing slashes trimmed. */
|
|
23
67
|
function normalizePath(value) {
|
|
24
68
|
const normalized = value.replaceAll("\\", "/");
|
|
@@ -76,14 +120,18 @@ export function matchCatalogEntry(model, catalog) {
|
|
|
76
120
|
export function enrichWithCatalog(models, catalog) {
|
|
77
121
|
return models.map((model) => {
|
|
78
122
|
const entry = matchCatalogEntry(model, catalog);
|
|
79
|
-
if (!entry)
|
|
80
|
-
|
|
123
|
+
if (!entry) {
|
|
124
|
+
const enriched = enrichDiscoveredProjector(model);
|
|
125
|
+
const family = familyFromGgufMetadata(enriched);
|
|
126
|
+
return family ? { ...enriched, family } : enriched;
|
|
127
|
+
}
|
|
81
128
|
const components = resolveComponents(model, entry);
|
|
82
129
|
const projector = components?.find((component) => component.role === "vision_projector");
|
|
83
130
|
return {
|
|
84
131
|
...model,
|
|
85
132
|
catalogId: entry.id,
|
|
86
133
|
catalogHfRepo: entry.hfRepo,
|
|
134
|
+
family: entry.family,
|
|
87
135
|
components,
|
|
88
136
|
// A manifest is authoritative. Do not pair a random same-directory
|
|
89
137
|
// projector when the catalog declares the exact companion artifact.
|
package/dist/ops/calibrate.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Supervisor } from "../service/supervisor.js";
|
|
1
2
|
import type { Model, Runtime } from "../types.js";
|
|
2
3
|
import type { Profile } from "../config/schema.js";
|
|
3
4
|
/**
|
|
@@ -31,6 +32,8 @@ export interface CalibrateOptions {
|
|
|
31
32
|
profile: Profile;
|
|
32
33
|
samples?: number[];
|
|
33
34
|
internalPort?: number;
|
|
35
|
+
/** Reuse the host's resident supervisor instead of creating a sidecar server. */
|
|
36
|
+
supervisor?: Supervisor;
|
|
34
37
|
onProgress?: (event: CalibrateProgress) => void;
|
|
35
38
|
}
|
|
36
39
|
/** The measured KV-cache profile calibration produces. */
|
|
@@ -45,5 +48,5 @@ export interface CalibrationMeasurement {
|
|
|
45
48
|
vision: boolean;
|
|
46
49
|
measuredAt: string;
|
|
47
50
|
}
|
|
48
|
-
export declare function calibrate({ runtime, model, profile, samples, internalPort, onProgress, }: CalibrateOptions): Promise<CalibrationMeasurement>;
|
|
51
|
+
export declare function calibrate({ runtime, model, profile, samples, internalPort, supervisor: optionsSupervisor, onProgress, }: CalibrateOptions): Promise<CalibrationMeasurement>;
|
|
49
52
|
//# sourceMappingURL=calibrate.d.ts.map
|
package/dist/ops/calibrate.js
CHANGED
|
@@ -12,21 +12,24 @@ import { DEFAULT_INTERNAL_PORT, Supervisor } from "../service/supervisor.js";
|
|
|
12
12
|
* buffers) cancels out of the difference.
|
|
13
13
|
*/
|
|
14
14
|
export const DEFAULT_SAMPLES = [8192, 65536];
|
|
15
|
-
export async function calibrate({ runtime, model, profile, samples
|
|
16
|
-
|
|
15
|
+
export async function calibrate({ runtime, model, profile, samples, internalPort = DEFAULT_INTERNAL_PORT + 1, supervisor: optionsSupervisor, onProgress = () => { }, }) {
|
|
16
|
+
const nativeContext = model.metadata?.contextLength ?? null;
|
|
17
|
+
const effectiveSamples = samples ??
|
|
18
|
+
(nativeContext ? [8192, nativeContext * profile.contextMultiplier] : DEFAULT_SAMPLES);
|
|
19
|
+
if (effectiveSamples.length < 2)
|
|
17
20
|
throw new Error("calibration needs at least two context sizes");
|
|
18
|
-
const native =
|
|
21
|
+
const native = (nativeContext || Math.max(...effectiveSamples)) * profile.contextMultiplier;
|
|
19
22
|
const points = [];
|
|
20
|
-
for (const contextSize of
|
|
23
|
+
for (const contextSize of effectiveSamples) {
|
|
21
24
|
if (contextSize > native) {
|
|
22
|
-
onProgress({ phase: "skip", contextSize, reason: `exceeds
|
|
25
|
+
onProgress({ phase: "skip", contextSize, reason: `exceeds configured context ${native}` });
|
|
23
26
|
continue;
|
|
24
27
|
}
|
|
25
|
-
const supervisor = new Supervisor({ runtime, internalPort });
|
|
28
|
+
const supervisor = optionsSupervisor ?? new Supervisor({ runtime, internalPort });
|
|
26
29
|
onProgress({ phase: "loading", contextSize });
|
|
27
30
|
const baseline = await usedBytes();
|
|
28
31
|
try {
|
|
29
|
-
await supervisor.start(model, { ...profile, contextSize });
|
|
32
|
+
await supervisor.start(model, { ...profile, contextSize }, { preserveLogs: Boolean(optionsSupervisor) });
|
|
30
33
|
const used = supervisor.vramAtReadyBytes ?? (await usedBytes());
|
|
31
34
|
const delta = Number(used) - Number(supervisor.vramBaselineBytes ?? baseline);
|
|
32
35
|
points.push({ contextSize, deltaBytes: delta, loadSeconds: supervisor.loadSeconds });
|
package/dist/ops/sweep.d.ts
CHANGED
|
@@ -70,8 +70,10 @@ export interface SweepOptions {
|
|
|
70
70
|
maxTokens?: number;
|
|
71
71
|
temperature?: number;
|
|
72
72
|
internalPort?: number;
|
|
73
|
+
/** Reuse the host's resident supervisor instead of creating a sidecar server. */
|
|
74
|
+
supervisor?: Supervisor;
|
|
73
75
|
onProgress?: (event: SweepProgress) => void;
|
|
74
76
|
}
|
|
75
|
-
export declare function sweep({ runtime, model, profile, budgets, maxTokens, temperature, internalPort, onProgress, }: SweepOptions): Promise<SweepReport>;
|
|
77
|
+
export declare function sweep({ runtime, model, profile, budgets, maxTokens, temperature, internalPort, supervisor: optionsSupervisor, onProgress, }: SweepOptions): Promise<SweepReport>;
|
|
76
78
|
export {};
|
|
77
79
|
//# sourceMappingURL=sweep.d.ts.map
|
package/dist/ops/sweep.js
CHANGED
|
@@ -81,13 +81,13 @@ export async function runTrial({ supervisor, maxTokens, temperature, }) {
|
|
|
81
81
|
contentPerSecond: elapsedSeconds > 0 ? content.length / elapsedSeconds : 0,
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
export async function sweep({ runtime, model, profile, budgets = DEFAULT_BUDGETS, maxTokens = 8192, temperature = 0.7, internalPort = DEFAULT_INTERNAL_PORT + 2, onProgress = () => { }, }) {
|
|
84
|
+
export async function sweep({ runtime, model, profile, budgets = DEFAULT_BUDGETS, maxTokens = 8192, temperature = 0.7, internalPort = DEFAULT_INTERNAL_PORT + 2, supervisor: optionsSupervisor, onProgress = () => { }, }) {
|
|
85
85
|
const results = [];
|
|
86
86
|
for (const budget of budgets) {
|
|
87
|
-
const supervisor = new Supervisor({ runtime, internalPort });
|
|
87
|
+
const supervisor = optionsSupervisor ?? new Supervisor({ runtime, internalPort });
|
|
88
88
|
onProgress({ phase: "loading", budget });
|
|
89
89
|
try {
|
|
90
|
-
await supervisor.start(model, { ...profile, reasoningBudget: budget });
|
|
90
|
+
await supervisor.start(model, { ...profile, reasoningBudget: budget }, { preserveLogs: Boolean(optionsSupervisor) });
|
|
91
91
|
onProgress({ phase: "generating", budget });
|
|
92
92
|
const trial = await runTrial({ supervisor, maxTokens, temperature });
|
|
93
93
|
results.push({ budget, ...trial, error: null });
|
package/dist/runtime/args.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* managed one, since both resolve to a `Runtime` (exe + optional vendorDir).
|
|
5
5
|
*/
|
|
6
6
|
import type { Profile } from "../config/schema.js";
|
|
7
|
-
import type { Runtime } from "../types.js";
|
|
7
|
+
import type { Model, Runtime } from "../types.js";
|
|
8
8
|
export interface ServeTarget {
|
|
9
9
|
port: number;
|
|
10
10
|
host?: string;
|
|
@@ -28,7 +28,7 @@ export declare function buildEnv(runtime: Runtime, baseEnv?: NodeJS.ProcessEnv,
|
|
|
28
28
|
* Only settings that demonstrably matter for stable local inference are emitted -
|
|
29
29
|
* no experimental sampler knobs.
|
|
30
30
|
*/
|
|
31
|
-
export declare function buildArgs(profile: Profile, { port, host }: ServeTarget): string[];
|
|
31
|
+
export declare function buildArgs(profile: Profile, { port, host }: ServeTarget, model?: Model): string[];
|
|
32
32
|
/** The same command as a copy-pasteable shell line, for the TUI to display. */
|
|
33
33
|
export declare function formatCommand(runtime: Runtime, args: string[]): string;
|
|
34
34
|
//# sourceMappingURL=args.d.ts.map
|
package/dist/runtime/args.js
CHANGED
|
@@ -29,7 +29,7 @@ export function buildEnv(runtime, baseEnv = process.env, platform = process.plat
|
|
|
29
29
|
* Only settings that demonstrably matter for stable local inference are emitted -
|
|
30
30
|
* no experimental sampler knobs.
|
|
31
31
|
*/
|
|
32
|
-
export function buildArgs(profile, { port, host = "127.0.0.1" }) {
|
|
32
|
+
export function buildArgs(profile, { port, host = "127.0.0.1" }, model) {
|
|
33
33
|
const args = [
|
|
34
34
|
"-m",
|
|
35
35
|
profile.modelPath ?? "",
|
|
@@ -66,10 +66,22 @@ export function buildArgs(profile, { port, host = "127.0.0.1" }) {
|
|
|
66
66
|
}
|
|
67
67
|
if (profile.parallelSlots)
|
|
68
68
|
args.push("--parallel", String(profile.parallelSlots));
|
|
69
|
+
if (profile.contextMultiplier > 1) {
|
|
70
|
+
const nativeContext = model?.metadata?.contextLength;
|
|
71
|
+
args.push("--rope-scaling", "yarn", "--rope-scale", String(profile.contextMultiplier), ...(nativeContext ? ["--yarn-orig-ctx", String(nativeContext)] : []), ...(model?.metadata?.arch
|
|
72
|
+
? ["--override-kv", `${model.metadata.arch}.context_length=int:${profile.contextSize}`]
|
|
73
|
+
: []));
|
|
74
|
+
}
|
|
69
75
|
if (profile.batchSize)
|
|
70
76
|
args.push("-b", String(profile.batchSize));
|
|
71
77
|
if (profile.ubatchSize)
|
|
72
78
|
args.push("-ub", String(profile.ubatchSize));
|
|
79
|
+
if (profile.chatTemplateFile) {
|
|
80
|
+
args.push("--chat-template-file", profile.chatTemplateFile);
|
|
81
|
+
if (Object.keys(profile.chatTemplateKwargs ?? {}).length > 0) {
|
|
82
|
+
args.push("--chat-template-kwargs", JSON.stringify(profile.chatTemplateKwargs));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
73
85
|
if (profile.extraArgs && profile.extraArgs.length)
|
|
74
86
|
args.push(...profile.extraArgs);
|
|
75
87
|
return args;
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -14,6 +14,13 @@ export declare function listAllRuntimes(env?: NodeJS.ProcessEnv): Runtime[];
|
|
|
14
14
|
export declare function probeNvidiaGpu(): Promise<boolean>;
|
|
15
15
|
/** The runtime to use given config, or null when none is available. */
|
|
16
16
|
export declare function resolveRuntime(config: BrainConfig, env?: NodeJS.ProcessEnv): Runtime | null;
|
|
17
|
+
/**
|
|
18
|
+
* The numeric llama.cpp build carried by a resolved runtime, when its source
|
|
19
|
+
* identifies one. LM Studio and explicit overrides need not expose their
|
|
20
|
+
* upstream build, so callers must treat null as incompatible with a component
|
|
21
|
+
* that declares a minimum build rather than guessing compatibility.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runtimeBuild(runtime: Runtime | null | undefined): number | null;
|
|
17
24
|
/** Ensure a runtime exists, downloading the default managed build if none does. */
|
|
18
25
|
export declare function ensureRuntime(config: BrainConfig, env?: NodeJS.ProcessEnv, onProgress?: (progress: InstallProgress) => void, target?: RuntimeTarget): Promise<Runtime>;
|
|
19
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/runtime/index.js
CHANGED
|
@@ -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);
|
|
@@ -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";
|
|
@@ -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;
|
|
@@ -155,6 +157,19 @@ export interface InventoryRow {
|
|
|
155
157
|
warnings: ReturnType<typeof profileWarnings>;
|
|
156
158
|
components: NonNullable<Model["components"]> | null;
|
|
157
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;
|
|
158
173
|
/**
|
|
159
174
|
* Join one model's scan row, GGUF metadata, saved profile, calibration, VRAM
|
|
160
175
|
* budget and benchmark score into the single shape the Models tab renders.
|
|
@@ -169,6 +184,7 @@ export declare function buildInventoryRow(params: {
|
|
|
169
184
|
gpu: GpuInfo | null;
|
|
170
185
|
ranking: RankedModel[];
|
|
171
186
|
supervisor: Supervisor;
|
|
187
|
+
runtimeBuild?: number | null;
|
|
172
188
|
}): InventoryRow;
|
|
173
189
|
export interface HostApi {
|
|
174
190
|
/** Returns true when it answered the request, false to fall through. */
|