@kapeta/local-cluster-service 0.74.1 → 0.74.2
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 +8 -0
- package/dist/cjs/src/storm/PageGenerator.js +6 -2
- package/dist/cjs/src/storm/events.d.ts +2 -0
- package/dist/cjs/src/storm/routes.js +11 -3
- package/dist/esm/src/storm/PageGenerator.js +6 -2
- package/dist/esm/src/storm/events.d.ts +2 -0
- package/dist/esm/src/storm/routes.js +11 -3
- package/package.json +1 -1
- package/src/storm/PageGenerator.ts +11 -4
- package/src/storm/events.ts +2 -0
- package/src/storm/routes.ts +27 -13
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## [0.74.2](https://github.com/kapetacom/local-cluster-service/compare/v0.74.1...v0.74.2) (2024-09-27)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* add more bail checkpoints for aborted request ([1f4823c](https://github.com/kapetacom/local-cluster-service/commit/1f4823c648d21f3ea8c8f8f2ead156d1e43c7252))
|
7
|
+
* define conversationIds for pages as early as possible ([7b020a5](https://github.com/kapetacom/local-cluster-service/commit/7b020a52ddeba60329989dafda62abc8d3565039))
|
8
|
+
|
1
9
|
## [0.74.1](https://github.com/kapetacom/local-cluster-service/compare/v0.74.0...v0.74.1) (2024-09-27)
|
2
10
|
|
3
11
|
|
@@ -37,6 +37,7 @@ const node_events_1 = require("node:events");
|
|
37
37
|
const p_queue_1 = __importDefault(require("p-queue"));
|
38
38
|
const page_utils_1 = require("./page-utils");
|
39
39
|
const mimetypes = __importStar(require("mime-types"));
|
40
|
+
const node_crypto_1 = require("node:crypto");
|
40
41
|
class PageQueue extends node_events_1.EventEmitter {
|
41
42
|
queue;
|
42
43
|
eventQueue;
|
@@ -166,6 +167,8 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
166
167
|
}
|
167
168
|
this.pages.set(normalizedPath, reference.description);
|
168
169
|
initialPrompts.push({
|
170
|
+
conversationId: (0, node_crypto_1.randomUUID)(),
|
171
|
+
id: (0, node_crypto_1.randomUUID)(),
|
169
172
|
name: reference.name,
|
170
173
|
title: reference.title,
|
171
174
|
path: normalizedPath,
|
@@ -193,20 +196,21 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
193
196
|
reason: 'reference',
|
194
197
|
created: Date.now(),
|
195
198
|
payload: {
|
199
|
+
id: prompt.id,
|
200
|
+
conversationId: prompt.conversationId,
|
196
201
|
name: prompt.name,
|
197
202
|
title: prompt.title,
|
198
203
|
filename: prompt.filename,
|
199
204
|
method: 'GET',
|
200
205
|
path: prompt.path,
|
201
206
|
prompt: prompt.description,
|
202
|
-
conversationId: '',
|
203
207
|
content: '',
|
204
208
|
description: prompt.description,
|
205
209
|
},
|
206
210
|
});
|
207
211
|
}
|
208
212
|
// Trigger but don't wait for the "bonus" pages
|
209
|
-
this.addPrompt(prompt).catch((err) => {
|
213
|
+
this.addPrompt(prompt, prompt.conversationId).catch((err) => {
|
210
214
|
console.error('Failed to generate page reference', prompt.name, err);
|
211
215
|
this.emit('error', err);
|
212
216
|
});
|
@@ -287,6 +287,7 @@ export interface StormEventPhases {
|
|
287
287
|
};
|
288
288
|
}
|
289
289
|
export interface Page {
|
290
|
+
id: string;
|
290
291
|
name: string;
|
291
292
|
filename: string;
|
292
293
|
title: string;
|
@@ -329,6 +330,7 @@ export interface UserJourneyScreen {
|
|
329
330
|
path: string;
|
330
331
|
method: string;
|
331
332
|
nextScreens: string[];
|
333
|
+
conversationId?: string;
|
332
334
|
}
|
333
335
|
export interface UserJourney {
|
334
336
|
title: string;
|
@@ -357,11 +357,11 @@ router.post('/:handle/ui', async (req, res) => {
|
|
357
357
|
let systemPrompt = aiRequest.prompt;
|
358
358
|
userJourneysStream.on('data', (data) => {
|
359
359
|
try {
|
360
|
-
sendEvent(res, data);
|
361
360
|
if (data.type === 'PROMPT_IMPROVE') {
|
362
361
|
systemPrompt = data.payload.prompt;
|
363
362
|
}
|
364
363
|
if (data.type !== 'USER_JOURNEY') {
|
364
|
+
sendEvent(res, data);
|
365
365
|
return;
|
366
366
|
}
|
367
367
|
if (userJourneysStream.isAborted()) {
|
@@ -369,9 +369,11 @@ router.post('/:handle/ui', async (req, res) => {
|
|
369
369
|
}
|
370
370
|
data.payload.screens.forEach((screen) => {
|
371
371
|
if (!uniqueUserJourneyScreens[screen.name]) {
|
372
|
+
screen.conversationId = (0, crypto_1.randomUUID)();
|
372
373
|
uniqueUserJourneyScreens[screen.name] = screen;
|
373
374
|
}
|
374
375
|
});
|
376
|
+
sendEvent(res, data);
|
375
377
|
}
|
376
378
|
catch (e) {
|
377
379
|
console.error('Failed to process event', e);
|
@@ -422,6 +424,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
422
424
|
});
|
423
425
|
}
|
424
426
|
await waitForStormStream(userJourneysStream);
|
427
|
+
if (req.socket.closed) {
|
428
|
+
return;
|
429
|
+
}
|
425
430
|
// Get the UI shells
|
426
431
|
const shellsStream = await stormClient.createUIShells({
|
427
432
|
theme: theme || undefined,
|
@@ -456,6 +461,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
456
461
|
sendError(error, res);
|
457
462
|
});
|
458
463
|
await waitForStormStream(shellsStream);
|
464
|
+
if (req.socket.closed) {
|
465
|
+
return;
|
466
|
+
}
|
459
467
|
UI_SERVERS[outerConversationId] = new UIServer_1.UIServer(outerConversationId);
|
460
468
|
await UI_SERVERS[outerConversationId].start();
|
461
469
|
sendEvent(res, {
|
@@ -470,7 +478,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
470
478
|
onRequestAborted(req, res, () => {
|
471
479
|
queue.cancel();
|
472
480
|
});
|
473
|
-
queue.on('page', (
|
481
|
+
queue.on('page', (pageEvent) => sendPageEvent(outerConversationId, pageEvent, res));
|
474
482
|
queue.on('event', (event) => {
|
475
483
|
if (event.type === 'FILE_CHUNK') {
|
476
484
|
return;
|
@@ -493,7 +501,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
493
501
|
filename: screen.filename,
|
494
502
|
storage_prefix: outerConversationId + '_',
|
495
503
|
theme,
|
496
|
-
})
|
504
|
+
}, screen.conversationId)
|
497
505
|
.catch((e) => {
|
498
506
|
console.error('Failed to generate page for screen %s', screen.name, e);
|
499
507
|
sendError(e, res);
|
@@ -37,6 +37,7 @@ const node_events_1 = require("node:events");
|
|
37
37
|
const p_queue_1 = __importDefault(require("p-queue"));
|
38
38
|
const page_utils_1 = require("./page-utils");
|
39
39
|
const mimetypes = __importStar(require("mime-types"));
|
40
|
+
const node_crypto_1 = require("node:crypto");
|
40
41
|
class PageQueue extends node_events_1.EventEmitter {
|
41
42
|
queue;
|
42
43
|
eventQueue;
|
@@ -166,6 +167,8 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
166
167
|
}
|
167
168
|
this.pages.set(normalizedPath, reference.description);
|
168
169
|
initialPrompts.push({
|
170
|
+
conversationId: (0, node_crypto_1.randomUUID)(),
|
171
|
+
id: (0, node_crypto_1.randomUUID)(),
|
169
172
|
name: reference.name,
|
170
173
|
title: reference.title,
|
171
174
|
path: normalizedPath,
|
@@ -193,20 +196,21 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
193
196
|
reason: 'reference',
|
194
197
|
created: Date.now(),
|
195
198
|
payload: {
|
199
|
+
id: prompt.id,
|
200
|
+
conversationId: prompt.conversationId,
|
196
201
|
name: prompt.name,
|
197
202
|
title: prompt.title,
|
198
203
|
filename: prompt.filename,
|
199
204
|
method: 'GET',
|
200
205
|
path: prompt.path,
|
201
206
|
prompt: prompt.description,
|
202
|
-
conversationId: '',
|
203
207
|
content: '',
|
204
208
|
description: prompt.description,
|
205
209
|
},
|
206
210
|
});
|
207
211
|
}
|
208
212
|
// Trigger but don't wait for the "bonus" pages
|
209
|
-
this.addPrompt(prompt).catch((err) => {
|
213
|
+
this.addPrompt(prompt, prompt.conversationId).catch((err) => {
|
210
214
|
console.error('Failed to generate page reference', prompt.name, err);
|
211
215
|
this.emit('error', err);
|
212
216
|
});
|
@@ -287,6 +287,7 @@ export interface StormEventPhases {
|
|
287
287
|
};
|
288
288
|
}
|
289
289
|
export interface Page {
|
290
|
+
id: string;
|
290
291
|
name: string;
|
291
292
|
filename: string;
|
292
293
|
title: string;
|
@@ -329,6 +330,7 @@ export interface UserJourneyScreen {
|
|
329
330
|
path: string;
|
330
331
|
method: string;
|
331
332
|
nextScreens: string[];
|
333
|
+
conversationId?: string;
|
332
334
|
}
|
333
335
|
export interface UserJourney {
|
334
336
|
title: string;
|
@@ -357,11 +357,11 @@ router.post('/:handle/ui', async (req, res) => {
|
|
357
357
|
let systemPrompt = aiRequest.prompt;
|
358
358
|
userJourneysStream.on('data', (data) => {
|
359
359
|
try {
|
360
|
-
sendEvent(res, data);
|
361
360
|
if (data.type === 'PROMPT_IMPROVE') {
|
362
361
|
systemPrompt = data.payload.prompt;
|
363
362
|
}
|
364
363
|
if (data.type !== 'USER_JOURNEY') {
|
364
|
+
sendEvent(res, data);
|
365
365
|
return;
|
366
366
|
}
|
367
367
|
if (userJourneysStream.isAborted()) {
|
@@ -369,9 +369,11 @@ router.post('/:handle/ui', async (req, res) => {
|
|
369
369
|
}
|
370
370
|
data.payload.screens.forEach((screen) => {
|
371
371
|
if (!uniqueUserJourneyScreens[screen.name]) {
|
372
|
+
screen.conversationId = (0, crypto_1.randomUUID)();
|
372
373
|
uniqueUserJourneyScreens[screen.name] = screen;
|
373
374
|
}
|
374
375
|
});
|
376
|
+
sendEvent(res, data);
|
375
377
|
}
|
376
378
|
catch (e) {
|
377
379
|
console.error('Failed to process event', e);
|
@@ -422,6 +424,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
422
424
|
});
|
423
425
|
}
|
424
426
|
await waitForStormStream(userJourneysStream);
|
427
|
+
if (req.socket.closed) {
|
428
|
+
return;
|
429
|
+
}
|
425
430
|
// Get the UI shells
|
426
431
|
const shellsStream = await stormClient.createUIShells({
|
427
432
|
theme: theme || undefined,
|
@@ -456,6 +461,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
456
461
|
sendError(error, res);
|
457
462
|
});
|
458
463
|
await waitForStormStream(shellsStream);
|
464
|
+
if (req.socket.closed) {
|
465
|
+
return;
|
466
|
+
}
|
459
467
|
UI_SERVERS[outerConversationId] = new UIServer_1.UIServer(outerConversationId);
|
460
468
|
await UI_SERVERS[outerConversationId].start();
|
461
469
|
sendEvent(res, {
|
@@ -470,7 +478,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
470
478
|
onRequestAborted(req, res, () => {
|
471
479
|
queue.cancel();
|
472
480
|
});
|
473
|
-
queue.on('page', (
|
481
|
+
queue.on('page', (pageEvent) => sendPageEvent(outerConversationId, pageEvent, res));
|
474
482
|
queue.on('event', (event) => {
|
475
483
|
if (event.type === 'FILE_CHUNK') {
|
476
484
|
return;
|
@@ -493,7 +501,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
493
501
|
filename: screen.filename,
|
494
502
|
storage_prefix: outerConversationId + '_',
|
495
503
|
theme,
|
496
|
-
})
|
504
|
+
}, screen.conversationId)
|
497
505
|
.catch((e) => {
|
498
506
|
console.error('Failed to generate page for screen %s', screen.name, e);
|
499
507
|
sendError(e, res);
|
package/package.json
CHANGED
@@ -11,6 +11,7 @@ import PQueue from 'p-queue';
|
|
11
11
|
|
12
12
|
import { hasPageOnDisk, normalizePath, writeImageToDisk } from './page-utils';
|
13
13
|
import * as mimetypes from 'mime-types';
|
14
|
+
import { randomUUID } from 'node:crypto';
|
14
15
|
|
15
16
|
export interface ImagePrompt {
|
16
17
|
name: string;
|
@@ -22,7 +23,10 @@ export interface ImagePrompt {
|
|
22
23
|
content: string;
|
23
24
|
}
|
24
25
|
|
25
|
-
type InitialPrompt = Omit<UIPagePrompt, 'shell_page'> & {
|
26
|
+
type InitialPrompt = Omit<UIPagePrompt, 'shell_page'> & {
|
27
|
+
shellType?: 'public' | 'admin' | 'user';
|
28
|
+
};
|
29
|
+
type PagePrompt = InitialPrompt & { conversationId: string; id: string };
|
26
30
|
|
27
31
|
export class PageQueue extends EventEmitter {
|
28
32
|
private readonly queue: PQueue;
|
@@ -149,7 +153,7 @@ export class PageQueue extends EventEmitter {
|
|
149
153
|
new RegExp(`^${path.replaceAll('/*', '/[^/]+')}$`).test(url)
|
150
154
|
);
|
151
155
|
};
|
152
|
-
const initialPrompts:
|
156
|
+
const initialPrompts: PagePrompt[] = [];
|
153
157
|
const resourcePromises = references.map(async (reference) => {
|
154
158
|
if (
|
155
159
|
reference.url.startsWith('#') ||
|
@@ -185,6 +189,8 @@ export class PageQueue extends EventEmitter {
|
|
185
189
|
this.pages.set(normalizedPath, reference.description);
|
186
190
|
|
187
191
|
initialPrompts.push({
|
192
|
+
conversationId: randomUUID(),
|
193
|
+
id: randomUUID(),
|
188
194
|
name: reference.name,
|
189
195
|
title: reference.title,
|
190
196
|
path: normalizedPath,
|
@@ -215,20 +221,21 @@ export class PageQueue extends EventEmitter {
|
|
215
221
|
reason: 'reference',
|
216
222
|
created: Date.now(),
|
217
223
|
payload: {
|
224
|
+
id: prompt.id,
|
225
|
+
conversationId: prompt.conversationId,
|
218
226
|
name: prompt.name,
|
219
227
|
title: prompt.title,
|
220
228
|
filename: prompt.filename,
|
221
229
|
method: 'GET',
|
222
230
|
path: prompt.path,
|
223
231
|
prompt: prompt.description,
|
224
|
-
conversationId: '',
|
225
232
|
content: '',
|
226
233
|
description: prompt.description,
|
227
234
|
},
|
228
235
|
});
|
229
236
|
}
|
230
237
|
// Trigger but don't wait for the "bonus" pages
|
231
|
-
this.addPrompt(prompt).catch((err) => {
|
238
|
+
this.addPrompt(prompt, prompt.conversationId).catch((err) => {
|
232
239
|
console.error('Failed to generate page reference', prompt.name, err);
|
233
240
|
this.emit('error', err);
|
234
241
|
});
|
package/src/storm/events.ts
CHANGED
@@ -347,6 +347,7 @@ export interface StormEventPhases {
|
|
347
347
|
}
|
348
348
|
|
349
349
|
export interface Page {
|
350
|
+
id: string;
|
350
351
|
name: string;
|
351
352
|
filename: string;
|
352
353
|
title: string;
|
@@ -394,6 +395,7 @@ export interface UserJourneyScreen {
|
|
394
395
|
path: string;
|
395
396
|
method: string;
|
396
397
|
nextScreens: string[];
|
398
|
+
conversationId?: string;
|
397
399
|
}
|
398
400
|
|
399
401
|
export interface UserJourney {
|
package/src/storm/routes.ts
CHANGED
@@ -458,11 +458,11 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
458
458
|
|
459
459
|
userJourneysStream.on('data', (data: StormEvent) => {
|
460
460
|
try {
|
461
|
-
sendEvent(res, data);
|
462
461
|
if (data.type === 'PROMPT_IMPROVE') {
|
463
462
|
systemPrompt = data.payload.prompt;
|
464
463
|
}
|
465
464
|
if (data.type !== 'USER_JOURNEY') {
|
465
|
+
sendEvent(res, data);
|
466
466
|
return;
|
467
467
|
}
|
468
468
|
|
@@ -472,9 +472,12 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
472
472
|
|
473
473
|
data.payload.screens.forEach((screen) => {
|
474
474
|
if (!uniqueUserJourneyScreens[screen.name]) {
|
475
|
+
screen.conversationId = randomUUID();
|
475
476
|
uniqueUserJourneyScreens[screen.name] = screen;
|
476
477
|
}
|
477
478
|
});
|
479
|
+
|
480
|
+
sendEvent(res, data);
|
478
481
|
} catch (e) {
|
479
482
|
console.error('Failed to process event', e);
|
480
483
|
}
|
@@ -528,6 +531,10 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
528
531
|
|
529
532
|
await waitForStormStream(userJourneysStream);
|
530
533
|
|
534
|
+
if (req.socket.closed) {
|
535
|
+
return;
|
536
|
+
}
|
537
|
+
|
531
538
|
// Get the UI shells
|
532
539
|
const shellsStream = await stormClient.createUIShells(
|
533
540
|
{
|
@@ -574,6 +581,10 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
574
581
|
|
575
582
|
await waitForStormStream(shellsStream);
|
576
583
|
|
584
|
+
if (req.socket.closed) {
|
585
|
+
return;
|
586
|
+
}
|
587
|
+
|
577
588
|
UI_SERVERS[outerConversationId] = new UIServer(outerConversationId);
|
578
589
|
await UI_SERVERS[outerConversationId].start();
|
579
590
|
|
@@ -591,7 +602,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
591
602
|
queue.cancel();
|
592
603
|
});
|
593
604
|
|
594
|
-
queue.on('page', (
|
605
|
+
queue.on('page', (pageEvent: StormEventPage) => sendPageEvent(outerConversationId, pageEvent, res));
|
595
606
|
|
596
607
|
queue.on('event', (event: StormEvent) => {
|
597
608
|
if (event.type === 'FILE_CHUNK') {
|
@@ -607,17 +618,20 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
607
618
|
|
608
619
|
for (const screen of Object.values(uniqueUserJourneyScreens)) {
|
609
620
|
queue
|
610
|
-
.addPrompt(
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
+
.addPrompt(
|
622
|
+
{
|
623
|
+
prompt: screen.requirements,
|
624
|
+
method: screen.method,
|
625
|
+
path: screen.path,
|
626
|
+
description: screen.requirements,
|
627
|
+
name: screen.name,
|
628
|
+
title: screen.title,
|
629
|
+
filename: screen.filename,
|
630
|
+
storage_prefix: outerConversationId + '_',
|
631
|
+
theme,
|
632
|
+
},
|
633
|
+
screen.conversationId
|
634
|
+
)
|
621
635
|
.catch((e) => {
|
622
636
|
console.error('Failed to generate page for screen %s', screen.name, e);
|
623
637
|
sendError(e as any, res);
|