@kapeta/local-cluster-service 0.73.0 → 0.74.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.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/src/storm/PageGenerator.js +6 -3
- package/dist/cjs/src/storm/codegen.js +11 -7
- package/dist/cjs/src/storm/events.d.ts +9 -1
- package/dist/cjs/src/storm/routes.js +20 -11
- 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 +3 -2
- package/dist/cjs/src/stormService.js +44 -21
- package/dist/esm/src/storm/PageGenerator.js +6 -3
- package/dist/esm/src/storm/codegen.js +11 -7
- package/dist/esm/src/storm/events.d.ts +9 -1
- package/dist/esm/src/storm/routes.js +20 -11
- 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 +3 -2
- package/dist/esm/src/stormService.js +44 -21
- package/package.json +1 -1
- package/src/storm/PageGenerator.ts +7 -4
- package/src/storm/codegen.ts +5 -2
- package/src/storm/events.ts +12 -2
- package/src/storm/routes.ts +16 -9
- package/src/storm/stormClient.ts +12 -7
- package/src/storm/stream.ts +2 -0
- package/src/stormService.ts +63 -39
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.74.1](https://github.com/kapetacom/local-cluster-service/compare/v0.74.0...v0.74.1) (2024-09-27)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* adding system id to all calls ([b3e978f](https://github.com/kapetacom/local-cluster-service/commit/b3e978ffb74fe0ab1a46006412e52c63fe49eff8))
|
7
|
+
|
8
|
+
# [0.74.0](https://github.com/kapetacom/local-cluster-service/compare/v0.73.0...v0.74.0) (2024-09-26)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* add URL extraction to conversations endpoint ([#259](https://github.com/kapetacom/local-cluster-service/issues/259)) ([b4a38cd](https://github.com/kapetacom/local-cluster-service/commit/b4a38cdd97d94e3c69091f87bd10487c000ba631))
|
14
|
+
|
1
15
|
# [0.73.0](https://github.com/kapetacom/local-cluster-service/compare/v0.72.0...v0.73.0) (2024-09-25)
|
2
16
|
|
3
17
|
|
@@ -240,8 +240,9 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
240
240
|
console.warn('Skipping image reference of type %s for url %s', mimeType, prompt.url);
|
241
241
|
return;
|
242
242
|
}
|
243
|
+
const client = new stormClient_1.StormClient(this.systemId);
|
243
244
|
this.images.set(prompt.url, prompt.description);
|
244
|
-
const result = await
|
245
|
+
const result = await client.createImage(`Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim());
|
245
246
|
let imageEvent = null;
|
246
247
|
result.on('data', (event) => {
|
247
248
|
if (event.type === 'IMAGE') {
|
@@ -256,7 +257,8 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
256
257
|
this.emit('image', imageEvent, prompt);
|
257
258
|
}
|
258
259
|
async generate(prompt, conversationId) {
|
259
|
-
const
|
260
|
+
const client = new stormClient_1.StormClient(this.systemId);
|
261
|
+
const screenStream = await client.createUIPage(prompt, conversationId);
|
260
262
|
let pageEvent = null;
|
261
263
|
screenStream.on('data', (event) => {
|
262
264
|
if (event.type === 'PAGE') {
|
@@ -273,7 +275,8 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
273
275
|
await this.processPageEventWithReferences(pageEvent);
|
274
276
|
}
|
275
277
|
async resolveReferences(content) {
|
276
|
-
const
|
278
|
+
const client = new stormClient_1.StormClient(this.systemId);
|
279
|
+
const referenceStream = await client.classifyUIReferences(content);
|
277
280
|
const references = [];
|
278
281
|
referenceStream.on('data', (referenceData) => {
|
279
282
|
if (referenceData.type !== 'REF_CLASSIFICATION') {
|
@@ -311,9 +311,10 @@ class StormCodegen {
|
|
311
311
|
permissions: '0644',
|
312
312
|
});
|
313
313
|
const uiEvents = [];
|
314
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
314
315
|
// generate screens
|
315
316
|
if (uiTemplates.length) {
|
316
|
-
const screenStream = await
|
317
|
+
const screenStream = await stormClient.listScreens({
|
317
318
|
events: filteredEvents,
|
318
319
|
templates: uiTemplates,
|
319
320
|
context: relevantFiles,
|
@@ -346,7 +347,7 @@ class StormCodegen {
|
|
346
347
|
context: relevantFiles.concat([getScreenEventsFile()]),
|
347
348
|
prompt: this.userPrompt,
|
348
349
|
};
|
349
|
-
const uiStream = await
|
350
|
+
const uiStream = await stormClient.createUIImplementation(payload);
|
350
351
|
uiStream.on('data', (evt) => {
|
351
352
|
const uiFile = this.handleUiOutput(blockUri, block.aiName, evt);
|
352
353
|
if (uiFile != undefined) {
|
@@ -374,13 +375,13 @@ class StormCodegen {
|
|
374
375
|
});
|
375
376
|
allFiles.push(...screenFilesConverted);
|
376
377
|
let webRouters = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.WEB_ROUTER);
|
377
|
-
webRouters = await this.processTemplates(blockUri, block.aiName,
|
378
|
+
webRouters = await this.processTemplates(blockUri, block.aiName, stormClient.generateCode.bind(stormClient), webRouters, screenFilesConverted.concat([getScreenEventsFile()]));
|
378
379
|
// Gather the context files for implementation. These will be all be passed to the AI
|
379
380
|
const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN, codegen_1.AIFileTypes.WEB_ROUTER].includes(file.type));
|
380
381
|
// Send the service and UI templates to the AI. These will be sent one-by-one in addition to the context files
|
381
382
|
let serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
|
382
383
|
if (serviceFiles.length > 0) {
|
383
|
-
serviceFiles = await this.processTemplates(blockUri, block.aiName,
|
384
|
+
serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient.createServiceImplementation.bind(stormClient), serviceFiles, contextFiles);
|
384
385
|
}
|
385
386
|
if (this.isAborted()) {
|
386
387
|
return;
|
@@ -510,7 +511,8 @@ class StormCodegen {
|
|
510
511
|
}
|
511
512
|
}
|
512
513
|
async classifyErrors(errors, basePath) {
|
513
|
-
const
|
514
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
515
|
+
const errorStream = await stormClient.createErrorClassification(errors, []);
|
514
516
|
const fixes = new Map();
|
515
517
|
this.out.on('aborted', () => {
|
516
518
|
errorStream.abort();
|
@@ -547,7 +549,8 @@ class StormCodegen {
|
|
547
549
|
error: error,
|
548
550
|
projectFiles: allFiles.map((f) => f.filename),
|
549
551
|
};
|
550
|
-
const
|
552
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
553
|
+
const detailsStream = await stormClient.createErrorDetails(JSON.stringify(request), []);
|
551
554
|
detailsStream.on('data', (evt) => {
|
552
555
|
if (evt.type === 'ERROR_DETAILS') {
|
553
556
|
resolve(evt.payload.files);
|
@@ -611,7 +614,8 @@ class StormCodegen {
|
|
611
614
|
async codeFix(blockUri, blockName, fix, history) {
|
612
615
|
return new Promise(async (resolve, reject) => {
|
613
616
|
try {
|
614
|
-
const
|
617
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
618
|
+
const fixStream = await stormClient.createCodeFix(fix, history, this.conversationId);
|
615
619
|
let resolved = false;
|
616
620
|
fixStream.on('data', (evt) => {
|
617
621
|
if (this.handleFileEvents(blockUri, blockName, evt)) {
|
@@ -404,5 +404,13 @@ export interface StormEventSystemReady {
|
|
404
404
|
systemUrl: string;
|
405
405
|
};
|
406
406
|
}
|
407
|
-
export
|
407
|
+
export interface StormEventModelResponse {
|
408
|
+
type: 'MODEL_RESPONSE';
|
409
|
+
reason: string;
|
410
|
+
created: number;
|
411
|
+
payload: {
|
412
|
+
text: string;
|
413
|
+
};
|
414
|
+
}
|
415
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileDone | StormEventFileFailed | StormEventFileChunk | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventErrorDetails | StormEventBlockReady | StormEventPhases | StormEventBlockStatus | StormEventCreateDSLRetry | StormEventUserJourney | StormEventUIShell | StormEventPage | StormEventPageUrl | StormEventPromptImprove | StormEventLandingPage | StormEventReferenceClassification | StormEventApiBase | StormEventUIStarted | StormImage | StormEventSystemReady | StormEventModelResponse;
|
408
416
|
export {};
|
@@ -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
|
});
|
@@ -378,7 +384,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
378
384
|
});
|
379
385
|
let theme = '';
|
380
386
|
try {
|
381
|
-
const themeStream = await
|
387
|
+
const themeStream = await stormClient.createTheme(aiRequest, outerConversationId);
|
382
388
|
onRequestAborted(req, res, () => {
|
383
389
|
themeStream.abort();
|
384
390
|
});
|
@@ -417,7 +423,7 @@ router.post('/:handle/ui', async (req, res) => {
|
|
417
423
|
}
|
418
424
|
await waitForStormStream(userJourneysStream);
|
419
425
|
// Get the UI shells
|
420
|
-
const shellsStream = await
|
426
|
+
const shellsStream = await stormClient.createUIShells({
|
421
427
|
theme: theme || undefined,
|
422
428
|
pages: Object.values(uniqueUserJourneyScreens).map((screen) => ({
|
423
429
|
name: screen.name,
|
@@ -574,7 +580,8 @@ router.post('/ui/vote', async (req, res) => {
|
|
574
580
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
575
581
|
const { topic, vote, mainConversationId } = aiRequest;
|
576
582
|
try {
|
577
|
-
|
583
|
+
const stormClient = new stormClient_1.StormClient(mainConversationId);
|
584
|
+
await stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
|
578
585
|
}
|
579
586
|
catch (e) {
|
580
587
|
res.status(500).send({ error: e.message });
|
@@ -585,7 +592,8 @@ router.post('/ui/get-vote', async (req, res) => {
|
|
585
592
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
586
593
|
const { topic, mainConversationId } = aiRequest;
|
587
594
|
try {
|
588
|
-
const
|
595
|
+
const stormClient = new stormClient_1.StormClient(mainConversationId);
|
596
|
+
const vote = await stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
|
589
597
|
res.send({ vote });
|
590
598
|
}
|
591
599
|
catch (e) {
|
@@ -609,7 +617,8 @@ async function handleAll(req, res) {
|
|
609
617
|
const eventParser = new event_parser_1.StormEventParser(stormOptions);
|
610
618
|
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
611
619
|
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
612
|
-
const
|
620
|
+
const stormClient = new stormClient_1.StormClient(systemId);
|
621
|
+
const metaStream = await stormClient.createMetadata(aiRequest, conversationId);
|
613
622
|
onRequestAborted(req, res, () => {
|
614
623
|
metaStream.abort();
|
615
624
|
});
|
@@ -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;
|
@@ -7,13 +7,14 @@ export declare class StormService {
|
|
7
7
|
id: string;
|
8
8
|
description: string;
|
9
9
|
title: string;
|
10
|
+
url?: string | undefined;
|
10
11
|
}[]>;
|
11
12
|
getConversation(conversationId: string): Promise<string>;
|
12
13
|
saveConversation(conversationId: string, events: StormEvent[]): Promise<void>;
|
13
14
|
appendConversation(conversationId: string, events: StormEvent[]): Promise<void>;
|
14
15
|
deleteConversation(conversationId: string): Promise<void>;
|
15
|
-
uploadConversation(handle: string,
|
16
|
-
installProjectById(handle: string,
|
16
|
+
uploadConversation(handle: string, systemId: string): Promise<void>;
|
17
|
+
installProjectById(handle: string, systemId: string): Promise<void>;
|
17
18
|
}
|
18
19
|
declare const _default: StormService;
|
19
20
|
export default _default;
|
@@ -54,22 +54,43 @@ class StormService {
|
|
54
54
|
// Returns list of UUIDs - probably want to make it more useful than that
|
55
55
|
const conversations = [];
|
56
56
|
for (const file of eventFiles) {
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
try {
|
58
|
+
const nldContents = await promises_1.default.readFile(file, 'utf8');
|
59
|
+
const events = nldContents.split('\n').map((e) => JSON.parse(e));
|
60
|
+
// find the shell and get the title tag
|
61
|
+
const shellEvent = events.find((e) => e.type === 'AI' && e.event.type === 'UI_SHELL')?.event;
|
62
|
+
const html = shellEvent?.payload.content;
|
63
|
+
const title = html?.match(/<title>(.*?)<\/title>/)?.[1];
|
64
|
+
const id = events.find((e) => e.type === 'AI')?.systemId;
|
65
|
+
const initialPrompt = events.find((e) => e.type === 'AI' && e.event.type === 'PROMPT_IMPROVE')?.event?.payload?.prompt || events[0].text;
|
66
|
+
if (!id) {
|
67
|
+
continue;
|
68
|
+
}
|
69
|
+
let url = undefined;
|
70
|
+
// Find the last model response event that has a URL in the payload (in case it changed over time)
|
71
|
+
for (const evt of [...events].reverse()) {
|
72
|
+
const event = evt.event;
|
73
|
+
if (evt.type === 'AI' && event.type === 'MODEL_RESPONSE') {
|
74
|
+
// Look for a URL in the model response markdown
|
75
|
+
const regex = /\[(.*?)\]\((.*?)\)/g;
|
76
|
+
const match = regex.exec(event.payload.text);
|
77
|
+
const [, _linkText, linkUrl] = match || [];
|
78
|
+
if (linkUrl?.startsWith('http')) {
|
79
|
+
url = linkUrl;
|
80
|
+
break;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
conversations.push({
|
85
|
+
id,
|
86
|
+
description: initialPrompt,
|
87
|
+
title: title || 'New system',
|
88
|
+
url,
|
89
|
+
});
|
90
|
+
}
|
91
|
+
catch (e) {
|
92
|
+
console.error('Failed to load conversation at %s', file, e);
|
67
93
|
}
|
68
|
-
conversations.push({
|
69
|
-
id,
|
70
|
-
description: initialPrompt,
|
71
|
-
title: title || 'New system',
|
72
|
-
});
|
73
94
|
}
|
74
95
|
return conversations;
|
75
96
|
}
|
@@ -98,8 +119,8 @@ class StormService {
|
|
98
119
|
await promises_1.default.rm(conversationDir, { recursive: true, force: true });
|
99
120
|
}
|
100
121
|
}
|
101
|
-
async uploadConversation(handle,
|
102
|
-
const tarballFile = this.getConversationTarball(
|
122
|
+
async uploadConversation(handle, systemId) {
|
123
|
+
const tarballFile = this.getConversationTarball(systemId);
|
103
124
|
const destDir = path_1.default.dirname(tarballFile);
|
104
125
|
const tarballName = path_1.default.basename(tarballFile);
|
105
126
|
await tar.create({
|
@@ -108,12 +129,14 @@ class StormService {
|
|
108
129
|
gzip: true,
|
109
130
|
filter: (entry) => !entry.includes(tarballName),
|
110
131
|
}, ['.']);
|
111
|
-
|
132
|
+
const stormClient = new stormClient_1.StormClient(systemId);
|
133
|
+
await stormClient.uploadSystem(handle, systemId, await promises_1.default.readFile(tarballFile));
|
112
134
|
}
|
113
|
-
async installProjectById(handle,
|
114
|
-
const tarballFile = this.getConversationTarball(
|
135
|
+
async installProjectById(handle, systemId) {
|
136
|
+
const tarballFile = this.getConversationTarball(systemId);
|
115
137
|
const destDir = path_1.default.dirname(tarballFile);
|
116
|
-
const
|
138
|
+
const stormClient = new stormClient_1.StormClient(systemId);
|
139
|
+
const buffer = await stormClient.downloadSystem(handle, systemId);
|
117
140
|
await promises_1.default.mkdir(destDir, { recursive: true });
|
118
141
|
await promises_1.default.writeFile(tarballFile, buffer);
|
119
142
|
await tar.extract({
|
@@ -240,8 +240,9 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
240
240
|
console.warn('Skipping image reference of type %s for url %s', mimeType, prompt.url);
|
241
241
|
return;
|
242
242
|
}
|
243
|
+
const client = new stormClient_1.StormClient(this.systemId);
|
243
244
|
this.images.set(prompt.url, prompt.description);
|
244
|
-
const result = await
|
245
|
+
const result = await client.createImage(`Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim());
|
245
246
|
let imageEvent = null;
|
246
247
|
result.on('data', (event) => {
|
247
248
|
if (event.type === 'IMAGE') {
|
@@ -256,7 +257,8 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
256
257
|
this.emit('image', imageEvent, prompt);
|
257
258
|
}
|
258
259
|
async generate(prompt, conversationId) {
|
259
|
-
const
|
260
|
+
const client = new stormClient_1.StormClient(this.systemId);
|
261
|
+
const screenStream = await client.createUIPage(prompt, conversationId);
|
260
262
|
let pageEvent = null;
|
261
263
|
screenStream.on('data', (event) => {
|
262
264
|
if (event.type === 'PAGE') {
|
@@ -273,7 +275,8 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
273
275
|
await this.processPageEventWithReferences(pageEvent);
|
274
276
|
}
|
275
277
|
async resolveReferences(content) {
|
276
|
-
const
|
278
|
+
const client = new stormClient_1.StormClient(this.systemId);
|
279
|
+
const referenceStream = await client.classifyUIReferences(content);
|
277
280
|
const references = [];
|
278
281
|
referenceStream.on('data', (referenceData) => {
|
279
282
|
if (referenceData.type !== 'REF_CLASSIFICATION') {
|
@@ -311,9 +311,10 @@ class StormCodegen {
|
|
311
311
|
permissions: '0644',
|
312
312
|
});
|
313
313
|
const uiEvents = [];
|
314
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
314
315
|
// generate screens
|
315
316
|
if (uiTemplates.length) {
|
316
|
-
const screenStream = await
|
317
|
+
const screenStream = await stormClient.listScreens({
|
317
318
|
events: filteredEvents,
|
318
319
|
templates: uiTemplates,
|
319
320
|
context: relevantFiles,
|
@@ -346,7 +347,7 @@ class StormCodegen {
|
|
346
347
|
context: relevantFiles.concat([getScreenEventsFile()]),
|
347
348
|
prompt: this.userPrompt,
|
348
349
|
};
|
349
|
-
const uiStream = await
|
350
|
+
const uiStream = await stormClient.createUIImplementation(payload);
|
350
351
|
uiStream.on('data', (evt) => {
|
351
352
|
const uiFile = this.handleUiOutput(blockUri, block.aiName, evt);
|
352
353
|
if (uiFile != undefined) {
|
@@ -374,13 +375,13 @@ class StormCodegen {
|
|
374
375
|
});
|
375
376
|
allFiles.push(...screenFilesConverted);
|
376
377
|
let webRouters = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.WEB_ROUTER);
|
377
|
-
webRouters = await this.processTemplates(blockUri, block.aiName,
|
378
|
+
webRouters = await this.processTemplates(blockUri, block.aiName, stormClient.generateCode.bind(stormClient), webRouters, screenFilesConverted.concat([getScreenEventsFile()]));
|
378
379
|
// Gather the context files for implementation. These will be all be passed to the AI
|
379
380
|
const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN, codegen_1.AIFileTypes.WEB_ROUTER].includes(file.type));
|
380
381
|
// Send the service and UI templates to the AI. These will be sent one-by-one in addition to the context files
|
381
382
|
let serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
|
382
383
|
if (serviceFiles.length > 0) {
|
383
|
-
serviceFiles = await this.processTemplates(blockUri, block.aiName,
|
384
|
+
serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient.createServiceImplementation.bind(stormClient), serviceFiles, contextFiles);
|
384
385
|
}
|
385
386
|
if (this.isAborted()) {
|
386
387
|
return;
|
@@ -510,7 +511,8 @@ class StormCodegen {
|
|
510
511
|
}
|
511
512
|
}
|
512
513
|
async classifyErrors(errors, basePath) {
|
513
|
-
const
|
514
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
515
|
+
const errorStream = await stormClient.createErrorClassification(errors, []);
|
514
516
|
const fixes = new Map();
|
515
517
|
this.out.on('aborted', () => {
|
516
518
|
errorStream.abort();
|
@@ -547,7 +549,8 @@ class StormCodegen {
|
|
547
549
|
error: error,
|
548
550
|
projectFiles: allFiles.map((f) => f.filename),
|
549
551
|
};
|
550
|
-
const
|
552
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
553
|
+
const detailsStream = await stormClient.createErrorDetails(JSON.stringify(request), []);
|
551
554
|
detailsStream.on('data', (evt) => {
|
552
555
|
if (evt.type === 'ERROR_DETAILS') {
|
553
556
|
resolve(evt.payload.files);
|
@@ -611,7 +614,8 @@ class StormCodegen {
|
|
611
614
|
async codeFix(blockUri, blockName, fix, history) {
|
612
615
|
return new Promise(async (resolve, reject) => {
|
613
616
|
try {
|
614
|
-
const
|
617
|
+
const stormClient = new stormClient_1.StormClient(this.uiSystemId);
|
618
|
+
const fixStream = await stormClient.createCodeFix(fix, history, this.conversationId);
|
615
619
|
let resolved = false;
|
616
620
|
fixStream.on('data', (evt) => {
|
617
621
|
if (this.handleFileEvents(blockUri, blockName, evt)) {
|
@@ -404,5 +404,13 @@ export interface StormEventSystemReady {
|
|
404
404
|
systemUrl: string;
|
405
405
|
};
|
406
406
|
}
|
407
|
-
export
|
407
|
+
export interface StormEventModelResponse {
|
408
|
+
type: 'MODEL_RESPONSE';
|
409
|
+
reason: string;
|
410
|
+
created: number;
|
411
|
+
payload: {
|
412
|
+
text: string;
|
413
|
+
};
|
414
|
+
}
|
415
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileDone | StormEventFileFailed | StormEventFileChunk | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventErrorDetails | StormEventBlockReady | StormEventPhases | StormEventBlockStatus | StormEventCreateDSLRetry | StormEventUserJourney | StormEventUIShell | StormEventPage | StormEventPageUrl | StormEventPromptImprove | StormEventLandingPage | StormEventReferenceClassification | StormEventApiBase | StormEventUIStarted | StormImage | StormEventSystemReady | StormEventModelResponse;
|
408
416
|
export {};
|