@otto-code/brain 0.8.12 → 0.8.14
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 +2 -2
- package/dist/commands/calibrate.js +8 -8
- package/dist/commands/pull.d.ts +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/profile-edit.js +16 -16
- package/dist/config/profiles.d.ts +19 -3
- package/dist/config/profiles.js +36 -4
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.js +7 -3
- package/dist/ops/archive.d.ts +14 -1
- package/dist/ops/archive.js +9 -5
- package/dist/ops/results.d.ts +21 -1
- package/dist/ops/results.js +10 -5
- package/dist/service/activity.d.ts +95 -14
- package/dist/service/activity.js +206 -78
- package/dist/service/host-api.d.ts +7 -4
- package/dist/service/host-api.js +31 -16
- package/dist/service/process-pool.d.ts +45 -0
- package/dist/service/process-pool.js +271 -0
- package/dist/service/router.d.ts +7 -4
- package/dist/service/router.js +123 -59
- package/dist/service/scheduler.d.ts +41 -10
- package/dist/service/scheduler.js +49 -3
- package/dist/service/serve.js +107 -58
- package/dist/service/status-events.js +1 -0
- package/dist/service/supervisor.js +2 -2
- package/dist/tui/app.js +18 -9
- package/package.json +1 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _ModelProcessPool_instances, _ModelProcessPool_createSupervisor, _ModelProcessPool_createScheduler, _ModelProcessPool_loadModel, _ModelProcessPool_onChange, _ModelProcessPool_logger, _ModelProcessPool_slots, _ModelProcessPool_queue, _ModelProcessPool_maxModels, _ModelProcessPool_busy, _ModelProcessPool_dirty, _ModelProcessPool_clock, _ModelProcessPool_makeSlot, _ModelProcessPool_announce, _ModelProcessPool_dispatch, _ModelProcessPool_pass, _ModelProcessPool_slotFor, _ModelProcessPool_nextSlotIndex, _ModelProcessPool_releaseSlot, _ModelProcessPool_trimIdleSlots;
|
|
13
|
+
/**
|
|
14
|
+
* Global admission and eviction boundary for independently hosted models.
|
|
15
|
+
*
|
|
16
|
+
* Each resident model owns one Supervisor/Scheduler pair and therefore one
|
|
17
|
+
* llama-server process, port, KV pool, and lifecycle. Requests for a resident
|
|
18
|
+
* model go directly to that process. An unloaded model claims a free process
|
|
19
|
+
* slot, or evicts the least-recently-used idle process when the configured
|
|
20
|
+
* model limit is full. Busy processes are never evicted; the request remains
|
|
21
|
+
* queued until one becomes idle.
|
|
22
|
+
*/
|
|
23
|
+
export class ModelProcessPool {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
_ModelProcessPool_instances.add(this);
|
|
26
|
+
_ModelProcessPool_createSupervisor.set(this, void 0);
|
|
27
|
+
_ModelProcessPool_createScheduler.set(this, void 0);
|
|
28
|
+
_ModelProcessPool_loadModel.set(this, void 0);
|
|
29
|
+
_ModelProcessPool_onChange.set(this, void 0);
|
|
30
|
+
_ModelProcessPool_logger.set(this, void 0);
|
|
31
|
+
_ModelProcessPool_slots.set(this, []);
|
|
32
|
+
_ModelProcessPool_queue.set(this, []);
|
|
33
|
+
_ModelProcessPool_maxModels.set(this, void 0);
|
|
34
|
+
_ModelProcessPool_busy.set(this, false);
|
|
35
|
+
_ModelProcessPool_dirty.set(this, false);
|
|
36
|
+
_ModelProcessPool_clock.set(this, 0);
|
|
37
|
+
__classPrivateFieldSet(this, _ModelProcessPool_createSupervisor, options.createSupervisor, "f");
|
|
38
|
+
__classPrivateFieldSet(this, _ModelProcessPool_createScheduler, options.createScheduler, "f");
|
|
39
|
+
__classPrivateFieldSet(this, _ModelProcessPool_loadModel, options.loadModel, "f");
|
|
40
|
+
__classPrivateFieldSet(this, _ModelProcessPool_onChange, options.onChange ?? null, "f");
|
|
41
|
+
__classPrivateFieldSet(this, _ModelProcessPool_logger, options.logger ?? null, "f");
|
|
42
|
+
__classPrivateFieldSet(this, _ModelProcessPool_maxModels, normalizeModelLimit(options.maxModels), "f");
|
|
43
|
+
__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").push(__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_makeSlot).call(this, 0, options.initialSupervisor));
|
|
44
|
+
}
|
|
45
|
+
get maxModels() {
|
|
46
|
+
return __classPrivateFieldGet(this, _ModelProcessPool_maxModels, "f");
|
|
47
|
+
}
|
|
48
|
+
submit(model, run, options = {}) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
__classPrivateFieldGet(this, _ModelProcessPool_queue, "f").push({ model, run, options, resolve, reject });
|
|
51
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
52
|
+
queueMicrotask(() => void __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_dispatch).call(this));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Load a model and leave it resident without consuming an inference slot. */
|
|
56
|
+
async preload(model) {
|
|
57
|
+
await this.submit(model, async () => undefined);
|
|
58
|
+
}
|
|
59
|
+
/** Apply the host-owned process limit and retire excess idle processes. */
|
|
60
|
+
async configure(maxModels) {
|
|
61
|
+
__classPrivateFieldSet(this, _ModelProcessPool_maxModels, normalizeModelLimit(maxModels), "f");
|
|
62
|
+
await __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_trimIdleSlots).call(this);
|
|
63
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
64
|
+
void __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_dispatch).call(this);
|
|
65
|
+
}
|
|
66
|
+
supervisorFor(modelId) {
|
|
67
|
+
return (__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").find((slot) => slot.assignedModelId === modelId ||
|
|
68
|
+
(slot.supervisor.model?.id === modelId && slot.supervisor.state !== "stopped"))?.supervisor ?? null);
|
|
69
|
+
}
|
|
70
|
+
supervisors() {
|
|
71
|
+
return __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").map((slot) => slot.supervisor);
|
|
72
|
+
}
|
|
73
|
+
/** Complete statuses for every process that currently owns a model. */
|
|
74
|
+
residentSupervisors() {
|
|
75
|
+
return __classPrivateFieldGet(this, _ModelProcessPool_slots, "f")
|
|
76
|
+
.filter((slot) => slot.assignedModelId !== null && slot.supervisor.state !== "stopped")
|
|
77
|
+
.map((slot) => slot.supervisor);
|
|
78
|
+
}
|
|
79
|
+
reservationFor(modelId) {
|
|
80
|
+
return __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").find((slot) => slot.assignedModelId === modelId)?.reservationBytes ?? 0;
|
|
81
|
+
}
|
|
82
|
+
async unload(modelId) {
|
|
83
|
+
const targets = modelId
|
|
84
|
+
? __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").filter((slot) => slot.assignedModelId === modelId)
|
|
85
|
+
: [...__classPrivateFieldGet(this, _ModelProcessPool_slots, "f")];
|
|
86
|
+
for (const slot of targets) {
|
|
87
|
+
if (!slot.scheduler.isIdle || slot.pending > 0) {
|
|
88
|
+
throw new Error(modelId ? "the model is still serving requests" : "a model is still serving requests");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
await Promise.all(targets.map((slot) => __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_releaseSlot).call(this, slot)));
|
|
92
|
+
await __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_trimIdleSlots).call(this);
|
|
93
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
94
|
+
void __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_dispatch).call(this);
|
|
95
|
+
}
|
|
96
|
+
async stop() {
|
|
97
|
+
const error = new Error("Brain process pool stopped");
|
|
98
|
+
for (const job of __classPrivateFieldGet(this, _ModelProcessPool_queue, "f").splice(0))
|
|
99
|
+
job.reject(error);
|
|
100
|
+
await Promise.all(__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").map((slot) => slot.supervisor.stop()));
|
|
101
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
102
|
+
}
|
|
103
|
+
forgetSlots() {
|
|
104
|
+
for (const slot of __classPrivateFieldGet(this, _ModelProcessPool_slots, "f"))
|
|
105
|
+
slot.scheduler.forgetSlots();
|
|
106
|
+
}
|
|
107
|
+
stats() {
|
|
108
|
+
const waiting = {};
|
|
109
|
+
const waitingModelIds = {};
|
|
110
|
+
let queued = 0;
|
|
111
|
+
let lastTurn = null;
|
|
112
|
+
let active = null;
|
|
113
|
+
for (const job of __classPrivateFieldGet(this, _ModelProcessPool_queue, "f")) {
|
|
114
|
+
queued += 1;
|
|
115
|
+
waiting[job.model.displayName] = (waiting[job.model.displayName] ?? 0) + 1;
|
|
116
|
+
waitingModelIds[job.model.id] = (waitingModelIds[job.model.id] ?? 0) + 1;
|
|
117
|
+
}
|
|
118
|
+
for (const slot of __classPrivateFieldGet(this, _ModelProcessPool_slots, "f")) {
|
|
119
|
+
const stats = slot.scheduler.stats();
|
|
120
|
+
queued += stats.queued;
|
|
121
|
+
for (const [name, count] of Object.entries(stats.waiting)) {
|
|
122
|
+
waiting[name] = (waiting[name] ?? 0) + count;
|
|
123
|
+
}
|
|
124
|
+
for (const [id, count] of Object.entries(stats.waitingModelIds)) {
|
|
125
|
+
waitingModelIds[id] = (waitingModelIds[id] ?? 0) + count;
|
|
126
|
+
}
|
|
127
|
+
lastTurn = stats.lastTurn ?? lastTurn;
|
|
128
|
+
active = active ?? stats.active;
|
|
129
|
+
}
|
|
130
|
+
return { queued, waiting, waitingModelIds, lastTurn, active };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
_ModelProcessPool_createSupervisor = new WeakMap(), _ModelProcessPool_createScheduler = new WeakMap(), _ModelProcessPool_loadModel = new WeakMap(), _ModelProcessPool_onChange = new WeakMap(), _ModelProcessPool_logger = new WeakMap(), _ModelProcessPool_slots = new WeakMap(), _ModelProcessPool_queue = new WeakMap(), _ModelProcessPool_maxModels = new WeakMap(), _ModelProcessPool_busy = new WeakMap(), _ModelProcessPool_dirty = new WeakMap(), _ModelProcessPool_clock = new WeakMap(), _ModelProcessPool_instances = new WeakSet(), _ModelProcessPool_makeSlot = function _ModelProcessPool_makeSlot(index, supervisor) {
|
|
134
|
+
var _a;
|
|
135
|
+
const slot = {
|
|
136
|
+
index,
|
|
137
|
+
supervisor,
|
|
138
|
+
scheduler: null,
|
|
139
|
+
assignedModelId: supervisor.model?.id ?? null,
|
|
140
|
+
pending: 0,
|
|
141
|
+
reservationBytes: 0,
|
|
142
|
+
lastUsedAt: __classPrivateFieldSet(this, _ModelProcessPool_clock, (_a = __classPrivateFieldGet(this, _ModelProcessPool_clock, "f"), ++_a), "f"),
|
|
143
|
+
};
|
|
144
|
+
slot.scheduler = __classPrivateFieldGet(this, _ModelProcessPool_createScheduler, "f").call(this, supervisor, async (model) => {
|
|
145
|
+
var _a;
|
|
146
|
+
const reservedElsewhereBytes = __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").reduce((total, candidate) => (candidate === slot ? total : total + candidate.reservationBytes), 0);
|
|
147
|
+
slot.reservationBytes = await __classPrivateFieldGet(this, _ModelProcessPool_loadModel, "f").call(this, supervisor, model, reservedElsewhereBytes);
|
|
148
|
+
slot.assignedModelId = model.id;
|
|
149
|
+
slot.lastUsedAt = __classPrivateFieldSet(this, _ModelProcessPool_clock, (_a = __classPrivateFieldGet(this, _ModelProcessPool_clock, "f"), ++_a), "f");
|
|
150
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
151
|
+
}, () => {
|
|
152
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
153
|
+
void __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_dispatch).call(this);
|
|
154
|
+
});
|
|
155
|
+
return slot;
|
|
156
|
+
}, _ModelProcessPool_announce = function _ModelProcessPool_announce() {
|
|
157
|
+
try {
|
|
158
|
+
__classPrivateFieldGet(this, _ModelProcessPool_onChange, "f")?.call(this);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Status observers are not allowed to fail admission or eviction.
|
|
162
|
+
}
|
|
163
|
+
}, _ModelProcessPool_dispatch = async function _ModelProcessPool_dispatch() {
|
|
164
|
+
if (__classPrivateFieldGet(this, _ModelProcessPool_busy, "f")) {
|
|
165
|
+
__classPrivateFieldSet(this, _ModelProcessPool_dirty, true, "f");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
__classPrivateFieldSet(this, _ModelProcessPool_busy, true, "f");
|
|
169
|
+
try {
|
|
170
|
+
do {
|
|
171
|
+
__classPrivateFieldSet(this, _ModelProcessPool_dirty, false, "f");
|
|
172
|
+
await __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_pass).call(this);
|
|
173
|
+
} while (__classPrivateFieldGet(this, _ModelProcessPool_dirty, "f"));
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
__classPrivateFieldSet(this, _ModelProcessPool_busy, false, "f");
|
|
177
|
+
}
|
|
178
|
+
}, _ModelProcessPool_pass = async function _ModelProcessPool_pass() {
|
|
179
|
+
var _a;
|
|
180
|
+
await __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_trimIdleSlots).call(this);
|
|
181
|
+
for (;;) {
|
|
182
|
+
let selectedIndex = -1;
|
|
183
|
+
let selectedSlot = null;
|
|
184
|
+
for (let index = 0; index < __classPrivateFieldGet(this, _ModelProcessPool_queue, "f").length; index += 1) {
|
|
185
|
+
const candidate = __classPrivateFieldGet(this, _ModelProcessPool_queue, "f")[index];
|
|
186
|
+
const slot = await __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_slotFor).call(this, candidate.model);
|
|
187
|
+
if (!slot)
|
|
188
|
+
continue;
|
|
189
|
+
selectedIndex = index;
|
|
190
|
+
selectedSlot = slot;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
if (selectedIndex < 0 || !selectedSlot)
|
|
194
|
+
return;
|
|
195
|
+
const [job] = __classPrivateFieldGet(this, _ModelProcessPool_queue, "f").splice(selectedIndex, 1);
|
|
196
|
+
if (!job)
|
|
197
|
+
return;
|
|
198
|
+
const slot = selectedSlot;
|
|
199
|
+
slot.pending += 1;
|
|
200
|
+
slot.lastUsedAt = __classPrivateFieldSet(this, _ModelProcessPool_clock, (_a = __classPrivateFieldGet(this, _ModelProcessPool_clock, "f"), ++_a), "f");
|
|
201
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
202
|
+
void slot.scheduler
|
|
203
|
+
.submit(job.model, job.run, job.options)
|
|
204
|
+
.then(job.resolve, job.reject)
|
|
205
|
+
.finally(() => {
|
|
206
|
+
var _a;
|
|
207
|
+
slot.pending = Math.max(0, slot.pending - 1);
|
|
208
|
+
slot.lastUsedAt = __classPrivateFieldSet(this, _ModelProcessPool_clock, (_a = __classPrivateFieldGet(this, _ModelProcessPool_clock, "f"), ++_a), "f");
|
|
209
|
+
if (slot.supervisor.state === "failed" && slot.pending === 0) {
|
|
210
|
+
void __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_releaseSlot).call(this, slot).finally(() => void __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_dispatch).call(this));
|
|
211
|
+
}
|
|
212
|
+
__classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_announce).call(this);
|
|
213
|
+
void __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_dispatch).call(this);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}, _ModelProcessPool_slotFor = async function _ModelProcessPool_slotFor(model) {
|
|
217
|
+
const resident = __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").find((slot) => slot.assignedModelId === model.id);
|
|
218
|
+
if (resident)
|
|
219
|
+
return resident;
|
|
220
|
+
let empty = __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").find((slot) => slot.assignedModelId === null && slot.scheduler.isIdle && slot.pending === 0);
|
|
221
|
+
if (!empty && __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").length < __classPrivateFieldGet(this, _ModelProcessPool_maxModels, "f")) {
|
|
222
|
+
const index = __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_nextSlotIndex).call(this);
|
|
223
|
+
empty = __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_makeSlot).call(this, index, __classPrivateFieldGet(this, _ModelProcessPool_createSupervisor, "f").call(this, index));
|
|
224
|
+
__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").push(empty);
|
|
225
|
+
}
|
|
226
|
+
if (empty) {
|
|
227
|
+
empty.assignedModelId = model.id;
|
|
228
|
+
return empty;
|
|
229
|
+
}
|
|
230
|
+
const evictable = __classPrivateFieldGet(this, _ModelProcessPool_slots, "f")
|
|
231
|
+
.filter((slot) => slot.scheduler.isIdle && slot.pending === 0)
|
|
232
|
+
.sort((left, right) => left.lastUsedAt - right.lastUsedAt)[0];
|
|
233
|
+
if (!evictable)
|
|
234
|
+
return null;
|
|
235
|
+
__classPrivateFieldGet(this, _ModelProcessPool_logger, "f")?.call(this, `evicting ${evictable.supervisor.model?.displayName ?? "idle model"} for ${model.displayName}`);
|
|
236
|
+
await __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_releaseSlot).call(this, evictable);
|
|
237
|
+
evictable.assignedModelId = model.id;
|
|
238
|
+
return evictable;
|
|
239
|
+
}, _ModelProcessPool_nextSlotIndex = function _ModelProcessPool_nextSlotIndex() {
|
|
240
|
+
const used = new Set(__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").map((slot) => slot.index));
|
|
241
|
+
let index = 0;
|
|
242
|
+
while (used.has(index))
|
|
243
|
+
index += 1;
|
|
244
|
+
return index;
|
|
245
|
+
}, _ModelProcessPool_releaseSlot = async function _ModelProcessPool_releaseSlot(slot) {
|
|
246
|
+
var _a;
|
|
247
|
+
await slot.supervisor.stop();
|
|
248
|
+
slot.scheduler.forgetSlots();
|
|
249
|
+
slot.assignedModelId = null;
|
|
250
|
+
slot.reservationBytes = 0;
|
|
251
|
+
slot.lastUsedAt = __classPrivateFieldSet(this, _ModelProcessPool_clock, (_a = __classPrivateFieldGet(this, _ModelProcessPool_clock, "f"), ++_a), "f");
|
|
252
|
+
}, _ModelProcessPool_trimIdleSlots = async function _ModelProcessPool_trimIdleSlots() {
|
|
253
|
+
if (__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").length <= __classPrivateFieldGet(this, _ModelProcessPool_maxModels, "f"))
|
|
254
|
+
return;
|
|
255
|
+
const removable = __classPrivateFieldGet(this, _ModelProcessPool_slots, "f")
|
|
256
|
+
.filter((slot) => slot.scheduler.isIdle && slot.pending === 0)
|
|
257
|
+
.sort((left, right) => left.lastUsedAt - right.lastUsedAt);
|
|
258
|
+
while (__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").length > __classPrivateFieldGet(this, _ModelProcessPool_maxModels, "f") && removable.length > 0) {
|
|
259
|
+
const slot = removable.shift();
|
|
260
|
+
await __classPrivateFieldGet(this, _ModelProcessPool_instances, "m", _ModelProcessPool_releaseSlot).call(this, slot);
|
|
261
|
+
const index = __classPrivateFieldGet(this, _ModelProcessPool_slots, "f").indexOf(slot);
|
|
262
|
+
if (index >= 0)
|
|
263
|
+
__classPrivateFieldGet(this, _ModelProcessPool_slots, "f").splice(index, 1);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
export function normalizeModelLimit(value) {
|
|
267
|
+
if (!Number.isFinite(value))
|
|
268
|
+
return 1;
|
|
269
|
+
return Math.max(1, Math.min(16, Math.floor(value)));
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=process-pool.js.map
|
package/dist/service/router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
-
import {
|
|
2
|
+
import { type ModelScheduler } from "./scheduler.js";
|
|
3
3
|
import type { Supervisor } from "./supervisor.js";
|
|
4
4
|
import { type RankedModel } from "../ops/results.js";
|
|
5
5
|
import type { GpuInfo, Model } from "../types.js";
|
|
@@ -108,7 +108,7 @@ export declare function describeModel(model: Model | null, options?: DescribeOpt
|
|
|
108
108
|
* the supervisor is running marked 'loaded'. Falls back to just the running
|
|
109
109
|
* model when no catalog provider is wired in.
|
|
110
110
|
*/
|
|
111
|
-
export declare function buildModelList(supervisor: Supervisor, getCatalog: GetCatalog): ModelEntry[];
|
|
111
|
+
export declare function buildModelList(supervisor: Supervisor, getCatalog: GetCatalog, scheduler?: ModelScheduler<Supervisor> | null): ModelEntry[];
|
|
112
112
|
/**
|
|
113
113
|
* Map an OpenAI-compatible effort request onto a model's own chat-template
|
|
114
114
|
* arguments. llama.cpp does not know every model's dialect: Qwen3.8 calls the
|
|
@@ -207,6 +207,7 @@ export declare function decideModelGate(params: {
|
|
|
207
207
|
lockModel: boolean;
|
|
208
208
|
requestedName: string | null;
|
|
209
209
|
pinned: Model | null;
|
|
210
|
+
pinnedModels?: Model[];
|
|
210
211
|
resolved: Model | null;
|
|
211
212
|
}): ModelGateResult;
|
|
212
213
|
export interface RouterOptions {
|
|
@@ -227,6 +228,8 @@ export interface RouterOptions {
|
|
|
227
228
|
getLockModel?: () => boolean;
|
|
228
229
|
/** Live: the configured default model, the pin target before one is resident. */
|
|
229
230
|
getDefaultModel?: () => string | null;
|
|
231
|
+
/** Live: the complete model set served while locking is enabled. */
|
|
232
|
+
getLockedModels?: () => string[];
|
|
230
233
|
/**
|
|
231
234
|
* Apply an editable config patch (write config.json, live-switch the model,
|
|
232
235
|
* update the lock), for POST /__host/config. Absent = the write endpoint is
|
|
@@ -262,8 +265,8 @@ export interface RouterOptions {
|
|
|
262
265
|
*/
|
|
263
266
|
statusEvents?: BrainStatusPublisher | null;
|
|
264
267
|
/** A service shares this scheduler with host-owned model operations. */
|
|
265
|
-
scheduler?:
|
|
268
|
+
scheduler?: ModelScheduler<Supervisor> | null;
|
|
266
269
|
}
|
|
267
|
-
export declare function createRouter({ supervisor, telemetry, logger, getCatalog, loadModel, loadRanking, queryGpuInfo, version, getConfig, getEvals, getLockModel, getDefaultModel, applyConfigPatch, getAllowConfigWrite, hostApi, getResources, statusEvents, scheduler: suppliedScheduler, }: RouterOptions): (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
|
270
|
+
export declare function createRouter({ supervisor, telemetry, logger, getCatalog, loadModel, loadRanking, queryGpuInfo, version, getConfig, getEvals, getLockModel, getDefaultModel, getLockedModels, applyConfigPatch, getAllowConfigWrite, hostApi, getResources, statusEvents, scheduler: suppliedScheduler, }: RouterOptions): (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
|
268
271
|
export {};
|
|
269
272
|
//# sourceMappingURL=router.d.ts.map
|