@kapeta/local-cluster-service 0.49.0 → 0.51.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.d.ts +5 -2
- package/dist/cjs/src/storm/codegen.js +203 -27
- package/dist/cjs/src/storm/event-parser.d.ts +4 -1
- package/dist/cjs/src/storm/event-parser.js +21 -7
- package/dist/cjs/src/storm/events.d.ts +37 -9
- package/dist/cjs/src/storm/events.js +7 -0
- package/dist/cjs/src/storm/routes.js +66 -9
- package/dist/cjs/src/storm/stormClient.js +5 -1
- package/dist/cjs/src/storm/stream.d.ts +5 -0
- package/dist/cjs/src/storm/stream.js +11 -0
- package/dist/esm/src/storm/codegen.d.ts +5 -2
- package/dist/esm/src/storm/codegen.js +203 -27
- package/dist/esm/src/storm/event-parser.d.ts +4 -1
- package/dist/esm/src/storm/event-parser.js +21 -7
- package/dist/esm/src/storm/events.d.ts +37 -9
- package/dist/esm/src/storm/events.js +7 -0
- package/dist/esm/src/storm/routes.js +66 -9
- package/dist/esm/src/storm/stormClient.js +5 -1
- package/dist/esm/src/storm/stream.d.ts +5 -0
- package/dist/esm/src/storm/stream.js +11 -0
- package/package.json +1 -1
- package/src/storm/codegen.ts +231 -33
- package/src/storm/event-parser.ts +29 -8
- package/src/storm/events.ts +48 -10
- package/src/storm/routes.ts +87 -23
- package/src/storm/stormClient.ts +8 -1
- package/src/storm/stream.ts +15 -0
package/src/storm/routes.ts
CHANGED
@@ -11,12 +11,17 @@ import { stringBody } from '../middleware/stringBody';
|
|
11
11
|
import { KapetaBodyRequest } from '../types';
|
12
12
|
import { StormContextRequest, StormCreateBlockRequest, StormStream } from './stream';
|
13
13
|
import { ConversationIdHeader, stormClient } from './stormClient';
|
14
|
-
import { StormEvent } from './events';
|
15
|
-
import {
|
14
|
+
import { StormEvent, StormEventPhaseType } from './events';
|
15
|
+
import {
|
16
|
+
createPhaseEndEvent,
|
17
|
+
createPhaseStartEvent,
|
18
|
+
resolveOptions,
|
19
|
+
StormDefinitions,
|
20
|
+
StormEventParser,
|
21
|
+
} from './event-parser';
|
16
22
|
import { StormCodegen } from './codegen';
|
17
23
|
import { assetManager } from '../assetManager';
|
18
24
|
import Path from 'path';
|
19
|
-
import { normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
20
25
|
|
21
26
|
const router = Router();
|
22
27
|
|
@@ -36,18 +41,48 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
|
|
36
41
|
const aiRequest: StormContextRequest = JSON.parse(req.stringBody ?? '{}');
|
37
42
|
const metaStream = await stormClient.createMetadata(aiRequest.prompt, conversationId);
|
38
43
|
|
44
|
+
onRequestAborted(req, res, () => {
|
45
|
+
metaStream.abort();
|
46
|
+
});
|
47
|
+
|
39
48
|
res.set('Content-Type', 'application/x-ndjson');
|
40
49
|
res.set('Access-Control-Expose-Headers', ConversationIdHeader);
|
41
50
|
res.set(ConversationIdHeader, metaStream.getConversationId());
|
42
51
|
|
52
|
+
let currentPhase = StormEventPhaseType.META;
|
53
|
+
|
43
54
|
metaStream.on('data', (data: StormEvent) => {
|
44
55
|
const result = eventParser.processEvent(req.params.handle, data);
|
45
56
|
|
57
|
+
switch (data.type) {
|
58
|
+
case 'CREATE_API':
|
59
|
+
case 'CREATE_MODEL':
|
60
|
+
case 'CREATE_TYPE':
|
61
|
+
if (currentPhase !== StormEventPhaseType.DEFINITIONS) {
|
62
|
+
sendEvent(res, createPhaseEndEvent(StormEventPhaseType.META));
|
63
|
+
currentPhase = StormEventPhaseType.DEFINITIONS;
|
64
|
+
sendEvent(res, createPhaseStartEvent(StormEventPhaseType.DEFINITIONS));
|
65
|
+
}
|
66
|
+
break;
|
67
|
+
}
|
68
|
+
|
46
69
|
sendEvent(res, data);
|
47
70
|
sendDefinitions(res, result);
|
48
71
|
});
|
49
72
|
|
50
|
-
|
73
|
+
try {
|
74
|
+
sendEvent(res, createPhaseStartEvent(StormEventPhaseType.META));
|
75
|
+
|
76
|
+
await waitForStormStream(metaStream);
|
77
|
+
} finally {
|
78
|
+
if (!metaStream.isAborted()) {
|
79
|
+
sendEvent(res, createPhaseEndEvent(currentPhase));
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
if (metaStream.isAborted()) {
|
84
|
+
return;
|
85
|
+
}
|
51
86
|
|
52
87
|
if (!eventParser.isValid()) {
|
53
88
|
// We can't continue if the meta stream is invalid
|
@@ -63,27 +98,44 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
|
|
63
98
|
|
64
99
|
const result = eventParser.toResult(handle);
|
65
100
|
|
101
|
+
if (metaStream.isAborted()) {
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
|
66
105
|
sendDefinitions(res, result);
|
67
106
|
|
68
107
|
if (!req.query.skipCodegen) {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
108
|
+
try {
|
109
|
+
sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENTATION));
|
110
|
+
const stormCodegen = new StormCodegen(
|
111
|
+
metaStream.getConversationId(),
|
112
|
+
aiRequest.prompt,
|
113
|
+
result.blocks,
|
114
|
+
eventParser.getEvents()
|
115
|
+
);
|
116
|
+
|
117
|
+
onRequestAborted(req, res, () => {
|
118
|
+
stormCodegen.abort();
|
119
|
+
});
|
120
|
+
|
121
|
+
const codegenPromise = streamStormPartialResponse(stormCodegen.getStream(), res);
|
122
|
+
|
123
|
+
await stormCodegen.process();
|
124
|
+
|
125
|
+
await codegenPromise;
|
126
|
+
} finally {
|
127
|
+
if (!metaStream.isAborted()) {
|
128
|
+
sendEvent(res, createPhaseEndEvent(StormEventPhaseType.IMPLEMENTATION));
|
129
|
+
}
|
130
|
+
}
|
81
131
|
}
|
82
132
|
|
83
133
|
sendDone(res);
|
84
134
|
} catch (err: any) {
|
85
135
|
sendError(err, res);
|
86
|
-
res.
|
136
|
+
if (!res.closed) {
|
137
|
+
res.end();
|
138
|
+
}
|
87
139
|
}
|
88
140
|
});
|
89
141
|
|
@@ -93,19 +145,13 @@ router.post('/block/create', async (req: KapetaBodyRequest, res: Response) => {
|
|
93
145
|
try {
|
94
146
|
const ymlPath = Path.join(createRequest.newPath, 'kapeta.yml');
|
95
147
|
|
96
|
-
console.log('Creating block at', ymlPath);
|
97
|
-
|
98
148
|
const [asset] = await assetManager.createAsset(ymlPath, createRequest.definition);
|
99
149
|
|
100
150
|
if (await FS.pathExists(createRequest.tmpPath)) {
|
101
|
-
console.log('Moving block from', createRequest.tmpPath, 'to', createRequest.newPath);
|
102
|
-
|
103
151
|
await FS.move(createRequest.tmpPath, createRequest.newPath, {
|
104
152
|
overwrite: true,
|
105
153
|
});
|
106
154
|
|
107
|
-
console.log('Updating asset', asset.ref);
|
108
|
-
|
109
155
|
res.send(await assetManager.updateAsset(asset.ref, createRequest.definition));
|
110
156
|
} else {
|
111
157
|
res.send(asset);
|
@@ -125,6 +171,9 @@ function sendDefinitions(res: Response, result: StormDefinitions) {
|
|
125
171
|
}
|
126
172
|
|
127
173
|
function sendDone(res: Response) {
|
174
|
+
if (res.closed) {
|
175
|
+
return;
|
176
|
+
}
|
128
177
|
sendEvent(res, {
|
129
178
|
type: 'DONE',
|
130
179
|
created: Date.now(),
|
@@ -134,6 +183,9 @@ function sendDone(res: Response) {
|
|
134
183
|
}
|
135
184
|
|
136
185
|
function sendError(err: Error, res: Response) {
|
186
|
+
if (res.closed) {
|
187
|
+
return;
|
188
|
+
}
|
137
189
|
console.error('Failed to send prompt', err);
|
138
190
|
if (res.headersSent) {
|
139
191
|
sendEvent(res, {
|
@@ -175,7 +227,19 @@ function streamStormPartialResponse(result: StormStream, res: Response) {
|
|
175
227
|
}
|
176
228
|
|
177
229
|
function sendEvent(res: Response, evt: StormEvent) {
|
230
|
+
if (res.closed) {
|
231
|
+
return;
|
232
|
+
}
|
178
233
|
res.write(JSON.stringify(evt) + '\n');
|
179
234
|
}
|
180
235
|
|
236
|
+
function onRequestAborted(req: KapetaBodyRequest, res: Response, onAborted: () => void) {
|
237
|
+
req.on('close', () => {
|
238
|
+
onAborted();
|
239
|
+
});
|
240
|
+
res.on('close', () => {
|
241
|
+
onAborted();
|
242
|
+
});
|
243
|
+
}
|
244
|
+
|
181
245
|
export default router;
|
package/src/storm/stormClient.ts
CHANGED
@@ -13,6 +13,7 @@ import {
|
|
13
13
|
StormStream,
|
14
14
|
StormUIImplementationPrompt,
|
15
15
|
} from './stream';
|
16
|
+
import { getRawAsset } from 'node:sea';
|
16
17
|
|
17
18
|
export const STORM_ID = 'storm';
|
18
19
|
|
@@ -60,6 +61,9 @@ class StormClient {
|
|
60
61
|
conversationId: body.conversationId,
|
61
62
|
});
|
62
63
|
|
64
|
+
const abort = new AbortController();
|
65
|
+
options.signal = abort.signal;
|
66
|
+
|
63
67
|
const response = await fetch(options.url, options);
|
64
68
|
|
65
69
|
if (response.status !== 200) {
|
@@ -69,7 +73,6 @@ class StormClient {
|
|
69
73
|
}
|
70
74
|
|
71
75
|
const conversationId = response.headers.get(ConversationIdHeader);
|
72
|
-
console.log('Received conversationId', conversationId);
|
73
76
|
|
74
77
|
const out = new StormStream(stringPrompt, conversationId);
|
75
78
|
|
@@ -87,6 +90,10 @@ class StormClient {
|
|
87
90
|
out.end();
|
88
91
|
});
|
89
92
|
|
93
|
+
out.on('aborted', () => {
|
94
|
+
abort.abort();
|
95
|
+
});
|
96
|
+
|
90
97
|
return out;
|
91
98
|
}
|
92
99
|
|
package/src/storm/stream.ts
CHANGED
@@ -10,6 +10,7 @@ import { BlockDefinition } from '@kapeta/schemas';
|
|
10
10
|
export class StormStream extends EventEmitter {
|
11
11
|
private conversationId: string = '';
|
12
12
|
private lines: string[] = [];
|
13
|
+
private aborted: boolean = false;
|
13
14
|
|
14
15
|
constructor(prompt: string = '', conversationId?: string | null) {
|
15
16
|
super();
|
@@ -20,6 +21,10 @@ export class StormStream extends EventEmitter {
|
|
20
21
|
return this.conversationId;
|
21
22
|
}
|
22
23
|
|
24
|
+
isAborted() {
|
25
|
+
return this.aborted;
|
26
|
+
}
|
27
|
+
|
23
28
|
addJSONLine(line: string) {
|
24
29
|
try {
|
25
30
|
this.lines.push(line);
|
@@ -38,6 +43,7 @@ export class StormStream extends EventEmitter {
|
|
38
43
|
}
|
39
44
|
|
40
45
|
on(event: 'end', listener: () => void): this;
|
46
|
+
on(event: 'aborted', listener: () => void): this;
|
41
47
|
on(event: 'error', listener: (e: Error) => void): this;
|
42
48
|
on(event: 'data', listener: (data: StormEvent) => void): this;
|
43
49
|
on(event: string, listener: (...args: any[]) => void): this {
|
@@ -45,6 +51,7 @@ export class StormStream extends EventEmitter {
|
|
45
51
|
}
|
46
52
|
|
47
53
|
emit(event: 'end'): boolean;
|
54
|
+
emit(event: 'aborted'): void;
|
48
55
|
emit(event: 'error', e: Error): boolean;
|
49
56
|
emit(event: 'data', data: StormEvent): boolean;
|
50
57
|
emit(eventName: string | symbol, ...args: any[]): boolean {
|
@@ -62,6 +69,14 @@ export class StormStream extends EventEmitter {
|
|
62
69
|
});
|
63
70
|
});
|
64
71
|
}
|
72
|
+
|
73
|
+
abort() {
|
74
|
+
if (this.aborted) {
|
75
|
+
return;
|
76
|
+
}
|
77
|
+
this.aborted = true;
|
78
|
+
this.emit('aborted');
|
79
|
+
}
|
65
80
|
}
|
66
81
|
|
67
82
|
export interface ConversationItem {
|