@kapeta/local-cluster-service 0.66.0 → 0.67.1

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.
@@ -23,7 +23,7 @@ import {
23
23
  UIPageVoteRequest,
24
24
  UIPageGetVoteRequest,
25
25
  } from './stormClient';
26
- import { Page, StormEvent, StormEventPage, StormEventPhaseType, UserJourneyScreen } from './events';
26
+ import { Page, StormEvent, StormEventPage, StormEventPhaseType, StormImage, UserJourneyScreen } from './events';
27
27
 
28
28
  import {
29
29
  createPhaseEndEvent,
@@ -40,11 +40,12 @@ import {
40
40
  readPageFromDiskAsString,
41
41
  SystemIdHeader,
42
42
  writeAssetToDisk,
43
+ writeImageToDisk,
43
44
  writePageToDisk,
44
45
  } from './page-utils';
45
46
  import { UIServer } from './UIServer';
46
- import { PageQueue } from './PageGenerator';
47
47
  import { randomUUID } from 'crypto';
48
+ import { ImagePrompt, PageQueue } from './PageGenerator';
48
49
 
49
50
  const UI_SERVERS: { [key: string]: UIServer } = {};
50
51
  const router = Router();
@@ -114,6 +115,21 @@ router.post('/ui/screen', async (req: KapetaBodyRequest, res: Response) => {
114
115
  }
115
116
  });
116
117
 
118
+ queue.on('image', async (screenData, prompt, future) => {
119
+ if (!systemId) {
120
+ return;
121
+ }
122
+ try {
123
+ const promise = handleImageEvent(systemId, screenData, prompt);
124
+ promises.push(promise);
125
+ await promise;
126
+ future.resolve();
127
+ } catch (e) {
128
+ console.error('Failed to handle image event', e);
129
+ future.reject(e);
130
+ }
131
+ });
132
+
117
133
  await queue.addPrompt(aiRequest, conversationId, true);
118
134
 
119
135
  await queue.wait();
@@ -211,6 +227,18 @@ router.post('/:handle/ui/iterative', async (req: KapetaBodyRequest, res: Respons
211
227
  pageEventPromises.push(sendPageEvent(landingPagesStream.getConversationId(), screenData, res));
212
228
  });
213
229
 
230
+ pageQueue.on('image', async (screenData, prompt, future) => {
231
+ try {
232
+ const promise = handleImageEvent(landingPagesStream.getConversationId(), screenData, prompt);
233
+ pageEventPromises.push(promise);
234
+ await promise;
235
+ future.resolve();
236
+ } catch (e) {
237
+ console.error('Failed to handle image event', e);
238
+ future.reject(e);
239
+ }
240
+ });
241
+
214
242
  pageQueue.on('event', (screenData: StormEvent) => {
215
243
  sendEvent(res, screenData);
216
244
  });
@@ -347,7 +375,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
347
375
  queue.setUiTheme(theme);
348
376
 
349
377
  shellsStream.on('data', (data: StormEvent) => {
350
- console.log('Processing shell event', data);
378
+ //console.log('Processing shell event', data);
351
379
  sendEvent(res, data);
352
380
 
353
381
  if (data.type !== 'UI_SHELL') {
@@ -394,6 +422,18 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
394
422
  pageEventPromises.push(sendPageEvent(outerConversationId, screenData, res));
395
423
  });
396
424
 
425
+ queue.on('image', async (screenData, prompt, future) => {
426
+ try {
427
+ const promise = handleImageEvent(outerConversationId, screenData, prompt);
428
+ pageEventPromises.push(promise);
429
+ await promise;
430
+ future.resolve();
431
+ } catch (e) {
432
+ console.error('Failed to handle image event', e);
433
+ future.reject(e);
434
+ }
435
+ });
436
+
397
437
  queue.on('event', (screenData: StormEvent) => {
398
438
  sendEvent(res, screenData);
399
439
  });
@@ -433,67 +473,53 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
433
473
 
434
474
  router.post('/ui/edit', async (req: KapetaBodyRequest, res: Response) => {
435
475
  try {
436
- const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
476
+ const systemId = (req.headers[SystemIdHeader.toLowerCase()] ||
477
+ req.headers[ConversationIdHeader.toLowerCase()]) as string | undefined;
437
478
 
438
479
  const aiRequest: StormContextRequest<UIPageEditRequest> = JSON.parse(req.stringBody ?? '{}');
439
-
440
- const pages = aiRequest.prompt.pages
441
- .map((page) => {
442
- const content = readPageFromDiskAsString(conversationId!, page.path, page.method);
443
- if (!content) {
444
- console.warn('Page not found', page);
445
- return undefined;
446
- }
447
-
448
- return {
449
- filename: page.filename,
450
- path: page.path,
451
- method: page.method,
452
- title: page.title,
453
- conversationId: page.conversationId,
454
- prompt: page.prompt,
455
- name: page.name,
456
- description: page.description,
457
- content,
458
- } satisfies Page;
459
- })
460
- .filter((page: Page | undefined) => !!page) as UIPageEditPrompt['pages'];
461
-
462
- const editStream = await stormClient.editPages(
463
- {
464
- prompt: aiRequest.prompt.prompt.prompt,
465
- blockDescription: aiRequest.prompt.blockDescription,
466
- planDescription: aiRequest.prompt.planDescription,
467
- pages,
468
- },
469
- conversationId
470
- );
480
+ const storagePrefix = systemId ? systemId + '_' : 'mock_';
481
+ const queue = new PageQueue(storagePrefix, 5);
471
482
 
472
483
  onRequestAborted(req, res, () => {
473
- editStream.abort();
484
+ queue.cancel();
474
485
  });
475
486
 
476
- res.set('Content-Type', 'application/x-ndjson');
477
- res.set('Access-Control-Expose-Headers', ConversationIdHeader);
478
- res.set(ConversationIdHeader, editStream.getConversationId());
479
-
480
487
  const promises: Promise<void>[] = [];
481
- editStream.on('data', (data: StormEvent) => {
482
- try {
483
- if (data.type === 'PAGE') {
484
- promises.push(sendPageEvent(editStream.getConversationId(), data, res));
485
- } else {
486
- sendEvent(res, data);
487
- }
488
- } catch (e) {
489
- console.error('Failed to process event', e);
488
+
489
+ queue.on('page', (data) => {
490
+ if (systemId) {
491
+ promises.push(sendPageEvent(systemId, data, res));
490
492
  }
491
493
  });
492
494
 
493
- await waitForStormStream(editStream);
494
- if (editStream.isAborted()) {
495
- return;
496
- }
495
+ queue.on('event', (data) => {
496
+ if (data.type === 'FILE_START' || data.type === 'FILE_DONE' || data.type === 'FILE_STATE') {
497
+ sendEvent(res, data);
498
+ }
499
+ });
500
+
501
+ await Promise.allSettled(
502
+ aiRequest.prompt.pages.map((page) => {
503
+ if (page.conversationId) {
504
+ return queue.addPrompt(
505
+ {
506
+ title: page.title,
507
+ name: page.name,
508
+ method: page.method,
509
+ path: page.path,
510
+ description: page.description ?? '',
511
+ filename: page.filename,
512
+ prompt: aiRequest.prompt.prompt.prompt,
513
+ storage_prefix: storagePrefix,
514
+ },
515
+ page.conversationId,
516
+ true
517
+ );
518
+ }
519
+ })
520
+ );
521
+
522
+ await queue.wait();
497
523
  await Promise.all(promises);
498
524
 
499
525
  sendDone(res);
@@ -766,4 +792,12 @@ async function sendPageEvent(mainConversationId: string, data: StormEventPage, r
766
792
  sendEvent(res, convertPageEvent(data, data.payload.conversationId, mainConversationId));
767
793
  }
768
794
 
795
+ async function handleImageEvent(mainConversationId: string, data: StormImage, prompt: ImagePrompt) {
796
+ try {
797
+ await writeImageToDisk(mainConversationId, data, prompt);
798
+ } catch (err) {
799
+ console.error('Failed to write image to disk', err);
800
+ }
801
+ }
802
+
769
803
  export default router;
@@ -44,7 +44,7 @@ export interface UIPagePrompt {
44
44
  storage_prefix: string;
45
45
  shell_page?: string;
46
46
  // contents of theme.css
47
- theme: string;
47
+ theme?: string;
48
48
  }
49
49
 
50
50
  export interface UIPageSamplePrompt extends UIPagePrompt {
@@ -263,6 +263,13 @@ class StormClient {
263
263
  });
264
264
  }
265
265
 
266
+ public createImage(prompt: string, conversationId?: string) {
267
+ return this.send('/v2/ui/image', {
268
+ prompt,
269
+ conversationId,
270
+ });
271
+ }
272
+
266
273
  public createUIImplementation(prompt: StormUIImplementationPrompt, conversationId?: string) {
267
274
  return this.send('/v2/ui/merge', {
268
275
  prompt,