@kapeta/local-cluster-service 0.46.0 → 0.47.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 +16 -15
- package/dist/cjs/src/storm/event-parser.d.ts +4 -4
- package/dist/cjs/src/storm/event-parser.js +64 -18
- package/dist/cjs/src/storm/events.d.ts +9 -1
- package/dist/cjs/src/storm/events.js +0 -4
- package/dist/cjs/src/storm/routes.js +20 -7
- package/dist/cjs/src/storm/stormClient.js +3 -0
- package/dist/cjs/src/storm/stream.d.ts +1 -0
- package/dist/cjs/test/storm/event-parser.test.d.ts +5 -0
- package/dist/cjs/test/storm/event-parser.test.js +161 -0
- package/dist/esm/src/storm/codegen.js +16 -15
- package/dist/esm/src/storm/event-parser.d.ts +4 -4
- package/dist/esm/src/storm/event-parser.js +64 -18
- package/dist/esm/src/storm/events.d.ts +9 -1
- package/dist/esm/src/storm/events.js +0 -4
- package/dist/esm/src/storm/routes.js +20 -7
- package/dist/esm/src/storm/stormClient.js +3 -0
- package/dist/esm/src/storm/stream.d.ts +1 -0
- package/dist/esm/test/storm/event-parser.test.d.ts +5 -0
- package/dist/esm/test/storm/event-parser.test.js +161 -0
- package/package.json +3 -1
- package/src/storm/codegen.ts +23 -14
- package/src/storm/event-parser.ts +107 -28
- package/src/storm/events.ts +11 -1
- package/src/storm/routes.ts +33 -19
- package/src/storm/stormClient.ts +4 -0
- package/src/storm/stream.ts +1 -0
- package/test/storm/event-parser.test.ts +182 -0
package/src/storm/codegen.ts
CHANGED
@@ -8,7 +8,7 @@ import { AIFileTypes, BlockCodeGenerator, GeneratedFile, GeneratedResult } from
|
|
8
8
|
import { BlockDefinition } from '@kapeta/schemas';
|
9
9
|
import { codeGeneratorManager } from '../codeGeneratorManager';
|
10
10
|
import { STORM_ID, stormClient } from './stormClient';
|
11
|
-
import {
|
11
|
+
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';
|
@@ -42,16 +42,16 @@ export class StormCodegen {
|
|
42
42
|
return this.out;
|
43
43
|
}
|
44
44
|
|
45
|
-
private handleTemplateFileOutput(blockUri: KapetaURI, template: StormFileInfo, data: StormEvent) {
|
45
|
+
private handleTemplateFileOutput(blockUri: KapetaURI, aiName: string, template: StormFileInfo, data: StormEvent) {
|
46
46
|
switch (data.type) {
|
47
47
|
case 'FILE':
|
48
48
|
template.filename = data.payload.filename;
|
49
49
|
template.content = data.payload.content;
|
50
|
-
return this.handleFileOutput(blockUri, data);
|
50
|
+
return this.handleFileOutput(blockUri, aiName, data);
|
51
51
|
}
|
52
52
|
}
|
53
53
|
|
54
|
-
private handleUiOutput(blockUri: KapetaURI, data: StormEvent) {
|
54
|
+
private handleUiOutput(blockUri: KapetaURI, aiName: string, data: StormEvent) {
|
55
55
|
switch (data.type) {
|
56
56
|
case 'SCREEN':
|
57
57
|
this.out.emit('data', {
|
@@ -60,18 +60,18 @@ export class StormCodegen {
|
|
60
60
|
created: Date.now(),
|
61
61
|
payload: {
|
62
62
|
...data.payload,
|
63
|
-
blockName:
|
63
|
+
blockName: aiName,
|
64
64
|
},
|
65
65
|
});
|
66
66
|
case 'FILE':
|
67
|
-
return this.handleFileOutput(blockUri, data);
|
67
|
+
return this.handleFileOutput(blockUri, aiName, data);
|
68
68
|
}
|
69
69
|
}
|
70
70
|
|
71
|
-
private handleFileOutput(blockUri: KapetaURI, data: StormEvent) {
|
71
|
+
private handleFileOutput(blockUri: KapetaURI, aiName: string, data: StormEvent) {
|
72
72
|
switch (data.type) {
|
73
73
|
case 'FILE':
|
74
|
-
this.emitFile(blockUri, data.payload.filename, data.payload.content, data.reason);
|
74
|
+
this.emitFile(blockUri, aiName, data.payload.filename, data.payload.content, data.reason);
|
75
75
|
return {
|
76
76
|
type: 'FILE',
|
77
77
|
created: Date.now(),
|
@@ -96,7 +96,7 @@ export class StormCodegen {
|
|
96
96
|
const allFiles = this.toStormFiles(generatedResult);
|
97
97
|
|
98
98
|
// Send all the non-ai files to the stream
|
99
|
-
this.emitFiles(block.uri, allFiles);
|
99
|
+
this.emitFiles(block.uri, block.aiName, allFiles);
|
100
100
|
|
101
101
|
const relevantFiles: StormFileInfo[] = allFiles.filter(
|
102
102
|
(file) => file.type !== AIFileTypes.IGNORE && file.type !== AIFileTypes.WEB_SCREEN
|
@@ -112,7 +112,7 @@ export class StormCodegen {
|
|
112
112
|
});
|
113
113
|
|
114
114
|
uiStream.on('data', (evt) => {
|
115
|
-
this.handleUiOutput(block.uri, evt);
|
115
|
+
this.handleUiOutput(block.uri, block.aiName, evt);
|
116
116
|
});
|
117
117
|
|
118
118
|
await uiStream.waitForDone();
|
@@ -128,6 +128,7 @@ export class StormCodegen {
|
|
128
128
|
if (serviceFiles.length > 0) {
|
129
129
|
await this.processTemplates(
|
130
130
|
block.uri,
|
131
|
+
block.aiName,
|
131
132
|
stormClient.createServiceImplementation.bind(stormClient),
|
132
133
|
serviceFiles,
|
133
134
|
contextFiles
|
@@ -138,7 +139,7 @@ export class StormCodegen {
|
|
138
139
|
/**
|
139
140
|
* Emits the text-based files to the stream
|
140
141
|
*/
|
141
|
-
private emitFiles(uri: KapetaURI, files: StormFileInfo[]) {
|
142
|
+
private emitFiles(uri: KapetaURI, aiName: string, files: StormFileInfo[]) {
|
142
143
|
files.forEach((file) => {
|
143
144
|
if (!file.content || typeof file.content !== 'string') {
|
144
145
|
return;
|
@@ -156,11 +157,17 @@ export class StormCodegen {
|
|
156
157
|
return;
|
157
158
|
}
|
158
159
|
|
159
|
-
this.emitFile(uri, file.filename, file.content);
|
160
|
+
this.emitFile(uri, aiName, file.filename, file.content);
|
160
161
|
});
|
161
162
|
}
|
162
163
|
|
163
|
-
private emitFile(
|
164
|
+
private emitFile(
|
165
|
+
uri: KapetaURI,
|
166
|
+
blockName: string,
|
167
|
+
filename: string,
|
168
|
+
content: string,
|
169
|
+
reason: string = 'File generated'
|
170
|
+
) {
|
164
171
|
this.out.emit('data', {
|
165
172
|
type: 'FILE',
|
166
173
|
reason,
|
@@ -168,6 +175,7 @@ export class StormCodegen {
|
|
168
175
|
payload: {
|
169
176
|
filename: filename,
|
170
177
|
content: content,
|
178
|
+
blockName,
|
171
179
|
blockRef: uri.toNormalizedString(),
|
172
180
|
},
|
173
181
|
} satisfies StormEventFile);
|
@@ -178,6 +186,7 @@ export class StormCodegen {
|
|
178
186
|
*/
|
179
187
|
private async processTemplates(
|
180
188
|
blockUri: KapetaURI,
|
189
|
+
aiName: string,
|
181
190
|
generator: ImplementationGenerator,
|
182
191
|
templates: StormFileInfo[],
|
183
192
|
contextFiles: StormFileInfo[]
|
@@ -192,7 +201,7 @@ export class StormCodegen {
|
|
192
201
|
const files: StormEventFile[] = [];
|
193
202
|
|
194
203
|
stream.on('data', (evt) => {
|
195
|
-
const file = this.handleTemplateFileOutput(blockUri, templateFile, evt);
|
204
|
+
const file = this.handleTemplateFileOutput(blockUri, aiName, templateFile, evt);
|
196
205
|
if (file) {
|
197
206
|
files.push(file);
|
198
207
|
}
|
@@ -3,14 +3,7 @@
|
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
5
|
|
6
|
-
import {
|
7
|
-
ScreenTemplate,
|
8
|
-
StormBlockInfoFilled,
|
9
|
-
StormBlockType,
|
10
|
-
StormConnection,
|
11
|
-
StormEvent,
|
12
|
-
StormResourceType,
|
13
|
-
} from './events';
|
6
|
+
import { StormBlockInfoFilled, StormBlockType, StormConnection, StormEvent, StormResourceType } from './events';
|
14
7
|
import {
|
15
8
|
BlockDefinition,
|
16
9
|
BlockInstance,
|
@@ -21,8 +14,17 @@ import {
|
|
21
14
|
SourceCode,
|
22
15
|
} from '@kapeta/schemas';
|
23
16
|
import { KapetaURI, normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
24
|
-
import {
|
25
|
-
|
17
|
+
import {
|
18
|
+
DSLAPIParser,
|
19
|
+
DSLController,
|
20
|
+
DSLConverters,
|
21
|
+
DSLDataTypeParser,
|
22
|
+
DSLMethod,
|
23
|
+
KAPLANG_ID,
|
24
|
+
KAPLANG_VERSION,
|
25
|
+
KaplangWriter,
|
26
|
+
} from '@kapeta/kaplang-core';
|
27
|
+
import { v5 as uuid } from 'uuid';
|
26
28
|
import { definitionsManager } from '../definitionsManager';
|
27
29
|
|
28
30
|
export interface BlockDefinitionInfo {
|
@@ -31,7 +33,7 @@ export interface BlockDefinitionInfo {
|
|
31
33
|
aiName: string;
|
32
34
|
}
|
33
35
|
|
34
|
-
export interface
|
36
|
+
export interface StormDefinitions {
|
35
37
|
plan: Plan;
|
36
38
|
blocks: BlockDefinitionInfo[];
|
37
39
|
}
|
@@ -196,8 +198,9 @@ export class StormEventParser {
|
|
196
198
|
this.connections = [];
|
197
199
|
}
|
198
200
|
|
199
|
-
public addEvent(evt: StormEvent):
|
201
|
+
public addEvent(handle: string, evt: StormEvent): StormDefinitions {
|
200
202
|
this.events.push(evt);
|
203
|
+
console.log('evt', evt);
|
201
204
|
switch (evt.type) {
|
202
205
|
case 'CREATE_PLAN_PROPERTIES':
|
203
206
|
this.planName = evt.payload.name;
|
@@ -236,6 +239,8 @@ export class StormEventParser {
|
|
236
239
|
case 'FILE':
|
237
240
|
break;
|
238
241
|
}
|
242
|
+
|
243
|
+
return this.toResult(handle);
|
239
244
|
}
|
240
245
|
|
241
246
|
public getEvents(): StormEvent[] {
|
@@ -243,6 +248,9 @@ export class StormEventParser {
|
|
243
248
|
}
|
244
249
|
|
245
250
|
public isValid(): boolean {
|
251
|
+
if (!this.planName) {
|
252
|
+
return false;
|
253
|
+
}
|
246
254
|
return !this.failed;
|
247
255
|
}
|
248
256
|
|
@@ -250,17 +258,13 @@ export class StormEventParser {
|
|
250
258
|
return this.error;
|
251
259
|
}
|
252
260
|
|
253
|
-
|
254
|
-
|
255
|
-
}
|
256
|
-
|
257
|
-
public toResult(handle: string): ParsedResult {
|
258
|
-
const planRef = this.toRef(handle, this.planName);
|
261
|
+
public toResult(handle: string): StormDefinitions {
|
262
|
+
const planRef = this.toRef(handle, this.planName ?? 'undefined');
|
259
263
|
const blockDefinitions = this.toBlockDefinitions(handle);
|
260
264
|
const refIdMap: { [key: string]: string } = {};
|
261
|
-
const screens: { [key: string]: ScreenTemplate[] } = {};
|
262
265
|
const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
|
263
|
-
|
266
|
+
// Create a deterministic uuid
|
267
|
+
const id = uuid(ref, uuid.URL);
|
264
268
|
refIdMap[ref] = id;
|
265
269
|
return {
|
266
270
|
id,
|
@@ -271,7 +275,7 @@ export class StormEventParser {
|
|
271
275
|
dimensions: {
|
272
276
|
left: 0,
|
273
277
|
top: 0,
|
274
|
-
width:
|
278
|
+
width: 150,
|
275
279
|
height: 200,
|
276
280
|
},
|
277
281
|
} satisfies BlockInstance;
|
@@ -327,13 +331,41 @@ export class StormEventParser {
|
|
327
331
|
return;
|
328
332
|
}
|
329
333
|
|
334
|
+
if (apiProviderBlock.content.spec.entities?.source?.value) {
|
335
|
+
if (!clientConsumerBlock.content.spec.entities) {
|
336
|
+
clientConsumerBlock.content.spec.entities = {
|
337
|
+
types: [],
|
338
|
+
source: {
|
339
|
+
type: KAPLANG_ID,
|
340
|
+
version: KAPLANG_VERSION,
|
341
|
+
value: '',
|
342
|
+
},
|
343
|
+
};
|
344
|
+
}
|
345
|
+
|
346
|
+
const clientTypes = DSLDataTypeParser.parse(
|
347
|
+
clientConsumerBlock.content.spec.entities.source!.value
|
348
|
+
);
|
349
|
+
const apiTypes = DSLDataTypeParser.parse(apiProviderBlock.content.spec.entities?.source?.value);
|
350
|
+
|
351
|
+
apiTypes.forEach((apiType) => {
|
352
|
+
if (clientTypes.some((clientType) => clientType.name === apiType.name)) {
|
353
|
+
// Already exists
|
354
|
+
return;
|
355
|
+
}
|
356
|
+
clientTypes.push(apiType);
|
357
|
+
});
|
358
|
+
|
359
|
+
clientConsumerBlock.content.spec.entities.source!.value = KaplangWriter.write(clientTypes);
|
360
|
+
}
|
330
361
|
clientResource.spec.methods = apiResource.spec.methods;
|
331
362
|
clientResource.spec.source = apiResource.spec.source;
|
332
363
|
});
|
333
364
|
|
334
|
-
const connections = this.connections.map((connection) => {
|
365
|
+
const connections: Connection[] = this.connections.map((connection) => {
|
335
366
|
const fromRef = this.toRef(handle, connection.fromComponent);
|
336
367
|
const toRef = this.toRef(handle, connection.toComponent);
|
368
|
+
|
337
369
|
return {
|
338
370
|
port: {
|
339
371
|
type: this.toPortType(connection.fromResourceType),
|
@@ -346,9 +378,7 @@ export class StormEventParser {
|
|
346
378
|
blockId: refIdMap[fromRef.toNormalizedString()],
|
347
379
|
resourceName: connection.fromResource,
|
348
380
|
},
|
349
|
-
mapping:
|
350
|
-
//TODO: Add mapping
|
351
|
-
},
|
381
|
+
mapping: this.toConnectionMapping(handle, connection, blockDefinitions),
|
352
382
|
} satisfies Connection;
|
353
383
|
});
|
354
384
|
|
@@ -365,10 +395,10 @@ export class StormEventParser {
|
|
365
395
|
},
|
366
396
|
};
|
367
397
|
|
368
|
-
return
|
398
|
+
return {
|
369
399
|
plan,
|
370
400
|
blocks: Object.values(blockDefinitions),
|
371
|
-
}
|
401
|
+
};
|
372
402
|
}
|
373
403
|
|
374
404
|
private toSafeName(name: string): string {
|
@@ -504,7 +534,7 @@ export class StormEventParser {
|
|
504
534
|
} satisfies SourceCode,
|
505
535
|
},
|
506
536
|
};
|
507
|
-
blockSpec.
|
537
|
+
blockSpec.consumers!.push(dbResource);
|
508
538
|
break;
|
509
539
|
case 'JWTCONSUMER':
|
510
540
|
case 'WEBFRAGMENT':
|
@@ -596,6 +626,55 @@ export class StormEventParser {
|
|
596
626
|
return '';
|
597
627
|
}
|
598
628
|
|
629
|
+
private toConnectionMapping(
|
630
|
+
handle: string,
|
631
|
+
connection: StormConnection,
|
632
|
+
blockDefinitions: { [key: string]: BlockDefinitionInfo }
|
633
|
+
): any {
|
634
|
+
if (connection.fromResourceType !== 'API') {
|
635
|
+
return;
|
636
|
+
}
|
637
|
+
|
638
|
+
const fromRef = this.toRef(handle, connection.fromComponent);
|
639
|
+
|
640
|
+
const apiProviderBlock = blockDefinitions[fromRef.toNormalizedString()];
|
641
|
+
if (!apiProviderBlock) {
|
642
|
+
console.warn('Provider block not found: %s', connection.fromComponent, connection);
|
643
|
+
return;
|
644
|
+
}
|
645
|
+
|
646
|
+
const apiResource = apiProviderBlock.content.spec.providers?.find(
|
647
|
+
(p) => p.kind === this.options.apiKind && p.metadata.name === connection.fromResource
|
648
|
+
);
|
649
|
+
|
650
|
+
if (!apiResource) {
|
651
|
+
console.warn(
|
652
|
+
'API resource not found: %s on %s',
|
653
|
+
connection.fromResource,
|
654
|
+
fromRef.toNormalizedString(),
|
655
|
+
connection
|
656
|
+
);
|
657
|
+
return;
|
658
|
+
}
|
659
|
+
|
660
|
+
const apiMethods = DSLConverters.toSchemaMethods(
|
661
|
+
DSLAPIParser.parse(apiResource.spec?.source?.value ?? '', {
|
662
|
+
ignoreSemantics: true,
|
663
|
+
}) as (DSLMethod | DSLController)[]
|
664
|
+
);
|
665
|
+
|
666
|
+
const mapping: any = {};
|
667
|
+
|
668
|
+
Object.entries(apiMethods).forEach(([methodId, method]) => {
|
669
|
+
mapping[methodId] = {
|
670
|
+
targetId: methodId,
|
671
|
+
type: 'EXACT',
|
672
|
+
};
|
673
|
+
});
|
674
|
+
|
675
|
+
return mapping;
|
676
|
+
}
|
677
|
+
|
599
678
|
private toPortType(type: StormResourceType) {
|
600
679
|
switch (type) {
|
601
680
|
case 'API':
|
package/src/storm/events.ts
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
+
import { StormDefinitions } from './event-parser';
|
5
6
|
|
6
7
|
export type StormResourceType =
|
7
8
|
| 'API'
|
@@ -145,6 +146,7 @@ export interface StormEventFile {
|
|
145
146
|
payload: {
|
146
147
|
filename: string;
|
147
148
|
content: string;
|
149
|
+
blockName: string;
|
148
150
|
blockRef: string;
|
149
151
|
};
|
150
152
|
}
|
@@ -154,6 +156,13 @@ export interface StormEventDone {
|
|
154
156
|
created: number;
|
155
157
|
}
|
156
158
|
|
159
|
+
export interface StormEventDefinitionChange {
|
160
|
+
type: 'DEFINITION_CHANGE';
|
161
|
+
reason: string;
|
162
|
+
created: number;
|
163
|
+
payload: StormDefinitions;
|
164
|
+
}
|
165
|
+
|
157
166
|
export type StormEvent =
|
158
167
|
| StormEventCreateBlock
|
159
168
|
| StormEventCreateConnection
|
@@ -165,4 +174,5 @@ export type StormEvent =
|
|
165
174
|
| StormEventScreen
|
166
175
|
| StormEventScreenCandidate
|
167
176
|
| StormEventFile
|
168
|
-
| StormEventDone
|
177
|
+
| StormEventDone
|
178
|
+
| StormEventDefinitionChange;
|
package/src/storm/routes.ts
CHANGED
@@ -11,7 +11,7 @@ import { KapetaBodyRequest } from '../types';
|
|
11
11
|
import { StormContextRequest, StormFileImplementationPrompt, StormFileInfo, StormStream } from './stream';
|
12
12
|
import { stormClient } from './stormClient';
|
13
13
|
import { StormEvent } from './events';
|
14
|
-
import { resolveOptions, StormEventParser } from './event-parser';
|
14
|
+
import { resolveOptions, StormDefinitions, StormEventParser } from './event-parser';
|
15
15
|
import { StormCodegen } from './codegen';
|
16
16
|
|
17
17
|
const router = Router();
|
@@ -33,24 +33,29 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
|
|
33
33
|
res.set('Content-Type', 'application/x-ndjson');
|
34
34
|
|
35
35
|
metaStream.on('data', (data: StormEvent) => {
|
36
|
-
eventParser.addEvent(data);
|
36
|
+
const result = eventParser.addEvent(req.params.handle, data);
|
37
|
+
|
38
|
+
sendDefinitions(res, result);
|
37
39
|
});
|
38
40
|
|
39
41
|
await streamStormPartialResponse(metaStream, res);
|
40
42
|
|
41
43
|
if (!eventParser.isValid()) {
|
42
44
|
// We can't continue if the meta stream is invalid
|
43
|
-
res
|
45
|
+
sendEvent(res, {
|
44
46
|
type: 'ERROR_INTERNAL',
|
45
47
|
payload: { error: eventParser.getError() },
|
46
48
|
reason: 'Failed to generate system',
|
47
49
|
created: Date.now(),
|
48
|
-
}
|
50
|
+
});
|
49
51
|
res.end();
|
50
52
|
return;
|
51
53
|
}
|
54
|
+
|
52
55
|
const result = eventParser.toResult(handle);
|
53
56
|
|
57
|
+
sendDefinitions(res, result);
|
58
|
+
|
54
59
|
const stormCodegen = new StormCodegen(aiRequest.prompt, result.blocks, eventParser.getEvents());
|
55
60
|
|
56
61
|
const codegenStream = streamStormPartialResponse(stormCodegen.getStream(), res);
|
@@ -65,13 +70,20 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
|
|
65
70
|
}
|
66
71
|
});
|
67
72
|
|
73
|
+
function sendDefinitions(res: Response, result: StormDefinitions) {
|
74
|
+
sendEvent(res, {
|
75
|
+
type: 'DEFINITION_CHANGE',
|
76
|
+
payload: result,
|
77
|
+
reason: 'Updates to definition',
|
78
|
+
created: Date.now(),
|
79
|
+
});
|
80
|
+
}
|
81
|
+
|
68
82
|
function sendDone(res: Response) {
|
69
|
-
res
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
} satisfies StormEvent) + '\n'
|
74
|
-
);
|
83
|
+
sendEvent(res, {
|
84
|
+
type: 'DONE',
|
85
|
+
created: Date.now(),
|
86
|
+
});
|
75
87
|
|
76
88
|
res.end();
|
77
89
|
}
|
@@ -79,14 +91,12 @@ function sendDone(res: Response) {
|
|
79
91
|
function sendError(err: Error, res: Response) {
|
80
92
|
console.error('Failed to send prompt', err);
|
81
93
|
if (res.headersSent) {
|
82
|
-
res
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
} satisfies StormEvent) + '\n'
|
89
|
-
);
|
94
|
+
sendEvent(res, {
|
95
|
+
type: 'ERROR_INTERNAL',
|
96
|
+
created: Date.now(),
|
97
|
+
payload: { error: err.message },
|
98
|
+
reason: 'Failed while sending prompt',
|
99
|
+
});
|
90
100
|
} else {
|
91
101
|
res.status(400).send({ error: err.message });
|
92
102
|
}
|
@@ -95,7 +105,7 @@ function sendError(err: Error, res: Response) {
|
|
95
105
|
function streamStormPartialResponse(result: StormStream, res: Response) {
|
96
106
|
return new Promise<void>((resolve, reject) => {
|
97
107
|
result.on('data', (data) => {
|
98
|
-
res
|
108
|
+
sendEvent(res, data);
|
99
109
|
});
|
100
110
|
|
101
111
|
result.on('error', (err) => {
|
@@ -108,4 +118,8 @@ function streamStormPartialResponse(result: StormStream, res: Response) {
|
|
108
118
|
});
|
109
119
|
}
|
110
120
|
|
121
|
+
function sendEvent(res: Response, evt: StormEvent) {
|
122
|
+
res.write(JSON.stringify(evt) + '\n');
|
123
|
+
}
|
124
|
+
|
111
125
|
export default router;
|
package/src/storm/stormClient.ts
CHANGED