@kapeta/local-cluster-service 0.55.2 → 0.56.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 +20 -0
- package/definitions.d.ts +1 -1
- package/dist/cjs/src/storm/codegen.d.ts +2 -1
- package/dist/cjs/src/storm/codegen.js +4 -1
- package/dist/cjs/src/storm/event-parser.d.ts +2 -1
- package/dist/cjs/src/storm/event-parser.js +37 -9
- package/dist/cjs/src/storm/routes.js +26 -7
- package/dist/cjs/src/storm/stream.d.ts +7 -0
- package/dist/cjs/test/storm/blog-events.json +860 -0
- package/dist/cjs/test/storm/codegen.test.js +2 -2
- package/dist/cjs/test/storm/event-parser.test.js +25 -7
- package/dist/cjs/test/storm/predefined-user-events.json +9 -0
- package/dist/esm/src/storm/codegen.d.ts +2 -1
- package/dist/esm/src/storm/codegen.js +4 -1
- package/dist/esm/src/storm/event-parser.d.ts +2 -1
- package/dist/esm/src/storm/event-parser.js +37 -9
- package/dist/esm/src/storm/routes.js +26 -7
- package/dist/esm/src/storm/stream.d.ts +7 -0
- package/dist/esm/test/storm/blog-events.json +860 -0
- package/dist/esm/test/storm/codegen.test.js +2 -2
- package/dist/esm/test/storm/event-parser.test.js +25 -7
- package/dist/esm/test/storm/predefined-user-events.json +9 -0
- package/package.json +1 -1
- package/src/storm/codegen.ts +7 -2
- package/src/storm/event-parser.ts +60 -25
- package/src/storm/routes.ts +32 -9
- package/src/storm/stream.ts +8 -0
- package/test/storm/blog-events.json +860 -0
- package/test/storm/codegen.test.ts +4 -2
- package/test/storm/event-parser.test.ts +27 -7
- package/test/storm/predefined-user-events.json +9 -0
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
# [0.56.0](https://github.com/kapetacom/local-cluster-service/compare/v0.55.3...v0.56.0) (2024-07-15)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* better abort handling on reqs ([412c2f9](https://github.com/kapetacom/local-cluster-service/commit/412c2f9ab7e49f5e5d35d6b1ad4d683dafb170b8))
|
7
|
+
* guard against \\n in output ([5ae55ed](https://github.com/kapetacom/local-cluster-service/commit/5ae55ed2d6cacedb6867256a4245b413b14e061c))
|
8
|
+
|
9
|
+
|
10
|
+
### Features
|
11
|
+
|
12
|
+
* add /block/codegen endpoint to (re)run codegen on a block ([0fec94b](https://github.com/kapetacom/local-cluster-service/commit/0fec94b563fd1bbc340c478b937f2848c9a4e644))
|
13
|
+
|
14
|
+
## [0.55.3](https://github.com/kapetacom/local-cluster-service/compare/v0.55.2...v0.55.3) (2024-07-12)
|
15
|
+
|
16
|
+
|
17
|
+
### Bug Fixes
|
18
|
+
|
19
|
+
* ensure versions on predefined blocks are the latest ([0a841bc](https://github.com/kapetacom/local-cluster-service/commit/0a841bc221cfac09cac41992a06385be1da24197))
|
20
|
+
|
1
21
|
## [0.55.2](https://github.com/kapetacom/local-cluster-service/compare/v0.55.1...v0.55.2) (2024-07-12)
|
2
22
|
|
3
23
|
|
package/definitions.d.ts
CHANGED
@@ -10,9 +10,10 @@ export declare class StormCodegen {
|
|
10
10
|
private readonly blocks;
|
11
11
|
private readonly out;
|
12
12
|
private readonly events;
|
13
|
-
private
|
13
|
+
private tmpDir;
|
14
14
|
private readonly conversationId;
|
15
15
|
constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]);
|
16
|
+
setTmpDir(tmpDir: string): void;
|
16
17
|
process(): Promise<void>;
|
17
18
|
isAborted(): boolean;
|
18
19
|
getStream(): StormStream;
|
@@ -107,6 +107,9 @@ class StormCodegen {
|
|
107
107
|
this.tmpDir = path_2.default.join(node_os_1.default.tmpdir(), conversationId);
|
108
108
|
this.conversationId = conversationId;
|
109
109
|
}
|
110
|
+
setTmpDir(tmpDir) {
|
111
|
+
this.tmpDir = tmpDir;
|
112
|
+
}
|
110
113
|
async process() {
|
111
114
|
const promises = this.blocks.map((block) => {
|
112
115
|
if (block.archetype) {
|
@@ -241,7 +244,7 @@ class StormCodegen {
|
|
241
244
|
payload: {
|
242
245
|
filename: data.payload.filename,
|
243
246
|
path: (0, path_2.join)(this.getBasePath(blockUri.fullName), data.payload.filename),
|
244
|
-
content: data.payload.content,
|
247
|
+
content: data.payload.content.replace(/\\n/g, '\n'),
|
245
248
|
blockRef: ref,
|
246
249
|
instanceId: event_parser_1.StormEventParser.toInstanceIdFromRef(ref),
|
247
250
|
},
|
@@ -39,6 +39,7 @@ export interface StormOptions {
|
|
39
39
|
desktopKind: string;
|
40
40
|
desktopLanguage: string;
|
41
41
|
gatewayKind: string;
|
42
|
+
[key: string]: string;
|
42
43
|
}
|
43
44
|
export declare function createPhaseStartEvent(type: StormEventPhaseType): StormEventPhases;
|
44
45
|
export declare function createPhaseEndEvent(type: StormEventPhaseType): StormEventPhases;
|
@@ -66,7 +67,7 @@ export declare class StormEventParser {
|
|
66
67
|
getEvents(): StormEvent[];
|
67
68
|
isValid(): boolean;
|
68
69
|
getError(): string;
|
69
|
-
toResult(handle: string): Promise<StormDefinitions>;
|
70
|
+
toResult(handle: string, warn?: boolean): Promise<StormDefinitions>;
|
70
71
|
toBlockDefinitions(handle: string): Promise<{
|
71
72
|
[key: string]: BlockDefinitionInfo;
|
72
73
|
}>;
|
@@ -244,7 +244,7 @@ class StormEventParser {
|
|
244
244
|
getError() {
|
245
245
|
return this.error;
|
246
246
|
}
|
247
|
-
async toResult(handle) {
|
247
|
+
async toResult(handle, warn = false) {
|
248
248
|
const planRef = StormEventParser.toRef(handle, this.planName || 'undefined');
|
249
249
|
const blockDefinitions = await this.toBlockDefinitions(handle);
|
250
250
|
const refIdMap = {};
|
@@ -284,7 +284,9 @@ class StormEventParser {
|
|
284
284
|
}
|
285
285
|
const apiResource = apiProviderBlock.content.spec.providers?.find((p) => p.kind === this.options.apiKind && p.metadata.name === apiConnection.fromResource);
|
286
286
|
if (!apiResource) {
|
287
|
-
|
287
|
+
if (warn) {
|
288
|
+
console.warn('API resource not found: %s on %s', apiConnection.fromResource, apiProviderRef.toNormalizedString(), apiConnection);
|
289
|
+
}
|
288
290
|
return;
|
289
291
|
}
|
290
292
|
const clientResource = clientConsumerBlock.content.spec.consumers?.find((clientResource) => {
|
@@ -295,7 +297,9 @@ class StormEventParser {
|
|
295
297
|
return clientResource.metadata.name === apiConnection.toResource;
|
296
298
|
});
|
297
299
|
if (!clientResource) {
|
298
|
-
|
300
|
+
if (warn) {
|
301
|
+
console.warn('Client resource not found: %s on %s', apiConnection.toResource, clientConsumerRef.toNormalizedString(), apiConnection);
|
302
|
+
}
|
299
303
|
return;
|
300
304
|
}
|
301
305
|
if (apiProviderBlock.content.spec.entities?.source?.value) {
|
@@ -340,7 +344,7 @@ class StormEventParser {
|
|
340
344
|
blockId: refIdMap[fromRef.toNormalizedString()],
|
341
345
|
resourceName: connection.fromResource,
|
342
346
|
},
|
343
|
-
mapping: this.toConnectionMapping(handle, connection, blockDefinitions),
|
347
|
+
mapping: this.toConnectionMapping(handle, connection, blockDefinitions, warn),
|
344
348
|
};
|
345
349
|
});
|
346
350
|
const plan = {
|
@@ -368,7 +372,7 @@ class StormEventParser {
|
|
368
372
|
const blockRef = StormEventParser.toRef(handle, blockInfo.name);
|
369
373
|
let blockDefinitionInfo;
|
370
374
|
if (blockInfo.archetype) {
|
371
|
-
blockDefinitionInfo = await this.resolveArchetypeBlockDefinition(blockRef, blockInfo);
|
375
|
+
blockDefinitionInfo = await this.resolveArchetypeBlockDefinition(blockRef, blockInfo, handle);
|
372
376
|
}
|
373
377
|
else {
|
374
378
|
blockDefinitionInfo = this.createBlockDefinitionInfo(blockRef, blockInfo, handle);
|
@@ -533,7 +537,7 @@ class StormEventParser {
|
|
533
537
|
firstEntry.spec.source.value += api + '\n\n';
|
534
538
|
}
|
535
539
|
else {
|
536
|
-
|
540
|
+
// this might be ok as we might receive api and types before resources from the ai-service
|
537
541
|
}
|
538
542
|
}
|
539
543
|
});
|
@@ -596,7 +600,7 @@ class StormEventParser {
|
|
596
600
|
}
|
597
601
|
return '';
|
598
602
|
}
|
599
|
-
toConnectionMapping(handle, connection, blockDefinitions) {
|
603
|
+
toConnectionMapping(handle, connection, blockDefinitions, warn) {
|
600
604
|
if (connection.fromResourceType !== 'API') {
|
601
605
|
return;
|
602
606
|
}
|
@@ -608,7 +612,9 @@ class StormEventParser {
|
|
608
612
|
}
|
609
613
|
const apiResource = apiProviderBlock.content.spec.providers?.find((p) => p.kind === this.options.apiKind && p.metadata.name === connection.fromResource);
|
610
614
|
if (!apiResource) {
|
611
|
-
|
615
|
+
if (warn) {
|
616
|
+
console.warn('API resource not found: %s on %s', connection.fromResource, fromRef.toNormalizedString(), connection);
|
617
|
+
}
|
612
618
|
return;
|
613
619
|
}
|
614
620
|
const apiMethods = kaplang_core_1.DSLConverters.toSchemaMethods(kaplang_core_1.DSLAPIParser.parse(apiResource.spec?.source?.value ?? '', {
|
@@ -681,14 +687,36 @@ class StormEventParser {
|
|
681
687
|
}
|
682
688
|
return undefined;
|
683
689
|
}
|
684
|
-
async resolveArchetypeBlockDefinition(blockRef, blockInfo) {
|
690
|
+
async resolveArchetypeBlockDefinition(blockRef, blockInfo, handle) {
|
685
691
|
const predefinedBlock = predefined_1.PREDEFINED_BLOCKS.get(blockInfo.archetype);
|
686
692
|
if (!predefinedBlock) {
|
687
693
|
throw new Error('Predefined block not found for archetype [' + blockInfo.archetype + ']');
|
688
694
|
}
|
695
|
+
const target = this.toBlockTarget(handle, blockInfo.type);
|
689
696
|
const blockDefinition = await predefinedBlock.getBlockDefinition();
|
690
697
|
lodash_1.default.set(blockDefinition, ['metadata', 'name'], blockRef.fullName);
|
691
698
|
lodash_1.default.set(blockDefinition, ['metadata', 'title'], blockRef.name);
|
699
|
+
lodash_1.default.set(blockDefinition, ['spec', 'target', 'options'], target?.options);
|
700
|
+
const options = this.options;
|
701
|
+
function getKind(kind) {
|
702
|
+
for (const prop in options) {
|
703
|
+
if (options.hasOwnProperty(prop)) {
|
704
|
+
const value = options[prop];
|
705
|
+
if (value.startsWith(kind)) {
|
706
|
+
return value;
|
707
|
+
}
|
708
|
+
}
|
709
|
+
}
|
710
|
+
}
|
711
|
+
blockDefinition.kind = getKind((0, nodejs_utils_1.parseKapetaUri)(blockDefinition.kind).fullName) ?? blockDefinition.kind;
|
712
|
+
for (const provider of blockDefinition.spec.providers ?? []) {
|
713
|
+
provider.kind = getKind((0, nodejs_utils_1.parseKapetaUri)(provider.kind).fullName) ?? provider.kind;
|
714
|
+
}
|
715
|
+
for (const consumer of blockDefinition.spec.consumers ?? []) {
|
716
|
+
consumer.kind = getKind((0, nodejs_utils_1.parseKapetaUri)(consumer.kind).fullName) ?? consumer.kind;
|
717
|
+
}
|
718
|
+
blockDefinition.spec.target.kind =
|
719
|
+
getKind((0, nodejs_utils_1.parseKapetaUri)(blockDefinition.spec.target.kind).fullName) ?? blockDefinition.spec.target.kind;
|
692
720
|
return {
|
693
721
|
uri: blockRef.toNormalizedString(),
|
694
722
|
aiName: blockInfo.name,
|
@@ -9,6 +9,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
10
10
|
const express_promise_router_1 = __importDefault(require("express-promise-router"));
|
11
11
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
12
|
+
const path_1 = __importDefault(require("path"));
|
13
|
+
const lodash_1 = __importDefault(require("lodash"));
|
12
14
|
const cors_1 = require("../middleware/cors");
|
13
15
|
const stringBody_1 = require("../middleware/stringBody");
|
14
16
|
const stormClient_1 = require("./stormClient");
|
@@ -16,8 +18,6 @@ const events_1 = require("./events");
|
|
16
18
|
const event_parser_1 = require("./event-parser");
|
17
19
|
const codegen_1 = require("./codegen");
|
18
20
|
const assetManager_1 = require("../assetManager");
|
19
|
-
const path_1 = __importDefault(require("path"));
|
20
|
-
const lodash_1 = __importDefault(require("lodash"));
|
21
21
|
const router = (0, express_promise_router_1.default)();
|
22
22
|
router.use('/', cors_1.corsHandler);
|
23
23
|
router.use('/', stringBody_1.stringBody);
|
@@ -82,10 +82,12 @@ router.post('/:handle/all', async (req, res) => {
|
|
82
82
|
res.end();
|
83
83
|
return;
|
84
84
|
}
|
85
|
-
const result = await eventParser.toResult(handle);
|
85
|
+
const result = await eventParser.toResult(handle, true);
|
86
86
|
if (metaStream.isAborted()) {
|
87
87
|
return;
|
88
88
|
}
|
89
|
+
// Cancel debounce, we don't need to send the plan again
|
90
|
+
sendUpdatedPlan.cancel();
|
89
91
|
sendDefinitions(res, result);
|
90
92
|
if (!req.query.skipCodegen) {
|
91
93
|
try {
|
@@ -134,6 +136,26 @@ router.post('/block/create', async (req, res) => {
|
|
134
136
|
res.status(500).send({ error: err.message });
|
135
137
|
}
|
136
138
|
});
|
139
|
+
router.post('/block/codegen', async (req, res) => {
|
140
|
+
const body = JSON.parse(req.stringBody ?? '{}');
|
141
|
+
console.log('Codegen request', body);
|
142
|
+
const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
|
143
|
+
try {
|
144
|
+
const stormCodegen = new codegen_1.StormCodegen(conversationId ?? '', body.prompt, [body.block], body.events || []);
|
145
|
+
stormCodegen.setTmpDir(body.outDir);
|
146
|
+
onRequestAborted(req, res, () => {
|
147
|
+
stormCodegen.abort();
|
148
|
+
});
|
149
|
+
const codegenPromise = streamStormPartialResponse(stormCodegen.getStream(), res);
|
150
|
+
await stormCodegen.process();
|
151
|
+
await codegenPromise;
|
152
|
+
sendDone(res);
|
153
|
+
}
|
154
|
+
catch (err) {
|
155
|
+
console.error('Failed to generate code', err);
|
156
|
+
res.status(500).send({ error: err.message });
|
157
|
+
}
|
158
|
+
});
|
137
159
|
function sendDefinitions(res, result) {
|
138
160
|
sendEvent(res, {
|
139
161
|
type: 'DEFINITION_CHANGE',
|
@@ -187,10 +209,7 @@ function sendEvent(res, evt) {
|
|
187
209
|
res.write(JSON.stringify(evt) + '\n');
|
188
210
|
}
|
189
211
|
function onRequestAborted(req, res, onAborted) {
|
190
|
-
req.on('close', () => {
|
191
|
-
onAborted();
|
192
|
-
});
|
193
|
-
res.on('close', () => {
|
212
|
+
req.socket.on('close', () => {
|
194
213
|
onAborted();
|
195
214
|
});
|
196
215
|
}
|
@@ -7,6 +7,7 @@ import { EventEmitter } from 'node:events';
|
|
7
7
|
import { StormEvent } from './events';
|
8
8
|
import { AIFileTypes, GeneratedFile } from '@kapeta/codegen';
|
9
9
|
import { BlockDefinition } from '@kapeta/schemas';
|
10
|
+
import { BlockDefinitionInfo } from './event-parser';
|
10
11
|
export declare class StormStream extends EventEmitter {
|
11
12
|
private conversationId;
|
12
13
|
private lines;
|
@@ -41,6 +42,12 @@ export interface StormCreateBlockRequest {
|
|
41
42
|
tmpPath: string;
|
42
43
|
newPath: string;
|
43
44
|
}
|
45
|
+
export interface StormCodegenRequest {
|
46
|
+
block: BlockDefinitionInfo;
|
47
|
+
prompt: string;
|
48
|
+
events: StormEvent[];
|
49
|
+
outDir: string;
|
50
|
+
}
|
44
51
|
export interface StormFileInfo extends GeneratedFile {
|
45
52
|
type: AIFileTypes;
|
46
53
|
}
|