@kapeta/local-cluster-service 0.73.0 → 0.74.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 +7 -0
- package/dist/cjs/src/storm/events.d.ts +9 -1
- package/dist/cjs/src/stormService.d.ts +1 -0
- package/dist/cjs/src/stormService.js +36 -15
- package/dist/esm/src/storm/events.d.ts +9 -1
- package/dist/esm/src/stormService.d.ts +1 -0
- package/dist/esm/src/stormService.js +36 -15
- package/package.json +1 -1
- package/src/storm/events.ts +12 -2
- package/src/storm/routes.ts +1 -0
- package/src/stormService.ts +54 -32
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# [0.74.0](https://github.com/kapetacom/local-cluster-service/compare/v0.73.0...v0.74.0) (2024-09-26)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* add URL extraction to conversations endpoint ([#259](https://github.com/kapetacom/local-cluster-service/issues/259)) ([b4a38cd](https://github.com/kapetacom/local-cluster-service/commit/b4a38cdd97d94e3c69091f87bd10487c000ba631))
|
7
|
+
|
1
8
|
# [0.73.0](https://github.com/kapetacom/local-cluster-service/compare/v0.72.0...v0.73.0) (2024-09-25)
|
2
9
|
|
3
10
|
|
@@ -404,5 +404,13 @@ export interface StormEventSystemReady {
|
|
404
404
|
systemUrl: string;
|
405
405
|
};
|
406
406
|
}
|
407
|
-
export
|
407
|
+
export interface StormEventModelResponse {
|
408
|
+
type: 'MODEL_RESPONSE';
|
409
|
+
reason: string;
|
410
|
+
created: number;
|
411
|
+
payload: {
|
412
|
+
text: string;
|
413
|
+
};
|
414
|
+
}
|
415
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileDone | StormEventFileFailed | StormEventFileChunk | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventErrorDetails | StormEventBlockReady | StormEventPhases | StormEventBlockStatus | StormEventCreateDSLRetry | StormEventUserJourney | StormEventUIShell | StormEventPage | StormEventPageUrl | StormEventPromptImprove | StormEventLandingPage | StormEventReferenceClassification | StormEventApiBase | StormEventUIStarted | StormImage | StormEventSystemReady | StormEventModelResponse;
|
408
416
|
export {};
|
@@ -7,6 +7,7 @@ export declare class StormService {
|
|
7
7
|
id: string;
|
8
8
|
description: string;
|
9
9
|
title: string;
|
10
|
+
url?: string | undefined;
|
10
11
|
}[]>;
|
11
12
|
getConversation(conversationId: string): Promise<string>;
|
12
13
|
saveConversation(conversationId: string, events: StormEvent[]): Promise<void>;
|
@@ -54,22 +54,43 @@ class StormService {
|
|
54
54
|
// Returns list of UUIDs - probably want to make it more useful than that
|
55
55
|
const conversations = [];
|
56
56
|
for (const file of eventFiles) {
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
try {
|
58
|
+
const nldContents = await promises_1.default.readFile(file, 'utf8');
|
59
|
+
const events = nldContents.split('\n').map((e) => JSON.parse(e));
|
60
|
+
// find the shell and get the title tag
|
61
|
+
const shellEvent = events.find((e) => e.type === 'AI' && e.event.type === 'UI_SHELL')?.event;
|
62
|
+
const html = shellEvent?.payload.content;
|
63
|
+
const title = html?.match(/<title>(.*?)<\/title>/)?.[1];
|
64
|
+
const id = events.find((e) => e.type === 'AI')?.systemId;
|
65
|
+
const initialPrompt = events.find((e) => e.type === 'AI' && e.event.type === 'PROMPT_IMPROVE')?.event?.payload?.prompt || events[0].text;
|
66
|
+
if (!id) {
|
67
|
+
continue;
|
68
|
+
}
|
69
|
+
let url = undefined;
|
70
|
+
// Find the last model response event that has a URL in the payload (in case it changed over time)
|
71
|
+
for (const evt of [...events].reverse()) {
|
72
|
+
const event = evt.event;
|
73
|
+
if (evt.type === 'AI' && event.type === 'MODEL_RESPONSE') {
|
74
|
+
// Look for a URL in the model response markdown
|
75
|
+
const regex = /\[(.*?)\]\((.*?)\)/g;
|
76
|
+
const match = regex.exec(event.payload.text);
|
77
|
+
const [, _linkText, linkUrl] = match || [];
|
78
|
+
if (linkUrl?.startsWith('http')) {
|
79
|
+
url = linkUrl;
|
80
|
+
break;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
conversations.push({
|
85
|
+
id,
|
86
|
+
description: initialPrompt,
|
87
|
+
title: title || 'New system',
|
88
|
+
url,
|
89
|
+
});
|
90
|
+
}
|
91
|
+
catch (e) {
|
92
|
+
console.error('Failed to load conversation at %s', file, e);
|
67
93
|
}
|
68
|
-
conversations.push({
|
69
|
-
id,
|
70
|
-
description: initialPrompt,
|
71
|
-
title: title || 'New system',
|
72
|
-
});
|
73
94
|
}
|
74
95
|
return conversations;
|
75
96
|
}
|
@@ -404,5 +404,13 @@ export interface StormEventSystemReady {
|
|
404
404
|
systemUrl: string;
|
405
405
|
};
|
406
406
|
}
|
407
|
-
export
|
407
|
+
export interface StormEventModelResponse {
|
408
|
+
type: 'MODEL_RESPONSE';
|
409
|
+
reason: string;
|
410
|
+
created: number;
|
411
|
+
payload: {
|
412
|
+
text: string;
|
413
|
+
};
|
414
|
+
}
|
415
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileDone | StormEventFileFailed | StormEventFileChunk | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventErrorDetails | StormEventBlockReady | StormEventPhases | StormEventBlockStatus | StormEventCreateDSLRetry | StormEventUserJourney | StormEventUIShell | StormEventPage | StormEventPageUrl | StormEventPromptImprove | StormEventLandingPage | StormEventReferenceClassification | StormEventApiBase | StormEventUIStarted | StormImage | StormEventSystemReady | StormEventModelResponse;
|
408
416
|
export {};
|
@@ -7,6 +7,7 @@ export declare class StormService {
|
|
7
7
|
id: string;
|
8
8
|
description: string;
|
9
9
|
title: string;
|
10
|
+
url?: string | undefined;
|
10
11
|
}[]>;
|
11
12
|
getConversation(conversationId: string): Promise<string>;
|
12
13
|
saveConversation(conversationId: string, events: StormEvent[]): Promise<void>;
|
@@ -54,22 +54,43 @@ class StormService {
|
|
54
54
|
// Returns list of UUIDs - probably want to make it more useful than that
|
55
55
|
const conversations = [];
|
56
56
|
for (const file of eventFiles) {
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
try {
|
58
|
+
const nldContents = await promises_1.default.readFile(file, 'utf8');
|
59
|
+
const events = nldContents.split('\n').map((e) => JSON.parse(e));
|
60
|
+
// find the shell and get the title tag
|
61
|
+
const shellEvent = events.find((e) => e.type === 'AI' && e.event.type === 'UI_SHELL')?.event;
|
62
|
+
const html = shellEvent?.payload.content;
|
63
|
+
const title = html?.match(/<title>(.*?)<\/title>/)?.[1];
|
64
|
+
const id = events.find((e) => e.type === 'AI')?.systemId;
|
65
|
+
const initialPrompt = events.find((e) => e.type === 'AI' && e.event.type === 'PROMPT_IMPROVE')?.event?.payload?.prompt || events[0].text;
|
66
|
+
if (!id) {
|
67
|
+
continue;
|
68
|
+
}
|
69
|
+
let url = undefined;
|
70
|
+
// Find the last model response event that has a URL in the payload (in case it changed over time)
|
71
|
+
for (const evt of [...events].reverse()) {
|
72
|
+
const event = evt.event;
|
73
|
+
if (evt.type === 'AI' && event.type === 'MODEL_RESPONSE') {
|
74
|
+
// Look for a URL in the model response markdown
|
75
|
+
const regex = /\[(.*?)\]\((.*?)\)/g;
|
76
|
+
const match = regex.exec(event.payload.text);
|
77
|
+
const [, _linkText, linkUrl] = match || [];
|
78
|
+
if (linkUrl?.startsWith('http')) {
|
79
|
+
url = linkUrl;
|
80
|
+
break;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
conversations.push({
|
85
|
+
id,
|
86
|
+
description: initialPrompt,
|
87
|
+
title: title || 'New system',
|
88
|
+
url,
|
89
|
+
});
|
90
|
+
}
|
91
|
+
catch (e) {
|
92
|
+
console.error('Failed to load conversation at %s', file, e);
|
67
93
|
}
|
68
|
-
conversations.push({
|
69
|
-
id,
|
70
|
-
description: initialPrompt,
|
71
|
-
title: title || 'New system',
|
72
|
-
});
|
73
94
|
}
|
74
95
|
return conversations;
|
75
96
|
}
|
package/package.json
CHANGED
package/src/storm/events.ts
CHANGED
@@ -480,7 +480,16 @@ export interface StormEventSystemReady {
|
|
480
480
|
created: number;
|
481
481
|
payload: {
|
482
482
|
systemUrl: string;
|
483
|
-
}
|
483
|
+
};
|
484
|
+
}
|
485
|
+
|
486
|
+
export interface StormEventModelResponse {
|
487
|
+
type: 'MODEL_RESPONSE';
|
488
|
+
reason: string;
|
489
|
+
created: number;
|
490
|
+
payload: {
|
491
|
+
text: string;
|
492
|
+
};
|
484
493
|
}
|
485
494
|
|
486
495
|
export type StormEvent =
|
@@ -518,4 +527,5 @@ export type StormEvent =
|
|
518
527
|
| StormEventApiBase
|
519
528
|
| StormEventUIStarted
|
520
529
|
| StormImage
|
521
|
-
| StormEventSystemReady
|
530
|
+
| StormEventSystemReady
|
531
|
+
| StormEventModelResponse;
|
package/src/storm/routes.ts
CHANGED
@@ -165,6 +165,7 @@ router.post('/ui/create-system/:handle/:systemId', async (req: KapetaBodyRequest
|
|
165
165
|
const pagesWithImplementation = await stormClient.replaceMockWithAPICall({
|
166
166
|
pages: pagesFromDisk,
|
167
167
|
});
|
168
|
+
|
168
169
|
await copyDirectory(srcDir, destDir, (fileName, content) => {
|
169
170
|
// find the page from result1 and write the content to the file
|
170
171
|
const page = pagesWithImplementation.find((p) => p.fileName === fileName);
|
package/src/stormService.ts
CHANGED
@@ -3,7 +3,7 @@ import { glob } from 'glob';
|
|
3
3
|
import { filesystemManager } from './filesystemManager';
|
4
4
|
import path from 'path';
|
5
5
|
import { existsSync } from 'fs';
|
6
|
-
import { StormEvent, StormEventPromptImprove, StormEventUIShell } from './storm/events';
|
6
|
+
import { StormEvent, StormEventModelResponse, StormEventPromptImprove, StormEventUIShell } from './storm/events';
|
7
7
|
import * as tar from 'tar';
|
8
8
|
import { stormClient } from './storm/stormClient';
|
9
9
|
|
@@ -28,39 +28,61 @@ export class StormService {
|
|
28
28
|
absolute: true,
|
29
29
|
});
|
30
30
|
// Returns list of UUIDs - probably want to make it more useful than that
|
31
|
-
const conversations: { id: string; description: string; title: string }[] = [];
|
31
|
+
const conversations: { id: string; description: string; title: string; url?: string }[] = [];
|
32
32
|
for (const file of eventFiles) {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
33
|
+
try {
|
34
|
+
const nldContents = await fs.readFile(file as string, 'utf8');
|
35
|
+
const events = nldContents.split('\n').map((e) => JSON.parse(e)) as {
|
36
|
+
// | { type: 'USER'; event: any } // IS stupid!
|
37
|
+
type: 'AI';
|
38
|
+
systemId: string;
|
39
|
+
event: StormEvent;
|
40
|
+
reason: string;
|
41
|
+
}[];
|
42
|
+
// find the shell and get the title tag
|
43
|
+
const shellEvent = events.find((e) => e.type === 'AI' && e.event.type === 'UI_SHELL')?.event as
|
44
|
+
| StormEventUIShell
|
45
|
+
| undefined;
|
46
|
+
const html = shellEvent?.payload.content;
|
47
|
+
const title = html?.match(/<title>(.*?)<\/title>/)?.[1];
|
48
|
+
const id = events.find((e) => e.type === 'AI')?.systemId;
|
49
|
+
|
50
|
+
const initialPrompt =
|
51
|
+
(
|
52
|
+
events.find((e) => e.type === 'AI' && e.event.type === 'PROMPT_IMPROVE')?.event as
|
53
|
+
| StormEventPromptImprove
|
54
|
+
| undefined
|
55
|
+
)?.payload?.prompt || (events[0] as any).text;
|
56
|
+
|
57
|
+
if (!id) {
|
58
|
+
continue;
|
59
|
+
}
|
60
|
+
|
61
|
+
let url = undefined;
|
62
|
+
// Find the last model response event that has a URL in the payload (in case it changed over time)
|
63
|
+
for (const evt of [...events].reverse()) {
|
64
|
+
const event = evt.event;
|
65
|
+
if (evt.type === 'AI' && event.type === 'MODEL_RESPONSE') {
|
66
|
+
// Look for a URL in the model response markdown
|
67
|
+
const regex = /\[(.*?)\]\((.*?)\)/g;
|
68
|
+
const match = regex.exec(event.payload.text);
|
69
|
+
const [, _linkText, linkUrl] = match || [];
|
70
|
+
if (linkUrl?.startsWith('http')) {
|
71
|
+
url = linkUrl;
|
72
|
+
break;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
conversations.push({
|
78
|
+
id,
|
79
|
+
description: initialPrompt,
|
80
|
+
title: title || 'New system',
|
81
|
+
url,
|
82
|
+
});
|
83
|
+
} catch (e) {
|
84
|
+
console.error('Failed to load conversation at %s', file, e);
|
57
85
|
}
|
58
|
-
|
59
|
-
conversations.push({
|
60
|
-
id,
|
61
|
-
description: initialPrompt,
|
62
|
-
title: title || 'New system',
|
63
|
-
});
|
64
86
|
}
|
65
87
|
|
66
88
|
return conversations;
|