@kapeta/local-cluster-service 0.74.0 → 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 +15 -0
- package/dist/cjs/src/storm/PageGenerator.js +12 -5
- package/dist/cjs/src/storm/codegen.js +11 -7
- package/dist/cjs/src/storm/events.d.ts +2 -0
- package/dist/cjs/src/storm/routes.js +31 -14
- package/dist/cjs/src/storm/stormClient.d.ts +4 -4
- package/dist/cjs/src/storm/stormClient.js +13 -3
- package/dist/cjs/src/storm/stream.d.ts +2 -0
- package/dist/cjs/src/stormService.d.ts +2 -2
- package/dist/cjs/src/stormService.js +8 -6
- package/dist/esm/src/storm/PageGenerator.js +12 -5
- package/dist/esm/src/storm/codegen.js +11 -7
- package/dist/esm/src/storm/events.d.ts +2 -0
- package/dist/esm/src/storm/routes.js +31 -14
- package/dist/esm/src/storm/stormClient.d.ts +4 -4
- package/dist/esm/src/storm/stormClient.js +13 -3
- package/dist/esm/src/storm/stream.d.ts +2 -0
- package/dist/esm/src/stormService.d.ts +2 -2
- package/dist/esm/src/stormService.js +8 -6
- package/package.json +1 -1
- package/src/storm/PageGenerator.ts +18 -8
- package/src/storm/codegen.ts +5 -2
- package/src/storm/events.ts +2 -0
- package/src/storm/routes.ts +42 -22
- package/src/storm/stormClient.ts +12 -7
- package/src/storm/stream.ts +2 -0
- package/src/stormService.ts +9 -7
@@ -117,8 +117,10 @@ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
|
|
117
117
|
res.set(stormClient_1.ConversationIdHeader, systemId);
|
118
118
|
sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.IMPLEMENT_APIS));
|
119
119
|
const pagesFromDisk = (0, utils_1.readFilesAndContent)(srcDir);
|
120
|
-
const
|
120
|
+
const client = new stormClient_1.StormClient(systemId);
|
121
|
+
const pagesWithImplementation = await client.replaceMockWithAPICall({
|
121
122
|
pages: pagesFromDisk,
|
123
|
+
systemId: systemId,
|
122
124
|
});
|
123
125
|
await (0, utils_1.copyDirectory)(srcDir, destDir, (fileName, content) => {
|
124
126
|
// find the page from result1 and write the content to the file
|
@@ -131,7 +133,7 @@ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
|
|
131
133
|
const pageContents = pagesWithImplementation.map((page) => {
|
132
134
|
return page.content;
|
133
135
|
});
|
134
|
-
const prompt = await
|
136
|
+
const prompt = await client.generatePrompt(pageContents);
|
135
137
|
sendEvent(res, (0, event_parser_1.createPhaseEndEvent)(events_1.StormEventPhaseType.COMPOSE_SYSTEM_PROMPT));
|
136
138
|
req.query.systemId = systemId;
|
137
139
|
const promptRequest = {
|
@@ -149,10 +151,12 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req, res) => {
|
|
149
151
|
//res.set('Access-Control-Expose-Headers', ConversationIdHeader);
|
150
152
|
//res.set(ConversationIdHeader, systemId);
|
151
153
|
//sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENT_APIS));
|
154
|
+
const client = new stormClient_1.StormClient(systemId);
|
152
155
|
try {
|
153
156
|
const pagesFromDisk = (0, utils_1.readFilesAndContent)(srcDir);
|
154
|
-
const pagesWithImplementation = await
|
157
|
+
const pagesWithImplementation = await client.replaceMockWithAPICall({
|
155
158
|
pages: pagesFromDisk,
|
159
|
+
systemId: systemId,
|
156
160
|
});
|
157
161
|
//sendEvent(res, createPhaseEndEvent(StormEventPhaseType.IMPLEMENT_APIS));
|
158
162
|
//sendEvent(res, createPhaseStartEvent(StormEventPhaseType.COMPOSE_SYSTEM));
|
@@ -165,7 +169,7 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req, res) => {
|
|
165
169
|
}
|
166
170
|
return page;
|
167
171
|
});
|
168
|
-
const systemUrl = await
|
172
|
+
const systemUrl = await client.createSimpleBackend(handle, systemId, { pages: allFiles });
|
169
173
|
//sendEvent(res, {type: 'SYSTEM_READY', created: Math.floor(Date.now() / 1000), reason: 'System Ready', payload: { systemUrl: systemUrl }});
|
170
174
|
//sendEvent(res, createPhaseEndEvent(StormEventPhaseType.COMPOSE_SYSTEM));
|
171
175
|
//sendDone(res);
|
@@ -250,7 +254,8 @@ router.post('/:handle/ui/iterative', async (req, res) => {
|
|
250
254
|
try {
|
251
255
|
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
252
256
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
253
|
-
const
|
257
|
+
const client = new stormClient_1.StormClient(conversationId); //todo is this correct we are using the landing page getConversationId down below as well
|
258
|
+
const landingPagesStream = await client.createUILandingPages(aiRequest, conversationId);
|
254
259
|
onRequestAborted(req, res, () => {
|
255
260
|
landingPagesStream.abort();
|
256
261
|
});
|
@@ -339,8 +344,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
339
344
|
try {
|
340
345
|
const outerConversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()] || (0, crypto_1.randomUUID)();
|
341
346
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
347
|
+
const stormClient = new stormClient_1.StormClient(outerConversationId);
|
342
348
|
// Get user journeys
|
343
|
-
const userJourneysStream = await
|
349
|
+
const userJourneysStream = await stormClient.createUIUserJourneys(aiRequest, outerConversationId);
|
344
350
|
onRequestAborted(req, res, () => {
|
345
351
|
userJourneysStream.abort();
|
346
352
|
});
|
@@ -351,11 +357,11 @@ router.post('/:handle/ui', async (req, res) => {
|
|
351
357
|
let systemPrompt = aiRequest.prompt;
|
352
358
|
userJourneysStream.on('data', (data) => {
|
353
359
|
try {
|
354
|
-
sendEvent(res, data);
|
355
360
|
if (data.type === 'PROMPT_IMPROVE') {
|
356
361
|
systemPrompt = data.payload.prompt;
|
357
362
|
}
|
358
363
|
if (data.type !== 'USER_JOURNEY') {
|
364
|
+
sendEvent(res, data);
|
359
365
|
return;
|
360
366
|
}
|
361
367
|
if (userJourneysStream.isAborted()) {
|
@@ -363,9 +369,11 @@ router.post('/:handle/ui', async (req, res) => {
|
|
363
369
|
}
|
364
370
|
data.payload.screens.forEach((screen) => {
|
365
371
|
if (!uniqueUserJourneyScreens[screen.name]) {
|
372
|
+
screen.conversationId = (0, crypto_1.randomUUID)();
|
366
373
|
uniqueUserJourneyScreens[screen.name] = screen;
|
367
374
|
}
|
368
375
|
});
|
376
|
+
sendEvent(res, data);
|
369
377
|
}
|
370
378
|
catch (e) {
|
371
379
|
console.error('Failed to process event', e);
|
@@ -378,7 +386,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
378
386
|
});
|
379
387
|
let theme = '';
|
380
388
|
try {
|
381
|
-
const themeStream = await
|
389
|
+
const themeStream = await stormClient.createTheme(aiRequest, outerConversationId);
|
382
390
|
onRequestAborted(req, res, () => {
|
383
391
|
themeStream.abort();
|
384
392
|
});
|
@@ -416,8 +424,11 @@ router.post('/:handle/ui', async (req, res) => {
|
|
416
424
|
});
|
417
425
|
}
|
418
426
|
await waitForStormStream(userJourneysStream);
|
427
|
+
if (req.socket.closed) {
|
428
|
+
return;
|
429
|
+
}
|
419
430
|
// Get the UI shells
|
420
|
-
const shellsStream = await
|
431
|
+
const shellsStream = await stormClient.createUIShells({
|
421
432
|
theme: theme || undefined,
|
422
433
|
pages: Object.values(uniqueUserJourneyScreens).map((screen) => ({
|
423
434
|
name: screen.name,
|
@@ -450,6 +461,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
450
461
|
sendError(error, res);
|
451
462
|
});
|
452
463
|
await waitForStormStream(shellsStream);
|
464
|
+
if (req.socket.closed) {
|
465
|
+
return;
|
466
|
+
}
|
453
467
|
UI_SERVERS[outerConversationId] = new UIServer_1.UIServer(outerConversationId);
|
454
468
|
await UI_SERVERS[outerConversationId].start();
|
455
469
|
sendEvent(res, {
|
@@ -464,7 +478,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
464
478
|
onRequestAborted(req, res, () => {
|
465
479
|
queue.cancel();
|
466
480
|
});
|
467
|
-
queue.on('page', (
|
481
|
+
queue.on('page', (pageEvent) => sendPageEvent(outerConversationId, pageEvent, res));
|
468
482
|
queue.on('event', (event) => {
|
469
483
|
if (event.type === 'FILE_CHUNK') {
|
470
484
|
return;
|
@@ -487,7 +501,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
487
501
|
filename: screen.filename,
|
488
502
|
storage_prefix: outerConversationId + '_',
|
489
503
|
theme,
|
490
|
-
})
|
504
|
+
}, screen.conversationId)
|
491
505
|
.catch((e) => {
|
492
506
|
console.error('Failed to generate page for screen %s', screen.name, e);
|
493
507
|
sendError(e, res);
|
@@ -574,7 +588,8 @@ router.post('/ui/vote', async (req, res) => {
|
|
574
588
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
575
589
|
const { topic, vote, mainConversationId } = aiRequest;
|
576
590
|
try {
|
577
|
-
|
591
|
+
const stormClient = new stormClient_1.StormClient(mainConversationId);
|
592
|
+
await stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
|
578
593
|
}
|
579
594
|
catch (e) {
|
580
595
|
res.status(500).send({ error: e.message });
|
@@ -585,7 +600,8 @@ router.post('/ui/get-vote', async (req, res) => {
|
|
585
600
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
586
601
|
const { topic, mainConversationId } = aiRequest;
|
587
602
|
try {
|
588
|
-
const
|
603
|
+
const stormClient = new stormClient_1.StormClient(mainConversationId);
|
604
|
+
const vote = await stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
|
589
605
|
res.send({ vote });
|
590
606
|
}
|
591
607
|
catch (e) {
|
@@ -609,7 +625,8 @@ async function handleAll(req, res) {
|
|
609
625
|
const eventParser = new event_parser_1.StormEventParser(stormOptions);
|
610
626
|
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
611
627
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
612
|
-
const
|
628
|
+
const stormClient = new stormClient_1.StormClient(systemId);
|
629
|
+
const metaStream = await stormClient.createMetadata(aiRequest, conversationId);
|
613
630
|
onRequestAborted(req, res, () => {
|
614
631
|
metaStream.abort();
|
615
632
|
});
|
@@ -4,6 +4,7 @@ import { ConversationItem, CreateSimpleBackendRequest, HTMLPage, ImplementAPICli
|
|
4
4
|
import { Page, StormEventPageUrl } from './events';
|
5
5
|
export declare const STORM_ID = "storm";
|
6
6
|
export declare const ConversationIdHeader = "Conversation-Id";
|
7
|
+
export declare const SystemIdHeader = "System-Id";
|
7
8
|
export interface UIShellsPrompt {
|
8
9
|
theme?: string;
|
9
10
|
pages: {
|
@@ -55,9 +56,10 @@ export interface BasePromptRequest {
|
|
55
56
|
prompt: string;
|
56
57
|
skipImprovement?: boolean;
|
57
58
|
}
|
58
|
-
declare class StormClient {
|
59
|
+
export declare class StormClient {
|
59
60
|
private readonly _baseUrl;
|
60
|
-
|
61
|
+
private readonly _systemId;
|
62
|
+
constructor(systemId?: string);
|
61
63
|
private createOptions;
|
62
64
|
private send;
|
63
65
|
createMetadata(prompt: BasePromptRequest, conversationId?: string): Promise<StormStream>;
|
@@ -88,5 +90,3 @@ declare class StormClient {
|
|
88
90
|
downloadSystem(handle: string, conversationId: string): Promise<Buffer>;
|
89
91
|
uploadSystem(handle: string, conversationId: string, buffer: Buffer): Promise<Response>;
|
90
92
|
}
|
91
|
-
export declare const stormClient: StormClient;
|
92
|
-
export {};
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.
|
6
|
+
exports.StormClient = exports.SystemIdHeader = exports.ConversationIdHeader = exports.STORM_ID = void 0;
|
7
7
|
/**
|
8
8
|
* Copyright 2023 Kapeta Inc.
|
9
9
|
* SPDX-License-Identifier: BUSL-1.1
|
@@ -17,10 +17,13 @@ const fetch_retry_1 = __importDefault(require("fetch-retry"));
|
|
17
17
|
const fetchWithRetries = (0, fetch_retry_1.default)(global.fetch, { retries: 5, retryDelay: 10 });
|
18
18
|
exports.STORM_ID = 'storm';
|
19
19
|
exports.ConversationIdHeader = 'Conversation-Id';
|
20
|
+
exports.SystemIdHeader = 'System-Id';
|
20
21
|
class StormClient {
|
21
22
|
_baseUrl;
|
22
|
-
|
23
|
+
_systemId;
|
24
|
+
constructor(systemId) {
|
23
25
|
this._baseUrl = (0, utils_1.getRemoteUrl)('ai-service', 'https://ai.kapeta.com');
|
26
|
+
this._systemId = systemId || "";
|
24
27
|
}
|
25
28
|
async createOptions(path, method, body) {
|
26
29
|
const url = `${this._baseUrl}${path}`;
|
@@ -35,6 +38,9 @@ class StormClient {
|
|
35
38
|
if (body.conversationId) {
|
36
39
|
headers[exports.ConversationIdHeader] = body.conversationId;
|
37
40
|
}
|
41
|
+
if (this._systemId) {
|
42
|
+
headers[exports.SystemIdHeader] = this._systemId;
|
43
|
+
}
|
38
44
|
return {
|
39
45
|
url,
|
40
46
|
method: method,
|
@@ -103,6 +109,7 @@ class StormClient {
|
|
103
109
|
createUIShells(prompt, conversationId) {
|
104
110
|
return this.send('/v2/ui/shells', {
|
105
111
|
prompt: JSON.stringify(prompt),
|
112
|
+
conversationId: conversationId,
|
106
113
|
});
|
107
114
|
}
|
108
115
|
createUILandingPages(prompt, conversationId) {
|
@@ -138,6 +145,9 @@ class StormClient {
|
|
138
145
|
const response = await fetch(u, {
|
139
146
|
method: 'POST',
|
140
147
|
body: JSON.stringify(prompt.pages),
|
148
|
+
headers: {
|
149
|
+
'systemId': prompt.systemId,
|
150
|
+
},
|
141
151
|
});
|
142
152
|
return (await response.json());
|
143
153
|
}
|
@@ -252,4 +262,4 @@ class StormClient {
|
|
252
262
|
return response;
|
253
263
|
}
|
254
264
|
}
|
255
|
-
exports.
|
265
|
+
exports.StormClient = StormClient;
|
@@ -35,6 +35,7 @@ export interface ConversationItem {
|
|
35
35
|
}
|
36
36
|
export interface StormContextRequest<T = string> {
|
37
37
|
conversationId?: string;
|
38
|
+
systemId?: string;
|
38
39
|
history?: ConversationItem[];
|
39
40
|
prompt: T;
|
40
41
|
}
|
@@ -78,6 +79,7 @@ export declare enum HTMLPageEncoding {
|
|
78
79
|
}
|
79
80
|
export interface ImplementAPIClients {
|
80
81
|
pages: HTMLPage[];
|
82
|
+
systemId: string;
|
81
83
|
}
|
82
84
|
export interface HTMLPage {
|
83
85
|
fileName: string;
|
@@ -13,8 +13,8 @@ export declare class StormService {
|
|
13
13
|
saveConversation(conversationId: string, events: StormEvent[]): Promise<void>;
|
14
14
|
appendConversation(conversationId: string, events: StormEvent[]): Promise<void>;
|
15
15
|
deleteConversation(conversationId: string): Promise<void>;
|
16
|
-
uploadConversation(handle: string,
|
17
|
-
installProjectById(handle: string,
|
16
|
+
uploadConversation(handle: string, systemId: string): Promise<void>;
|
17
|
+
installProjectById(handle: string, systemId: string): Promise<void>;
|
18
18
|
}
|
19
19
|
declare const _default: StormService;
|
20
20
|
export default _default;
|
@@ -119,8 +119,8 @@ class StormService {
|
|
119
119
|
await promises_1.default.rm(conversationDir, { recursive: true, force: true });
|
120
120
|
}
|
121
121
|
}
|
122
|
-
async uploadConversation(handle,
|
123
|
-
const tarballFile = this.getConversationTarball(
|
122
|
+
async uploadConversation(handle, systemId) {
|
123
|
+
const tarballFile = this.getConversationTarball(systemId);
|
124
124
|
const destDir = path_1.default.dirname(tarballFile);
|
125
125
|
const tarballName = path_1.default.basename(tarballFile);
|
126
126
|
await tar.create({
|
@@ -129,12 +129,14 @@ class StormService {
|
|
129
129
|
gzip: true,
|
130
130
|
filter: (entry) => !entry.includes(tarballName),
|
131
131
|
}, ['.']);
|
132
|
-
|
132
|
+
const stormClient = new stormClient_1.StormClient(systemId);
|
133
|
+
await stormClient.uploadSystem(handle, systemId, await promises_1.default.readFile(tarballFile));
|
133
134
|
}
|
134
|
-
async installProjectById(handle,
|
135
|
-
const tarballFile = this.getConversationTarball(
|
135
|
+
async installProjectById(handle, systemId) {
|
136
|
+
const tarballFile = this.getConversationTarball(systemId);
|
136
137
|
const destDir = path_1.default.dirname(tarballFile);
|
137
|
-
const
|
138
|
+
const stormClient = new stormClient_1.StormClient(systemId);
|
139
|
+
const buffer = await stormClient.downloadSystem(handle, systemId);
|
138
140
|
await promises_1.default.mkdir(destDir, { recursive: true });
|
139
141
|
await promises_1.default.writeFile(tarballFile, buffer);
|
140
142
|
await tar.extract({
|
package/package.json
CHANGED
@@ -4,13 +4,14 @@
|
|
4
4
|
*/
|
5
5
|
|
6
6
|
import uuid from 'node-uuid';
|
7
|
-
import {
|
7
|
+
import { StormClient, UIPagePrompt } from './stormClient';
|
8
8
|
import { ReferenceClassification, StormEvent, StormEventPage, StormImage, UIShell } from './events';
|
9
9
|
import { EventEmitter } from 'node:events';
|
10
10
|
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
|
});
|
@@ -265,8 +272,9 @@ export class PageQueue extends EventEmitter {
|
|
265
272
|
return;
|
266
273
|
}
|
267
274
|
|
275
|
+
const client = new StormClient(this.systemId);
|
268
276
|
this.images.set(prompt.url, prompt.description);
|
269
|
-
const result = await
|
277
|
+
const result = await client.createImage(
|
270
278
|
`Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim()
|
271
279
|
);
|
272
280
|
|
@@ -287,7 +295,8 @@ export class PageQueue extends EventEmitter {
|
|
287
295
|
}
|
288
296
|
|
289
297
|
public async generate(prompt: UIPagePrompt, conversationId: string) {
|
290
|
-
const
|
298
|
+
const client = new StormClient(this.systemId);
|
299
|
+
const screenStream = await client.createUIPage(prompt, conversationId);
|
291
300
|
let pageEvent: StormEventPage | null = null;
|
292
301
|
screenStream.on('data', (event: StormEvent) => {
|
293
302
|
if (event.type === 'PAGE') {
|
@@ -306,7 +315,8 @@ export class PageQueue extends EventEmitter {
|
|
306
315
|
}
|
307
316
|
|
308
317
|
private async resolveReferences(content: string): Promise<ReferenceClassification[]> {
|
309
|
-
const
|
318
|
+
const client = new StormClient(this.systemId);
|
319
|
+
const referenceStream = await client.classifyUIReferences(content);
|
310
320
|
|
311
321
|
const references: ReferenceClassification[] = [];
|
312
322
|
|
package/src/storm/codegen.ts
CHANGED
@@ -17,7 +17,7 @@ import {
|
|
17
17
|
|
18
18
|
import { BlockDefinition } from '@kapeta/schemas';
|
19
19
|
import { codeGeneratorManager } from '../codeGeneratorManager';
|
20
|
-
import { STORM_ID,
|
20
|
+
import { STORM_ID, StormClient } from './stormClient';
|
21
21
|
import {
|
22
22
|
StormEvent,
|
23
23
|
StormEventBlockStatusType,
|
@@ -371,6 +371,7 @@ export class StormCodegen {
|
|
371
371
|
});
|
372
372
|
const uiEvents = [];
|
373
373
|
|
374
|
+
const stormClient = new StormClient(this.uiSystemId);
|
374
375
|
// generate screens
|
375
376
|
if (uiTemplates.length) {
|
376
377
|
const screenStream = await stormClient.listScreens({
|
@@ -663,6 +664,7 @@ export class StormCodegen {
|
|
663
664
|
}
|
664
665
|
|
665
666
|
private async classifyErrors(errors: string, basePath: string): Promise<Map<string, ErrorClassification[]>> {
|
667
|
+
const stormClient = new StormClient(this.uiSystemId);
|
666
668
|
const errorStream = await stormClient.createErrorClassification(errors, []);
|
667
669
|
const fixes = new Map<string, ErrorClassification[]>();
|
668
670
|
|
@@ -711,7 +713,7 @@ export class StormCodegen {
|
|
711
713
|
error: error,
|
712
714
|
projectFiles: allFiles.map((f) => f.filename),
|
713
715
|
};
|
714
|
-
|
716
|
+
const stormClient = new StormClient(this.uiSystemId);
|
715
717
|
const detailsStream = await stormClient.createErrorDetails(JSON.stringify(request), []);
|
716
718
|
detailsStream.on('data', (evt) => {
|
717
719
|
if (evt.type === 'ERROR_DETAILS') {
|
@@ -799,6 +801,7 @@ export class StormCodegen {
|
|
799
801
|
): Promise<StormEventErrorDetailsFile> {
|
800
802
|
return new Promise<StormEventErrorDetailsFile>(async (resolve, reject) => {
|
801
803
|
try {
|
804
|
+
const stormClient = new StormClient(this.uiSystemId);
|
802
805
|
const fixStream = await stormClient.createCodeFix(fix, history, this.conversationId);
|
803
806
|
let resolved = false;
|
804
807
|
fixStream.on('data', (evt) => {
|
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
@@ -21,12 +21,12 @@ import {
|
|
21
21
|
|
22
22
|
import {
|
23
23
|
ConversationIdHeader,
|
24
|
-
stormClient,
|
25
24
|
UIPagePrompt,
|
26
25
|
UIPageEditRequest,
|
27
26
|
BasePromptRequest,
|
28
27
|
UIPageVoteRequest,
|
29
28
|
UIPageGetVoteRequest,
|
29
|
+
StormClient,
|
30
30
|
} from './stormClient';
|
31
31
|
import { StormEvent, StormEventPage, StormEventPhaseType, UserJourneyScreen } from './events';
|
32
32
|
|
@@ -162,8 +162,10 @@ router.post('/ui/create-system/:handle/:systemId', async (req: KapetaBodyRequest
|
|
162
162
|
sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENT_APIS));
|
163
163
|
|
164
164
|
const pagesFromDisk = readFilesAndContent(srcDir);
|
165
|
-
const
|
165
|
+
const client = new StormClient(systemId)
|
166
|
+
const pagesWithImplementation = await client.replaceMockWithAPICall({
|
166
167
|
pages: pagesFromDisk,
|
168
|
+
systemId: systemId,
|
167
169
|
});
|
168
170
|
|
169
171
|
await copyDirectory(srcDir, destDir, (fileName, content) => {
|
@@ -181,7 +183,7 @@ router.post('/ui/create-system/:handle/:systemId', async (req: KapetaBodyRequest
|
|
181
183
|
return page.content;
|
182
184
|
});
|
183
185
|
|
184
|
-
const prompt = await
|
186
|
+
const prompt = await client.generatePrompt(pageContents);
|
185
187
|
|
186
188
|
sendEvent(res, createPhaseEndEvent(StormEventPhaseType.COMPOSE_SYSTEM_PROMPT));
|
187
189
|
|
@@ -204,11 +206,12 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req: KapetaBody
|
|
204
206
|
//res.set(ConversationIdHeader, systemId);
|
205
207
|
|
206
208
|
//sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENT_APIS));
|
207
|
-
|
209
|
+
const client = new StormClient(systemId);
|
208
210
|
try {
|
209
211
|
const pagesFromDisk = readFilesAndContent(srcDir);
|
210
|
-
const pagesWithImplementation = await
|
212
|
+
const pagesWithImplementation = await client.replaceMockWithAPICall({
|
211
213
|
pages: pagesFromDisk,
|
214
|
+
systemId: systemId,
|
212
215
|
});
|
213
216
|
|
214
217
|
//sendEvent(res, createPhaseEndEvent(StormEventPhaseType.IMPLEMENT_APIS));
|
@@ -227,7 +230,7 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req: KapetaBody
|
|
227
230
|
return page;
|
228
231
|
});
|
229
232
|
|
230
|
-
const systemUrl = await
|
233
|
+
const systemUrl = await client.createSimpleBackend(handle, systemId, { pages: allFiles });
|
231
234
|
//sendEvent(res, {type: 'SYSTEM_READY', created: Math.floor(Date.now() / 1000), reason: 'System Ready', payload: { systemUrl: systemUrl }});
|
232
235
|
|
233
236
|
//sendEvent(res, createPhaseEndEvent(StormEventPhaseType.COMPOSE_SYSTEM));
|
@@ -328,8 +331,8 @@ router.post('/:handle/ui/iterative', async (req: KapetaBodyRequest, res: Respons
|
|
328
331
|
const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
|
329
332
|
|
330
333
|
const aiRequest: BasePromptRequest = JSON.parse(req.stringBody ?? '{}');
|
331
|
-
|
332
|
-
const landingPagesStream = await
|
334
|
+
const client = new StormClient(conversationId); //todo is this correct we are using the landing page getConversationId down below as well
|
335
|
+
const landingPagesStream = await client.createUILandingPages(aiRequest, conversationId);
|
333
336
|
|
334
337
|
onRequestAborted(req, res, () => {
|
335
338
|
landingPagesStream.abort();
|
@@ -437,7 +440,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
437
440
|
(req.headers[ConversationIdHeader.toLowerCase()] as string | undefined) || randomUUID();
|
438
441
|
|
439
442
|
const aiRequest: BasePromptRequest = JSON.parse(req.stringBody ?? '{}');
|
440
|
-
|
443
|
+
const stormClient = new StormClient(outerConversationId);
|
441
444
|
// Get user journeys
|
442
445
|
const userJourneysStream = await stormClient.createUIUserJourneys(aiRequest, outerConversationId);
|
443
446
|
|
@@ -455,11 +458,11 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
455
458
|
|
456
459
|
userJourneysStream.on('data', (data: StormEvent) => {
|
457
460
|
try {
|
458
|
-
sendEvent(res, data);
|
459
461
|
if (data.type === 'PROMPT_IMPROVE') {
|
460
462
|
systemPrompt = data.payload.prompt;
|
461
463
|
}
|
462
464
|
if (data.type !== 'USER_JOURNEY') {
|
465
|
+
sendEvent(res, data);
|
463
466
|
return;
|
464
467
|
}
|
465
468
|
|
@@ -469,9 +472,12 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
469
472
|
|
470
473
|
data.payload.screens.forEach((screen) => {
|
471
474
|
if (!uniqueUserJourneyScreens[screen.name]) {
|
475
|
+
screen.conversationId = randomUUID();
|
472
476
|
uniqueUserJourneyScreens[screen.name] = screen;
|
473
477
|
}
|
474
478
|
});
|
479
|
+
|
480
|
+
sendEvent(res, data);
|
475
481
|
} catch (e) {
|
476
482
|
console.error('Failed to process event', e);
|
477
483
|
}
|
@@ -525,6 +531,10 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
525
531
|
|
526
532
|
await waitForStormStream(userJourneysStream);
|
527
533
|
|
534
|
+
if (req.socket.closed) {
|
535
|
+
return;
|
536
|
+
}
|
537
|
+
|
528
538
|
// Get the UI shells
|
529
539
|
const shellsStream = await stormClient.createUIShells(
|
530
540
|
{
|
@@ -571,6 +581,10 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
571
581
|
|
572
582
|
await waitForStormStream(shellsStream);
|
573
583
|
|
584
|
+
if (req.socket.closed) {
|
585
|
+
return;
|
586
|
+
}
|
587
|
+
|
574
588
|
UI_SERVERS[outerConversationId] = new UIServer(outerConversationId);
|
575
589
|
await UI_SERVERS[outerConversationId].start();
|
576
590
|
|
@@ -588,7 +602,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
588
602
|
queue.cancel();
|
589
603
|
});
|
590
604
|
|
591
|
-
queue.on('page', (
|
605
|
+
queue.on('page', (pageEvent: StormEventPage) => sendPageEvent(outerConversationId, pageEvent, res));
|
592
606
|
|
593
607
|
queue.on('event', (event: StormEvent) => {
|
594
608
|
if (event.type === 'FILE_CHUNK') {
|
@@ -604,17 +618,20 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
604
618
|
|
605
619
|
for (const screen of Object.values(uniqueUserJourneyScreens)) {
|
606
620
|
queue
|
607
|
-
.addPrompt(
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
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
|
+
)
|
618
635
|
.catch((e) => {
|
619
636
|
console.error('Failed to generate page for screen %s', screen.name, e);
|
620
637
|
sendError(e as any, res);
|
@@ -719,6 +736,7 @@ router.post('/ui/vote', async (req: KapetaBodyRequest, res: Response) => {
|
|
719
736
|
const aiRequest: UIPageVoteRequest = JSON.parse(req.stringBody ?? '{}');
|
720
737
|
const { topic, vote, mainConversationId } = aiRequest;
|
721
738
|
try {
|
739
|
+
const stormClient = new StormClient(mainConversationId);
|
722
740
|
await stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
|
723
741
|
} catch (e: any) {
|
724
742
|
res.status(500).send({ error: e.message });
|
@@ -730,6 +748,7 @@ router.post('/ui/get-vote', async (req: KapetaBodyRequest, res: Response) => {
|
|
730
748
|
const aiRequest: UIPageGetVoteRequest = JSON.parse(req.stringBody ?? '{}');
|
731
749
|
const { topic, mainConversationId } = aiRequest;
|
732
750
|
try {
|
751
|
+
const stormClient = new StormClient(mainConversationId);
|
733
752
|
const vote = await stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
|
734
753
|
res.send({ vote });
|
735
754
|
} catch (e: any) {
|
@@ -761,6 +780,7 @@ async function handleAll(req: KapetaBodyRequest, res: Response) {
|
|
761
780
|
const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
|
762
781
|
|
763
782
|
const aiRequest: BasePromptRequest = JSON.parse(req.stringBody ?? '{}');
|
783
|
+
const stormClient = new StormClient(systemId);
|
764
784
|
const metaStream = await stormClient.createMetadata(aiRequest, conversationId);
|
765
785
|
|
766
786
|
onRequestAborted(req, res, () => {
|