@kapeta/local-cluster-service 0.43.3 → 0.45.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/index.js +2 -0
  3. package/dist/cjs/src/codeGeneratorManager.d.ts +1 -0
  4. package/dist/cjs/src/codeGeneratorManager.js +12 -6
  5. package/dist/cjs/src/middleware/cors.d.ts +1 -0
  6. package/dist/cjs/src/middleware/kapeta.d.ts +1 -0
  7. package/dist/cjs/src/middleware/stringBody.d.ts +1 -0
  8. package/dist/cjs/src/proxy/routes.js +19 -19
  9. package/dist/cjs/src/proxy/types/rest.d.ts +2 -4
  10. package/dist/cjs/src/proxy/types/rest.js +45 -6
  11. package/dist/cjs/src/proxy/types/web.d.ts +2 -4
  12. package/dist/cjs/src/proxy/types/web.js +2 -2
  13. package/dist/cjs/src/storm/codegen.d.ts +36 -0
  14. package/dist/cjs/src/storm/codegen.js +160 -0
  15. package/dist/cjs/src/storm/event-parser.d.ts +70 -0
  16. package/dist/cjs/src/storm/event-parser.js +543 -0
  17. package/dist/cjs/src/storm/events.d.ts +127 -0
  18. package/dist/cjs/src/storm/events.js +6 -0
  19. package/dist/cjs/src/storm/routes.d.ts +7 -0
  20. package/dist/cjs/src/storm/routes.js +109 -0
  21. package/dist/cjs/src/storm/stormClient.d.ts +13 -0
  22. package/dist/cjs/src/storm/stormClient.js +87 -0
  23. package/dist/cjs/src/storm/stream.d.ts +38 -0
  24. package/dist/cjs/src/storm/stream.js +57 -0
  25. package/dist/cjs/src/types.d.ts +5 -2
  26. package/dist/esm/index.js +2 -0
  27. package/dist/esm/src/codeGeneratorManager.d.ts +1 -0
  28. package/dist/esm/src/codeGeneratorManager.js +12 -6
  29. package/dist/esm/src/middleware/cors.d.ts +1 -0
  30. package/dist/esm/src/middleware/kapeta.d.ts +1 -0
  31. package/dist/esm/src/middleware/stringBody.d.ts +1 -0
  32. package/dist/esm/src/proxy/routes.js +19 -19
  33. package/dist/esm/src/proxy/types/rest.d.ts +2 -4
  34. package/dist/esm/src/proxy/types/rest.js +45 -6
  35. package/dist/esm/src/proxy/types/web.d.ts +2 -4
  36. package/dist/esm/src/proxy/types/web.js +2 -2
  37. package/dist/esm/src/storm/codegen.d.ts +36 -0
  38. package/dist/esm/src/storm/codegen.js +160 -0
  39. package/dist/esm/src/storm/event-parser.d.ts +70 -0
  40. package/dist/esm/src/storm/event-parser.js +543 -0
  41. package/dist/esm/src/storm/events.d.ts +127 -0
  42. package/dist/esm/src/storm/events.js +6 -0
  43. package/dist/esm/src/storm/routes.d.ts +7 -0
  44. package/dist/esm/src/storm/routes.js +109 -0
  45. package/dist/esm/src/storm/stormClient.d.ts +13 -0
  46. package/dist/esm/src/storm/stormClient.js +87 -0
  47. package/dist/esm/src/storm/stream.d.ts +38 -0
  48. package/dist/esm/src/storm/stream.js +57 -0
  49. package/dist/esm/src/types.d.ts +5 -2
  50. package/index.ts +2 -0
  51. package/package.json +6 -3
  52. package/src/codeGeneratorManager.ts +17 -8
  53. package/src/proxy/routes.ts +26 -20
  54. package/src/proxy/types/rest.ts +73 -11
  55. package/src/proxy/types/web.ts +3 -5
  56. package/src/storm/codegen.ts +210 -0
  57. package/src/storm/event-parser.ts +688 -0
  58. package/src/storm/events.ts +169 -0
  59. package/src/storm/routes.ts +143 -0
  60. package/src/storm/stormClient.ts +114 -0
  61. package/src/storm/stream.ts +88 -0
  62. package/src/types.ts +5 -2
  63. package/src/utils/BlockInstanceRunner.ts +4 -2
@@ -7,12 +7,10 @@ import request from 'request';
7
7
  import _ from 'lodash';
8
8
  import { networkManager } from '../../networkManager';
9
9
  import { socketManager } from '../../socketManager';
10
- import { Response } from 'express';
11
- import { ProxyRequestInfo, SimpleRequest, StringMap } from '../../types';
12
- import { StringBodyRequest } from '../../middleware/stringBody';
10
+ import { ProxyRequestHandler, SimpleRequest, StringMap } from '../../types';
13
11
  import { stringify } from 'qs';
14
12
 
15
- export function proxyHttpRequest(req: StringBodyRequest, res: Response, opts: ProxyRequestInfo) {
13
+ export const proxyHttpRequest: ProxyRequestHandler = (req, res, opts) => {
16
14
  const requestHeaders = _.clone(req.headers);
17
15
 
18
16
  delete requestHeaders['content-length'];
@@ -67,4 +65,4 @@ export function proxyHttpRequest(req: StringBodyRequest, res: Response, opts: Pr
67
65
 
68
66
  //We need to pipe the proxy response to the client response to handle sockets and event streams
69
67
  proxyReq.pipe(res);
70
- }
68
+ };
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import { Definition } from '@kapeta/local-cluster-config';
7
+ import { AIFileTypes, BlockCodeGenerator, GeneratedFile, GeneratedResult } from '@kapeta/codegen';
8
+ import { BlockDefinition } from '@kapeta/schemas';
9
+ import { codeGeneratorManager } from '../codeGeneratorManager';
10
+ import { STORM_ID, stormClient } from './stormClient';
11
+ import { ScreenTemplate, StormEvent, StormEventFile } from './events';
12
+ import { BlockDefinitionInfo } from './event-parser';
13
+ import { ConversationItem, StormFileImplementationPrompt, StormFileInfo, StormStream } from './stream';
14
+ import { KapetaURI } from '@kapeta/nodejs-utils';
15
+
16
+ type ImplementationGenerator = (
17
+ prompt: StormFileImplementationPrompt,
18
+ history?: ConversationItem[]
19
+ ) => Promise<StormStream>;
20
+
21
+ export class StormCodegen {
22
+ private readonly userPrompt: string;
23
+ private readonly blocks: BlockDefinitionInfo[];
24
+ private readonly out = new StormStream();
25
+
26
+ constructor(userPrompt: string, blocks: BlockDefinitionInfo[]) {
27
+ this.userPrompt = userPrompt;
28
+ this.blocks = blocks;
29
+ }
30
+
31
+ public async process() {
32
+ console.log('Processing blocks', this.blocks.length);
33
+
34
+ for (const block of this.blocks) {
35
+ await this.processBlockCode(block);
36
+ }
37
+
38
+ this.out.end();
39
+ }
40
+
41
+ public getStream() {
42
+ return this.out;
43
+ }
44
+
45
+ private handleFileOutput(blockUri: KapetaURI, template: StormFileInfo, data: StormEvent) {
46
+ switch (data.type) {
47
+ case 'FILE':
48
+ template.filename = data.payload.filename;
49
+ template.content = data.payload.content;
50
+ this.emitFile(blockUri, data.payload.filename, data.payload.content, data.reason);
51
+ break;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Generates the code for a block and sends it to the AI
57
+ */
58
+ private async processBlockCode(block: BlockDefinitionInfo) {
59
+ // Generate the code for the block using the standard codegen templates
60
+ const generatedResult = await this.generateBlock(block.content, block.screens);
61
+ if (!generatedResult) {
62
+ console.warn('No generated result for block', block.uri);
63
+ return;
64
+ }
65
+
66
+ const allFiles = this.toStormFiles(generatedResult);
67
+
68
+ // Send all the non-ai files to the stream
69
+ this.emitFiles(block.uri, allFiles);
70
+
71
+ const implementFiles = [AIFileTypes.SERVICE, AIFileTypes.WEB_SCREEN];
72
+
73
+ // Gather the context files. These will be all be passed to the AI
74
+ const contextFiles: StormFileInfo[] = allFiles.filter(
75
+ (file) => file.type !== AIFileTypes.IGNORE && !implementFiles.includes(file.type)
76
+ );
77
+
78
+ // Send the service and UI templates to the AI. These will be send one-by-one in addition to the context files
79
+ const serviceFiles: StormFileInfo[] = allFiles.filter((file) => file.type === AIFileTypes.SERVICE);
80
+ await this.processTemplates(
81
+ block.uri,
82
+ stormClient.createServiceImplementation.bind(stormClient),
83
+ serviceFiles,
84
+ contextFiles
85
+ );
86
+
87
+ //const uiTemplates: StormFileInfo[] = allFiles.filter((file) => file.type === 'ui');
88
+ //await this.processTemplates(stormClient.createUIImplementation, uiTemplates, contextFiles);
89
+ }
90
+
91
+ /**
92
+ * Emits the text-based files to the stream
93
+ */
94
+ private emitFiles(uri: KapetaURI, files: StormFileInfo[]) {
95
+ files.forEach((file) => {
96
+ if (!file.content || typeof file.content !== 'string') {
97
+ return;
98
+ }
99
+
100
+ if (file.type === AIFileTypes.SERVICE) {
101
+ // Don't send the service files to the stream yet
102
+ // They will need to be implemented by the AI
103
+ return;
104
+ }
105
+
106
+ if (file.type === AIFileTypes.WEB_SCREEN) {
107
+ // Don't send the web screen files to the stream yet
108
+ // They will need to be implemented by the AI
109
+ return;
110
+ }
111
+
112
+ this.emitFile(uri, file.filename, file.content);
113
+ });
114
+ }
115
+
116
+ private emitFile(uri: KapetaURI, filename: string, content: string, reason: string = 'File generated') {
117
+ this.out.emit('data', {
118
+ type: 'FILE',
119
+ reason,
120
+ created: Date.now(),
121
+ payload: {
122
+ filename: filename,
123
+ content: content,
124
+ blockRef: uri.toNormalizedString(),
125
+ },
126
+ } satisfies StormEventFile);
127
+ }
128
+
129
+ /**
130
+ * Sends the template to the AI and processes the response
131
+ */
132
+ private processTemplates(
133
+ blockUri: KapetaURI,
134
+ generator: ImplementationGenerator,
135
+ templates: StormFileInfo[],
136
+ contextFiles: StormFileInfo[]
137
+ ) {
138
+ const promises = templates.map(async (templateFile) => {
139
+ const stream = await generator({
140
+ context: contextFiles,
141
+ template: templateFile,
142
+ prompt: this.userPrompt,
143
+ });
144
+
145
+ stream.on('data', (evt) => this.handleFileOutput(blockUri, templateFile, evt));
146
+
147
+ return stream.waitForDone();
148
+ });
149
+
150
+ return Promise.all(promises);
151
+ }
152
+
153
+ /**
154
+ * Converts the generated files to a format that can be sent to the AI
155
+ */
156
+ private toStormFiles(generatedResult: GeneratedResult) {
157
+ const allFiles: StormFileInfo[] = generatedResult.files.map((file: GeneratedFile) => {
158
+ if (!file.content) {
159
+ return {
160
+ ...file,
161
+ type: AIFileTypes.IGNORE,
162
+ };
163
+ }
164
+ if (typeof file.content !== 'string') {
165
+ return {
166
+ ...file,
167
+ type: AIFileTypes.IGNORE,
168
+ };
169
+ }
170
+
171
+ const rx = /\/\/AI-TYPE:([a-z0-9- _]+)\n/gi;
172
+
173
+ const match = rx.exec(file.content);
174
+ if (!match) {
175
+ return {
176
+ ...file,
177
+ type: AIFileTypes.IGNORE,
178
+ };
179
+ }
180
+ const type = match[1].trim() as AIFileTypes;
181
+ file.content = file.content.replace(rx, '');
182
+
183
+ return {
184
+ ...file,
185
+ type,
186
+ };
187
+ });
188
+ return allFiles;
189
+ }
190
+
191
+ /**
192
+ * Generates the code using codegen for a given block.
193
+ */
194
+ private async generateBlock(yamlContent: Definition, screens: ScreenTemplate[] | undefined) {
195
+ if (!yamlContent.spec.target?.kind) {
196
+ //Not all block types have targets
197
+ return;
198
+ }
199
+
200
+ if (!(await codeGeneratorManager.ensureTarget(yamlContent.spec.target?.kind))) {
201
+ return;
202
+ }
203
+
204
+ const codeGenerator = new BlockCodeGenerator(yamlContent as BlockDefinition);
205
+ codeGenerator.withOption('AIContext', STORM_ID);
206
+ codeGenerator.withOption('AIScreens', screens ?? []);
207
+
208
+ return codeGenerator.generate();
209
+ }
210
+ }