@kapeta/local-cluster-service 0.55.3 → 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 +13 -0
- package/dist/cjs/src/storm/codegen.d.ts +2 -1
- package/dist/cjs/src/storm/codegen.js +4 -1
- package/dist/cjs/src/storm/routes.js +25 -6
- package/dist/cjs/src/storm/stream.d.ts +7 -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/routes.js +25 -6
- package/dist/esm/src/storm/stream.d.ts +7 -0
- package/package.json +1 -1
- package/src/storm/codegen.ts +7 -2
- package/src/storm/routes.ts +31 -8
- package/src/storm/stream.ts +8 -0
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
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
|
+
|
1
14
|
## [0.55.3](https://github.com/kapetacom/local-cluster-service/compare/v0.55.2...v0.55.3) (2024-07-12)
|
2
15
|
|
3
16
|
|
@@ -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
|
},
|
@@ -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);
|
@@ -86,6 +86,8 @@ router.post('/:handle/all', async (req, res) => {
|
|
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
|
}
|
@@ -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
|
},
|
@@ -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);
|
@@ -86,6 +86,8 @@ router.post('/:handle/all', async (req, res) => {
|
|
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
|
}
|
package/package.json
CHANGED
package/src/storm/codegen.ts
CHANGED
@@ -106,7 +106,7 @@ export class StormCodegen {
|
|
106
106
|
private readonly blocks: BlockDefinitionInfo[];
|
107
107
|
private readonly out = new StormStream();
|
108
108
|
private readonly events: StormEvent[];
|
109
|
-
private
|
109
|
+
private tmpDir: string;
|
110
110
|
private readonly conversationId: string;
|
111
111
|
|
112
112
|
constructor(conversationId: string, userPrompt: string, blocks: BlockDefinitionInfo[], events: StormEvent[]) {
|
@@ -117,6 +117,10 @@ export class StormCodegen {
|
|
117
117
|
this.conversationId = conversationId;
|
118
118
|
}
|
119
119
|
|
120
|
+
public setTmpDir(tmpDir: string) {
|
121
|
+
this.tmpDir = tmpDir;
|
122
|
+
}
|
123
|
+
|
120
124
|
public async process() {
|
121
125
|
const promises = this.blocks.map((block) => {
|
122
126
|
if (block.archetype) {
|
@@ -273,7 +277,7 @@ export class StormCodegen {
|
|
273
277
|
payload: {
|
274
278
|
filename: data.payload.filename,
|
275
279
|
path: join(this.getBasePath(blockUri.fullName), data.payload.filename),
|
276
|
-
content: data.payload.content,
|
280
|
+
content: data.payload.content.replace(/\\n/g, '\n'),
|
277
281
|
blockRef: ref,
|
278
282
|
instanceId: StormEventParser.toInstanceIdFromRef(ref),
|
279
283
|
},
|
@@ -320,6 +324,7 @@ export class StormCodegen {
|
|
320
324
|
file.type !== AIFileTypes.WEB_ROUTER
|
321
325
|
);
|
322
326
|
const uiTemplates = allFiles.filter((file) => file.type === AIFileTypes.WEB_SCREEN);
|
327
|
+
|
323
328
|
const screenFiles: StormEventFileDone[] = [];
|
324
329
|
|
325
330
|
let filteredEvents = [] as StormEvent[];
|
package/src/storm/routes.ts
CHANGED
@@ -6,10 +6,12 @@
|
|
6
6
|
import Router from 'express-promise-router';
|
7
7
|
import FS from 'fs-extra';
|
8
8
|
import { Response } from 'express';
|
9
|
+
import Path from 'path';
|
10
|
+
import _ from 'lodash';
|
9
11
|
import { corsHandler } from '../middleware/cors';
|
10
12
|
import { stringBody } from '../middleware/stringBody';
|
11
13
|
import { KapetaBodyRequest } from '../types';
|
12
|
-
import { StormContextRequest, StormCreateBlockRequest, StormStream } from './stream';
|
14
|
+
import { StormCodegenRequest, StormContextRequest, StormCreateBlockRequest, StormStream } from './stream';
|
13
15
|
import { ConversationIdHeader, stormClient } from './stormClient';
|
14
16
|
import { StormEvent, StormEventPhaseType } from './events';
|
15
17
|
import {
|
@@ -21,8 +23,6 @@ import {
|
|
21
23
|
} from './event-parser';
|
22
24
|
import { StormCodegen } from './codegen';
|
23
25
|
import { assetManager } from '../assetManager';
|
24
|
-
import Path from 'path';
|
25
|
-
import _ from 'lodash';
|
26
26
|
|
27
27
|
const router = Router();
|
28
28
|
|
@@ -108,7 +108,8 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
|
|
108
108
|
if (metaStream.isAborted()) {
|
109
109
|
return;
|
110
110
|
}
|
111
|
-
|
111
|
+
// Cancel debounce, we don't need to send the plan again
|
112
|
+
sendUpdatedPlan.cancel();
|
112
113
|
sendDefinitions(res, result);
|
113
114
|
|
114
115
|
if (!req.query.skipCodegen) {
|
@@ -169,6 +170,31 @@ router.post('/block/create', async (req: KapetaBodyRequest, res: Response) => {
|
|
169
170
|
}
|
170
171
|
});
|
171
172
|
|
173
|
+
router.post('/block/codegen', async (req: KapetaBodyRequest, res: Response) => {
|
174
|
+
const body: StormCodegenRequest = JSON.parse(req.stringBody ?? '{}');
|
175
|
+
console.log('Codegen request', body);
|
176
|
+
const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
|
177
|
+
try {
|
178
|
+
const stormCodegen = new StormCodegen(conversationId ?? '', body.prompt, [body.block], body.events || []);
|
179
|
+
stormCodegen.setTmpDir(body.outDir);
|
180
|
+
|
181
|
+
onRequestAborted(req, res, () => {
|
182
|
+
stormCodegen.abort();
|
183
|
+
});
|
184
|
+
|
185
|
+
const codegenPromise = streamStormPartialResponse(stormCodegen.getStream(), res);
|
186
|
+
|
187
|
+
await stormCodegen.process();
|
188
|
+
|
189
|
+
await codegenPromise;
|
190
|
+
|
191
|
+
sendDone(res);
|
192
|
+
} catch (err: any) {
|
193
|
+
console.error('Failed to generate code', err);
|
194
|
+
res.status(500).send({ error: err.message });
|
195
|
+
}
|
196
|
+
});
|
197
|
+
|
172
198
|
function sendDefinitions(res: Response, result: StormDefinitions) {
|
173
199
|
sendEvent(res, {
|
174
200
|
type: 'DEFINITION_CHANGE',
|
@@ -228,10 +254,7 @@ function sendEvent(res: Response, evt: StormEvent) {
|
|
228
254
|
}
|
229
255
|
|
230
256
|
function onRequestAborted(req: KapetaBodyRequest, res: Response, onAborted: () => void) {
|
231
|
-
req.on('close', () => {
|
232
|
-
onAborted();
|
233
|
-
});
|
234
|
-
res.on('close', () => {
|
257
|
+
req.socket.on('close', () => {
|
235
258
|
onAborted();
|
236
259
|
});
|
237
260
|
}
|
package/src/storm/stream.ts
CHANGED
@@ -6,6 +6,7 @@ import { EventEmitter } from 'node:events';
|
|
6
6
|
import { StormEvent } from './events';
|
7
7
|
import { AIFileTypes, GeneratedFile } from '@kapeta/codegen';
|
8
8
|
import { BlockDefinition } from '@kapeta/schemas';
|
9
|
+
import { BlockDefinitionInfo } from './event-parser';
|
9
10
|
|
10
11
|
export class StormStream extends EventEmitter {
|
11
12
|
private conversationId: string = '';
|
@@ -106,6 +107,13 @@ export interface StormCreateBlockRequest {
|
|
106
107
|
newPath: string;
|
107
108
|
}
|
108
109
|
|
110
|
+
export interface StormCodegenRequest {
|
111
|
+
block: BlockDefinitionInfo;
|
112
|
+
prompt: string;
|
113
|
+
events: StormEvent[];
|
114
|
+
outDir: string;
|
115
|
+
}
|
116
|
+
|
109
117
|
export interface StormFileInfo extends GeneratedFile {
|
110
118
|
type: AIFileTypes;
|
111
119
|
}
|