@kapeta/local-cluster-service 0.56.1 → 0.57.0
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/codegen.js +3 -3
- package/dist/cjs/src/storm/event-parser.js +1 -1
- package/dist/cjs/src/storm/events.d.ts +32 -2
- package/dist/cjs/src/storm/routes.js +66 -0
- package/dist/cjs/src/storm/stormClient.d.ts +11 -0
- package/dist/cjs/src/storm/stormClient.js +19 -1
- package/dist/esm/src/storm/codegen.js +3 -3
- package/dist/esm/src/storm/event-parser.js +1 -1
- package/dist/esm/src/storm/events.d.ts +32 -2
- package/dist/esm/src/storm/routes.js +66 -0
- package/dist/esm/src/storm/stormClient.d.ts +11 -0
- package/dist/esm/src/storm/stormClient.js +19 -1
- package/package.json +1 -1
- package/src/storm/codegen.ts +4 -3
- package/src/storm/event-parser.ts +1 -1
- package/src/storm/events.ts +41 -2
- package/src/storm/routes.ts +78 -0
- package/src/storm/stormClient.ts +31 -1
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# [0.57.0](https://github.com/kapetacom/local-cluster-service/compare/v0.56.2...v0.57.0) (2024-07-22)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* Add support for pages and journeys ([743f6fd](https://github.com/kapetacom/local-cluster-service/commit/743f6fdb1851eb2c7a76e79bc68a74f529af9207))
|
7
|
+
|
8
|
+
## [0.56.2](https://github.com/kapetacom/local-cluster-service/compare/v0.56.1...v0.56.2) (2024-07-19)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* missing pural on MODEL RETRY ([ca33bdd](https://github.com/kapetacom/local-cluster-service/commit/ca33bdd5e47b0c601d19ddf4149de5e925aa0040))
|
14
|
+
|
1
15
|
## [0.56.1](https://github.com/kapetacom/local-cluster-service/compare/v0.56.0...v0.56.1) (2024-07-17)
|
2
16
|
|
3
17
|
|
@@ -263,11 +263,14 @@ class StormCodegen {
|
|
263
263
|
}
|
264
264
|
const kapetaYaml = yaml_1.default.stringify(block.content);
|
265
265
|
const blockDefinition = block.content;
|
266
|
+
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
266
267
|
// Generate the code for the block using the standard codegen templates
|
267
268
|
const generatedResult = await this.generateBlock(blockDefinition);
|
268
269
|
if (!generatedResult) {
|
269
270
|
return;
|
270
271
|
}
|
272
|
+
const kapetaYmlPath = (0, path_2.join)(basePath, 'kapeta.yml');
|
273
|
+
await (0, promises_1.writeFile)(kapetaYmlPath, kapetaYaml);
|
271
274
|
const allFiles = this.toStormFiles(generatedResult);
|
272
275
|
// Send all the non-ai files to the stream
|
273
276
|
await this.emitStaticFiles((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, allFiles);
|
@@ -357,7 +360,6 @@ class StormCodegen {
|
|
357
360
|
if (this.isAborted()) {
|
358
361
|
return;
|
359
362
|
}
|
360
|
-
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
361
363
|
const screenFilesConverted = screenFiles.map((screenFile) => {
|
362
364
|
return {
|
363
365
|
filename: screenFile.payload.filename,
|
@@ -397,8 +399,6 @@ class StormCodegen {
|
|
397
399
|
const filePath = (0, path_2.join)(basePath, webRouterFile.filename);
|
398
400
|
await (0, promises_1.writeFile)(filePath, webRouterFile.content);
|
399
401
|
}
|
400
|
-
const kapetaYmlPath = (0, path_2.join)(basePath, 'kapeta.yml');
|
401
|
-
await (0, promises_1.writeFile)(kapetaYmlPath, kapetaYaml);
|
402
402
|
const blockRef = block.uri;
|
403
403
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.QA);
|
404
404
|
/* TODO: temporarily disabled - enable again when codegen is more stable
|
@@ -94,7 +94,7 @@ export interface StormEventCreateDSLResource extends Omit<StormEventCreateDSL, '
|
|
94
94
|
};
|
95
95
|
}
|
96
96
|
export interface StormEventCreateDSLRetry {
|
97
|
-
type: 'API_RETRY' | '
|
97
|
+
type: 'API_RETRY' | 'MODELS_RETRY';
|
98
98
|
reason: string;
|
99
99
|
created: number;
|
100
100
|
payload: {
|
@@ -266,4 +266,34 @@ export interface StormEventPhases {
|
|
266
266
|
phaseType: StormEventPhaseType;
|
267
267
|
};
|
268
268
|
}
|
269
|
-
export
|
269
|
+
export interface Page {
|
270
|
+
name: string;
|
271
|
+
description: string;
|
272
|
+
content: string;
|
273
|
+
path: string;
|
274
|
+
method: string;
|
275
|
+
}
|
276
|
+
export interface StormEventPage {
|
277
|
+
type: 'PAGE';
|
278
|
+
reason: string;
|
279
|
+
created: number;
|
280
|
+
payload: Page;
|
281
|
+
}
|
282
|
+
export interface UserJourneyScreen {
|
283
|
+
name: string;
|
284
|
+
filename: string;
|
285
|
+
requirements: string;
|
286
|
+
path: string;
|
287
|
+
method: string;
|
288
|
+
nextScreens: string[];
|
289
|
+
}
|
290
|
+
export interface UserJourney {
|
291
|
+
screens: UserJourneyScreen[];
|
292
|
+
}
|
293
|
+
export interface StormEventUserJourney {
|
294
|
+
type: 'USER_JOURNEY';
|
295
|
+
reason: string;
|
296
|
+
created: number;
|
297
|
+
payload: UserJourney;
|
298
|
+
}
|
299
|
+
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 | StormEventPage;
|
@@ -21,6 +21,72 @@ const assetManager_1 = require("../assetManager");
|
|
21
21
|
const router = (0, express_promise_router_1.default)();
|
22
22
|
router.use('/', cors_1.corsHandler);
|
23
23
|
router.use('/', stringBody_1.stringBody);
|
24
|
+
router.post('/:handle/ui', async (req, res) => {
|
25
|
+
const handle = req.params.handle;
|
26
|
+
try {
|
27
|
+
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
28
|
+
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
29
|
+
const userJourneysStream = await stormClient_1.stormClient.createUIUserJourneys(aiRequest.prompt, conversationId);
|
30
|
+
onRequestAborted(req, res, () => {
|
31
|
+
userJourneysStream.abort();
|
32
|
+
});
|
33
|
+
res.set('Content-Type', 'application/x-ndjson');
|
34
|
+
res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
|
35
|
+
res.set(stormClient_1.ConversationIdHeader, userJourneysStream.getConversationId());
|
36
|
+
const promises = {};
|
37
|
+
userJourneysStream.on('data', async (data) => {
|
38
|
+
try {
|
39
|
+
console.log('Processing user journey event', data);
|
40
|
+
sendEvent(res, data);
|
41
|
+
if (data.type !== 'USER_JOURNEY') {
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
data.payload.screens.forEach((screen) => {
|
45
|
+
if (screen.name in promises) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
promises[screen.name] = new Promise(async (resolve, reject) => {
|
49
|
+
try {
|
50
|
+
const screenStream = await stormClient_1.stormClient.createUIPage({
|
51
|
+
prompt: screen.requirements,
|
52
|
+
method: screen.method,
|
53
|
+
path: screen.path,
|
54
|
+
description: screen.requirements,
|
55
|
+
name: screen.name,
|
56
|
+
filename: screen.filename,
|
57
|
+
}, conversationId);
|
58
|
+
screenStream.on('data', (screenData) => {
|
59
|
+
console.log('Processing screen event', screenData);
|
60
|
+
sendEvent(res, screenData);
|
61
|
+
});
|
62
|
+
screenStream.on('end', () => {
|
63
|
+
resolve();
|
64
|
+
});
|
65
|
+
}
|
66
|
+
catch (e) {
|
67
|
+
reject(e);
|
68
|
+
}
|
69
|
+
});
|
70
|
+
});
|
71
|
+
}
|
72
|
+
catch (e) {
|
73
|
+
console.error('Failed to process event', e);
|
74
|
+
}
|
75
|
+
});
|
76
|
+
await waitForStormStream(userJourneysStream);
|
77
|
+
if (userJourneysStream.isAborted()) {
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
await Promise.all(Object.values(promises));
|
81
|
+
sendDone(res);
|
82
|
+
}
|
83
|
+
catch (err) {
|
84
|
+
sendError(err, res);
|
85
|
+
if (!res.closed) {
|
86
|
+
res.end();
|
87
|
+
}
|
88
|
+
}
|
89
|
+
});
|
24
90
|
router.post('/:handle/all', async (req, res) => {
|
25
91
|
const handle = req.params.handle;
|
26
92
|
try {
|
@@ -1,12 +1,23 @@
|
|
1
1
|
import { ConversationItem, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
|
2
2
|
export declare const STORM_ID = "storm";
|
3
3
|
export declare const ConversationIdHeader = "Conversation-Id";
|
4
|
+
interface UIPagePrompt {
|
5
|
+
name: string;
|
6
|
+
filename: string;
|
7
|
+
prompt: string;
|
8
|
+
path: string;
|
9
|
+
method: string;
|
10
|
+
description: string;
|
11
|
+
}
|
4
12
|
declare class StormClient {
|
5
13
|
private readonly _baseUrl;
|
6
14
|
constructor();
|
7
15
|
private createOptions;
|
8
16
|
private send;
|
9
17
|
createMetadata(prompt: string, conversationId?: string): Promise<StormStream>;
|
18
|
+
createUIPages(prompt: string, conversationId?: string): Promise<StormStream>;
|
19
|
+
createUIUserJourneys(prompt: string, conversationId?: string): Promise<StormStream>;
|
20
|
+
createUIPage(prompt: UIPagePrompt, conversationId?: string): Promise<StormStream>;
|
10
21
|
listScreens(prompt: StormUIListPrompt, conversationId?: string): Promise<StormStream>;
|
11
22
|
createUIImplementation(prompt: StormUIImplementationPrompt, conversationId?: string): Promise<StormStream>;
|
12
23
|
createServiceImplementation(prompt: StormFileImplementationPrompt, conversationId?: string): Promise<StormStream>;
|
@@ -28,7 +28,7 @@ class StormClient {
|
|
28
28
|
const api = new nodejs_api_client_1.KapetaAPI();
|
29
29
|
if (api.hasToken()) {
|
30
30
|
const token = await api.getAccessToken();
|
31
|
-
headers['Authorization'] = `Bearer ${token}`;
|
31
|
+
//headers['Authorization'] = `Bearer ${token}`;
|
32
32
|
}
|
33
33
|
if (body.conversationId) {
|
34
34
|
headers[exports.ConversationIdHeader] = body.conversationId;
|
@@ -80,6 +80,24 @@ class StormClient {
|
|
80
80
|
conversationId,
|
81
81
|
});
|
82
82
|
}
|
83
|
+
createUIPages(prompt, conversationId) {
|
84
|
+
return this.send('/v2/ui/pages', {
|
85
|
+
prompt: prompt,
|
86
|
+
conversationId,
|
87
|
+
});
|
88
|
+
}
|
89
|
+
createUIUserJourneys(prompt, conversationId) {
|
90
|
+
return this.send('/v2/ui/user-journeys', {
|
91
|
+
prompt: prompt,
|
92
|
+
conversationId,
|
93
|
+
});
|
94
|
+
}
|
95
|
+
createUIPage(prompt, conversationId) {
|
96
|
+
return this.send('/v2/ui/page', {
|
97
|
+
prompt: prompt,
|
98
|
+
conversationId,
|
99
|
+
});
|
100
|
+
}
|
83
101
|
listScreens(prompt, conversationId) {
|
84
102
|
return this.send('/v2/ui/list', {
|
85
103
|
prompt,
|
@@ -263,11 +263,14 @@ class StormCodegen {
|
|
263
263
|
}
|
264
264
|
const kapetaYaml = yaml_1.default.stringify(block.content);
|
265
265
|
const blockDefinition = block.content;
|
266
|
+
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
266
267
|
// Generate the code for the block using the standard codegen templates
|
267
268
|
const generatedResult = await this.generateBlock(blockDefinition);
|
268
269
|
if (!generatedResult) {
|
269
270
|
return;
|
270
271
|
}
|
272
|
+
const kapetaYmlPath = (0, path_2.join)(basePath, 'kapeta.yml');
|
273
|
+
await (0, promises_1.writeFile)(kapetaYmlPath, kapetaYaml);
|
271
274
|
const allFiles = this.toStormFiles(generatedResult);
|
272
275
|
// Send all the non-ai files to the stream
|
273
276
|
await this.emitStaticFiles((0, nodejs_utils_1.parseKapetaUri)(block.uri), block.aiName, allFiles);
|
@@ -357,7 +360,6 @@ class StormCodegen {
|
|
357
360
|
if (this.isAborted()) {
|
358
361
|
return;
|
359
362
|
}
|
360
|
-
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
361
363
|
const screenFilesConverted = screenFiles.map((screenFile) => {
|
362
364
|
return {
|
363
365
|
filename: screenFile.payload.filename,
|
@@ -397,8 +399,6 @@ class StormCodegen {
|
|
397
399
|
const filePath = (0, path_2.join)(basePath, webRouterFile.filename);
|
398
400
|
await (0, promises_1.writeFile)(filePath, webRouterFile.content);
|
399
401
|
}
|
400
|
-
const kapetaYmlPath = (0, path_2.join)(basePath, 'kapeta.yml');
|
401
|
-
await (0, promises_1.writeFile)(kapetaYmlPath, kapetaYaml);
|
402
402
|
const blockRef = block.uri;
|
403
403
|
this.emitBlockStatus(blockUri, block.aiName, events_1.StormEventBlockStatusType.QA);
|
404
404
|
/* TODO: temporarily disabled - enable again when codegen is more stable
|
@@ -94,7 +94,7 @@ export interface StormEventCreateDSLResource extends Omit<StormEventCreateDSL, '
|
|
94
94
|
};
|
95
95
|
}
|
96
96
|
export interface StormEventCreateDSLRetry {
|
97
|
-
type: 'API_RETRY' | '
|
97
|
+
type: 'API_RETRY' | 'MODELS_RETRY';
|
98
98
|
reason: string;
|
99
99
|
created: number;
|
100
100
|
payload: {
|
@@ -266,4 +266,34 @@ export interface StormEventPhases {
|
|
266
266
|
phaseType: StormEventPhaseType;
|
267
267
|
};
|
268
268
|
}
|
269
|
-
export
|
269
|
+
export interface Page {
|
270
|
+
name: string;
|
271
|
+
description: string;
|
272
|
+
content: string;
|
273
|
+
path: string;
|
274
|
+
method: string;
|
275
|
+
}
|
276
|
+
export interface StormEventPage {
|
277
|
+
type: 'PAGE';
|
278
|
+
reason: string;
|
279
|
+
created: number;
|
280
|
+
payload: Page;
|
281
|
+
}
|
282
|
+
export interface UserJourneyScreen {
|
283
|
+
name: string;
|
284
|
+
filename: string;
|
285
|
+
requirements: string;
|
286
|
+
path: string;
|
287
|
+
method: string;
|
288
|
+
nextScreens: string[];
|
289
|
+
}
|
290
|
+
export interface UserJourney {
|
291
|
+
screens: UserJourneyScreen[];
|
292
|
+
}
|
293
|
+
export interface StormEventUserJourney {
|
294
|
+
type: 'USER_JOURNEY';
|
295
|
+
reason: string;
|
296
|
+
created: number;
|
297
|
+
payload: UserJourney;
|
298
|
+
}
|
299
|
+
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 | StormEventPage;
|
@@ -21,6 +21,72 @@ const assetManager_1 = require("../assetManager");
|
|
21
21
|
const router = (0, express_promise_router_1.default)();
|
22
22
|
router.use('/', cors_1.corsHandler);
|
23
23
|
router.use('/', stringBody_1.stringBody);
|
24
|
+
router.post('/:handle/ui', async (req, res) => {
|
25
|
+
const handle = req.params.handle;
|
26
|
+
try {
|
27
|
+
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
28
|
+
const aiRequest = JSON.parse(req.stringBody ?? '{}');
|
29
|
+
const userJourneysStream = await stormClient_1.stormClient.createUIUserJourneys(aiRequest.prompt, conversationId);
|
30
|
+
onRequestAborted(req, res, () => {
|
31
|
+
userJourneysStream.abort();
|
32
|
+
});
|
33
|
+
res.set('Content-Type', 'application/x-ndjson');
|
34
|
+
res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
|
35
|
+
res.set(stormClient_1.ConversationIdHeader, userJourneysStream.getConversationId());
|
36
|
+
const promises = {};
|
37
|
+
userJourneysStream.on('data', async (data) => {
|
38
|
+
try {
|
39
|
+
console.log('Processing user journey event', data);
|
40
|
+
sendEvent(res, data);
|
41
|
+
if (data.type !== 'USER_JOURNEY') {
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
data.payload.screens.forEach((screen) => {
|
45
|
+
if (screen.name in promises) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
promises[screen.name] = new Promise(async (resolve, reject) => {
|
49
|
+
try {
|
50
|
+
const screenStream = await stormClient_1.stormClient.createUIPage({
|
51
|
+
prompt: screen.requirements,
|
52
|
+
method: screen.method,
|
53
|
+
path: screen.path,
|
54
|
+
description: screen.requirements,
|
55
|
+
name: screen.name,
|
56
|
+
filename: screen.filename,
|
57
|
+
}, conversationId);
|
58
|
+
screenStream.on('data', (screenData) => {
|
59
|
+
console.log('Processing screen event', screenData);
|
60
|
+
sendEvent(res, screenData);
|
61
|
+
});
|
62
|
+
screenStream.on('end', () => {
|
63
|
+
resolve();
|
64
|
+
});
|
65
|
+
}
|
66
|
+
catch (e) {
|
67
|
+
reject(e);
|
68
|
+
}
|
69
|
+
});
|
70
|
+
});
|
71
|
+
}
|
72
|
+
catch (e) {
|
73
|
+
console.error('Failed to process event', e);
|
74
|
+
}
|
75
|
+
});
|
76
|
+
await waitForStormStream(userJourneysStream);
|
77
|
+
if (userJourneysStream.isAborted()) {
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
await Promise.all(Object.values(promises));
|
81
|
+
sendDone(res);
|
82
|
+
}
|
83
|
+
catch (err) {
|
84
|
+
sendError(err, res);
|
85
|
+
if (!res.closed) {
|
86
|
+
res.end();
|
87
|
+
}
|
88
|
+
}
|
89
|
+
});
|
24
90
|
router.post('/:handle/all', async (req, res) => {
|
25
91
|
const handle = req.params.handle;
|
26
92
|
try {
|
@@ -1,12 +1,23 @@
|
|
1
1
|
import { ConversationItem, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
|
2
2
|
export declare const STORM_ID = "storm";
|
3
3
|
export declare const ConversationIdHeader = "Conversation-Id";
|
4
|
+
interface UIPagePrompt {
|
5
|
+
name: string;
|
6
|
+
filename: string;
|
7
|
+
prompt: string;
|
8
|
+
path: string;
|
9
|
+
method: string;
|
10
|
+
description: string;
|
11
|
+
}
|
4
12
|
declare class StormClient {
|
5
13
|
private readonly _baseUrl;
|
6
14
|
constructor();
|
7
15
|
private createOptions;
|
8
16
|
private send;
|
9
17
|
createMetadata(prompt: string, conversationId?: string): Promise<StormStream>;
|
18
|
+
createUIPages(prompt: string, conversationId?: string): Promise<StormStream>;
|
19
|
+
createUIUserJourneys(prompt: string, conversationId?: string): Promise<StormStream>;
|
20
|
+
createUIPage(prompt: UIPagePrompt, conversationId?: string): Promise<StormStream>;
|
10
21
|
listScreens(prompt: StormUIListPrompt, conversationId?: string): Promise<StormStream>;
|
11
22
|
createUIImplementation(prompt: StormUIImplementationPrompt, conversationId?: string): Promise<StormStream>;
|
12
23
|
createServiceImplementation(prompt: StormFileImplementationPrompt, conversationId?: string): Promise<StormStream>;
|
@@ -28,7 +28,7 @@ class StormClient {
|
|
28
28
|
const api = new nodejs_api_client_1.KapetaAPI();
|
29
29
|
if (api.hasToken()) {
|
30
30
|
const token = await api.getAccessToken();
|
31
|
-
headers['Authorization'] = `Bearer ${token}`;
|
31
|
+
//headers['Authorization'] = `Bearer ${token}`;
|
32
32
|
}
|
33
33
|
if (body.conversationId) {
|
34
34
|
headers[exports.ConversationIdHeader] = body.conversationId;
|
@@ -80,6 +80,24 @@ class StormClient {
|
|
80
80
|
conversationId,
|
81
81
|
});
|
82
82
|
}
|
83
|
+
createUIPages(prompt, conversationId) {
|
84
|
+
return this.send('/v2/ui/pages', {
|
85
|
+
prompt: prompt,
|
86
|
+
conversationId,
|
87
|
+
});
|
88
|
+
}
|
89
|
+
createUIUserJourneys(prompt, conversationId) {
|
90
|
+
return this.send('/v2/ui/user-journeys', {
|
91
|
+
prompt: prompt,
|
92
|
+
conversationId,
|
93
|
+
});
|
94
|
+
}
|
95
|
+
createUIPage(prompt, conversationId) {
|
96
|
+
return this.send('/v2/ui/page', {
|
97
|
+
prompt: prompt,
|
98
|
+
conversationId,
|
99
|
+
});
|
100
|
+
}
|
83
101
|
listScreens(prompt, conversationId) {
|
84
102
|
return this.send('/v2/ui/list', {
|
85
103
|
prompt,
|
package/package.json
CHANGED
package/src/storm/codegen.ts
CHANGED
@@ -299,6 +299,7 @@ export class StormCodegen {
|
|
299
299
|
|
300
300
|
const kapetaYaml = YAML.stringify(block.content);
|
301
301
|
const blockDefinition = block.content;
|
302
|
+
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
302
303
|
|
303
304
|
// Generate the code for the block using the standard codegen templates
|
304
305
|
const generatedResult = await this.generateBlock(blockDefinition);
|
@@ -306,6 +307,9 @@ export class StormCodegen {
|
|
306
307
|
return;
|
307
308
|
}
|
308
309
|
|
310
|
+
const kapetaYmlPath = join(basePath, 'kapeta.yml');
|
311
|
+
await writeFile(kapetaYmlPath, kapetaYaml);
|
312
|
+
|
309
313
|
const allFiles = this.toStormFiles(generatedResult);
|
310
314
|
|
311
315
|
// Send all the non-ai files to the stream
|
@@ -421,7 +425,6 @@ export class StormCodegen {
|
|
421
425
|
if (this.isAborted()) {
|
422
426
|
return;
|
423
427
|
}
|
424
|
-
const basePath = this.getBasePath(blockDefinition.metadata.name);
|
425
428
|
|
426
429
|
const screenFilesConverted = screenFiles.map((screenFile) => {
|
427
430
|
return {
|
@@ -485,8 +488,6 @@ export class StormCodegen {
|
|
485
488
|
await writeFile(filePath, webRouterFile.content);
|
486
489
|
}
|
487
490
|
|
488
|
-
const kapetaYmlPath = join(basePath, 'kapeta.yml');
|
489
|
-
await writeFile(kapetaYmlPath, kapetaYaml);
|
490
491
|
|
491
492
|
const blockRef = block.uri;
|
492
493
|
|
package/src/storm/events.ts
CHANGED
@@ -120,7 +120,7 @@ export interface StormEventCreateDSLResource extends Omit<StormEventCreateDSL, '
|
|
120
120
|
}
|
121
121
|
|
122
122
|
export interface StormEventCreateDSLRetry {
|
123
|
-
type: 'API_RETRY' | '
|
123
|
+
type: 'API_RETRY' | 'MODELS_RETRY';
|
124
124
|
reason: string;
|
125
125
|
created: number;
|
126
126
|
payload: {
|
@@ -316,6 +316,43 @@ export interface StormEventPhases {
|
|
316
316
|
};
|
317
317
|
}
|
318
318
|
|
319
|
+
export interface Page {
|
320
|
+
name: string;
|
321
|
+
description: string;
|
322
|
+
content: string;
|
323
|
+
path: string;
|
324
|
+
method: string;
|
325
|
+
}
|
326
|
+
|
327
|
+
// Event for creating a page
|
328
|
+
export interface StormEventPage {
|
329
|
+
type: 'PAGE';
|
330
|
+
reason: string;
|
331
|
+
created: number;
|
332
|
+
payload: Page;
|
333
|
+
}
|
334
|
+
|
335
|
+
export interface UserJourneyScreen {
|
336
|
+
name: string;
|
337
|
+
filename: string;
|
338
|
+
requirements: string;
|
339
|
+
path: string;
|
340
|
+
method: string;
|
341
|
+
nextScreens: string[];
|
342
|
+
}
|
343
|
+
|
344
|
+
export interface UserJourney {
|
345
|
+
screens: UserJourneyScreen[];
|
346
|
+
}
|
347
|
+
|
348
|
+
// Event for defining a user journey
|
349
|
+
export interface StormEventUserJourney {
|
350
|
+
type: 'USER_JOURNEY';
|
351
|
+
reason: string;
|
352
|
+
created: number;
|
353
|
+
payload: UserJourney;
|
354
|
+
}
|
355
|
+
|
319
356
|
export type StormEvent =
|
320
357
|
| StormEventCreateBlock
|
321
358
|
| StormEventCreateConnection
|
@@ -340,4 +377,6 @@ export type StormEvent =
|
|
340
377
|
| StormEventBlockReady
|
341
378
|
| StormEventPhases
|
342
379
|
| StormEventBlockStatus
|
343
|
-
| StormEventCreateDSLRetry
|
380
|
+
| StormEventCreateDSLRetry
|
381
|
+
| StormEventUserJourney
|
382
|
+
| StormEventPage;
|
package/src/storm/routes.ts
CHANGED
@@ -29,6 +29,84 @@ const router = Router();
|
|
29
29
|
router.use('/', corsHandler);
|
30
30
|
router.use('/', stringBody);
|
31
31
|
|
32
|
+
router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
33
|
+
const handle = req.params.handle as string;
|
34
|
+
try {
|
35
|
+
const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
|
36
|
+
|
37
|
+
const aiRequest: StormContextRequest = JSON.parse(req.stringBody ?? '{}');
|
38
|
+
|
39
|
+
const userJourneysStream = await stormClient.createUIUserJourneys(aiRequest.prompt, conversationId);
|
40
|
+
|
41
|
+
onRequestAborted(req, res, () => {
|
42
|
+
userJourneysStream.abort();
|
43
|
+
});
|
44
|
+
|
45
|
+
res.set('Content-Type', 'application/x-ndjson');
|
46
|
+
res.set('Access-Control-Expose-Headers', ConversationIdHeader);
|
47
|
+
res.set(ConversationIdHeader, userJourneysStream.getConversationId());
|
48
|
+
|
49
|
+
const promises: { [key: string]: Promise<void> } = {};
|
50
|
+
|
51
|
+
userJourneysStream.on('data', async (data: StormEvent) => {
|
52
|
+
try {
|
53
|
+
console.log('Processing user journey event', data);
|
54
|
+
sendEvent(res, data);
|
55
|
+
if (data.type !== 'USER_JOURNEY') {
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
|
59
|
+
data.payload.screens.forEach((screen) => {
|
60
|
+
if (screen.name in promises) {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
promises[screen.name] = new Promise(async (resolve, reject) => {
|
64
|
+
try {
|
65
|
+
const screenStream = await stormClient.createUIPage(
|
66
|
+
{
|
67
|
+
prompt: screen.requirements,
|
68
|
+
method: screen.method,
|
69
|
+
path: screen.path,
|
70
|
+
description: screen.requirements,
|
71
|
+
name: screen.name,
|
72
|
+
filename: screen.filename,
|
73
|
+
},
|
74
|
+
conversationId
|
75
|
+
);
|
76
|
+
screenStream.on('data', (screenData: StormEvent) => {
|
77
|
+
console.log('Processing screen event', screenData);
|
78
|
+
sendEvent(res, screenData);
|
79
|
+
});
|
80
|
+
screenStream.on('end', () => {
|
81
|
+
resolve();
|
82
|
+
});
|
83
|
+
} catch (e: any) {
|
84
|
+
reject(e);
|
85
|
+
}
|
86
|
+
});
|
87
|
+
});
|
88
|
+
} catch (e) {
|
89
|
+
console.error('Failed to process event', e);
|
90
|
+
}
|
91
|
+
});
|
92
|
+
|
93
|
+
await waitForStormStream(userJourneysStream);
|
94
|
+
|
95
|
+
if (userJourneysStream.isAborted()) {
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
|
99
|
+
await Promise.all(Object.values(promises));
|
100
|
+
|
101
|
+
sendDone(res);
|
102
|
+
} catch (err: any) {
|
103
|
+
sendError(err, res);
|
104
|
+
if (!res.closed) {
|
105
|
+
res.end();
|
106
|
+
}
|
107
|
+
}
|
108
|
+
});
|
109
|
+
|
32
110
|
router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
|
33
111
|
const handle = req.params.handle as string;
|
34
112
|
|
package/src/storm/stormClient.ts
CHANGED
@@ -20,6 +20,15 @@ export const STORM_ID = 'storm';
|
|
20
20
|
|
21
21
|
export const ConversationIdHeader = 'Conversation-Id';
|
22
22
|
|
23
|
+
interface UIPagePrompt {
|
24
|
+
name: string;
|
25
|
+
filename: string;
|
26
|
+
prompt: string;
|
27
|
+
path: string;
|
28
|
+
method: string;
|
29
|
+
description: string;
|
30
|
+
}
|
31
|
+
|
23
32
|
class StormClient {
|
24
33
|
private readonly _baseUrl: string;
|
25
34
|
|
@@ -39,7 +48,7 @@ class StormClient {
|
|
39
48
|
const api = new KapetaAPI();
|
40
49
|
if (api.hasToken()) {
|
41
50
|
const token = await api.getAccessToken();
|
42
|
-
headers['Authorization'] = `Bearer ${token}`;
|
51
|
+
//headers['Authorization'] = `Bearer ${token}`;
|
43
52
|
}
|
44
53
|
|
45
54
|
if (body.conversationId) {
|
@@ -113,6 +122,27 @@ class StormClient {
|
|
113
122
|
});
|
114
123
|
}
|
115
124
|
|
125
|
+
public createUIPages(prompt: string, conversationId?: string) {
|
126
|
+
return this.send('/v2/ui/pages', {
|
127
|
+
prompt: prompt,
|
128
|
+
conversationId,
|
129
|
+
});
|
130
|
+
}
|
131
|
+
|
132
|
+
public createUIUserJourneys(prompt: string, conversationId?: string) {
|
133
|
+
return this.send('/v2/ui/user-journeys', {
|
134
|
+
prompt: prompt,
|
135
|
+
conversationId,
|
136
|
+
});
|
137
|
+
}
|
138
|
+
|
139
|
+
public createUIPage(prompt: UIPagePrompt, conversationId?: string) {
|
140
|
+
return this.send('/v2/ui/page', {
|
141
|
+
prompt: prompt,
|
142
|
+
conversationId,
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
116
146
|
public listScreens(prompt: StormUIListPrompt, conversationId?: string) {
|
117
147
|
return this.send('/v2/ui/list', {
|
118
148
|
prompt,
|