@kapeta/local-cluster-service 0.74.0 → 0.74.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 CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.74.1](https://github.com/kapetacom/local-cluster-service/compare/v0.74.0...v0.74.1) (2024-09-27)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * adding system id to all calls ([b3e978f](https://github.com/kapetacom/local-cluster-service/commit/b3e978ffb74fe0ab1a46006412e52c63fe49eff8))
7
+
1
8
  # [0.74.0](https://github.com/kapetacom/local-cluster-service/compare/v0.73.0...v0.74.0) (2024-09-26)
2
9
 
3
10
 
@@ -240,8 +240,9 @@ class PageQueue extends node_events_1.EventEmitter {
240
240
  console.warn('Skipping image reference of type %s for url %s', mimeType, prompt.url);
241
241
  return;
242
242
  }
243
+ const client = new stormClient_1.StormClient(this.systemId);
243
244
  this.images.set(prompt.url, prompt.description);
244
- const result = await stormClient_1.stormClient.createImage(`Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim());
245
+ const result = await client.createImage(`Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim());
245
246
  let imageEvent = null;
246
247
  result.on('data', (event) => {
247
248
  if (event.type === 'IMAGE') {
@@ -256,7 +257,8 @@ class PageQueue extends node_events_1.EventEmitter {
256
257
  this.emit('image', imageEvent, prompt);
257
258
  }
258
259
  async generate(prompt, conversationId) {
259
- const screenStream = await stormClient_1.stormClient.createUIPage(prompt, conversationId);
260
+ const client = new stormClient_1.StormClient(this.systemId);
261
+ const screenStream = await client.createUIPage(prompt, conversationId);
260
262
  let pageEvent = null;
261
263
  screenStream.on('data', (event) => {
262
264
  if (event.type === 'PAGE') {
@@ -273,7 +275,8 @@ class PageQueue extends node_events_1.EventEmitter {
273
275
  await this.processPageEventWithReferences(pageEvent);
274
276
  }
275
277
  async resolveReferences(content) {
276
- const referenceStream = await stormClient_1.stormClient.classifyUIReferences(content);
278
+ const client = new stormClient_1.StormClient(this.systemId);
279
+ const referenceStream = await client.classifyUIReferences(content);
277
280
  const references = [];
278
281
  referenceStream.on('data', (referenceData) => {
279
282
  if (referenceData.type !== 'REF_CLASSIFICATION') {
@@ -311,9 +311,10 @@ class StormCodegen {
311
311
  permissions: '0644',
312
312
  });
313
313
  const uiEvents = [];
314
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
314
315
  // generate screens
315
316
  if (uiTemplates.length) {
316
- const screenStream = await stormClient_1.stormClient.listScreens({
317
+ const screenStream = await stormClient.listScreens({
317
318
  events: filteredEvents,
318
319
  templates: uiTemplates,
319
320
  context: relevantFiles,
@@ -346,7 +347,7 @@ class StormCodegen {
346
347
  context: relevantFiles.concat([getScreenEventsFile()]),
347
348
  prompt: this.userPrompt,
348
349
  };
349
- const uiStream = await stormClient_1.stormClient.createUIImplementation(payload);
350
+ const uiStream = await stormClient.createUIImplementation(payload);
350
351
  uiStream.on('data', (evt) => {
351
352
  const uiFile = this.handleUiOutput(blockUri, block.aiName, evt);
352
353
  if (uiFile != undefined) {
@@ -374,13 +375,13 @@ class StormCodegen {
374
375
  });
375
376
  allFiles.push(...screenFilesConverted);
376
377
  let webRouters = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.WEB_ROUTER);
377
- webRouters = await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.generateCode.bind(stormClient_1.stormClient), webRouters, screenFilesConverted.concat([getScreenEventsFile()]));
378
+ webRouters = await this.processTemplates(blockUri, block.aiName, stormClient.generateCode.bind(stormClient), webRouters, screenFilesConverted.concat([getScreenEventsFile()]));
378
379
  // Gather the context files for implementation. These will be all be passed to the AI
379
380
  const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN, codegen_1.AIFileTypes.WEB_ROUTER].includes(file.type));
380
381
  // Send the service and UI templates to the AI. These will be sent one-by-one in addition to the context files
381
382
  let serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
382
383
  if (serviceFiles.length > 0) {
383
- serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
384
+ serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient.createServiceImplementation.bind(stormClient), serviceFiles, contextFiles);
384
385
  }
385
386
  if (this.isAborted()) {
386
387
  return;
@@ -510,7 +511,8 @@ class StormCodegen {
510
511
  }
511
512
  }
512
513
  async classifyErrors(errors, basePath) {
513
- const errorStream = await stormClient_1.stormClient.createErrorClassification(errors, []);
514
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
515
+ const errorStream = await stormClient.createErrorClassification(errors, []);
514
516
  const fixes = new Map();
515
517
  this.out.on('aborted', () => {
516
518
  errorStream.abort();
@@ -547,7 +549,8 @@ class StormCodegen {
547
549
  error: error,
548
550
  projectFiles: allFiles.map((f) => f.filename),
549
551
  };
550
- const detailsStream = await stormClient_1.stormClient.createErrorDetails(JSON.stringify(request), []);
552
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
553
+ const detailsStream = await stormClient.createErrorDetails(JSON.stringify(request), []);
551
554
  detailsStream.on('data', (evt) => {
552
555
  if (evt.type === 'ERROR_DETAILS') {
553
556
  resolve(evt.payload.files);
@@ -611,7 +614,8 @@ class StormCodegen {
611
614
  async codeFix(blockUri, blockName, fix, history) {
612
615
  return new Promise(async (resolve, reject) => {
613
616
  try {
614
- const fixStream = await stormClient_1.stormClient.createCodeFix(fix, history, this.conversationId);
617
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
618
+ const fixStream = await stormClient.createCodeFix(fix, history, this.conversationId);
615
619
  let resolved = false;
616
620
  fixStream.on('data', (evt) => {
617
621
  if (this.handleFileEvents(blockUri, blockName, evt)) {
@@ -117,8 +117,10 @@ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
117
117
  res.set(stormClient_1.ConversationIdHeader, systemId);
118
118
  sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.IMPLEMENT_APIS));
119
119
  const pagesFromDisk = (0, utils_1.readFilesAndContent)(srcDir);
120
- const pagesWithImplementation = await stormClient_1.stormClient.replaceMockWithAPICall({
120
+ const client = new stormClient_1.StormClient(systemId);
121
+ const pagesWithImplementation = await client.replaceMockWithAPICall({
121
122
  pages: pagesFromDisk,
123
+ systemId: systemId,
122
124
  });
123
125
  await (0, utils_1.copyDirectory)(srcDir, destDir, (fileName, content) => {
124
126
  // find the page from result1 and write the content to the file
@@ -131,7 +133,7 @@ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
131
133
  const pageContents = pagesWithImplementation.map((page) => {
132
134
  return page.content;
133
135
  });
134
- const prompt = await stormClient_1.stormClient.generatePrompt(pageContents);
136
+ const prompt = await client.generatePrompt(pageContents);
135
137
  sendEvent(res, (0, event_parser_1.createPhaseEndEvent)(events_1.StormEventPhaseType.COMPOSE_SYSTEM_PROMPT));
136
138
  req.query.systemId = systemId;
137
139
  const promptRequest = {
@@ -149,10 +151,12 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req, res) => {
149
151
  //res.set('Access-Control-Expose-Headers', ConversationIdHeader);
150
152
  //res.set(ConversationIdHeader, systemId);
151
153
  //sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENT_APIS));
154
+ const client = new stormClient_1.StormClient(systemId);
152
155
  try {
153
156
  const pagesFromDisk = (0, utils_1.readFilesAndContent)(srcDir);
154
- const pagesWithImplementation = await stormClient_1.stormClient.replaceMockWithAPICall({
157
+ const pagesWithImplementation = await client.replaceMockWithAPICall({
155
158
  pages: pagesFromDisk,
159
+ systemId: systemId,
156
160
  });
157
161
  //sendEvent(res, createPhaseEndEvent(StormEventPhaseType.IMPLEMENT_APIS));
158
162
  //sendEvent(res, createPhaseStartEvent(StormEventPhaseType.COMPOSE_SYSTEM));
@@ -165,7 +169,7 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req, res) => {
165
169
  }
166
170
  return page;
167
171
  });
168
- const systemUrl = await stormClient_1.stormClient.createSimpleBackend(handle, systemId, { pages: allFiles });
172
+ const systemUrl = await client.createSimpleBackend(handle, systemId, { pages: allFiles });
169
173
  //sendEvent(res, {type: 'SYSTEM_READY', created: Math.floor(Date.now() / 1000), reason: 'System Ready', payload: { systemUrl: systemUrl }});
170
174
  //sendEvent(res, createPhaseEndEvent(StormEventPhaseType.COMPOSE_SYSTEM));
171
175
  //sendDone(res);
@@ -250,7 +254,8 @@ router.post('/:handle/ui/iterative', async (req, res) => {
250
254
  try {
251
255
  const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
252
256
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
253
- const landingPagesStream = await stormClient_1.stormClient.createUILandingPages(aiRequest, conversationId);
257
+ const client = new stormClient_1.StormClient(conversationId); //todo is this correct we are using the landing page getConversationId down below as well
258
+ const landingPagesStream = await client.createUILandingPages(aiRequest, conversationId);
254
259
  onRequestAborted(req, res, () => {
255
260
  landingPagesStream.abort();
256
261
  });
@@ -339,8 +344,9 @@ router.post('/:handle/ui', async (req, res) => {
339
344
  try {
340
345
  const outerConversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()] || (0, crypto_1.randomUUID)();
341
346
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
347
+ const stormClient = new stormClient_1.StormClient(outerConversationId);
342
348
  // Get user journeys
343
- const userJourneysStream = await stormClient_1.stormClient.createUIUserJourneys(aiRequest, outerConversationId);
349
+ const userJourneysStream = await stormClient.createUIUserJourneys(aiRequest, outerConversationId);
344
350
  onRequestAborted(req, res, () => {
345
351
  userJourneysStream.abort();
346
352
  });
@@ -378,7 +384,7 @@ router.post('/:handle/ui', async (req, res) => {
378
384
  });
379
385
  let theme = '';
380
386
  try {
381
- const themeStream = await stormClient_1.stormClient.createTheme(aiRequest, outerConversationId);
387
+ const themeStream = await stormClient.createTheme(aiRequest, outerConversationId);
382
388
  onRequestAborted(req, res, () => {
383
389
  themeStream.abort();
384
390
  });
@@ -417,7 +423,7 @@ router.post('/:handle/ui', async (req, res) => {
417
423
  }
418
424
  await waitForStormStream(userJourneysStream);
419
425
  // Get the UI shells
420
- const shellsStream = await stormClient_1.stormClient.createUIShells({
426
+ const shellsStream = await stormClient.createUIShells({
421
427
  theme: theme || undefined,
422
428
  pages: Object.values(uniqueUserJourneyScreens).map((screen) => ({
423
429
  name: screen.name,
@@ -574,7 +580,8 @@ router.post('/ui/vote', async (req, res) => {
574
580
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
575
581
  const { topic, vote, mainConversationId } = aiRequest;
576
582
  try {
577
- await stormClient_1.stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
583
+ const stormClient = new stormClient_1.StormClient(mainConversationId);
584
+ await stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
578
585
  }
579
586
  catch (e) {
580
587
  res.status(500).send({ error: e.message });
@@ -585,7 +592,8 @@ router.post('/ui/get-vote', async (req, res) => {
585
592
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
586
593
  const { topic, mainConversationId } = aiRequest;
587
594
  try {
588
- const vote = await stormClient_1.stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
595
+ const stormClient = new stormClient_1.StormClient(mainConversationId);
596
+ const vote = await stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
589
597
  res.send({ vote });
590
598
  }
591
599
  catch (e) {
@@ -609,7 +617,8 @@ async function handleAll(req, res) {
609
617
  const eventParser = new event_parser_1.StormEventParser(stormOptions);
610
618
  const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
611
619
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
612
- const metaStream = await stormClient_1.stormClient.createMetadata(aiRequest, conversationId);
620
+ const stormClient = new stormClient_1.StormClient(systemId);
621
+ const metaStream = await stormClient.createMetadata(aiRequest, conversationId);
613
622
  onRequestAborted(req, res, () => {
614
623
  metaStream.abort();
615
624
  });
@@ -4,6 +4,7 @@ import { ConversationItem, CreateSimpleBackendRequest, HTMLPage, ImplementAPICli
4
4
  import { Page, StormEventPageUrl } from './events';
5
5
  export declare const STORM_ID = "storm";
6
6
  export declare const ConversationIdHeader = "Conversation-Id";
7
+ export declare const SystemIdHeader = "System-Id";
7
8
  export interface UIShellsPrompt {
8
9
  theme?: string;
9
10
  pages: {
@@ -55,9 +56,10 @@ export interface BasePromptRequest {
55
56
  prompt: string;
56
57
  skipImprovement?: boolean;
57
58
  }
58
- declare class StormClient {
59
+ export declare class StormClient {
59
60
  private readonly _baseUrl;
60
- constructor();
61
+ private readonly _systemId;
62
+ constructor(systemId?: string);
61
63
  private createOptions;
62
64
  private send;
63
65
  createMetadata(prompt: BasePromptRequest, conversationId?: string): Promise<StormStream>;
@@ -88,5 +90,3 @@ declare class StormClient {
88
90
  downloadSystem(handle: string, conversationId: string): Promise<Buffer>;
89
91
  uploadSystem(handle: string, conversationId: string, buffer: Buffer): Promise<Response>;
90
92
  }
91
- export declare const stormClient: StormClient;
92
- export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.stormClient = exports.ConversationIdHeader = exports.STORM_ID = void 0;
6
+ exports.StormClient = exports.SystemIdHeader = exports.ConversationIdHeader = exports.STORM_ID = void 0;
7
7
  /**
8
8
  * Copyright 2023 Kapeta Inc.
9
9
  * SPDX-License-Identifier: BUSL-1.1
@@ -17,10 +17,13 @@ const fetch_retry_1 = __importDefault(require("fetch-retry"));
17
17
  const fetchWithRetries = (0, fetch_retry_1.default)(global.fetch, { retries: 5, retryDelay: 10 });
18
18
  exports.STORM_ID = 'storm';
19
19
  exports.ConversationIdHeader = 'Conversation-Id';
20
+ exports.SystemIdHeader = 'System-Id';
20
21
  class StormClient {
21
22
  _baseUrl;
22
- constructor() {
23
+ _systemId;
24
+ constructor(systemId) {
23
25
  this._baseUrl = (0, utils_1.getRemoteUrl)('ai-service', 'https://ai.kapeta.com');
26
+ this._systemId = systemId || "";
24
27
  }
25
28
  async createOptions(path, method, body) {
26
29
  const url = `${this._baseUrl}${path}`;
@@ -35,6 +38,9 @@ class StormClient {
35
38
  if (body.conversationId) {
36
39
  headers[exports.ConversationIdHeader] = body.conversationId;
37
40
  }
41
+ if (this._systemId) {
42
+ headers[exports.SystemIdHeader] = this._systemId;
43
+ }
38
44
  return {
39
45
  url,
40
46
  method: method,
@@ -103,6 +109,7 @@ class StormClient {
103
109
  createUIShells(prompt, conversationId) {
104
110
  return this.send('/v2/ui/shells', {
105
111
  prompt: JSON.stringify(prompt),
112
+ conversationId: conversationId,
106
113
  });
107
114
  }
108
115
  createUILandingPages(prompt, conversationId) {
@@ -138,6 +145,9 @@ class StormClient {
138
145
  const response = await fetch(u, {
139
146
  method: 'POST',
140
147
  body: JSON.stringify(prompt.pages),
148
+ headers: {
149
+ 'systemId': prompt.systemId,
150
+ },
141
151
  });
142
152
  return (await response.json());
143
153
  }
@@ -252,4 +262,4 @@ class StormClient {
252
262
  return response;
253
263
  }
254
264
  }
255
- exports.stormClient = new StormClient();
265
+ exports.StormClient = StormClient;
@@ -35,6 +35,7 @@ export interface ConversationItem {
35
35
  }
36
36
  export interface StormContextRequest<T = string> {
37
37
  conversationId?: string;
38
+ systemId?: string;
38
39
  history?: ConversationItem[];
39
40
  prompt: T;
40
41
  }
@@ -78,6 +79,7 @@ export declare enum HTMLPageEncoding {
78
79
  }
79
80
  export interface ImplementAPIClients {
80
81
  pages: HTMLPage[];
82
+ systemId: string;
81
83
  }
82
84
  export interface HTMLPage {
83
85
  fileName: string;
@@ -13,8 +13,8 @@ export declare class StormService {
13
13
  saveConversation(conversationId: string, events: StormEvent[]): Promise<void>;
14
14
  appendConversation(conversationId: string, events: StormEvent[]): Promise<void>;
15
15
  deleteConversation(conversationId: string): Promise<void>;
16
- uploadConversation(handle: string, conversationId: string): Promise<void>;
17
- installProjectById(handle: string, conversationId: string): Promise<void>;
16
+ uploadConversation(handle: string, systemId: string): Promise<void>;
17
+ installProjectById(handle: string, systemId: string): Promise<void>;
18
18
  }
19
19
  declare const _default: StormService;
20
20
  export default _default;
@@ -119,8 +119,8 @@ class StormService {
119
119
  await promises_1.default.rm(conversationDir, { recursive: true, force: true });
120
120
  }
121
121
  }
122
- async uploadConversation(handle, conversationId) {
123
- const tarballFile = this.getConversationTarball(conversationId);
122
+ async uploadConversation(handle, systemId) {
123
+ const tarballFile = this.getConversationTarball(systemId);
124
124
  const destDir = path_1.default.dirname(tarballFile);
125
125
  const tarballName = path_1.default.basename(tarballFile);
126
126
  await tar.create({
@@ -129,12 +129,14 @@ class StormService {
129
129
  gzip: true,
130
130
  filter: (entry) => !entry.includes(tarballName),
131
131
  }, ['.']);
132
- await stormClient_1.stormClient.uploadSystem(handle, conversationId, await promises_1.default.readFile(tarballFile));
132
+ const stormClient = new stormClient_1.StormClient(systemId);
133
+ await stormClient.uploadSystem(handle, systemId, await promises_1.default.readFile(tarballFile));
133
134
  }
134
- async installProjectById(handle, conversationId) {
135
- const tarballFile = this.getConversationTarball(conversationId);
135
+ async installProjectById(handle, systemId) {
136
+ const tarballFile = this.getConversationTarball(systemId);
136
137
  const destDir = path_1.default.dirname(tarballFile);
137
- const buffer = await stormClient_1.stormClient.downloadSystem(handle, conversationId);
138
+ const stormClient = new stormClient_1.StormClient(systemId);
139
+ const buffer = await stormClient.downloadSystem(handle, systemId);
138
140
  await promises_1.default.mkdir(destDir, { recursive: true });
139
141
  await promises_1.default.writeFile(tarballFile, buffer);
140
142
  await tar.extract({
@@ -240,8 +240,9 @@ class PageQueue extends node_events_1.EventEmitter {
240
240
  console.warn('Skipping image reference of type %s for url %s', mimeType, prompt.url);
241
241
  return;
242
242
  }
243
+ const client = new stormClient_1.StormClient(this.systemId);
243
244
  this.images.set(prompt.url, prompt.description);
244
- const result = await stormClient_1.stormClient.createImage(`Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim());
245
+ const result = await client.createImage(`Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim());
245
246
  let imageEvent = null;
246
247
  result.on('data', (event) => {
247
248
  if (event.type === 'IMAGE') {
@@ -256,7 +257,8 @@ class PageQueue extends node_events_1.EventEmitter {
256
257
  this.emit('image', imageEvent, prompt);
257
258
  }
258
259
  async generate(prompt, conversationId) {
259
- const screenStream = await stormClient_1.stormClient.createUIPage(prompt, conversationId);
260
+ const client = new stormClient_1.StormClient(this.systemId);
261
+ const screenStream = await client.createUIPage(prompt, conversationId);
260
262
  let pageEvent = null;
261
263
  screenStream.on('data', (event) => {
262
264
  if (event.type === 'PAGE') {
@@ -273,7 +275,8 @@ class PageQueue extends node_events_1.EventEmitter {
273
275
  await this.processPageEventWithReferences(pageEvent);
274
276
  }
275
277
  async resolveReferences(content) {
276
- const referenceStream = await stormClient_1.stormClient.classifyUIReferences(content);
278
+ const client = new stormClient_1.StormClient(this.systemId);
279
+ const referenceStream = await client.classifyUIReferences(content);
277
280
  const references = [];
278
281
  referenceStream.on('data', (referenceData) => {
279
282
  if (referenceData.type !== 'REF_CLASSIFICATION') {
@@ -311,9 +311,10 @@ class StormCodegen {
311
311
  permissions: '0644',
312
312
  });
313
313
  const uiEvents = [];
314
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
314
315
  // generate screens
315
316
  if (uiTemplates.length) {
316
- const screenStream = await stormClient_1.stormClient.listScreens({
317
+ const screenStream = await stormClient.listScreens({
317
318
  events: filteredEvents,
318
319
  templates: uiTemplates,
319
320
  context: relevantFiles,
@@ -346,7 +347,7 @@ class StormCodegen {
346
347
  context: relevantFiles.concat([getScreenEventsFile()]),
347
348
  prompt: this.userPrompt,
348
349
  };
349
- const uiStream = await stormClient_1.stormClient.createUIImplementation(payload);
350
+ const uiStream = await stormClient.createUIImplementation(payload);
350
351
  uiStream.on('data', (evt) => {
351
352
  const uiFile = this.handleUiOutput(blockUri, block.aiName, evt);
352
353
  if (uiFile != undefined) {
@@ -374,13 +375,13 @@ class StormCodegen {
374
375
  });
375
376
  allFiles.push(...screenFilesConverted);
376
377
  let webRouters = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.WEB_ROUTER);
377
- webRouters = await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.generateCode.bind(stormClient_1.stormClient), webRouters, screenFilesConverted.concat([getScreenEventsFile()]));
378
+ webRouters = await this.processTemplates(blockUri, block.aiName, stormClient.generateCode.bind(stormClient), webRouters, screenFilesConverted.concat([getScreenEventsFile()]));
378
379
  // Gather the context files for implementation. These will be all be passed to the AI
379
380
  const contextFiles = relevantFiles.filter((file) => ![codegen_1.AIFileTypes.SERVICE, codegen_1.AIFileTypes.WEB_SCREEN, codegen_1.AIFileTypes.WEB_ROUTER].includes(file.type));
380
381
  // Send the service and UI templates to the AI. These will be sent one-by-one in addition to the context files
381
382
  let serviceFiles = allFiles.filter((file) => file.type === codegen_1.AIFileTypes.SERVICE);
382
383
  if (serviceFiles.length > 0) {
383
- serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient_1.stormClient.createServiceImplementation.bind(stormClient_1.stormClient), serviceFiles, contextFiles);
384
+ serviceFiles = await this.processTemplates(blockUri, block.aiName, stormClient.createServiceImplementation.bind(stormClient), serviceFiles, contextFiles);
384
385
  }
385
386
  if (this.isAborted()) {
386
387
  return;
@@ -510,7 +511,8 @@ class StormCodegen {
510
511
  }
511
512
  }
512
513
  async classifyErrors(errors, basePath) {
513
- const errorStream = await stormClient_1.stormClient.createErrorClassification(errors, []);
514
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
515
+ const errorStream = await stormClient.createErrorClassification(errors, []);
514
516
  const fixes = new Map();
515
517
  this.out.on('aborted', () => {
516
518
  errorStream.abort();
@@ -547,7 +549,8 @@ class StormCodegen {
547
549
  error: error,
548
550
  projectFiles: allFiles.map((f) => f.filename),
549
551
  };
550
- const detailsStream = await stormClient_1.stormClient.createErrorDetails(JSON.stringify(request), []);
552
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
553
+ const detailsStream = await stormClient.createErrorDetails(JSON.stringify(request), []);
551
554
  detailsStream.on('data', (evt) => {
552
555
  if (evt.type === 'ERROR_DETAILS') {
553
556
  resolve(evt.payload.files);
@@ -611,7 +614,8 @@ class StormCodegen {
611
614
  async codeFix(blockUri, blockName, fix, history) {
612
615
  return new Promise(async (resolve, reject) => {
613
616
  try {
614
- const fixStream = await stormClient_1.stormClient.createCodeFix(fix, history, this.conversationId);
617
+ const stormClient = new stormClient_1.StormClient(this.uiSystemId);
618
+ const fixStream = await stormClient.createCodeFix(fix, history, this.conversationId);
615
619
  let resolved = false;
616
620
  fixStream.on('data', (evt) => {
617
621
  if (this.handleFileEvents(blockUri, blockName, evt)) {
@@ -117,8 +117,10 @@ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
117
117
  res.set(stormClient_1.ConversationIdHeader, systemId);
118
118
  sendEvent(res, (0, event_parser_1.createPhaseStartEvent)(events_1.StormEventPhaseType.IMPLEMENT_APIS));
119
119
  const pagesFromDisk = (0, utils_1.readFilesAndContent)(srcDir);
120
- const pagesWithImplementation = await stormClient_1.stormClient.replaceMockWithAPICall({
120
+ const client = new stormClient_1.StormClient(systemId);
121
+ const pagesWithImplementation = await client.replaceMockWithAPICall({
121
122
  pages: pagesFromDisk,
123
+ systemId: systemId,
122
124
  });
123
125
  await (0, utils_1.copyDirectory)(srcDir, destDir, (fileName, content) => {
124
126
  // find the page from result1 and write the content to the file
@@ -131,7 +133,7 @@ router.post('/ui/create-system/:handle/:systemId', async (req, res) => {
131
133
  const pageContents = pagesWithImplementation.map((page) => {
132
134
  return page.content;
133
135
  });
134
- const prompt = await stormClient_1.stormClient.generatePrompt(pageContents);
136
+ const prompt = await client.generatePrompt(pageContents);
135
137
  sendEvent(res, (0, event_parser_1.createPhaseEndEvent)(events_1.StormEventPhaseType.COMPOSE_SYSTEM_PROMPT));
136
138
  req.query.systemId = systemId;
137
139
  const promptRequest = {
@@ -149,10 +151,12 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req, res) => {
149
151
  //res.set('Access-Control-Expose-Headers', ConversationIdHeader);
150
152
  //res.set(ConversationIdHeader, systemId);
151
153
  //sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENT_APIS));
154
+ const client = new stormClient_1.StormClient(systemId);
152
155
  try {
153
156
  const pagesFromDisk = (0, utils_1.readFilesAndContent)(srcDir);
154
- const pagesWithImplementation = await stormClient_1.stormClient.replaceMockWithAPICall({
157
+ const pagesWithImplementation = await client.replaceMockWithAPICall({
155
158
  pages: pagesFromDisk,
159
+ systemId: systemId,
156
160
  });
157
161
  //sendEvent(res, createPhaseEndEvent(StormEventPhaseType.IMPLEMENT_APIS));
158
162
  //sendEvent(res, createPhaseStartEvent(StormEventPhaseType.COMPOSE_SYSTEM));
@@ -165,7 +169,7 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req, res) => {
165
169
  }
166
170
  return page;
167
171
  });
168
- const systemUrl = await stormClient_1.stormClient.createSimpleBackend(handle, systemId, { pages: allFiles });
172
+ const systemUrl = await client.createSimpleBackend(handle, systemId, { pages: allFiles });
169
173
  //sendEvent(res, {type: 'SYSTEM_READY', created: Math.floor(Date.now() / 1000), reason: 'System Ready', payload: { systemUrl: systemUrl }});
170
174
  //sendEvent(res, createPhaseEndEvent(StormEventPhaseType.COMPOSE_SYSTEM));
171
175
  //sendDone(res);
@@ -250,7 +254,8 @@ router.post('/:handle/ui/iterative', async (req, res) => {
250
254
  try {
251
255
  const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
252
256
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
253
- const landingPagesStream = await stormClient_1.stormClient.createUILandingPages(aiRequest, conversationId);
257
+ const client = new stormClient_1.StormClient(conversationId); //todo is this correct we are using the landing page getConversationId down below as well
258
+ const landingPagesStream = await client.createUILandingPages(aiRequest, conversationId);
254
259
  onRequestAborted(req, res, () => {
255
260
  landingPagesStream.abort();
256
261
  });
@@ -339,8 +344,9 @@ router.post('/:handle/ui', async (req, res) => {
339
344
  try {
340
345
  const outerConversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()] || (0, crypto_1.randomUUID)();
341
346
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
347
+ const stormClient = new stormClient_1.StormClient(outerConversationId);
342
348
  // Get user journeys
343
- const userJourneysStream = await stormClient_1.stormClient.createUIUserJourneys(aiRequest, outerConversationId);
349
+ const userJourneysStream = await stormClient.createUIUserJourneys(aiRequest, outerConversationId);
344
350
  onRequestAborted(req, res, () => {
345
351
  userJourneysStream.abort();
346
352
  });
@@ -378,7 +384,7 @@ router.post('/:handle/ui', async (req, res) => {
378
384
  });
379
385
  let theme = '';
380
386
  try {
381
- const themeStream = await stormClient_1.stormClient.createTheme(aiRequest, outerConversationId);
387
+ const themeStream = await stormClient.createTheme(aiRequest, outerConversationId);
382
388
  onRequestAborted(req, res, () => {
383
389
  themeStream.abort();
384
390
  });
@@ -417,7 +423,7 @@ router.post('/:handle/ui', async (req, res) => {
417
423
  }
418
424
  await waitForStormStream(userJourneysStream);
419
425
  // Get the UI shells
420
- const shellsStream = await stormClient_1.stormClient.createUIShells({
426
+ const shellsStream = await stormClient.createUIShells({
421
427
  theme: theme || undefined,
422
428
  pages: Object.values(uniqueUserJourneyScreens).map((screen) => ({
423
429
  name: screen.name,
@@ -574,7 +580,8 @@ router.post('/ui/vote', async (req, res) => {
574
580
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
575
581
  const { topic, vote, mainConversationId } = aiRequest;
576
582
  try {
577
- await stormClient_1.stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
583
+ const stormClient = new stormClient_1.StormClient(mainConversationId);
584
+ await stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
578
585
  }
579
586
  catch (e) {
580
587
  res.status(500).send({ error: e.message });
@@ -585,7 +592,8 @@ router.post('/ui/get-vote', async (req, res) => {
585
592
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
586
593
  const { topic, mainConversationId } = aiRequest;
587
594
  try {
588
- const vote = await stormClient_1.stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
595
+ const stormClient = new stormClient_1.StormClient(mainConversationId);
596
+ const vote = await stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
589
597
  res.send({ vote });
590
598
  }
591
599
  catch (e) {
@@ -609,7 +617,8 @@ async function handleAll(req, res) {
609
617
  const eventParser = new event_parser_1.StormEventParser(stormOptions);
610
618
  const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
611
619
  const aiRequest = JSON.parse(req.stringBody ?? '{}');
612
- const metaStream = await stormClient_1.stormClient.createMetadata(aiRequest, conversationId);
620
+ const stormClient = new stormClient_1.StormClient(systemId);
621
+ const metaStream = await stormClient.createMetadata(aiRequest, conversationId);
613
622
  onRequestAborted(req, res, () => {
614
623
  metaStream.abort();
615
624
  });
@@ -4,6 +4,7 @@ import { ConversationItem, CreateSimpleBackendRequest, HTMLPage, ImplementAPICli
4
4
  import { Page, StormEventPageUrl } from './events';
5
5
  export declare const STORM_ID = "storm";
6
6
  export declare const ConversationIdHeader = "Conversation-Id";
7
+ export declare const SystemIdHeader = "System-Id";
7
8
  export interface UIShellsPrompt {
8
9
  theme?: string;
9
10
  pages: {
@@ -55,9 +56,10 @@ export interface BasePromptRequest {
55
56
  prompt: string;
56
57
  skipImprovement?: boolean;
57
58
  }
58
- declare class StormClient {
59
+ export declare class StormClient {
59
60
  private readonly _baseUrl;
60
- constructor();
61
+ private readonly _systemId;
62
+ constructor(systemId?: string);
61
63
  private createOptions;
62
64
  private send;
63
65
  createMetadata(prompt: BasePromptRequest, conversationId?: string): Promise<StormStream>;
@@ -88,5 +90,3 @@ declare class StormClient {
88
90
  downloadSystem(handle: string, conversationId: string): Promise<Buffer>;
89
91
  uploadSystem(handle: string, conversationId: string, buffer: Buffer): Promise<Response>;
90
92
  }
91
- export declare const stormClient: StormClient;
92
- export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.stormClient = exports.ConversationIdHeader = exports.STORM_ID = void 0;
6
+ exports.StormClient = exports.SystemIdHeader = exports.ConversationIdHeader = exports.STORM_ID = void 0;
7
7
  /**
8
8
  * Copyright 2023 Kapeta Inc.
9
9
  * SPDX-License-Identifier: BUSL-1.1
@@ -17,10 +17,13 @@ const fetch_retry_1 = __importDefault(require("fetch-retry"));
17
17
  const fetchWithRetries = (0, fetch_retry_1.default)(global.fetch, { retries: 5, retryDelay: 10 });
18
18
  exports.STORM_ID = 'storm';
19
19
  exports.ConversationIdHeader = 'Conversation-Id';
20
+ exports.SystemIdHeader = 'System-Id';
20
21
  class StormClient {
21
22
  _baseUrl;
22
- constructor() {
23
+ _systemId;
24
+ constructor(systemId) {
23
25
  this._baseUrl = (0, utils_1.getRemoteUrl)('ai-service', 'https://ai.kapeta.com');
26
+ this._systemId = systemId || "";
24
27
  }
25
28
  async createOptions(path, method, body) {
26
29
  const url = `${this._baseUrl}${path}`;
@@ -35,6 +38,9 @@ class StormClient {
35
38
  if (body.conversationId) {
36
39
  headers[exports.ConversationIdHeader] = body.conversationId;
37
40
  }
41
+ if (this._systemId) {
42
+ headers[exports.SystemIdHeader] = this._systemId;
43
+ }
38
44
  return {
39
45
  url,
40
46
  method: method,
@@ -103,6 +109,7 @@ class StormClient {
103
109
  createUIShells(prompt, conversationId) {
104
110
  return this.send('/v2/ui/shells', {
105
111
  prompt: JSON.stringify(prompt),
112
+ conversationId: conversationId,
106
113
  });
107
114
  }
108
115
  createUILandingPages(prompt, conversationId) {
@@ -138,6 +145,9 @@ class StormClient {
138
145
  const response = await fetch(u, {
139
146
  method: 'POST',
140
147
  body: JSON.stringify(prompt.pages),
148
+ headers: {
149
+ 'systemId': prompt.systemId,
150
+ },
141
151
  });
142
152
  return (await response.json());
143
153
  }
@@ -252,4 +262,4 @@ class StormClient {
252
262
  return response;
253
263
  }
254
264
  }
255
- exports.stormClient = new StormClient();
265
+ exports.StormClient = StormClient;
@@ -35,6 +35,7 @@ export interface ConversationItem {
35
35
  }
36
36
  export interface StormContextRequest<T = string> {
37
37
  conversationId?: string;
38
+ systemId?: string;
38
39
  history?: ConversationItem[];
39
40
  prompt: T;
40
41
  }
@@ -78,6 +79,7 @@ export declare enum HTMLPageEncoding {
78
79
  }
79
80
  export interface ImplementAPIClients {
80
81
  pages: HTMLPage[];
82
+ systemId: string;
81
83
  }
82
84
  export interface HTMLPage {
83
85
  fileName: string;
@@ -13,8 +13,8 @@ export declare class StormService {
13
13
  saveConversation(conversationId: string, events: StormEvent[]): Promise<void>;
14
14
  appendConversation(conversationId: string, events: StormEvent[]): Promise<void>;
15
15
  deleteConversation(conversationId: string): Promise<void>;
16
- uploadConversation(handle: string, conversationId: string): Promise<void>;
17
- installProjectById(handle: string, conversationId: string): Promise<void>;
16
+ uploadConversation(handle: string, systemId: string): Promise<void>;
17
+ installProjectById(handle: string, systemId: string): Promise<void>;
18
18
  }
19
19
  declare const _default: StormService;
20
20
  export default _default;
@@ -119,8 +119,8 @@ class StormService {
119
119
  await promises_1.default.rm(conversationDir, { recursive: true, force: true });
120
120
  }
121
121
  }
122
- async uploadConversation(handle, conversationId) {
123
- const tarballFile = this.getConversationTarball(conversationId);
122
+ async uploadConversation(handle, systemId) {
123
+ const tarballFile = this.getConversationTarball(systemId);
124
124
  const destDir = path_1.default.dirname(tarballFile);
125
125
  const tarballName = path_1.default.basename(tarballFile);
126
126
  await tar.create({
@@ -129,12 +129,14 @@ class StormService {
129
129
  gzip: true,
130
130
  filter: (entry) => !entry.includes(tarballName),
131
131
  }, ['.']);
132
- await stormClient_1.stormClient.uploadSystem(handle, conversationId, await promises_1.default.readFile(tarballFile));
132
+ const stormClient = new stormClient_1.StormClient(systemId);
133
+ await stormClient.uploadSystem(handle, systemId, await promises_1.default.readFile(tarballFile));
133
134
  }
134
- async installProjectById(handle, conversationId) {
135
- const tarballFile = this.getConversationTarball(conversationId);
135
+ async installProjectById(handle, systemId) {
136
+ const tarballFile = this.getConversationTarball(systemId);
136
137
  const destDir = path_1.default.dirname(tarballFile);
137
- const buffer = await stormClient_1.stormClient.downloadSystem(handle, conversationId);
138
+ const stormClient = new stormClient_1.StormClient(systemId);
139
+ const buffer = await stormClient.downloadSystem(handle, systemId);
138
140
  await promises_1.default.mkdir(destDir, { recursive: true });
139
141
  await promises_1.default.writeFile(tarballFile, buffer);
140
142
  await tar.extract({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.74.0",
3
+ "version": "0.74.1",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import uuid from 'node-uuid';
7
- import { stormClient, UIPagePrompt } from './stormClient';
7
+ import { StormClient, UIPagePrompt } from './stormClient';
8
8
  import { ReferenceClassification, StormEvent, StormEventPage, StormImage, UIShell } from './events';
9
9
  import { EventEmitter } from 'node:events';
10
10
  import PQueue from 'p-queue';
@@ -265,8 +265,9 @@ export class PageQueue extends EventEmitter {
265
265
  return;
266
266
  }
267
267
 
268
+ const client = new StormClient(this.systemId);
268
269
  this.images.set(prompt.url, prompt.description);
269
- const result = await stormClient.createImage(
270
+ const result = await client.createImage(
270
271
  `Create an image for the url "${prompt.url}" with this description: ${prompt.description}`.trim()
271
272
  );
272
273
 
@@ -287,7 +288,8 @@ export class PageQueue extends EventEmitter {
287
288
  }
288
289
 
289
290
  public async generate(prompt: UIPagePrompt, conversationId: string) {
290
- const screenStream = await stormClient.createUIPage(prompt, conversationId);
291
+ const client = new StormClient(this.systemId);
292
+ const screenStream = await client.createUIPage(prompt, conversationId);
291
293
  let pageEvent: StormEventPage | null = null;
292
294
  screenStream.on('data', (event: StormEvent) => {
293
295
  if (event.type === 'PAGE') {
@@ -306,7 +308,8 @@ export class PageQueue extends EventEmitter {
306
308
  }
307
309
 
308
310
  private async resolveReferences(content: string): Promise<ReferenceClassification[]> {
309
- const referenceStream = await stormClient.classifyUIReferences(content);
311
+ const client = new StormClient(this.systemId);
312
+ const referenceStream = await client.classifyUIReferences(content);
310
313
 
311
314
  const references: ReferenceClassification[] = [];
312
315
 
@@ -17,7 +17,7 @@ import {
17
17
 
18
18
  import { BlockDefinition } from '@kapeta/schemas';
19
19
  import { codeGeneratorManager } from '../codeGeneratorManager';
20
- import { STORM_ID, stormClient } from './stormClient';
20
+ import { STORM_ID, StormClient } from './stormClient';
21
21
  import {
22
22
  StormEvent,
23
23
  StormEventBlockStatusType,
@@ -371,6 +371,7 @@ export class StormCodegen {
371
371
  });
372
372
  const uiEvents = [];
373
373
 
374
+ const stormClient = new StormClient(this.uiSystemId);
374
375
  // generate screens
375
376
  if (uiTemplates.length) {
376
377
  const screenStream = await stormClient.listScreens({
@@ -663,6 +664,7 @@ export class StormCodegen {
663
664
  }
664
665
 
665
666
  private async classifyErrors(errors: string, basePath: string): Promise<Map<string, ErrorClassification[]>> {
667
+ const stormClient = new StormClient(this.uiSystemId);
666
668
  const errorStream = await stormClient.createErrorClassification(errors, []);
667
669
  const fixes = new Map<string, ErrorClassification[]>();
668
670
 
@@ -711,7 +713,7 @@ export class StormCodegen {
711
713
  error: error,
712
714
  projectFiles: allFiles.map((f) => f.filename),
713
715
  };
714
-
716
+ const stormClient = new StormClient(this.uiSystemId);
715
717
  const detailsStream = await stormClient.createErrorDetails(JSON.stringify(request), []);
716
718
  detailsStream.on('data', (evt) => {
717
719
  if (evt.type === 'ERROR_DETAILS') {
@@ -799,6 +801,7 @@ export class StormCodegen {
799
801
  ): Promise<StormEventErrorDetailsFile> {
800
802
  return new Promise<StormEventErrorDetailsFile>(async (resolve, reject) => {
801
803
  try {
804
+ const stormClient = new StormClient(this.uiSystemId);
802
805
  const fixStream = await stormClient.createCodeFix(fix, history, this.conversationId);
803
806
  let resolved = false;
804
807
  fixStream.on('data', (evt) => {
@@ -21,12 +21,12 @@ import {
21
21
 
22
22
  import {
23
23
  ConversationIdHeader,
24
- stormClient,
25
24
  UIPagePrompt,
26
25
  UIPageEditRequest,
27
26
  BasePromptRequest,
28
27
  UIPageVoteRequest,
29
28
  UIPageGetVoteRequest,
29
+ StormClient,
30
30
  } from './stormClient';
31
31
  import { StormEvent, StormEventPage, StormEventPhaseType, UserJourneyScreen } from './events';
32
32
 
@@ -162,8 +162,10 @@ router.post('/ui/create-system/:handle/:systemId', async (req: KapetaBodyRequest
162
162
  sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENT_APIS));
163
163
 
164
164
  const pagesFromDisk = readFilesAndContent(srcDir);
165
- const pagesWithImplementation = await stormClient.replaceMockWithAPICall({
165
+ const client = new StormClient(systemId)
166
+ const pagesWithImplementation = await client.replaceMockWithAPICall({
166
167
  pages: pagesFromDisk,
168
+ systemId: systemId,
167
169
  });
168
170
 
169
171
  await copyDirectory(srcDir, destDir, (fileName, content) => {
@@ -181,7 +183,7 @@ router.post('/ui/create-system/:handle/:systemId', async (req: KapetaBodyRequest
181
183
  return page.content;
182
184
  });
183
185
 
184
- const prompt = await stormClient.generatePrompt(pageContents);
186
+ const prompt = await client.generatePrompt(pageContents);
185
187
 
186
188
  sendEvent(res, createPhaseEndEvent(StormEventPhaseType.COMPOSE_SYSTEM_PROMPT));
187
189
 
@@ -204,11 +206,12 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req: KapetaBody
204
206
  //res.set(ConversationIdHeader, systemId);
205
207
 
206
208
  //sendEvent(res, createPhaseStartEvent(StormEventPhaseType.IMPLEMENT_APIS));
207
-
209
+ const client = new StormClient(systemId);
208
210
  try {
209
211
  const pagesFromDisk = readFilesAndContent(srcDir);
210
- const pagesWithImplementation = await stormClient.replaceMockWithAPICall({
212
+ const pagesWithImplementation = await client.replaceMockWithAPICall({
211
213
  pages: pagesFromDisk,
214
+ systemId: systemId,
212
215
  });
213
216
 
214
217
  //sendEvent(res, createPhaseEndEvent(StormEventPhaseType.IMPLEMENT_APIS));
@@ -227,7 +230,7 @@ router.post('/ui/create-system-simple/:handle/:systemId', async (req: KapetaBody
227
230
  return page;
228
231
  });
229
232
 
230
- const systemUrl = await stormClient.createSimpleBackend(handle, systemId, { pages: allFiles });
233
+ const systemUrl = await client.createSimpleBackend(handle, systemId, { pages: allFiles });
231
234
  //sendEvent(res, {type: 'SYSTEM_READY', created: Math.floor(Date.now() / 1000), reason: 'System Ready', payload: { systemUrl: systemUrl }});
232
235
 
233
236
  //sendEvent(res, createPhaseEndEvent(StormEventPhaseType.COMPOSE_SYSTEM));
@@ -328,8 +331,8 @@ router.post('/:handle/ui/iterative', async (req: KapetaBodyRequest, res: Respons
328
331
  const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
329
332
 
330
333
  const aiRequest: BasePromptRequest = JSON.parse(req.stringBody ?? '{}');
331
-
332
- const landingPagesStream = await stormClient.createUILandingPages(aiRequest, conversationId);
334
+ const client = new StormClient(conversationId); //todo is this correct we are using the landing page getConversationId down below as well
335
+ const landingPagesStream = await client.createUILandingPages(aiRequest, conversationId);
333
336
 
334
337
  onRequestAborted(req, res, () => {
335
338
  landingPagesStream.abort();
@@ -437,7 +440,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
437
440
  (req.headers[ConversationIdHeader.toLowerCase()] as string | undefined) || randomUUID();
438
441
 
439
442
  const aiRequest: BasePromptRequest = JSON.parse(req.stringBody ?? '{}');
440
-
443
+ const stormClient = new StormClient(outerConversationId);
441
444
  // Get user journeys
442
445
  const userJourneysStream = await stormClient.createUIUserJourneys(aiRequest, outerConversationId);
443
446
 
@@ -719,6 +722,7 @@ router.post('/ui/vote', async (req: KapetaBodyRequest, res: Response) => {
719
722
  const aiRequest: UIPageVoteRequest = JSON.parse(req.stringBody ?? '{}');
720
723
  const { topic, vote, mainConversationId } = aiRequest;
721
724
  try {
725
+ const stormClient = new StormClient(mainConversationId);
722
726
  await stormClient.voteUIPage(topic, conversationId, vote, mainConversationId);
723
727
  } catch (e: any) {
724
728
  res.status(500).send({ error: e.message });
@@ -730,6 +734,7 @@ router.post('/ui/get-vote', async (req: KapetaBodyRequest, res: Response) => {
730
734
  const aiRequest: UIPageGetVoteRequest = JSON.parse(req.stringBody ?? '{}');
731
735
  const { topic, mainConversationId } = aiRequest;
732
736
  try {
737
+ const stormClient = new StormClient(mainConversationId);
733
738
  const vote = await stormClient.getVoteUIPage(topic, conversationId, mainConversationId);
734
739
  res.send({ vote });
735
740
  } catch (e: any) {
@@ -761,6 +766,7 @@ async function handleAll(req: KapetaBodyRequest, res: Response) {
761
766
  const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
762
767
 
763
768
  const aiRequest: BasePromptRequest = JSON.parse(req.stringBody ?? '{}');
769
+ const stormClient = new StormClient(systemId);
764
770
  const metaStream = await stormClient.createMetadata(aiRequest, conversationId);
765
771
 
766
772
  onRequestAborted(req, res, () => {
@@ -24,6 +24,7 @@ const fetchWithRetries = createFetch(global.fetch, { retries: 5, retryDelay: 10
24
24
  export const STORM_ID = 'storm';
25
25
 
26
26
  export const ConversationIdHeader = 'Conversation-Id';
27
+ export const SystemIdHeader = 'System-Id';
27
28
 
28
29
  export interface UIShellsPrompt {
29
30
  theme?: string;
@@ -85,11 +86,12 @@ export interface BasePromptRequest {
85
86
  skipImprovement?: boolean;
86
87
  }
87
88
 
88
- class StormClient {
89
+ export class StormClient {
89
90
  private readonly _baseUrl: string;
90
-
91
- constructor() {
91
+ private readonly _systemId: string;
92
+ constructor(systemId?: string) {
92
93
  this._baseUrl = getRemoteUrl('ai-service', 'https://ai.kapeta.com');
94
+ this._systemId = systemId || "";
93
95
  }
94
96
 
95
97
  private async createOptions(
@@ -110,7 +112,9 @@ class StormClient {
110
112
  if (body.conversationId) {
111
113
  headers[ConversationIdHeader] = body.conversationId;
112
114
  }
113
-
115
+ if (this._systemId) {
116
+ headers[SystemIdHeader] = this._systemId
117
+ }
114
118
  return {
115
119
  url,
116
120
  method: method,
@@ -130,7 +134,6 @@ class StormClient {
130
134
  prompt: stringPrompt,
131
135
  conversationId: body.conversationId,
132
136
  });
133
-
134
137
  const abort = new AbortController();
135
138
  options.signal = abort.signal;
136
139
 
@@ -202,6 +205,7 @@ class StormClient {
202
205
  public createUIShells(prompt: UIShellsPrompt, conversationId?: string) {
203
206
  return this.send('/v2/ui/shells', {
204
207
  prompt: JSON.stringify(prompt),
208
+ conversationId: conversationId,
205
209
  });
206
210
  }
207
211
 
@@ -245,6 +249,9 @@ class StormClient {
245
249
  const response = await fetch(u, {
246
250
  method: 'POST',
247
251
  body: JSON.stringify(prompt.pages),
252
+ headers: {
253
+ 'systemId': prompt.systemId,
254
+ },
248
255
  });
249
256
  return (await response.json()) as HTMLPage[];
250
257
  }
@@ -377,5 +384,3 @@ class StormClient {
377
384
  return response;
378
385
  }
379
386
  }
380
-
381
- export const stormClient = new StormClient();
@@ -99,6 +99,7 @@ export interface ConversationItem {
99
99
 
100
100
  export interface StormContextRequest<T = string> {
101
101
  conversationId?: string;
102
+ systemId?: string;
102
103
  history?: ConversationItem[];
103
104
  prompt: T;
104
105
  }
@@ -150,6 +151,7 @@ export enum HTMLPageEncoding {
150
151
 
151
152
  export interface ImplementAPIClients {
152
153
  pages: HTMLPage[];
154
+ systemId: string;
153
155
  }
154
156
 
155
157
  export interface HTMLPage {
@@ -5,7 +5,7 @@ import path from 'path';
5
5
  import { existsSync } from 'fs';
6
6
  import { StormEvent, StormEventModelResponse, StormEventPromptImprove, StormEventUIShell } from './storm/events';
7
7
  import * as tar from 'tar';
8
- import { stormClient } from './storm/stormClient';
8
+ import { StormClient } from './storm/stormClient';
9
9
 
10
10
  export class StormService {
11
11
  private getConversationFile(conversationId: string) {
@@ -118,8 +118,8 @@ export class StormService {
118
118
  }
119
119
  }
120
120
 
121
- async uploadConversation(handle: string, conversationId: string) {
122
- const tarballFile = this.getConversationTarball(conversationId);
121
+ async uploadConversation(handle: string, systemId: string) {
122
+ const tarballFile = this.getConversationTarball(systemId);
123
123
  const destDir = path.dirname(tarballFile);
124
124
  const tarballName = path.basename(tarballFile);
125
125
  await tar.create(
@@ -131,13 +131,15 @@ export class StormService {
131
131
  },
132
132
  ['.']
133
133
  );
134
- await stormClient.uploadSystem(handle, conversationId, await fs.readFile(tarballFile));
134
+ const stormClient = new StormClient(systemId);
135
+ await stormClient.uploadSystem(handle, systemId, await fs.readFile(tarballFile));
135
136
  }
136
137
 
137
- async installProjectById(handle: string, conversationId: string) {
138
- const tarballFile = this.getConversationTarball(conversationId);
138
+ async installProjectById(handle: string, systemId: string) {
139
+ const tarballFile = this.getConversationTarball(systemId);
139
140
  const destDir = path.dirname(tarballFile);
140
- const buffer = await stormClient.downloadSystem(handle, conversationId);
141
+ const stormClient = new StormClient(systemId);
142
+ const buffer = await stormClient.downloadSystem(handle, systemId);
141
143
  await fs.mkdir(destDir, { recursive: true });
142
144
  await fs.writeFile(tarballFile, buffer);
143
145
  await tar.extract({