@kapeta/local-cluster-service 0.47.4 → 0.48.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/codegen.js +46 -1
- package/dist/cjs/src/storm/event-parser.d.ts +6 -1
- package/dist/cjs/src/storm/event-parser.js +22 -3
- package/dist/cjs/src/storm/events.d.ts +6 -0
- package/dist/cjs/src/storm/routes.js +13 -3
- package/dist/cjs/test/storm/event-parser.test.js +1 -1
- package/dist/esm/src/storm/codegen.js +46 -1
- package/dist/esm/src/storm/event-parser.d.ts +6 -1
- package/dist/esm/src/storm/event-parser.js +22 -3
- package/dist/esm/src/storm/events.d.ts +6 -0
- package/dist/esm/src/storm/routes.js +13 -3
- package/dist/esm/test/storm/event-parser.test.js +1 -1
- package/package.json +1 -1
- package/src/storm/codegen.ts +25 -3
- package/src/storm/event-parser.ts +24 -3
- package/src/storm/events.ts +6 -0
- package/src/storm/routes.ts +14 -3
- package/test/storm/event-parser.test.ts +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.48.1](https://github.com/kapetacom/local-cluster-service/compare/v0.48.0...v0.48.1) (2024-06-03)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Enrich events with block refs and instance ids ([#156](https://github.com/kapetacom/local-cluster-service/issues/156)) ([c45d5d7](https://github.com/kapetacom/local-cluster-service/commit/c45d5d776760a51fb786582d74cb4d049e27f0db))
|
7
|
+
|
8
|
+
# [0.48.0](https://github.com/kapetacom/local-cluster-service/compare/v0.47.4...v0.48.0) (2024-06-03)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* write files to disk ([8cb2d28](https://github.com/kapetacom/local-cluster-service/commit/8cb2d28a3bc41fd8dc81f52f77054ccc7a9ac294))
|
14
|
+
|
1
15
|
## [0.47.4](https://github.com/kapetacom/local-cluster-service/compare/v0.47.3...v0.47.4) (2024-05-31)
|
2
16
|
|
3
17
|
|
@@ -3,12 +3,41 @@
|
|
3
3
|
* Copyright 2023 Kapeta Inc.
|
4
4
|
* SPDX-License-Identifier: BUSL-1.1
|
5
5
|
*/
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
7
|
+
if (k2 === undefined) k2 = k;
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
11
|
+
}
|
12
|
+
Object.defineProperty(o, k2, desc);
|
13
|
+
}) : (function(o, m, k, k2) {
|
14
|
+
if (k2 === undefined) k2 = k;
|
15
|
+
o[k2] = m[k];
|
16
|
+
}));
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
19
|
+
}) : function(o, v) {
|
20
|
+
o["default"] = v;
|
21
|
+
});
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
23
|
+
if (mod && mod.__esModule) return mod;
|
24
|
+
var result = {};
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
26
|
+
__setModuleDefault(result, mod);
|
27
|
+
return result;
|
28
|
+
};
|
29
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
30
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
31
|
+
};
|
6
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
7
33
|
exports.StormCodegen = void 0;
|
8
34
|
const codegen_1 = require("@kapeta/codegen");
|
9
35
|
const codeGeneratorManager_1 = require("../codeGeneratorManager");
|
10
36
|
const stormClient_1 = require("./stormClient");
|
11
37
|
const stream_1 = require("./stream");
|
38
|
+
const promises_1 = require("fs/promises");
|
39
|
+
const path_1 = __importStar(require("path"));
|
40
|
+
const node_os_1 = __importDefault(require("node:os"));
|
12
41
|
class StormCodegen {
|
13
42
|
userPrompt;
|
14
43
|
blocks;
|
@@ -100,6 +129,19 @@ class StormCodegen {
|
|
100
129
|
if (serviceFiles.length > 0) {
|
101
130
|
await this.processTemplates(block.uri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
102
131
|
}
|
132
|
+
const basePath = path_1.default.join(node_os_1.default.tmpdir(), block.content.metadata.name);
|
133
|
+
for (const serviceFile of serviceFiles) {
|
134
|
+
const filePath = (0, path_1.join)(basePath, serviceFile.filename);
|
135
|
+
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
136
|
+
}
|
137
|
+
for (const serviceFile of contextFiles) {
|
138
|
+
const filePath = (0, path_1.join)(basePath, serviceFile.filename);
|
139
|
+
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
140
|
+
}
|
141
|
+
for (const uiFile of uiTemplates) {
|
142
|
+
const filePath = (0, path_1.join)(basePath, uiFile.filename);
|
143
|
+
await (0, promises_1.writeFile)(filePath, uiFile.content);
|
144
|
+
}
|
103
145
|
}
|
104
146
|
/**
|
105
147
|
* Emits the text-based files to the stream
|
@@ -203,9 +245,12 @@ class StormCodegen {
|
|
203
245
|
if (!(await codeGeneratorManager_1.codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
|
204
246
|
return;
|
205
247
|
}
|
248
|
+
const basePath = path_1.default.join(node_os_1.default.tmpdir(), yamlContent.metadata.name);
|
206
249
|
const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
|
207
250
|
codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
|
208
|
-
|
251
|
+
const generatedResult = await codeGenerator.generate();
|
252
|
+
new codegen_1.CodeWriter(basePath).write(generatedResult);
|
253
|
+
return generatedResult;
|
209
254
|
}
|
210
255
|
}
|
211
256
|
exports.StormCodegen = StormCodegen;
|
@@ -51,10 +51,15 @@ export declare class StormEventParser {
|
|
51
51
|
private options;
|
52
52
|
constructor(options: StormOptions);
|
53
53
|
private reset;
|
54
|
-
|
54
|
+
/**
|
55
|
+
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
56
|
+
*/
|
57
|
+
processEvent(handle: string, evt: StormEvent): StormDefinitions;
|
55
58
|
getEvents(): StormEvent[];
|
56
59
|
isValid(): boolean;
|
57
60
|
getError(): string;
|
61
|
+
private toInstanceId;
|
62
|
+
private toInstanceIdFromRef;
|
58
63
|
toResult(handle: string): StormDefinitions;
|
59
64
|
private toSafeName;
|
60
65
|
private toRef;
|
@@ -122,9 +122,11 @@ class StormEventParser {
|
|
122
122
|
this.blocks = {};
|
123
123
|
this.connections = [];
|
124
124
|
}
|
125
|
-
|
125
|
+
/**
|
126
|
+
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
127
|
+
*/
|
128
|
+
processEvent(handle, evt) {
|
126
129
|
this.events.push(evt);
|
127
|
-
console.log('evt', evt);
|
128
130
|
switch (evt.type) {
|
129
131
|
case 'CREATE_PLAN_PROPERTIES':
|
130
132
|
this.planName = evt.payload.name;
|
@@ -137,6 +139,8 @@ class StormEventParser {
|
|
137
139
|
models: [],
|
138
140
|
types: [],
|
139
141
|
};
|
142
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.name).toNormalizedString();
|
143
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
140
144
|
break;
|
141
145
|
case 'PLAN_RETRY':
|
142
146
|
this.reset();
|
@@ -147,14 +151,22 @@ class StormEventParser {
|
|
147
151
|
break;
|
148
152
|
case 'CREATE_API':
|
149
153
|
this.blocks[evt.payload.blockName].apis.push(prettifyKaplang(evt.payload.content));
|
154
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
155
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
150
156
|
break;
|
151
157
|
case 'CREATE_TYPE':
|
152
158
|
this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
|
159
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
160
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
153
161
|
break;
|
154
162
|
case 'CREATE_MODEL':
|
155
163
|
this.blocks[evt.payload.blockName].models.push(prettifyKaplang(evt.payload.content));
|
164
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
165
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
156
166
|
break;
|
157
167
|
case 'CREATE_CONNECTION':
|
168
|
+
evt.payload.fromBlockId = this.toInstanceId(handle, evt.payload.fromComponent);
|
169
|
+
evt.payload.toBlockId = this.toInstanceId(handle, evt.payload.toComponent);
|
158
170
|
this.connections.push(evt.payload);
|
159
171
|
break;
|
160
172
|
default:
|
@@ -176,13 +188,20 @@ class StormEventParser {
|
|
176
188
|
getError() {
|
177
189
|
return this.error;
|
178
190
|
}
|
191
|
+
toInstanceId(handle, blockName) {
|
192
|
+
const ref = this.toRef(handle, blockName);
|
193
|
+
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
194
|
+
}
|
195
|
+
toInstanceIdFromRef(ref) {
|
196
|
+
return (0, uuid_1.v5)((0, nodejs_utils_1.normalizeKapetaUri)(ref), uuid_1.v5.URL);
|
197
|
+
}
|
179
198
|
toResult(handle) {
|
180
199
|
const planRef = this.toRef(handle, this.planName ?? 'undefined');
|
181
200
|
const blockDefinitions = this.toBlockDefinitions(handle);
|
182
201
|
const refIdMap = {};
|
183
202
|
const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
|
184
203
|
// Create a deterministic uuid
|
185
|
-
const id =
|
204
|
+
const id = this.toInstanceIdFromRef(ref);
|
186
205
|
refIdMap[ref] = id;
|
187
206
|
return {
|
188
207
|
id,
|
@@ -14,6 +14,8 @@ export interface StormBlockInfo {
|
|
14
14
|
name: string;
|
15
15
|
description: string;
|
16
16
|
}[];
|
17
|
+
blockRef?: string;
|
18
|
+
instanceId?: string;
|
17
19
|
}
|
18
20
|
export interface StormBlockInfoFilled extends StormBlockInfo {
|
19
21
|
apis: string[];
|
@@ -28,9 +30,11 @@ export interface StormEventCreateBlock {
|
|
28
30
|
}
|
29
31
|
export interface StormConnection {
|
30
32
|
fromComponent: string;
|
33
|
+
fromBlockId?: string;
|
31
34
|
fromResource: string;
|
32
35
|
fromResourceType: StormResourceType;
|
33
36
|
toComponent: string;
|
37
|
+
toBlockId?: string;
|
34
38
|
toResource: string;
|
35
39
|
toResourceType: StormResourceType;
|
36
40
|
}
|
@@ -72,6 +76,8 @@ export interface StormEventCreateDSL {
|
|
72
76
|
payload: {
|
73
77
|
blockName: string;
|
74
78
|
content: string;
|
79
|
+
blockRef?: string;
|
80
|
+
instanceId?: string;
|
75
81
|
};
|
76
82
|
}
|
77
83
|
export interface StormEventError {
|
@@ -27,12 +27,12 @@ router.post('/:handle/all', async (req, res) => {
|
|
27
27
|
res.set('Content-Type', 'application/x-ndjson');
|
28
28
|
res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
|
29
29
|
res.set(stormClient_1.ConversationIdHeader, metaStream.getConversationId());
|
30
|
-
console.log('metaStream.getConversationId()', metaStream.getConversationId());
|
31
30
|
metaStream.on('data', (data) => {
|
32
|
-
const result = eventParser.
|
31
|
+
const result = eventParser.processEvent(req.params.handle, data);
|
32
|
+
sendEvent(res, data);
|
33
33
|
sendDefinitions(res, result);
|
34
34
|
});
|
35
|
-
await
|
35
|
+
await waitForStormStream(metaStream);
|
36
36
|
if (!eventParser.isValid()) {
|
37
37
|
// We can't continue if the meta stream is invalid
|
38
38
|
sendEvent(res, {
|
@@ -88,6 +88,16 @@ function sendError(err, res) {
|
|
88
88
|
res.status(400).send({ error: err.message });
|
89
89
|
}
|
90
90
|
}
|
91
|
+
function waitForStormStream(result) {
|
92
|
+
return new Promise((resolve, reject) => {
|
93
|
+
result.on('error', (err) => {
|
94
|
+
reject(err);
|
95
|
+
});
|
96
|
+
result.on('end', () => {
|
97
|
+
resolve();
|
98
|
+
});
|
99
|
+
});
|
100
|
+
}
|
91
101
|
function streamStormPartialResponse(result, res) {
|
92
102
|
return new Promise((resolve, reject) => {
|
93
103
|
result.on('data', (data) => {
|
@@ -127,7 +127,7 @@ const events = [
|
|
127
127
|
describe('event-parser', () => {
|
128
128
|
it('it can parse events into a plan and blocks with proper layout', () => {
|
129
129
|
const parser = new event_parser_1.StormEventParser(parserOptions);
|
130
|
-
events.forEach((event) => parser.
|
130
|
+
events.forEach((event) => parser.processEvent('kapeta', event));
|
131
131
|
const result = parser.toResult('kapeta');
|
132
132
|
expect(result.plan.metadata.name).toBe('kapeta/my-plan');
|
133
133
|
expect(result.plan.metadata.description).toBe('my plan description');
|
@@ -3,12 +3,41 @@
|
|
3
3
|
* Copyright 2023 Kapeta Inc.
|
4
4
|
* SPDX-License-Identifier: BUSL-1.1
|
5
5
|
*/
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
7
|
+
if (k2 === undefined) k2 = k;
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
11
|
+
}
|
12
|
+
Object.defineProperty(o, k2, desc);
|
13
|
+
}) : (function(o, m, k, k2) {
|
14
|
+
if (k2 === undefined) k2 = k;
|
15
|
+
o[k2] = m[k];
|
16
|
+
}));
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
19
|
+
}) : function(o, v) {
|
20
|
+
o["default"] = v;
|
21
|
+
});
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
23
|
+
if (mod && mod.__esModule) return mod;
|
24
|
+
var result = {};
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
26
|
+
__setModuleDefault(result, mod);
|
27
|
+
return result;
|
28
|
+
};
|
29
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
30
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
31
|
+
};
|
6
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
7
33
|
exports.StormCodegen = void 0;
|
8
34
|
const codegen_1 = require("@kapeta/codegen");
|
9
35
|
const codeGeneratorManager_1 = require("../codeGeneratorManager");
|
10
36
|
const stormClient_1 = require("./stormClient");
|
11
37
|
const stream_1 = require("./stream");
|
38
|
+
const promises_1 = require("fs/promises");
|
39
|
+
const path_1 = __importStar(require("path"));
|
40
|
+
const node_os_1 = __importDefault(require("node:os"));
|
12
41
|
class StormCodegen {
|
13
42
|
userPrompt;
|
14
43
|
blocks;
|
@@ -100,6 +129,19 @@ class StormCodegen {
|
|
100
129
|
if (serviceFiles.length > 0) {
|
101
130
|
await this.processTemplates(block.uri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
|
102
131
|
}
|
132
|
+
const basePath = path_1.default.join(node_os_1.default.tmpdir(), block.content.metadata.name);
|
133
|
+
for (const serviceFile of serviceFiles) {
|
134
|
+
const filePath = (0, path_1.join)(basePath, serviceFile.filename);
|
135
|
+
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
136
|
+
}
|
137
|
+
for (const serviceFile of contextFiles) {
|
138
|
+
const filePath = (0, path_1.join)(basePath, serviceFile.filename);
|
139
|
+
await (0, promises_1.writeFile)(filePath, serviceFile.content);
|
140
|
+
}
|
141
|
+
for (const uiFile of uiTemplates) {
|
142
|
+
const filePath = (0, path_1.join)(basePath, uiFile.filename);
|
143
|
+
await (0, promises_1.writeFile)(filePath, uiFile.content);
|
144
|
+
}
|
103
145
|
}
|
104
146
|
/**
|
105
147
|
* Emits the text-based files to the stream
|
@@ -203,9 +245,12 @@ class StormCodegen {
|
|
203
245
|
if (!(await codeGeneratorManager_1.codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
|
204
246
|
return;
|
205
247
|
}
|
248
|
+
const basePath = path_1.default.join(node_os_1.default.tmpdir(), yamlContent.metadata.name);
|
206
249
|
const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
|
207
250
|
codeGenerator.withOption('AIContext', stormClient_1.STORM_ID);
|
208
|
-
|
251
|
+
const generatedResult = await codeGenerator.generate();
|
252
|
+
new codegen_1.CodeWriter(basePath).write(generatedResult);
|
253
|
+
return generatedResult;
|
209
254
|
}
|
210
255
|
}
|
211
256
|
exports.StormCodegen = StormCodegen;
|
@@ -51,10 +51,15 @@ export declare class StormEventParser {
|
|
51
51
|
private options;
|
52
52
|
constructor(options: StormOptions);
|
53
53
|
private reset;
|
54
|
-
|
54
|
+
/**
|
55
|
+
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
56
|
+
*/
|
57
|
+
processEvent(handle: string, evt: StormEvent): StormDefinitions;
|
55
58
|
getEvents(): StormEvent[];
|
56
59
|
isValid(): boolean;
|
57
60
|
getError(): string;
|
61
|
+
private toInstanceId;
|
62
|
+
private toInstanceIdFromRef;
|
58
63
|
toResult(handle: string): StormDefinitions;
|
59
64
|
private toSafeName;
|
60
65
|
private toRef;
|
@@ -122,9 +122,11 @@ class StormEventParser {
|
|
122
122
|
this.blocks = {};
|
123
123
|
this.connections = [];
|
124
124
|
}
|
125
|
-
|
125
|
+
/**
|
126
|
+
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
127
|
+
*/
|
128
|
+
processEvent(handle, evt) {
|
126
129
|
this.events.push(evt);
|
127
|
-
console.log('evt', evt);
|
128
130
|
switch (evt.type) {
|
129
131
|
case 'CREATE_PLAN_PROPERTIES':
|
130
132
|
this.planName = evt.payload.name;
|
@@ -137,6 +139,8 @@ class StormEventParser {
|
|
137
139
|
models: [],
|
138
140
|
types: [],
|
139
141
|
};
|
142
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.name).toNormalizedString();
|
143
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
140
144
|
break;
|
141
145
|
case 'PLAN_RETRY':
|
142
146
|
this.reset();
|
@@ -147,14 +151,22 @@ class StormEventParser {
|
|
147
151
|
break;
|
148
152
|
case 'CREATE_API':
|
149
153
|
this.blocks[evt.payload.blockName].apis.push(prettifyKaplang(evt.payload.content));
|
154
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
155
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
150
156
|
break;
|
151
157
|
case 'CREATE_TYPE':
|
152
158
|
this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
|
159
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
160
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
153
161
|
break;
|
154
162
|
case 'CREATE_MODEL':
|
155
163
|
this.blocks[evt.payload.blockName].models.push(prettifyKaplang(evt.payload.content));
|
164
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
165
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
156
166
|
break;
|
157
167
|
case 'CREATE_CONNECTION':
|
168
|
+
evt.payload.fromBlockId = this.toInstanceId(handle, evt.payload.fromComponent);
|
169
|
+
evt.payload.toBlockId = this.toInstanceId(handle, evt.payload.toComponent);
|
158
170
|
this.connections.push(evt.payload);
|
159
171
|
break;
|
160
172
|
default:
|
@@ -176,13 +188,20 @@ class StormEventParser {
|
|
176
188
|
getError() {
|
177
189
|
return this.error;
|
178
190
|
}
|
191
|
+
toInstanceId(handle, blockName) {
|
192
|
+
const ref = this.toRef(handle, blockName);
|
193
|
+
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
194
|
+
}
|
195
|
+
toInstanceIdFromRef(ref) {
|
196
|
+
return (0, uuid_1.v5)((0, nodejs_utils_1.normalizeKapetaUri)(ref), uuid_1.v5.URL);
|
197
|
+
}
|
179
198
|
toResult(handle) {
|
180
199
|
const planRef = this.toRef(handle, this.planName ?? 'undefined');
|
181
200
|
const blockDefinitions = this.toBlockDefinitions(handle);
|
182
201
|
const refIdMap = {};
|
183
202
|
const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
|
184
203
|
// Create a deterministic uuid
|
185
|
-
const id =
|
204
|
+
const id = this.toInstanceIdFromRef(ref);
|
186
205
|
refIdMap[ref] = id;
|
187
206
|
return {
|
188
207
|
id,
|
@@ -14,6 +14,8 @@ export interface StormBlockInfo {
|
|
14
14
|
name: string;
|
15
15
|
description: string;
|
16
16
|
}[];
|
17
|
+
blockRef?: string;
|
18
|
+
instanceId?: string;
|
17
19
|
}
|
18
20
|
export interface StormBlockInfoFilled extends StormBlockInfo {
|
19
21
|
apis: string[];
|
@@ -28,9 +30,11 @@ export interface StormEventCreateBlock {
|
|
28
30
|
}
|
29
31
|
export interface StormConnection {
|
30
32
|
fromComponent: string;
|
33
|
+
fromBlockId?: string;
|
31
34
|
fromResource: string;
|
32
35
|
fromResourceType: StormResourceType;
|
33
36
|
toComponent: string;
|
37
|
+
toBlockId?: string;
|
34
38
|
toResource: string;
|
35
39
|
toResourceType: StormResourceType;
|
36
40
|
}
|
@@ -72,6 +76,8 @@ export interface StormEventCreateDSL {
|
|
72
76
|
payload: {
|
73
77
|
blockName: string;
|
74
78
|
content: string;
|
79
|
+
blockRef?: string;
|
80
|
+
instanceId?: string;
|
75
81
|
};
|
76
82
|
}
|
77
83
|
export interface StormEventError {
|
@@ -27,12 +27,12 @@ router.post('/:handle/all', async (req, res) => {
|
|
27
27
|
res.set('Content-Type', 'application/x-ndjson');
|
28
28
|
res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
|
29
29
|
res.set(stormClient_1.ConversationIdHeader, metaStream.getConversationId());
|
30
|
-
console.log('metaStream.getConversationId()', metaStream.getConversationId());
|
31
30
|
metaStream.on('data', (data) => {
|
32
|
-
const result = eventParser.
|
31
|
+
const result = eventParser.processEvent(req.params.handle, data);
|
32
|
+
sendEvent(res, data);
|
33
33
|
sendDefinitions(res, result);
|
34
34
|
});
|
35
|
-
await
|
35
|
+
await waitForStormStream(metaStream);
|
36
36
|
if (!eventParser.isValid()) {
|
37
37
|
// We can't continue if the meta stream is invalid
|
38
38
|
sendEvent(res, {
|
@@ -88,6 +88,16 @@ function sendError(err, res) {
|
|
88
88
|
res.status(400).send({ error: err.message });
|
89
89
|
}
|
90
90
|
}
|
91
|
+
function waitForStormStream(result) {
|
92
|
+
return new Promise((resolve, reject) => {
|
93
|
+
result.on('error', (err) => {
|
94
|
+
reject(err);
|
95
|
+
});
|
96
|
+
result.on('end', () => {
|
97
|
+
resolve();
|
98
|
+
});
|
99
|
+
});
|
100
|
+
}
|
91
101
|
function streamStormPartialResponse(result, res) {
|
92
102
|
return new Promise((resolve, reject) => {
|
93
103
|
result.on('data', (data) => {
|
@@ -127,7 +127,7 @@ const events = [
|
|
127
127
|
describe('event-parser', () => {
|
128
128
|
it('it can parse events into a plan and blocks with proper layout', () => {
|
129
129
|
const parser = new event_parser_1.StormEventParser(parserOptions);
|
130
|
-
events.forEach((event) => parser.
|
130
|
+
events.forEach((event) => parser.processEvent('kapeta', event));
|
131
131
|
const result = parser.toResult('kapeta');
|
132
132
|
expect(result.plan.metadata.name).toBe('kapeta/my-plan');
|
133
133
|
expect(result.plan.metadata.description).toBe('my plan description');
|
package/package.json
CHANGED
package/src/storm/codegen.ts
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
*/
|
5
5
|
|
6
6
|
import { Definition } from '@kapeta/local-cluster-config';
|
7
|
-
import { AIFileTypes, BlockCodeGenerator, GeneratedFile, GeneratedResult } from '@kapeta/codegen';
|
7
|
+
import { AIFileTypes, BlockCodeGenerator, CodeWriter, GeneratedFile, GeneratedResult } from '@kapeta/codegen';
|
8
8
|
import { BlockDefinition } from '@kapeta/schemas';
|
9
9
|
import { codeGeneratorManager } from '../codeGeneratorManager';
|
10
10
|
import { STORM_ID, stormClient } from './stormClient';
|
@@ -12,6 +12,9 @@ import { StormEvent, StormEventFile } from './events';
|
|
12
12
|
import { BlockDefinitionInfo } from './event-parser';
|
13
13
|
import { ConversationItem, StormFileImplementationPrompt, StormFileInfo, StormStream } from './stream';
|
14
14
|
import { KapetaURI } from '@kapeta/nodejs-utils';
|
15
|
+
import { writeFile } from 'fs/promises';
|
16
|
+
import path, { join } from 'path';
|
17
|
+
import os from 'node:os';
|
15
18
|
|
16
19
|
type ImplementationGenerator = (prompt: StormFileImplementationPrompt, conversationId?: string) => Promise<StormStream>;
|
17
20
|
|
@@ -131,6 +134,23 @@ export class StormCodegen {
|
|
131
134
|
contextFiles
|
132
135
|
);
|
133
136
|
}
|
137
|
+
|
138
|
+
const basePath = path.join(os.tmpdir(), block.content.metadata.name);
|
139
|
+
|
140
|
+
for (const serviceFile of serviceFiles) {
|
141
|
+
const filePath = join(basePath, serviceFile.filename);
|
142
|
+
await writeFile(filePath, serviceFile.content);
|
143
|
+
}
|
144
|
+
|
145
|
+
for (const serviceFile of contextFiles) {
|
146
|
+
const filePath = join(basePath, serviceFile.filename);
|
147
|
+
await writeFile(filePath, serviceFile.content);
|
148
|
+
}
|
149
|
+
|
150
|
+
for (const uiFile of uiTemplates) {
|
151
|
+
const filePath = join(basePath, uiFile.filename);
|
152
|
+
await writeFile(filePath, uiFile.content);
|
153
|
+
}
|
134
154
|
}
|
135
155
|
|
136
156
|
/**
|
@@ -263,10 +283,12 @@ export class StormCodegen {
|
|
263
283
|
if (!(await codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
|
264
284
|
return;
|
265
285
|
}
|
286
|
+
const basePath = path.join(os.tmpdir(), yamlContent.metadata.name);
|
266
287
|
|
267
288
|
const codeGenerator = new BlockCodeGenerator(yamlContent as BlockDefinition);
|
268
289
|
codeGenerator.withOption('AIContext', STORM_ID);
|
269
|
-
|
270
|
-
|
290
|
+
const generatedResult = await codeGenerator.generate();
|
291
|
+
new CodeWriter(basePath).write(generatedResult);
|
292
|
+
return generatedResult;
|
271
293
|
}
|
272
294
|
}
|
@@ -221,9 +221,11 @@ export class StormEventParser {
|
|
221
221
|
this.connections = [];
|
222
222
|
}
|
223
223
|
|
224
|
-
|
224
|
+
/**
|
225
|
+
* Builds plan and block definitions - and enriches events with relevant refs and ids
|
226
|
+
*/
|
227
|
+
public processEvent(handle: string, evt: StormEvent): StormDefinitions {
|
225
228
|
this.events.push(evt);
|
226
|
-
console.log('evt', evt);
|
227
229
|
switch (evt.type) {
|
228
230
|
case 'CREATE_PLAN_PROPERTIES':
|
229
231
|
this.planName = evt.payload.name;
|
@@ -236,6 +238,8 @@ export class StormEventParser {
|
|
236
238
|
models: [],
|
237
239
|
types: [],
|
238
240
|
};
|
241
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.name).toNormalizedString();
|
242
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
239
243
|
break;
|
240
244
|
case 'PLAN_RETRY':
|
241
245
|
this.reset();
|
@@ -246,14 +250,22 @@ export class StormEventParser {
|
|
246
250
|
break;
|
247
251
|
case 'CREATE_API':
|
248
252
|
this.blocks[evt.payload.blockName].apis.push(prettifyKaplang(evt.payload.content));
|
253
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
254
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
249
255
|
break;
|
250
256
|
case 'CREATE_TYPE':
|
251
257
|
this.blocks[evt.payload.blockName].types.push(prettifyKaplang(evt.payload.content));
|
258
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
259
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
252
260
|
break;
|
253
261
|
case 'CREATE_MODEL':
|
254
262
|
this.blocks[evt.payload.blockName].models.push(prettifyKaplang(evt.payload.content));
|
263
|
+
evt.payload.blockRef = this.toRef(handle, evt.payload.blockName).toNormalizedString();
|
264
|
+
evt.payload.instanceId = this.toInstanceIdFromRef(evt.payload.blockRef);
|
255
265
|
break;
|
256
266
|
case 'CREATE_CONNECTION':
|
267
|
+
evt.payload.fromBlockId = this.toInstanceId(handle, evt.payload.fromComponent);
|
268
|
+
evt.payload.toBlockId = this.toInstanceId(handle, evt.payload.toComponent);
|
257
269
|
this.connections.push(evt.payload);
|
258
270
|
break;
|
259
271
|
|
@@ -281,13 +293,22 @@ export class StormEventParser {
|
|
281
293
|
return this.error;
|
282
294
|
}
|
283
295
|
|
296
|
+
private toInstanceId(handle: string, blockName: string) {
|
297
|
+
const ref = this.toRef(handle, blockName);
|
298
|
+
return this.toInstanceIdFromRef(ref.toNormalizedString());
|
299
|
+
}
|
300
|
+
|
301
|
+
private toInstanceIdFromRef(ref: string) {
|
302
|
+
return uuid(normalizeKapetaUri(ref), uuid.URL);
|
303
|
+
}
|
304
|
+
|
284
305
|
public toResult(handle: string): StormDefinitions {
|
285
306
|
const planRef = this.toRef(handle, this.planName ?? 'undefined');
|
286
307
|
const blockDefinitions = this.toBlockDefinitions(handle);
|
287
308
|
const refIdMap: { [key: string]: string } = {};
|
288
309
|
const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
|
289
310
|
// Create a deterministic uuid
|
290
|
-
const id =
|
311
|
+
const id = this.toInstanceIdFromRef(ref);
|
291
312
|
refIdMap[ref] = id;
|
292
313
|
return {
|
293
314
|
id,
|
package/src/storm/events.ts
CHANGED
@@ -30,6 +30,8 @@ export interface StormBlockInfo {
|
|
30
30
|
name: string;
|
31
31
|
description: string;
|
32
32
|
}[];
|
33
|
+
blockRef?: string;
|
34
|
+
instanceId?: string;
|
33
35
|
}
|
34
36
|
|
35
37
|
export interface StormBlockInfoFilled extends StormBlockInfo {
|
@@ -47,9 +49,11 @@ export interface StormEventCreateBlock {
|
|
47
49
|
|
48
50
|
export interface StormConnection {
|
49
51
|
fromComponent: string;
|
52
|
+
fromBlockId?: string;
|
50
53
|
fromResource: string;
|
51
54
|
fromResourceType: StormResourceType;
|
52
55
|
toComponent: string;
|
56
|
+
toBlockId?: string;
|
53
57
|
toResource: string;
|
54
58
|
toResourceType: StormResourceType;
|
55
59
|
}
|
@@ -96,6 +100,8 @@ export interface StormEventCreateDSL {
|
|
96
100
|
payload: {
|
97
101
|
blockName: string;
|
98
102
|
content: string;
|
103
|
+
blockRef?: string;
|
104
|
+
instanceId?: string;
|
99
105
|
};
|
100
106
|
}
|
101
107
|
|
package/src/storm/routes.ts
CHANGED
@@ -35,15 +35,15 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
|
|
35
35
|
res.set('Content-Type', 'application/x-ndjson');
|
36
36
|
res.set('Access-Control-Expose-Headers', ConversationIdHeader);
|
37
37
|
res.set(ConversationIdHeader, metaStream.getConversationId());
|
38
|
-
console.log('metaStream.getConversationId()', metaStream.getConversationId());
|
39
38
|
|
40
39
|
metaStream.on('data', (data: StormEvent) => {
|
41
|
-
const result = eventParser.
|
40
|
+
const result = eventParser.processEvent(req.params.handle, data);
|
42
41
|
|
42
|
+
sendEvent(res, data);
|
43
43
|
sendDefinitions(res, result);
|
44
44
|
});
|
45
45
|
|
46
|
-
await
|
46
|
+
await waitForStormStream(metaStream);
|
47
47
|
|
48
48
|
if (!eventParser.isValid()) {
|
49
49
|
// We can't continue if the meta stream is invalid
|
@@ -109,6 +109,17 @@ function sendError(err: Error, res: Response) {
|
|
109
109
|
res.status(400).send({ error: err.message });
|
110
110
|
}
|
111
111
|
}
|
112
|
+
function waitForStormStream(result: StormStream) {
|
113
|
+
return new Promise<void>((resolve, reject) => {
|
114
|
+
result.on('error', (err) => {
|
115
|
+
reject(err);
|
116
|
+
});
|
117
|
+
|
118
|
+
result.on('end', () => {
|
119
|
+
resolve();
|
120
|
+
});
|
121
|
+
});
|
122
|
+
}
|
112
123
|
|
113
124
|
function streamStormPartialResponse(result: StormStream, res: Response) {
|
114
125
|
return new Promise<void>((resolve, reject) => {
|
@@ -139,7 +139,7 @@ const events: StormEvent[] = [
|
|
139
139
|
describe('event-parser', () => {
|
140
140
|
it('it can parse events into a plan and blocks with proper layout', () => {
|
141
141
|
const parser = new StormEventParser(parserOptions);
|
142
|
-
events.forEach((event) => parser.
|
142
|
+
events.forEach((event) => parser.processEvent('kapeta', event));
|
143
143
|
|
144
144
|
const result = parser.toResult('kapeta');
|
145
145
|
|