@byollm/relay 0.1.0-alpha.5 → 0.1.0-alpha.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -4
- package/dist/chunk-XLWNHHLS.js +399 -0
- package/dist/chunk-XLWNHHLS.js.map +1 -0
- package/dist/index.d.ts +422 -149
- package/dist/index.js +959 -279
- package/dist/index.js.map +1 -1
- package/dist/store-C7jG6b9m.d.ts +627 -0
- package/dist/store-contract.d.ts +67 -0
- package/dist/store-contract.js +641 -0
- package/dist/store-contract.js.map +1 -0
- package/package.json +27 -4
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
import {
|
|
2
|
+
routeKey,
|
|
3
|
+
serviceKey
|
|
4
|
+
} from "./chunk-XLWNHHLS.js";
|
|
5
|
+
|
|
6
|
+
// src/store-contract.ts
|
|
7
|
+
import { generateKeys, publicIdentityOf } from "@byollm/protocol";
|
|
8
|
+
import { describe, expect, it } from "vitest";
|
|
9
|
+
var CONTRACT_SITE = "site_store";
|
|
10
|
+
var SITE_KEY_ID = "BYOLLM-TEST-SITE-KEY-ID";
|
|
11
|
+
var SITE = CONTRACT_SITE;
|
|
12
|
+
var stub = (id, owner = "alice") => ({
|
|
13
|
+
id,
|
|
14
|
+
kind: "llm.generate",
|
|
15
|
+
owner,
|
|
16
|
+
site: SITE_KEY_ID,
|
|
17
|
+
audience: "private",
|
|
18
|
+
sizeClass: "small",
|
|
19
|
+
streaming: false,
|
|
20
|
+
deadlineAt: 41024448e5
|
|
21
|
+
});
|
|
22
|
+
var capability = (model) => ({
|
|
23
|
+
kind: "llm.generate",
|
|
24
|
+
service: model,
|
|
25
|
+
isDefault: true,
|
|
26
|
+
backendId: "ollama",
|
|
27
|
+
backendClass: "process",
|
|
28
|
+
model,
|
|
29
|
+
offerScope: "private"
|
|
30
|
+
});
|
|
31
|
+
var ENVELOPE = {
|
|
32
|
+
ciphertext: "AAAA",
|
|
33
|
+
recipientKeyId: "r",
|
|
34
|
+
senderKeyId: "s",
|
|
35
|
+
direction: "payload",
|
|
36
|
+
deadlineAt: 41024448e5
|
|
37
|
+
};
|
|
38
|
+
var claimArgs = (over = {}) => {
|
|
39
|
+
const { owners = ["alice"], sites = [SITE], ...rest } = over;
|
|
40
|
+
return {
|
|
41
|
+
runnerId: "runner_1",
|
|
42
|
+
owner: "alice",
|
|
43
|
+
device: publicIdentityOf(generateKeys(Date.now())),
|
|
44
|
+
kinds: /* @__PURE__ */ new Set(["llm.generate"]),
|
|
45
|
+
// Empty by default — `serves` matters only to a job that named a service.
|
|
46
|
+
// A contract case that exercises selection passes its own.
|
|
47
|
+
serves: /* @__PURE__ */ new Set(),
|
|
48
|
+
routes: new Set(
|
|
49
|
+
sites.flatMap((site) => owners.map((owner) => routeKey(site, owner)))
|
|
50
|
+
),
|
|
51
|
+
max: 10,
|
|
52
|
+
leaseMs: 6e4,
|
|
53
|
+
...rest
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
function describeStoreContract(name, options) {
|
|
57
|
+
const { make, serialising, writeRawStub } = options;
|
|
58
|
+
describe(`the routing store \u2014 ${name}`, () => {
|
|
59
|
+
it("is idempotent by job id", async () => {
|
|
60
|
+
const { store, done } = await make();
|
|
61
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
62
|
+
await store.claim(claimArgs());
|
|
63
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
64
|
+
expect((await store.job(SITE, "a"))?.state).toBe("awaiting-payload");
|
|
65
|
+
await done();
|
|
66
|
+
});
|
|
67
|
+
it("gives two sites the same id without either seeing the other", async () => {
|
|
68
|
+
const { store, done } = await make();
|
|
69
|
+
await store.enqueue({ id: "shared", siteId: SITE, stub: stub("shared") });
|
|
70
|
+
await store.enqueue({
|
|
71
|
+
id: "shared",
|
|
72
|
+
siteId: "site_other",
|
|
73
|
+
stub: stub("shared", "bob")
|
|
74
|
+
});
|
|
75
|
+
expect((await store.job(SITE, "shared"))?.stub.owner).toBe("alice");
|
|
76
|
+
expect((await store.job("site_other", "shared"))?.stub.owner).toBe("bob");
|
|
77
|
+
const granted = await store.claim(claimArgs());
|
|
78
|
+
expect(granted.map((job) => job.id)).toEqual(["shared"]);
|
|
79
|
+
expect(granted[0]?.owner).toBe("alice");
|
|
80
|
+
const replay = await store.enqueue({
|
|
81
|
+
id: "shared",
|
|
82
|
+
siteId: SITE,
|
|
83
|
+
stub: stub("shared")
|
|
84
|
+
});
|
|
85
|
+
expect(replay).toEqual(await store.job(SITE, "shared"));
|
|
86
|
+
await done();
|
|
87
|
+
});
|
|
88
|
+
it("grants only what the owners set allows", async () => {
|
|
89
|
+
const { store, done } = await make();
|
|
90
|
+
await store.enqueue({ id: "mine", siteId: SITE, stub: stub("mine") });
|
|
91
|
+
await store.enqueue({
|
|
92
|
+
id: "theirs",
|
|
93
|
+
siteId: SITE,
|
|
94
|
+
stub: stub("theirs", "mallory")
|
|
95
|
+
});
|
|
96
|
+
const granted = await store.claim(claimArgs());
|
|
97
|
+
expect(granted.map((job) => job.id)).toEqual(["mine"]);
|
|
98
|
+
await done();
|
|
99
|
+
});
|
|
100
|
+
it("refuses a stale lease and honours the current one", async () => {
|
|
101
|
+
const { store, done } = await make();
|
|
102
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
103
|
+
const [granted] = await store.claim(claimArgs());
|
|
104
|
+
await store.seal({ jobId: "a", siteId: SITE, envelope: ENVELOPE });
|
|
105
|
+
expect(
|
|
106
|
+
await store.takePayload({
|
|
107
|
+
jobId: "a",
|
|
108
|
+
runnerId: "runner_1",
|
|
109
|
+
leaseId: "an-older-grant"
|
|
110
|
+
})
|
|
111
|
+
).toEqual({ refused: "stale-lease" });
|
|
112
|
+
expect(
|
|
113
|
+
await store.takePayload({
|
|
114
|
+
jobId: "a",
|
|
115
|
+
runnerId: "runner_1",
|
|
116
|
+
leaseId: granted.lease.id
|
|
117
|
+
})
|
|
118
|
+
).toEqual({ envelope: ENVELOPE });
|
|
119
|
+
await done();
|
|
120
|
+
});
|
|
121
|
+
it("will not answer for one job with another job's lease", async () => {
|
|
122
|
+
const { store, done } = await make();
|
|
123
|
+
await store.enqueue({ id: "mine", siteId: SITE, stub: stub("mine") });
|
|
124
|
+
await store.enqueue({ id: "other", siteId: SITE, stub: stub("other") });
|
|
125
|
+
const granted = await store.claim(claimArgs());
|
|
126
|
+
const mine = granted.find((job) => job.id === "mine");
|
|
127
|
+
const other = granted.find((job) => job.id === "other");
|
|
128
|
+
await store.seal({ jobId: "other", siteId: SITE, envelope: ENVELOPE });
|
|
129
|
+
expect(
|
|
130
|
+
await store.takePayload({
|
|
131
|
+
jobId: "other",
|
|
132
|
+
runnerId: "runner_1",
|
|
133
|
+
leaseId: mine.lease.id
|
|
134
|
+
})
|
|
135
|
+
).toEqual({ refused: "stale-lease" });
|
|
136
|
+
expect(
|
|
137
|
+
await store.takePayload({
|
|
138
|
+
jobId: "other",
|
|
139
|
+
runnerId: "runner_1",
|
|
140
|
+
leaseId: other.lease.id
|
|
141
|
+
})
|
|
142
|
+
).toEqual({ envelope: ENVELOPE });
|
|
143
|
+
await done();
|
|
144
|
+
});
|
|
145
|
+
it("renews a grant it still holds, and extends the sweep with it", async () => {
|
|
146
|
+
const { store, done } = await make();
|
|
147
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
148
|
+
const [granted] = await store.claim(claimArgs({ leaseMs: 1e3 }));
|
|
149
|
+
const renewed = await store.renewLeases({
|
|
150
|
+
runnerId: "runner_1",
|
|
151
|
+
leases: [{ jobId: "a", leaseId: granted.lease.id }],
|
|
152
|
+
leaseMs: 6e4
|
|
153
|
+
});
|
|
154
|
+
expect(renewed.lost).toEqual([]);
|
|
155
|
+
expect(renewed.renewed.map((r) => r.jobId)).toEqual(["a"]);
|
|
156
|
+
expect(renewed.renewed[0].expiresAt).toBeGreaterThan(
|
|
157
|
+
granted.lease.expiresAt
|
|
158
|
+
);
|
|
159
|
+
expect((await store.job(SITE, "a"))?.claimedBy?.leaseExpiresAt).toBe(
|
|
160
|
+
renewed.renewed[0].expiresAt
|
|
161
|
+
);
|
|
162
|
+
await done();
|
|
163
|
+
});
|
|
164
|
+
it("reports a lease the runner no longer holds as lost, and renews nothing", async () => {
|
|
165
|
+
const { store, done } = await make();
|
|
166
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
167
|
+
const [granted] = await store.claim(claimArgs({ leaseMs: 1e3 }));
|
|
168
|
+
const before = (await store.job(SITE, "a"))?.claimedBy?.leaseExpiresAt;
|
|
169
|
+
const renewed = await store.renewLeases({
|
|
170
|
+
runnerId: "runner_1",
|
|
171
|
+
leases: [{ jobId: "a", leaseId: "a-grant-that-ended" }],
|
|
172
|
+
leaseMs: 6e4
|
|
173
|
+
});
|
|
174
|
+
expect(renewed.renewed).toEqual([]);
|
|
175
|
+
expect(renewed.lost).toEqual([
|
|
176
|
+
{ jobId: "a", leaseId: "a-grant-that-ended" }
|
|
177
|
+
]);
|
|
178
|
+
expect((await store.job(SITE, "a"))?.claimedBy?.leaseExpiresAt).toBe(
|
|
179
|
+
before
|
|
180
|
+
);
|
|
181
|
+
expect((await store.job(SITE, "a"))?.claimedBy?.leaseId).toBe(
|
|
182
|
+
granted.lease.id
|
|
183
|
+
);
|
|
184
|
+
await done();
|
|
185
|
+
});
|
|
186
|
+
it("does not hand out a stub it cannot parse", async () => {
|
|
187
|
+
const { store, done } = await make();
|
|
188
|
+
if (!serialising || writeRawStub === void 0) {
|
|
189
|
+
await done();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
await store.enqueue({ id: "good", siteId: SITE, stub: stub("good") });
|
|
193
|
+
await store.enqueue({ id: "old", siteId: SITE, stub: stub("old") });
|
|
194
|
+
const legacy = { ...stub("old") };
|
|
195
|
+
delete legacy["site"];
|
|
196
|
+
await writeRawStub(store, "old", JSON.stringify(legacy));
|
|
197
|
+
const granted = await store.claim(claimArgs({ max: 10 }));
|
|
198
|
+
expect(granted.map((job) => job.id)).toEqual(["good"]);
|
|
199
|
+
expect((await store.job(SITE, "old"))?.state).toBe("queued");
|
|
200
|
+
await done();
|
|
201
|
+
});
|
|
202
|
+
it("honours a self job's audience", async () => {
|
|
203
|
+
const { store, done } = await make();
|
|
204
|
+
await store.enqueue({
|
|
205
|
+
id: "private",
|
|
206
|
+
siteId: SITE,
|
|
207
|
+
stub: { ...stub("private", "alice"), audience: "private" }
|
|
208
|
+
});
|
|
209
|
+
const granted = await store.claim(
|
|
210
|
+
claimArgs({ owner: "owner", owners: ["owner", "alice"] })
|
|
211
|
+
);
|
|
212
|
+
expect(granted).toEqual([]);
|
|
213
|
+
expect((await store.job(SITE, "private"))?.state).toBe("queued");
|
|
214
|
+
await done();
|
|
215
|
+
});
|
|
216
|
+
it("still offers that job to its owner's own machine", async () => {
|
|
217
|
+
const { store, done } = await make();
|
|
218
|
+
await store.enqueue({
|
|
219
|
+
id: "private",
|
|
220
|
+
siteId: SITE,
|
|
221
|
+
stub: { ...stub("private", "alice"), audience: "private" }
|
|
222
|
+
});
|
|
223
|
+
const granted = await store.claim(
|
|
224
|
+
claimArgs({ owner: "alice", owners: ["alice"] })
|
|
225
|
+
);
|
|
226
|
+
expect(granted.map((job) => job.id)).toEqual(["private"]);
|
|
227
|
+
await done();
|
|
228
|
+
});
|
|
229
|
+
it("does not offer a job back to the runner that refused it", async () => {
|
|
230
|
+
const { store, done } = await make();
|
|
231
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
232
|
+
const [granted] = await store.claim(claimArgs());
|
|
233
|
+
await store.releaseLeases({
|
|
234
|
+
runnerId: "runner_1",
|
|
235
|
+
leases: [{ jobId: "a", leaseId: granted.lease.id }],
|
|
236
|
+
reason: "refused"
|
|
237
|
+
});
|
|
238
|
+
expect(await store.claim(claimArgs())).toEqual([]);
|
|
239
|
+
expect((await store.job(SITE, "a"))?.state).toBe("queued");
|
|
240
|
+
expect(
|
|
241
|
+
(await store.claim(claimArgs({ runnerId: "runner_2" }))).map(
|
|
242
|
+
(j) => j.id
|
|
243
|
+
)
|
|
244
|
+
).toEqual(["a"]);
|
|
245
|
+
await done();
|
|
246
|
+
});
|
|
247
|
+
it("keeps a job claimable by a runner that only went away", async () => {
|
|
248
|
+
const { store, done } = await make();
|
|
249
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
250
|
+
const [granted] = await store.claim(claimArgs());
|
|
251
|
+
await store.releaseLeases({
|
|
252
|
+
runnerId: "runner_1",
|
|
253
|
+
leases: [{ jobId: "a", leaseId: granted.lease.id }],
|
|
254
|
+
reason: "shutdown"
|
|
255
|
+
});
|
|
256
|
+
expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual(["a"]);
|
|
257
|
+
await done();
|
|
258
|
+
});
|
|
259
|
+
it("stops offering a job past its deadline, and drops the ciphertext", async () => {
|
|
260
|
+
const { store, done } = await make();
|
|
261
|
+
await store.enqueue({
|
|
262
|
+
id: "late",
|
|
263
|
+
siteId: SITE,
|
|
264
|
+
stub: { ...stub("late"), deadlineAt: Date.now() - 1e3 }
|
|
265
|
+
});
|
|
266
|
+
expect(await store.claim(claimArgs())).toEqual([]);
|
|
267
|
+
expect(await store.job(SITE, "late")).toBeUndefined();
|
|
268
|
+
await done();
|
|
269
|
+
});
|
|
270
|
+
it("names a cancelled job to the device holding it, and offers it to nobody", async () => {
|
|
271
|
+
const { store, done } = await make();
|
|
272
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
273
|
+
await store.claim(claimArgs());
|
|
274
|
+
expect(await store.cancel({ jobId: "a", siteId: SITE })).toBe(true);
|
|
275
|
+
const held = (await store.job(SITE, "a"))?.claimedBy?.leaseId;
|
|
276
|
+
expect(await store.cancelRequests("runner_1")).toEqual([
|
|
277
|
+
{ jobId: "a", leaseId: held }
|
|
278
|
+
]);
|
|
279
|
+
expect(await store.cancel({ jobId: "a", siteId: "someone_else" })).toBe(
|
|
280
|
+
false
|
|
281
|
+
);
|
|
282
|
+
await done();
|
|
283
|
+
});
|
|
284
|
+
it("records a result once", async () => {
|
|
285
|
+
const { store, done } = await make();
|
|
286
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
287
|
+
const [granted] = await store.claim(claimArgs());
|
|
288
|
+
const first = await store.complete({
|
|
289
|
+
jobId: "a",
|
|
290
|
+
runnerId: "runner_1",
|
|
291
|
+
leaseId: granted.lease.id,
|
|
292
|
+
envelope: ENVELOPE,
|
|
293
|
+
disposition: "ok"
|
|
294
|
+
});
|
|
295
|
+
const replay = await store.complete({
|
|
296
|
+
jobId: "a",
|
|
297
|
+
runnerId: "runner_1",
|
|
298
|
+
leaseId: granted.lease.id,
|
|
299
|
+
envelope: { ...ENVELOPE, ciphertext: "different" },
|
|
300
|
+
disposition: "error"
|
|
301
|
+
});
|
|
302
|
+
expect(first).toEqual({ accepted: true, state: "done" });
|
|
303
|
+
expect(replay).toEqual({
|
|
304
|
+
accepted: false,
|
|
305
|
+
duplicate: true,
|
|
306
|
+
state: "done"
|
|
307
|
+
});
|
|
308
|
+
expect((await store.job(SITE, "a"))?.disposition).toBe("ok");
|
|
309
|
+
await done();
|
|
310
|
+
});
|
|
311
|
+
it("answers the device that finished it with `duplicate`", async () => {
|
|
312
|
+
const { store, done } = await make();
|
|
313
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
314
|
+
const [granted] = await store.claim(claimArgs());
|
|
315
|
+
const first = await store.complete({
|
|
316
|
+
jobId: "a",
|
|
317
|
+
runnerId: "runner_1",
|
|
318
|
+
leaseId: granted.lease.id,
|
|
319
|
+
envelope: ENVELOPE,
|
|
320
|
+
disposition: "ok"
|
|
321
|
+
});
|
|
322
|
+
expect(first).toEqual({ accepted: true, state: "done" });
|
|
323
|
+
const replay = await store.complete({
|
|
324
|
+
jobId: "a",
|
|
325
|
+
runnerId: "runner_1",
|
|
326
|
+
leaseId: granted.lease.id,
|
|
327
|
+
envelope: { ...ENVELOPE, ciphertext: "a-different-result" },
|
|
328
|
+
disposition: "error"
|
|
329
|
+
});
|
|
330
|
+
expect(replay).toEqual({
|
|
331
|
+
accepted: false,
|
|
332
|
+
duplicate: true,
|
|
333
|
+
state: "done"
|
|
334
|
+
});
|
|
335
|
+
const stranger = await store.complete({
|
|
336
|
+
jobId: "a",
|
|
337
|
+
runnerId: "runner_2",
|
|
338
|
+
leaseId: granted.lease.id,
|
|
339
|
+
envelope: ENVELOPE,
|
|
340
|
+
disposition: "ok"
|
|
341
|
+
});
|
|
342
|
+
expect(stranger).toEqual({ refused: "not-holder" });
|
|
343
|
+
expect((await store.job(SITE, "a"))?.disposition).toBe("ok");
|
|
344
|
+
await done();
|
|
345
|
+
});
|
|
346
|
+
it("refuses a result produced under a grant that ended", async () => {
|
|
347
|
+
const { store, done } = await make();
|
|
348
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
349
|
+
await store.claim(claimArgs());
|
|
350
|
+
const late = await store.complete({
|
|
351
|
+
jobId: "a",
|
|
352
|
+
runnerId: "runner_1",
|
|
353
|
+
leaseId: "a-grant-that-ended",
|
|
354
|
+
envelope: ENVELOPE,
|
|
355
|
+
disposition: "ok"
|
|
356
|
+
});
|
|
357
|
+
expect(late).toEqual({ refused: "stale-lease" });
|
|
358
|
+
expect((await store.job(SITE, "a"))?.state).toBe("awaiting-payload");
|
|
359
|
+
expect((await store.job(SITE, "a"))?.result).toBeUndefined();
|
|
360
|
+
await done();
|
|
361
|
+
});
|
|
362
|
+
it("refuses a payload for a job that already finished [V1-6]", async () => {
|
|
363
|
+
const { store, done } = await make();
|
|
364
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
365
|
+
const [granted] = await store.claim(claimArgs());
|
|
366
|
+
await store.seal({ jobId: "a", siteId: SITE, envelope: ENVELOPE });
|
|
367
|
+
await store.takePayload({
|
|
368
|
+
jobId: "a",
|
|
369
|
+
runnerId: "runner_1",
|
|
370
|
+
leaseId: granted.lease.id
|
|
371
|
+
});
|
|
372
|
+
await store.complete({
|
|
373
|
+
jobId: "a",
|
|
374
|
+
runnerId: "runner_1",
|
|
375
|
+
leaseId: granted.lease.id,
|
|
376
|
+
envelope: ENVELOPE,
|
|
377
|
+
disposition: "ok"
|
|
378
|
+
});
|
|
379
|
+
const replay = await store.takePayload({
|
|
380
|
+
jobId: "a",
|
|
381
|
+
runnerId: "runner_1",
|
|
382
|
+
leaseId: granted.lease.id
|
|
383
|
+
});
|
|
384
|
+
expect(replay).toEqual({ refused: "terminal" });
|
|
385
|
+
expect((await store.job(SITE, "a"))?.state).toBe("done");
|
|
386
|
+
await done();
|
|
387
|
+
});
|
|
388
|
+
it("tells a runner nothing about another site's job id [V1-8]", async () => {
|
|
389
|
+
const { store, done } = await make();
|
|
390
|
+
await store.enqueue({
|
|
391
|
+
id: "secret",
|
|
392
|
+
siteId: "site_somebody_else",
|
|
393
|
+
stub: stub("secret")
|
|
394
|
+
});
|
|
395
|
+
await store.enqueue({ id: "mine", siteId: SITE, stub: stub("mine") });
|
|
396
|
+
const granted = await store.claim(claimArgs());
|
|
397
|
+
const mine = granted.find((job) => job.id === "mine");
|
|
398
|
+
const other = await store.takePayload({
|
|
399
|
+
jobId: "secret",
|
|
400
|
+
runnerId: "runner_1",
|
|
401
|
+
leaseId: mine.lease.id
|
|
402
|
+
});
|
|
403
|
+
const absent = await store.takePayload({
|
|
404
|
+
jobId: "no-such-job-anywhere",
|
|
405
|
+
runnerId: "runner_1",
|
|
406
|
+
leaseId: mine.lease.id
|
|
407
|
+
});
|
|
408
|
+
expect(other).toEqual(absent);
|
|
409
|
+
expect(other).toEqual({ refused: "not-found" });
|
|
410
|
+
await done();
|
|
411
|
+
});
|
|
412
|
+
it("offers a selected job only to a device advertising that service", async () => {
|
|
413
|
+
const { store, done } = await make();
|
|
414
|
+
await store.enqueue({
|
|
415
|
+
id: "picked",
|
|
416
|
+
siteId: SITE,
|
|
417
|
+
stub: { ...stub("picked"), service: "studio" }
|
|
418
|
+
});
|
|
419
|
+
const wrong = await store.claim(
|
|
420
|
+
claimArgs({
|
|
421
|
+
runnerId: "runner_wrong",
|
|
422
|
+
serves: /* @__PURE__ */ new Set([serviceKey("llm.generate", "laptop")])
|
|
423
|
+
})
|
|
424
|
+
);
|
|
425
|
+
expect(wrong.map((job) => job.id)).toEqual([]);
|
|
426
|
+
const right = await store.claim(
|
|
427
|
+
claimArgs({
|
|
428
|
+
runnerId: "runner_right",
|
|
429
|
+
serves: /* @__PURE__ */ new Set([serviceKey("llm.generate", "studio")])
|
|
430
|
+
})
|
|
431
|
+
);
|
|
432
|
+
expect(right.map((job) => job.id)).toEqual(["picked"]);
|
|
433
|
+
await done();
|
|
434
|
+
});
|
|
435
|
+
it("never falls back from a selection to the default", async () => {
|
|
436
|
+
const { store, done } = await make();
|
|
437
|
+
await store.enqueue({
|
|
438
|
+
id: "no-fallback",
|
|
439
|
+
siteId: SITE,
|
|
440
|
+
stub: { ...stub("no-fallback"), service: "studio" }
|
|
441
|
+
});
|
|
442
|
+
const granted = await store.claim(
|
|
443
|
+
claimArgs({
|
|
444
|
+
// Serves the kind by default, advertises no service by that name.
|
|
445
|
+
kinds: /* @__PURE__ */ new Set(["llm.generate"]),
|
|
446
|
+
serves: /* @__PURE__ */ new Set()
|
|
447
|
+
})
|
|
448
|
+
);
|
|
449
|
+
expect(granted.map((job) => job.id)).toEqual([]);
|
|
450
|
+
expect((await store.job(SITE, "no-fallback"))?.state).toBe("queued");
|
|
451
|
+
await done();
|
|
452
|
+
});
|
|
453
|
+
it("sends an unselected job to the default, not to the menu", async () => {
|
|
454
|
+
const { store, done } = await make();
|
|
455
|
+
await store.enqueue({
|
|
456
|
+
id: "unselected",
|
|
457
|
+
siteId: SITE,
|
|
458
|
+
stub: stub("unselected")
|
|
459
|
+
});
|
|
460
|
+
const nothingDefault = await store.claim(
|
|
461
|
+
claimArgs({
|
|
462
|
+
runnerId: "runner_menu",
|
|
463
|
+
// A menu, and no default for this kind — the withheld state.
|
|
464
|
+
kinds: /* @__PURE__ */ new Set(),
|
|
465
|
+
serves: /* @__PURE__ */ new Set([
|
|
466
|
+
serviceKey("llm.generate", "studio"),
|
|
467
|
+
serviceKey("llm.generate", "laptop")
|
|
468
|
+
])
|
|
469
|
+
})
|
|
470
|
+
);
|
|
471
|
+
expect(nothingDefault.map((job) => job.id)).toEqual([]);
|
|
472
|
+
const hasDefault = await store.claim(
|
|
473
|
+
claimArgs({
|
|
474
|
+
runnerId: "runner_default",
|
|
475
|
+
kinds: /* @__PURE__ */ new Set(["llm.generate"]),
|
|
476
|
+
serves: /* @__PURE__ */ new Set([serviceKey("llm.generate", "studio")])
|
|
477
|
+
})
|
|
478
|
+
);
|
|
479
|
+
expect(hasDefault.map((job) => job.id)).toEqual(["unselected"]);
|
|
480
|
+
await done();
|
|
481
|
+
});
|
|
482
|
+
it("keeps the pair together, so two sets cannot multiply", async () => {
|
|
483
|
+
const { store, done } = await make();
|
|
484
|
+
await store.enqueue({
|
|
485
|
+
id: "crossed",
|
|
486
|
+
siteId: SITE,
|
|
487
|
+
stub: { ...stub("crossed"), kind: "llm.chat", service: "studio" }
|
|
488
|
+
});
|
|
489
|
+
const granted = await store.claim(
|
|
490
|
+
claimArgs({
|
|
491
|
+
kinds: /* @__PURE__ */ new Set(["llm.generate", "llm.chat"]),
|
|
492
|
+
serves: /* @__PURE__ */ new Set([
|
|
493
|
+
serviceKey("llm.generate", "studio"),
|
|
494
|
+
serviceKey("llm.chat", "claude")
|
|
495
|
+
])
|
|
496
|
+
})
|
|
497
|
+
);
|
|
498
|
+
expect(granted.map((job) => job.id)).toEqual([]);
|
|
499
|
+
await done();
|
|
500
|
+
});
|
|
501
|
+
it("remembers a device, and what it says it can run", async () => {
|
|
502
|
+
const { store, done } = await make();
|
|
503
|
+
const device = publicIdentityOf(generateKeys(3e12));
|
|
504
|
+
await store.seen({
|
|
505
|
+
runnerId: "runner_1",
|
|
506
|
+
owner: "alice",
|
|
507
|
+
device,
|
|
508
|
+
capabilities: [capability("llama3")],
|
|
509
|
+
withheld: []
|
|
510
|
+
});
|
|
511
|
+
const known = await store.presence("runner_1");
|
|
512
|
+
expect(known?.owner).toBe("alice");
|
|
513
|
+
expect(known?.device.identity).toBe(device.identity);
|
|
514
|
+
expect(known?.capabilities.map((row) => row.model)).toEqual(["llama3"]);
|
|
515
|
+
await done();
|
|
516
|
+
});
|
|
517
|
+
it("replaces the matrix rather than merging it", async () => {
|
|
518
|
+
const { store, done } = await make();
|
|
519
|
+
const device = publicIdentityOf(generateKeys(3000000000001));
|
|
520
|
+
const seen = { runnerId: "runner_2", owner: "alice", device };
|
|
521
|
+
await store.seen({
|
|
522
|
+
...seen,
|
|
523
|
+
capabilities: [capability("llama3")],
|
|
524
|
+
withheld: []
|
|
525
|
+
});
|
|
526
|
+
await store.seen({
|
|
527
|
+
...seen,
|
|
528
|
+
capabilities: [capability("mistral")],
|
|
529
|
+
withheld: []
|
|
530
|
+
});
|
|
531
|
+
const known = await store.presence("runner_2");
|
|
532
|
+
expect(known?.capabilities.map((row) => row.model)).toEqual(["mistral"]);
|
|
533
|
+
await done();
|
|
534
|
+
});
|
|
535
|
+
it("keeps an empty matrix, because empty is an answer", async () => {
|
|
536
|
+
const { store, done } = await make();
|
|
537
|
+
const device = publicIdentityOf(generateKeys(3000000000002));
|
|
538
|
+
const seen = { runnerId: "runner_3", owner: "alice", device };
|
|
539
|
+
await store.seen({
|
|
540
|
+
...seen,
|
|
541
|
+
capabilities: [capability("llama3")],
|
|
542
|
+
withheld: []
|
|
543
|
+
});
|
|
544
|
+
await store.seen({ ...seen, capabilities: [], withheld: [] });
|
|
545
|
+
expect((await store.presence("runner_3"))?.capabilities).toEqual([]);
|
|
546
|
+
await done();
|
|
547
|
+
});
|
|
548
|
+
it("remembers which kinds are withheld, and drops them when resolved", async () => {
|
|
549
|
+
const { store, done } = await make();
|
|
550
|
+
const device = publicIdentityOf(generateKeys(3000000000004));
|
|
551
|
+
const seen = { runnerId: "runner_withheld", owner: "alice", device };
|
|
552
|
+
await store.seen({
|
|
553
|
+
...seen,
|
|
554
|
+
capabilities: [],
|
|
555
|
+
withheld: [
|
|
556
|
+
{
|
|
557
|
+
kind: "llm.generate",
|
|
558
|
+
claimants: [
|
|
559
|
+
{ service: "ollama", offer: "team" },
|
|
560
|
+
{ service: "mlx", offer: "private" }
|
|
561
|
+
]
|
|
562
|
+
}
|
|
563
|
+
]
|
|
564
|
+
});
|
|
565
|
+
const held = await store.presence("runner_withheld");
|
|
566
|
+
expect(held?.withheld.map((row) => row.kind)).toEqual(["llm.generate"]);
|
|
567
|
+
expect(held?.withheld[0]?.claimants.map((c) => c.service)).toEqual([
|
|
568
|
+
"ollama",
|
|
569
|
+
"mlx"
|
|
570
|
+
]);
|
|
571
|
+
await store.seen({
|
|
572
|
+
...seen,
|
|
573
|
+
capabilities: [capability("llama3")],
|
|
574
|
+
withheld: []
|
|
575
|
+
});
|
|
576
|
+
expect((await store.presence("runner_withheld"))?.withheld).toEqual([]);
|
|
577
|
+
await done();
|
|
578
|
+
});
|
|
579
|
+
it("stamps presence from its own clock, and moves it on the next beat", async () => {
|
|
580
|
+
const { store, done } = await make();
|
|
581
|
+
const device = publicIdentityOf(generateKeys(3000000000003));
|
|
582
|
+
const seen = { runnerId: "runner_4", owner: "alice", device };
|
|
583
|
+
const before = await store.now();
|
|
584
|
+
const first = await store.seen({
|
|
585
|
+
...seen,
|
|
586
|
+
capabilities: [],
|
|
587
|
+
withheld: []
|
|
588
|
+
});
|
|
589
|
+
expect(first.lastSeenAt).toBeGreaterThanOrEqual(before);
|
|
590
|
+
const later = await store.seen({
|
|
591
|
+
...seen,
|
|
592
|
+
capabilities: [],
|
|
593
|
+
withheld: []
|
|
594
|
+
});
|
|
595
|
+
expect(later.lastSeenAt).toBeGreaterThanOrEqual(first.lastSeenAt);
|
|
596
|
+
await done();
|
|
597
|
+
});
|
|
598
|
+
it("lists everyone it has seen", async () => {
|
|
599
|
+
const { store, done } = await make();
|
|
600
|
+
await store.seen({
|
|
601
|
+
runnerId: "runner_5",
|
|
602
|
+
owner: "alice",
|
|
603
|
+
device: publicIdentityOf(generateKeys(3000000000004)),
|
|
604
|
+
capabilities: [],
|
|
605
|
+
withheld: []
|
|
606
|
+
});
|
|
607
|
+
await store.seen({
|
|
608
|
+
runnerId: "runner_6",
|
|
609
|
+
owner: "bob",
|
|
610
|
+
device: publicIdentityOf(generateKeys(3000000000005)),
|
|
611
|
+
capabilities: [],
|
|
612
|
+
withheld: []
|
|
613
|
+
});
|
|
614
|
+
const everyone = await store.everyone();
|
|
615
|
+
const ids = everyone.map((row) => row.runnerId);
|
|
616
|
+
expect(ids).toContain("runner_5");
|
|
617
|
+
expect(ids).toContain("runner_6");
|
|
618
|
+
await done();
|
|
619
|
+
});
|
|
620
|
+
it("does not know a runner it has never seen", async () => {
|
|
621
|
+
const { store, done } = await make();
|
|
622
|
+
expect(await store.presence("runner_never")).toBeUndefined();
|
|
623
|
+
await done();
|
|
624
|
+
});
|
|
625
|
+
it("stamps deadlines from its own clock", async () => {
|
|
626
|
+
const { store, done } = await make();
|
|
627
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
628
|
+
const before = await store.now();
|
|
629
|
+
const [granted] = await store.claim(claimArgs({ leaseMs: 45e3 }));
|
|
630
|
+
const after = await store.now();
|
|
631
|
+
expect(granted.lease.expiresAt).toBeGreaterThanOrEqual(before + 45e3);
|
|
632
|
+
expect(granted.lease.expiresAt).toBeLessThanOrEqual(after + 45e3);
|
|
633
|
+
await done();
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
export {
|
|
638
|
+
CONTRACT_SITE,
|
|
639
|
+
describeStoreContract
|
|
640
|
+
};
|
|
641
|
+
//# sourceMappingURL=store-contract.js.map
|