@khanglvm/llm-router 2.5.2 → 2.6.0
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/CHANGELOG.md +13 -0
- package/README.md +3 -0
- package/package.json +1 -1
- package/src/node/huggingface-gguf.js +12 -0
- package/src/node/llamacpp-managed-runtime.js +202 -0
- package/src/node/llamacpp-runtime-profile.js +133 -0
- package/src/node/llamacpp-runtime.js +256 -78
- package/src/node/local-models-service.js +25 -2
- package/src/node/local-server.js +60 -2
- package/src/node/web-console-client.js +20 -20
- package/src/node/web-console-server.js +64 -8
- package/src/node/web-console-styles.generated.js +1 -1
- package/src/node/web-console-ui/local-models-utils.js +33 -0
- package/src/runtime/handler/provider-call.js +36 -18
- package/src/runtime/handler/runtime-policy.js +4 -1
- package/src/runtime/local-models.js +36 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.6.0] - 2026-04-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Local `llama.cpp` variants can now persist a per-model runtime profile, including auto-tuned presets and custom launch overrides, so each GGUF variant can run with settings that match its own size and context shape instead of sharing one global `llama-server` startup profile.
|
|
14
|
+
- The Web UI now exposes managed `llama.cpp` runtime health for Local Models, including tracked instance counts, healthy/stale summaries, and persisted runtime-profile data for each saved variant.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Local variant requests are now resolved through a managed per-variant `llama.cpp` runtime layer that can reuse compatible instances, allocate fallback ports safely, and start the right runtime configuration for the specific model variant without exposing multi-process lifecycle management to the user.
|
|
18
|
+
- Hugging Face GGUF search/download flows now surface file size plus estimated runtime memory guidance directly in the Local Models workflow, making it easier to choose a viable quantization before download.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Managed `llama.cpp` runtimes now reconcile stale tracked instances before reuse, avoid reserving dead immediate-exit servers, and drain pending shutdown/startup edges more reliably so local per-model routing does not leave behind stale `llama-server` processes.
|
|
22
|
+
|
|
10
23
|
## [2.5.2] - 2026-04-23
|
|
11
24
|
|
|
12
25
|
### Fixed
|
package/README.md
CHANGED
|
@@ -44,6 +44,9 @@ Open `llr` and use the **Local Models** tab to manage local inference sources al
|
|
|
44
44
|
- **Native macOS browsing** — use the built-in file picker to choose a single GGUF file, scan a folder recursively for GGUF models, or browse directly to a local `llama-server` binary
|
|
45
45
|
- **Managed + attached model library** — stale or moved files stay visible instead of crashing the app, and can be repaired by locating the file again or removed cleanly
|
|
46
46
|
- **Router-visible local variants** — create friendly model variants with bounded presets, context-window metadata, preload toggles, and Mac unified-memory fit guidance with clearer safe/tight recommendations
|
|
47
|
+
- **Per-variant llama.cpp tuning** — each local variant can store its own runtime profile so balanced, throughput, long-context, low-memory, or custom launch overrides do not fight over one shared global `llama-server` config
|
|
48
|
+
- **Managed per-model runtimes** — the router automatically starts, reuses, and stops the right `llama.cpp` instance for the requested local variant, with stale-runtime cleanup handled internally instead of asking the user to manage separate servers
|
|
49
|
+
- **GGUF size + memory guidance** — Hugging Face search results now show model file size plus estimated runtime memory fit guidance before download, helping choose viable quantizations faster
|
|
47
50
|
- **Alias-ready local routing** — once saved, local variants behave like normal router models and can be used in aliases, capability flags, and fallback chains
|
|
48
51
|
|
|
49
52
|
For v1, the managed download flow only searches public Hugging Face GGUF files and the fit guidance is tuned for Macs with unified memory.
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
|
+
import { estimateLlamacppRuntimeBytes } from "./llamacpp-runtime-profile.js";
|
|
3
4
|
|
|
4
5
|
const HUGGING_FACE_API_URL = "https://huggingface.co/api/models";
|
|
5
6
|
const HUGGING_FACE_BASE_URL = "https://huggingface.co";
|
|
@@ -154,6 +155,13 @@ export function shapeHuggingFaceGgufResults(files, systemInfo = {}) {
|
|
|
154
155
|
expectedContextWindow: systemInfo?.expectedContextWindow
|
|
155
156
|
}, systemInfo);
|
|
156
157
|
const quantization = parseQuantizationFromFileName(file);
|
|
158
|
+
const estimatedRuntimeBytes = sizeBytes
|
|
159
|
+
? estimateLlamacppRuntimeBytes({
|
|
160
|
+
sizeBytes,
|
|
161
|
+
contextWindow: systemInfo?.expectedContextWindow,
|
|
162
|
+
preset: status.fit === "tight" ? "memory-safe" : "balanced"
|
|
163
|
+
})
|
|
164
|
+
: undefined;
|
|
157
165
|
const fitScore = status.fit === "safe" ? 30 : status.fit === "tight" ? 15 : status.fit === "unknown" ? 8 : -20;
|
|
158
166
|
const rankingScore = fitScore
|
|
159
167
|
+ (status.disabled ? -100 : 0)
|
|
@@ -166,6 +174,10 @@ export function shapeHuggingFaceGgufResults(files, systemInfo = {}) {
|
|
|
166
174
|
file,
|
|
167
175
|
quantization,
|
|
168
176
|
sizeBytes,
|
|
177
|
+
estimatedRuntimeBytes,
|
|
178
|
+
memoryLabel: estimatedRuntimeBytes
|
|
179
|
+
? `${(estimatedRuntimeBytes / (1024 ** 3)).toFixed(1)} GB runtime est.`
|
|
180
|
+
: "Runtime estimate unavailable",
|
|
169
181
|
disabled: status.disabled,
|
|
170
182
|
disabledReason: status.reason,
|
|
171
183
|
fit: status.fit,
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
export function createLlamacppManagedRuntimeRegistry(deps = {}) {
|
|
2
|
+
const instances = new Map();
|
|
3
|
+
const inFlightStarts = new Map();
|
|
4
|
+
let nextPort = 39391;
|
|
5
|
+
const MIN_PORT = 1;
|
|
6
|
+
const MAX_PORT = 65535;
|
|
7
|
+
|
|
8
|
+
function resolveSpawnRuntime(overrides = {}) {
|
|
9
|
+
if (typeof overrides.spawnRuntime === "function") return overrides.spawnRuntime;
|
|
10
|
+
if (typeof deps.spawnRuntime === "function") return deps.spawnRuntime;
|
|
11
|
+
return async ({ host = "127.0.0.1", port } = {}) => ({
|
|
12
|
+
pid: undefined,
|
|
13
|
+
host,
|
|
14
|
+
port,
|
|
15
|
+
baseUrl: `http://${host}:${port}/v1`
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveWaitForHealthy(overrides = {}) {
|
|
20
|
+
if (typeof overrides.waitForHealthy === "function") return overrides.waitForHealthy;
|
|
21
|
+
if (typeof deps.waitForHealthy === "function") return deps.waitForHealthy;
|
|
22
|
+
return async (instance) => ({ ...instance, healthy: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveListListeningPids(overrides = {}) {
|
|
26
|
+
if (typeof overrides.listListeningPids === "function") return overrides.listListeningPids;
|
|
27
|
+
if (typeof deps.listListeningPids === "function") return deps.listListeningPids;
|
|
28
|
+
return async () => [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveStopProcessByPid(overrides = {}) {
|
|
32
|
+
if (typeof overrides.stopProcessByPid === "function") return overrides.stopProcessByPid;
|
|
33
|
+
if (typeof deps.stopProcessByPid === "function") return deps.stopProcessByPid;
|
|
34
|
+
return async () => {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isTrackedInstanceReusable(instance) {
|
|
38
|
+
if (instance?.healthy !== true) return false;
|
|
39
|
+
const child = instance?.child;
|
|
40
|
+
if (child) {
|
|
41
|
+
return child.exitCode === null && child.killed !== true;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isChildAlive(child) {
|
|
47
|
+
if (!child) return true;
|
|
48
|
+
return child.exitCode === null && child.killed !== true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizeRuntimePort(value, fallback = null) {
|
|
52
|
+
const parsed = Number(value);
|
|
53
|
+
if (!Number.isInteger(parsed) || parsed < MIN_PORT || parsed > MAX_PORT) return fallback;
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function buildCompatibilityKey(variantKey, profileHash) {
|
|
58
|
+
return `${String(variantKey || "")}::${String(profileHash || "")}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function buildReservedPorts() {
|
|
62
|
+
const reserved = new Set();
|
|
63
|
+
for (const instance of instances.values()) {
|
|
64
|
+
if (!isChildAlive(instance?.child)) continue;
|
|
65
|
+
const port = normalizeRuntimePort(instance?.port);
|
|
66
|
+
if (port !== null) reserved.add(port);
|
|
67
|
+
}
|
|
68
|
+
for (const start of inFlightStarts.values()) {
|
|
69
|
+
const port = normalizeRuntimePort(start?.reservedPort);
|
|
70
|
+
if (port !== null) reserved.add(port);
|
|
71
|
+
}
|
|
72
|
+
return reserved;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function pruneDeadInstances() {
|
|
76
|
+
for (const [instanceId, instance] of instances.entries()) {
|
|
77
|
+
if (!isChildAlive(instance?.child)) {
|
|
78
|
+
instances.delete(instanceId);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function allocatePort(preferredPort) {
|
|
84
|
+
const reservedPorts = buildReservedPorts();
|
|
85
|
+
const preferred = normalizeRuntimePort(preferredPort);
|
|
86
|
+
if (preferred !== null && !reservedPorts.has(preferred)) {
|
|
87
|
+
if (preferred >= nextPort) nextPort = preferred + 1;
|
|
88
|
+
return preferred;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let port = Math.max(39391, nextPort);
|
|
92
|
+
if (port > MAX_PORT) {
|
|
93
|
+
port = 39391;
|
|
94
|
+
}
|
|
95
|
+
const startPort = port;
|
|
96
|
+
while (reservedPorts.has(port)) {
|
|
97
|
+
port += 1;
|
|
98
|
+
if (port > MAX_PORT) {
|
|
99
|
+
port = 39391;
|
|
100
|
+
}
|
|
101
|
+
if (port === startPort) {
|
|
102
|
+
throw new Error("No available managed runtime port.");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
nextPort = port + 1;
|
|
107
|
+
return port;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function ensureRuntimeForVariant({ variantKey, profileHash, launchArgs, preferredPort } = {}, runtimeDeps = {}) {
|
|
111
|
+
const spawnRuntime = resolveSpawnRuntime(runtimeDeps);
|
|
112
|
+
const waitForHealthy = resolveWaitForHealthy(runtimeDeps);
|
|
113
|
+
const compatibilityKey = buildCompatibilityKey(variantKey, profileHash);
|
|
114
|
+
pruneDeadInstances();
|
|
115
|
+
|
|
116
|
+
for (const instance of instances.values()) {
|
|
117
|
+
if (
|
|
118
|
+
instance.profileHash === profileHash
|
|
119
|
+
&& instance.variantKey === variantKey
|
|
120
|
+
&& isTrackedInstanceReusable(instance)
|
|
121
|
+
) {
|
|
122
|
+
return instance;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const inFlight = inFlightStarts.get(compatibilityKey);
|
|
127
|
+
if (inFlight?.promise) {
|
|
128
|
+
return inFlight.promise;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const port = allocatePort(preferredPort);
|
|
132
|
+
const startPromise = (async () => {
|
|
133
|
+
const spawned = await spawnRuntime({ variantKey, profileHash, launchArgs, port });
|
|
134
|
+
const healthy = await waitForHealthy(spawned);
|
|
135
|
+
const assignedPort = normalizeRuntimePort(healthy?.port, port);
|
|
136
|
+
if (!isChildAlive(healthy?.child)) {
|
|
137
|
+
throw new Error("Managed runtime exited before becoming healthy.");
|
|
138
|
+
}
|
|
139
|
+
const instance = {
|
|
140
|
+
instanceId: `${variantKey}:${profileHash}:${assignedPort}`,
|
|
141
|
+
owner: "llm-router",
|
|
142
|
+
variantKey,
|
|
143
|
+
profileHash,
|
|
144
|
+
healthy: true,
|
|
145
|
+
...healthy,
|
|
146
|
+
port: assignedPort
|
|
147
|
+
};
|
|
148
|
+
instances.set(instance.instanceId, instance);
|
|
149
|
+
return instance;
|
|
150
|
+
})().finally(() => {
|
|
151
|
+
inFlightStarts.delete(compatibilityKey);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
inFlightStarts.set(compatibilityKey, { promise: startPromise, reservedPort: port });
|
|
155
|
+
return startPromise;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function reconcile(runtimeDeps = {}) {
|
|
159
|
+
const listListeningPids = resolveListListeningPids(runtimeDeps);
|
|
160
|
+
const stopProcessByPid = resolveStopProcessByPid(runtimeDeps);
|
|
161
|
+
for (const [instanceId, instance] of instances.entries()) {
|
|
162
|
+
const probe = await listListeningPids(instance.port).catch(() => null);
|
|
163
|
+
const livePids = Array.isArray(probe)
|
|
164
|
+
? probe
|
|
165
|
+
: (probe && typeof probe === "object" && Array.isArray(probe.pids) ? probe.pids : null);
|
|
166
|
+
const probeFailed = Boolean(probe && typeof probe === "object" && probe.ok === false);
|
|
167
|
+
if (probeFailed || !Array.isArray(livePids)) continue;
|
|
168
|
+
if (livePids.includes(instance.pid)) continue;
|
|
169
|
+
if (instance.owner === "llm-router") {
|
|
170
|
+
await stopProcessByPid(instance.pid).catch(() => {});
|
|
171
|
+
}
|
|
172
|
+
instances.delete(instanceId);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function waitForInFlightStarts() {
|
|
177
|
+
while (inFlightStarts.size > 0) {
|
|
178
|
+
const pending = [...inFlightStarts.values()]
|
|
179
|
+
.map((entry) => entry?.promise)
|
|
180
|
+
.filter(Boolean)
|
|
181
|
+
.map((promise) => promise.catch(() => null));
|
|
182
|
+
if (pending.length === 0) return;
|
|
183
|
+
await Promise.all(pending);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
ensureRuntimeForVariant,
|
|
189
|
+
reconcile,
|
|
190
|
+
waitForInFlightStarts,
|
|
191
|
+
trackInstance: async (instance) => {
|
|
192
|
+
instances.set(instance.instanceId, { ...instance });
|
|
193
|
+
},
|
|
194
|
+
untrackInstance: async (instanceId) => {
|
|
195
|
+
instances.delete(instanceId);
|
|
196
|
+
},
|
|
197
|
+
clear: async () => {
|
|
198
|
+
instances.clear();
|
|
199
|
+
},
|
|
200
|
+
snapshot: () => [...instances.values()]
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
function isPlainObject(value) {
|
|
2
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function normalizeString(value) {
|
|
6
|
+
return typeof value === "string" ? value.trim() : "";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function toGiB(bytes) {
|
|
10
|
+
return Math.round((Number(bytes || 0) / (1024 ** 3)) * 10) / 10;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizePositiveInteger(value, fallback) {
|
|
14
|
+
const parsed = Number(value);
|
|
15
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
16
|
+
return Math.floor(parsed);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const LLAMACPP_PRESET_TUNING = Object.freeze({
|
|
20
|
+
balanced: Object.freeze({
|
|
21
|
+
canonicalPreset: "balanced",
|
|
22
|
+
batchSize: 64,
|
|
23
|
+
ubatchSize: 16,
|
|
24
|
+
gpuLayers: { darwin: 99, other: 0 },
|
|
25
|
+
penaltyRatio: 0.10,
|
|
26
|
+
noContBatching: false
|
|
27
|
+
}),
|
|
28
|
+
"long-context": Object.freeze({
|
|
29
|
+
canonicalPreset: "long-context",
|
|
30
|
+
batchSize: 32,
|
|
31
|
+
ubatchSize: 8,
|
|
32
|
+
gpuLayers: { darwin: 80, other: 0 },
|
|
33
|
+
penaltyRatio: 0.16,
|
|
34
|
+
noContBatching: false
|
|
35
|
+
}),
|
|
36
|
+
"low-memory": Object.freeze({
|
|
37
|
+
canonicalPreset: "low-memory",
|
|
38
|
+
batchSize: 32,
|
|
39
|
+
ubatchSize: 8,
|
|
40
|
+
gpuLayers: { darwin: 0, other: 0 },
|
|
41
|
+
penaltyRatio: 0.04,
|
|
42
|
+
noContBatching: true
|
|
43
|
+
}),
|
|
44
|
+
"fast-response": Object.freeze({
|
|
45
|
+
canonicalPreset: "fast-response",
|
|
46
|
+
batchSize: 16,
|
|
47
|
+
ubatchSize: 8,
|
|
48
|
+
gpuLayers: { darwin: 40, other: 0 },
|
|
49
|
+
penaltyRatio: 0.07,
|
|
50
|
+
noContBatching: false
|
|
51
|
+
}),
|
|
52
|
+
"cpu-safe": Object.freeze({
|
|
53
|
+
canonicalPreset: "cpu-safe",
|
|
54
|
+
batchSize: 32,
|
|
55
|
+
ubatchSize: 8,
|
|
56
|
+
gpuLayers: { darwin: 0, other: 0 },
|
|
57
|
+
penaltyRatio: 0.04,
|
|
58
|
+
noContBatching: true
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function resolveCanonicalPreset(requestedPreset) {
|
|
63
|
+
const normalizedPreset = normalizeString(requestedPreset).toLowerCase();
|
|
64
|
+
if (normalizedPreset === "throughput") return LLAMACPP_PRESET_TUNING["fast-response"];
|
|
65
|
+
if (normalizedPreset === "memory-safe") return LLAMACPP_PRESET_TUNING["low-memory"];
|
|
66
|
+
return LLAMACPP_PRESET_TUNING[normalizedPreset] || LLAMACPP_PRESET_TUNING.balanced;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function estimateLlamacppRuntimeBytes({
|
|
70
|
+
sizeBytes = 0,
|
|
71
|
+
contextWindow = 0,
|
|
72
|
+
preset = "balanced"
|
|
73
|
+
} = {}) {
|
|
74
|
+
const base = Number(sizeBytes || 0);
|
|
75
|
+
const contextBytes = Number(contextWindow || 0) * 163840;
|
|
76
|
+
const tuning = resolveCanonicalPreset(preset);
|
|
77
|
+
const presetPenalty = Math.floor(base * tuning.penaltyRatio);
|
|
78
|
+
return base + contextBytes + presetPenalty;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function deriveLlamacppLaunchProfile({
|
|
82
|
+
variant,
|
|
83
|
+
baseModel,
|
|
84
|
+
system
|
|
85
|
+
} = {}) {
|
|
86
|
+
const requestedPreset = normalizeString(variant?.preset)
|
|
87
|
+
|| normalizeString(variant?.runtimeProfile?.preset)
|
|
88
|
+
|| "balanced";
|
|
89
|
+
const failureCategory = normalizeString(
|
|
90
|
+
variant?.runtimeProfile?.lastFailure?.category || variant?.runtimeStatus?.lastFailure?.category
|
|
91
|
+
);
|
|
92
|
+
const tuning = resolveCanonicalPreset(failureCategory === "metal-oom" ? "cpu-safe" : requestedPreset);
|
|
93
|
+
const preset = tuning.canonicalPreset;
|
|
94
|
+
const contextWindow = normalizePositiveInteger(variant?.contextWindow, 2048);
|
|
95
|
+
const overrides = isPlainObject(variant?.runtimeProfile?.overrides) ? variant.runtimeProfile.overrides : {};
|
|
96
|
+
const extraArgs = Array.isArray(variant?.runtimeProfile?.extraArgs)
|
|
97
|
+
? variant.runtimeProfile.extraArgs.map((value) => normalizeString(value)).filter(Boolean)
|
|
98
|
+
: [];
|
|
99
|
+
const gpuLayers = Number.isFinite(Number(overrides.gpuLayers))
|
|
100
|
+
? Math.floor(Number(overrides.gpuLayers))
|
|
101
|
+
: (system?.platform === "darwin" ? tuning.gpuLayers.darwin : tuning.gpuLayers.other);
|
|
102
|
+
const batchSize = Number.isFinite(Number(overrides.batchSize))
|
|
103
|
+
? Math.floor(Number(overrides.batchSize))
|
|
104
|
+
: tuning.batchSize;
|
|
105
|
+
const ubatchSize = Number.isFinite(Number(overrides.ubatchSize))
|
|
106
|
+
? Math.floor(Number(overrides.ubatchSize))
|
|
107
|
+
: tuning.ubatchSize;
|
|
108
|
+
const estimatedRuntimeBytes = estimateLlamacppRuntimeBytes({
|
|
109
|
+
sizeBytes: baseModel?.metadata?.sizeBytes,
|
|
110
|
+
contextWindow,
|
|
111
|
+
preset
|
|
112
|
+
});
|
|
113
|
+
const args = [
|
|
114
|
+
"-m", normalizeString(baseModel?.path),
|
|
115
|
+
"-a", normalizeString(variant?.id),
|
|
116
|
+
"-c", String(contextWindow),
|
|
117
|
+
"-np", "1",
|
|
118
|
+
"-b", String(batchSize),
|
|
119
|
+
"-ub", String(ubatchSize),
|
|
120
|
+
"--cache-ram", "0",
|
|
121
|
+
"--no-warmup"
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
if (tuning.noContBatching) args.push("--no-cont-batching");
|
|
125
|
+
args.push("-ngl", String(gpuLayers), ...extraArgs);
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
preset,
|
|
129
|
+
args: args.filter(Boolean),
|
|
130
|
+
estimatedRuntimeBytes,
|
|
131
|
+
memoryLabel: `${toGiB(estimatedRuntimeBytes)} GB`
|
|
132
|
+
};
|
|
133
|
+
}
|