@kapeta/local-cluster-service 0.76.5 → 0.77.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,17 @@
1
+ ## [0.77.1](https://github.com/kapetacom/local-cluster-service/compare/v0.77.0...v0.77.1) (2024-10-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * disable timeouts for createSimpleBackend call ([#270](https://github.com/kapetacom/local-cluster-service/issues/270)) ([9a6ba9c](https://github.com/kapetacom/local-cluster-service/commit/9a6ba9cb09ff99a3e31e93e33c027232ddeff8fb))
7
+
8
+ # [0.77.0](https://github.com/kapetacom/local-cluster-service/compare/v0.76.5...v0.77.0) (2024-10-04)
9
+
10
+
11
+ ### Features
12
+
13
+ * Enable page agent to distinguish beween global and local edits ([1e24653](https://github.com/kapetacom/local-cluster-service/commit/1e246536272f9a141acf2233eb96fdec2a30c709))
14
+
1
15
  ## [0.76.5](https://github.com/kapetacom/local-cluster-service/compare/v0.76.4...v0.76.5) (2024-10-03)
2
16
 
3
17
 
@@ -41,9 +41,11 @@ export declare class PageQueue extends EventEmitter {
41
41
  addUiShell(uiShell: UIShell): void;
42
42
  setUiTheme(theme: string): void;
43
43
  private hasPrompt;
44
- addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean): Promise<void>;
45
- private getPrefix;
46
- private wrapPagePrompt;
44
+ addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean, globalEdit?: boolean): Promise<void>;
45
+ /**
46
+ * Get the existing pages
47
+ */
48
+ private getExistingPages;
47
49
  private processPageEventWithReferences;
48
50
  cancel(): void;
49
51
  wait(): Promise<void>;
@@ -87,7 +87,9 @@ class PageQueue extends node_events_1.EventEmitter {
87
87
  }
88
88
  return false;
89
89
  }
90
- addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false) {
90
+ addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false,
91
+ // Set globalEdit to true when the same prompt is being sent to multiple pages
92
+ globalEdit = false) {
91
93
  if (!overwrite && this.hasPrompt(initialPrompt.path)) {
92
94
  //console.log('Ignoring duplicate prompt', initialPrompt.path);
93
95
  return Promise.resolve();
@@ -96,39 +98,26 @@ class PageQueue extends node_events_1.EventEmitter {
96
98
  const prompt = {
97
99
  ...initialPrompt,
98
100
  shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
99
- prompt: this.wrapPagePrompt(initialPrompt.path, initialPrompt.prompt),
101
+ prompt: initialPrompt.prompt,
100
102
  theme: this.theme,
103
+ global_edit: globalEdit,
104
+ system_prompt: this.systemPrompt,
105
+ existing_pages: this.getExistingPages(initialPrompt.path),
106
+ existing_images: Object.entries(this.images).map(([path, description]) => ({ path, description })),
101
107
  };
102
108
  this.references.set(prompt.path, true);
103
109
  this.pages.set(prompt.path, prompt.description);
104
110
  return this.queue.add(() => this.generate(prompt, conversationId));
105
111
  }
106
- getPrefix() {
107
- let promptPrefix = '';
108
- if (this.systemPrompt) {
109
- promptPrefix = `For a system with this description: ${this.systemPrompt}\n`;
110
- }
111
- return promptPrefix;
112
- }
113
- wrapPagePrompt(pagePath, prompt) {
114
- const promptPrefix = this.getPrefix();
115
- let promptPostfix = '';
116
- if (this.pages.size > 0) {
117
- promptPostfix = `\nThe following pages are already implemented:\n`;
118
- this.pages.forEach((description, path) => {
119
- if (pagePath === path) {
120
- return;
121
- }
122
- promptPostfix += `- PAGE: '${path}' -> ${description}.\n`;
123
- });
124
- }
125
- if (this.images.size > 0) {
126
- promptPostfix += `\nThe following images already exist:\n`;
127
- this.images.forEach((description, path) => {
128
- promptPostfix += `- IMAGE: '${path}' -> ${description}.\n`;
129
- });
130
- }
131
- return promptPrefix + prompt + promptPostfix;
112
+ /**
113
+ * Get the existing pages
114
+ */
115
+ getExistingPages(excludePath) {
116
+ return (Object.entries(this.pages)
117
+ // Possibly exclude one page. This is useful when we don't want to include the page
118
+ // we're currently editing.
119
+ .filter(([path]) => (excludePath ? path !== excludePath : true))
120
+ .map(([path, description]) => ({ path, description })));
132
121
  }
133
122
  async processPageEventWithReferences(event) {
134
123
  try {
@@ -176,9 +165,17 @@ class PageQueue extends node_events_1.EventEmitter {
176
165
  path: normalizedPath,
177
166
  method: 'GET',
178
167
  storage_prefix: this.systemId + '_',
179
- prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}.\n` +
180
- `The page was referenced from this page: \n### PATH: ${event.payload.path}\n\`\`\`html\n${event.payload.content}\n\`\`\`\n`,
168
+ prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}`,
181
169
  description: reference.description,
170
+ referenced_from: {
171
+ path: event.payload.path,
172
+ content: event.payload.content,
173
+ },
174
+ existing_pages: this.getExistingPages(),
175
+ existing_images: Object.entries(this.images).map(([path, description]) => ({
176
+ path,
177
+ description,
178
+ })),
182
179
  // Only used for matching
183
180
  filename: reference.name + '.ref.html',
184
181
  theme: this.theme,
@@ -226,6 +226,9 @@ router.delete('/ui/serve/:systemId', async (req, res) => {
226
226
  }
227
227
  res.status(200).json({ status: 'ok' });
228
228
  });
229
+ /**
230
+ * Edit a single page
231
+ */
229
232
  router.post('/:handle/ui/screen', async (req, res) => {
230
233
  try {
231
234
  const handle = req.params.handle;
@@ -540,6 +543,9 @@ router.post('/:handle/ui', async (req, res) => {
540
543
  }
541
544
  }
542
545
  });
546
+ /**
547
+ * Edit all pages
548
+ */
543
549
  router.post('/:handle/ui/edit', async (req, res) => {
544
550
  try {
545
551
  const handle = req.params.handle;
@@ -586,7 +592,8 @@ router.post('/:handle/ui/edit', async (req, res) => {
586
592
  filename: page.filename,
587
593
  prompt: aiRequest.prompt.prompt.prompt,
588
594
  storage_prefix: storagePrefix,
589
- }, page.conversationId, true);
595
+ }, page.conversationId, true, true // this is a global edit
596
+ );
590
597
  }
591
598
  }));
592
599
  await queue.wait();
@@ -28,6 +28,20 @@ export interface UIPagePrompt {
28
28
  storage_prefix: string;
29
29
  shell_page?: string;
30
30
  theme?: string;
31
+ global_edit?: boolean;
32
+ system_prompt?: string;
33
+ existing_pages?: {
34
+ path: string;
35
+ description: string;
36
+ }[];
37
+ existing_images?: {
38
+ path: string;
39
+ description: string;
40
+ }[];
41
+ referenced_from?: {
42
+ path: string;
43
+ content: string;
44
+ };
31
45
  }
32
46
  export interface UIPageSamplePrompt extends UIPagePrompt {
33
47
  variantId: string;
@@ -14,6 +14,7 @@ const promises_1 = __importDefault(require("node:readline/promises"));
14
14
  const node_stream_1 = require("node:stream");
15
15
  const stream_1 = require("./stream");
16
16
  const fetch_retry_1 = __importDefault(require("fetch-retry"));
17
+ const undici_1 = require("undici");
17
18
  const fetchWithRetries = (0, fetch_retry_1.default)(global.fetch, { retries: 5, retryDelay: 10 });
18
19
  exports.STORM_ID = 'storm';
19
20
  exports.ConversationIdHeader = 'Conversation-Id';
@@ -189,12 +190,18 @@ class StormClient {
189
190
  headers[exports.HandleHeader] = this._handle;
190
191
  headers[exports.ConversationIdHeader] = this._systemId;
191
192
  headers[exports.SystemIdHeader] = this._systemId;
192
- const response = await fetch(u, {
193
+ // Fetch with undici to override the default timeouts
194
+ const response = await (0, undici_1.fetch)(u, {
193
195
  method: 'POST',
194
196
  body: JSON.stringify({
195
197
  pages: input.pages,
196
198
  }),
197
199
  headers: headers,
200
+ dispatcher: new undici_1.Agent({
201
+ headersTimeout: 0,
202
+ keepAliveTimeout: 0,
203
+ bodyTimeout: 0,
204
+ }),
198
205
  });
199
206
  if (!response.ok) {
200
207
  throw new Error(`HTTP error! Status: ${response.status}`);
@@ -41,9 +41,11 @@ export declare class PageQueue extends EventEmitter {
41
41
  addUiShell(uiShell: UIShell): void;
42
42
  setUiTheme(theme: string): void;
43
43
  private hasPrompt;
44
- addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean): Promise<void>;
45
- private getPrefix;
46
- private wrapPagePrompt;
44
+ addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean, globalEdit?: boolean): Promise<void>;
45
+ /**
46
+ * Get the existing pages
47
+ */
48
+ private getExistingPages;
47
49
  private processPageEventWithReferences;
48
50
  cancel(): void;
49
51
  wait(): Promise<void>;
@@ -87,7 +87,9 @@ class PageQueue extends node_events_1.EventEmitter {
87
87
  }
88
88
  return false;
89
89
  }
90
- addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false) {
90
+ addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false,
91
+ // Set globalEdit to true when the same prompt is being sent to multiple pages
92
+ globalEdit = false) {
91
93
  if (!overwrite && this.hasPrompt(initialPrompt.path)) {
92
94
  //console.log('Ignoring duplicate prompt', initialPrompt.path);
93
95
  return Promise.resolve();
@@ -96,39 +98,26 @@ class PageQueue extends node_events_1.EventEmitter {
96
98
  const prompt = {
97
99
  ...initialPrompt,
98
100
  shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
99
- prompt: this.wrapPagePrompt(initialPrompt.path, initialPrompt.prompt),
101
+ prompt: initialPrompt.prompt,
100
102
  theme: this.theme,
103
+ global_edit: globalEdit,
104
+ system_prompt: this.systemPrompt,
105
+ existing_pages: this.getExistingPages(initialPrompt.path),
106
+ existing_images: Object.entries(this.images).map(([path, description]) => ({ path, description })),
101
107
  };
102
108
  this.references.set(prompt.path, true);
103
109
  this.pages.set(prompt.path, prompt.description);
104
110
  return this.queue.add(() => this.generate(prompt, conversationId));
105
111
  }
106
- getPrefix() {
107
- let promptPrefix = '';
108
- if (this.systemPrompt) {
109
- promptPrefix = `For a system with this description: ${this.systemPrompt}\n`;
110
- }
111
- return promptPrefix;
112
- }
113
- wrapPagePrompt(pagePath, prompt) {
114
- const promptPrefix = this.getPrefix();
115
- let promptPostfix = '';
116
- if (this.pages.size > 0) {
117
- promptPostfix = `\nThe following pages are already implemented:\n`;
118
- this.pages.forEach((description, path) => {
119
- if (pagePath === path) {
120
- return;
121
- }
122
- promptPostfix += `- PAGE: '${path}' -> ${description}.\n`;
123
- });
124
- }
125
- if (this.images.size > 0) {
126
- promptPostfix += `\nThe following images already exist:\n`;
127
- this.images.forEach((description, path) => {
128
- promptPostfix += `- IMAGE: '${path}' -> ${description}.\n`;
129
- });
130
- }
131
- return promptPrefix + prompt + promptPostfix;
112
+ /**
113
+ * Get the existing pages
114
+ */
115
+ getExistingPages(excludePath) {
116
+ return (Object.entries(this.pages)
117
+ // Possibly exclude one page. This is useful when we don't want to include the page
118
+ // we're currently editing.
119
+ .filter(([path]) => (excludePath ? path !== excludePath : true))
120
+ .map(([path, description]) => ({ path, description })));
132
121
  }
133
122
  async processPageEventWithReferences(event) {
134
123
  try {
@@ -176,9 +165,17 @@ class PageQueue extends node_events_1.EventEmitter {
176
165
  path: normalizedPath,
177
166
  method: 'GET',
178
167
  storage_prefix: this.systemId + '_',
179
- prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}.\n` +
180
- `The page was referenced from this page: \n### PATH: ${event.payload.path}\n\`\`\`html\n${event.payload.content}\n\`\`\`\n`,
168
+ prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}`,
181
169
  description: reference.description,
170
+ referenced_from: {
171
+ path: event.payload.path,
172
+ content: event.payload.content,
173
+ },
174
+ existing_pages: this.getExistingPages(),
175
+ existing_images: Object.entries(this.images).map(([path, description]) => ({
176
+ path,
177
+ description,
178
+ })),
182
179
  // Only used for matching
183
180
  filename: reference.name + '.ref.html',
184
181
  theme: this.theme,
@@ -226,6 +226,9 @@ router.delete('/ui/serve/:systemId', async (req, res) => {
226
226
  }
227
227
  res.status(200).json({ status: 'ok' });
228
228
  });
229
+ /**
230
+ * Edit a single page
231
+ */
229
232
  router.post('/:handle/ui/screen', async (req, res) => {
230
233
  try {
231
234
  const handle = req.params.handle;
@@ -540,6 +543,9 @@ router.post('/:handle/ui', async (req, res) => {
540
543
  }
541
544
  }
542
545
  });
546
+ /**
547
+ * Edit all pages
548
+ */
543
549
  router.post('/:handle/ui/edit', async (req, res) => {
544
550
  try {
545
551
  const handle = req.params.handle;
@@ -586,7 +592,8 @@ router.post('/:handle/ui/edit', async (req, res) => {
586
592
  filename: page.filename,
587
593
  prompt: aiRequest.prompt.prompt.prompt,
588
594
  storage_prefix: storagePrefix,
589
- }, page.conversationId, true);
595
+ }, page.conversationId, true, true // this is a global edit
596
+ );
590
597
  }
591
598
  }));
592
599
  await queue.wait();
@@ -28,6 +28,20 @@ export interface UIPagePrompt {
28
28
  storage_prefix: string;
29
29
  shell_page?: string;
30
30
  theme?: string;
31
+ global_edit?: boolean;
32
+ system_prompt?: string;
33
+ existing_pages?: {
34
+ path: string;
35
+ description: string;
36
+ }[];
37
+ existing_images?: {
38
+ path: string;
39
+ description: string;
40
+ }[];
41
+ referenced_from?: {
42
+ path: string;
43
+ content: string;
44
+ };
31
45
  }
32
46
  export interface UIPageSamplePrompt extends UIPagePrompt {
33
47
  variantId: string;
@@ -14,6 +14,7 @@ const promises_1 = __importDefault(require("node:readline/promises"));
14
14
  const node_stream_1 = require("node:stream");
15
15
  const stream_1 = require("./stream");
16
16
  const fetch_retry_1 = __importDefault(require("fetch-retry"));
17
+ const undici_1 = require("undici");
17
18
  const fetchWithRetries = (0, fetch_retry_1.default)(global.fetch, { retries: 5, retryDelay: 10 });
18
19
  exports.STORM_ID = 'storm';
19
20
  exports.ConversationIdHeader = 'Conversation-Id';
@@ -189,12 +190,18 @@ class StormClient {
189
190
  headers[exports.HandleHeader] = this._handle;
190
191
  headers[exports.ConversationIdHeader] = this._systemId;
191
192
  headers[exports.SystemIdHeader] = this._systemId;
192
- const response = await fetch(u, {
193
+ // Fetch with undici to override the default timeouts
194
+ const response = await (0, undici_1.fetch)(u, {
193
195
  method: 'POST',
194
196
  body: JSON.stringify({
195
197
  pages: input.pages,
196
198
  }),
197
199
  headers: headers,
200
+ dispatcher: new undici_1.Agent({
201
+ headersTimeout: 0,
202
+ keepAliveTimeout: 0,
203
+ bodyTimeout: 0,
204
+ }),
198
205
  });
199
206
  if (!response.ok) {
200
207
  throw new Error(`HTTP error! Status: ${response.status}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.76.5",
3
+ "version": "0.77.1",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -90,6 +90,7 @@
90
90
  "tar": "^7.4.3",
91
91
  "tar-stream": "^3.1.6",
92
92
  "typescript": "^5.1.6",
93
+ "undici": "^6.19.8",
93
94
  "uuid": "^9.0.1",
94
95
  "yaml": "^1.6.0"
95
96
  },
@@ -95,7 +95,13 @@ export class PageQueue extends EventEmitter {
95
95
  return false;
96
96
  }
97
97
 
98
- public addPrompt(initialPrompt: InitialPrompt, conversationId: string = uuid.v4(), overwrite: boolean = false) {
98
+ public addPrompt(
99
+ initialPrompt: InitialPrompt,
100
+ conversationId: string = uuid.v4(),
101
+ overwrite: boolean = false,
102
+ // Set globalEdit to true when the same prompt is being sent to multiple pages
103
+ globalEdit: boolean = false
104
+ ) {
99
105
  if (!overwrite && this.hasPrompt(initialPrompt.path)) {
100
106
  //console.log('Ignoring duplicate prompt', initialPrompt.path);
101
107
  return Promise.resolve();
@@ -105,8 +111,12 @@ export class PageQueue extends EventEmitter {
105
111
  const prompt: UIPagePrompt = {
106
112
  ...initialPrompt,
107
113
  shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
108
- prompt: this.wrapPagePrompt(initialPrompt.path, initialPrompt.prompt),
114
+ prompt: initialPrompt.prompt,
109
115
  theme: this.theme,
116
+ global_edit: globalEdit,
117
+ system_prompt: this.systemPrompt,
118
+ existing_pages: this.getExistingPages(initialPrompt.path),
119
+ existing_images: Object.entries(this.images).map(([path, description]) => ({ path, description })),
110
120
  };
111
121
 
112
122
  this.references.set(prompt.path, true);
@@ -114,36 +124,18 @@ export class PageQueue extends EventEmitter {
114
124
 
115
125
  return this.queue.add<void>(() => this.generate(prompt, conversationId));
116
126
  }
117
- private getPrefix(): string {
118
- let promptPrefix = '';
119
- if (this.systemPrompt) {
120
- promptPrefix = `For a system with this description: ${this.systemPrompt}\n`;
121
- }
122
- return promptPrefix;
123
- }
124
-
125
- private wrapPagePrompt(pagePath: string, prompt: string): string {
126
- const promptPrefix = this.getPrefix();
127
- let promptPostfix = '';
128
-
129
- if (this.pages.size > 0) {
130
- promptPostfix = `\nThe following pages are already implemented:\n`;
131
- this.pages.forEach((description, path) => {
132
- if (pagePath === path) {
133
- return;
134
- }
135
- promptPostfix += `- PAGE: '${path}' -> ${description}.\n`;
136
- });
137
- }
138
-
139
- if (this.images.size > 0) {
140
- promptPostfix += `\nThe following images already exist:\n`;
141
- this.images.forEach((description, path) => {
142
- promptPostfix += `- IMAGE: '${path}' -> ${description}.\n`;
143
- });
144
- }
145
127
 
146
- return promptPrefix + prompt + promptPostfix;
128
+ /**
129
+ * Get the existing pages
130
+ */
131
+ private getExistingPages(excludePath?: string) {
132
+ return (
133
+ Object.entries(this.pages)
134
+ // Possibly exclude one page. This is useful when we don't want to include the page
135
+ // we're currently editing.
136
+ .filter(([path]) => (excludePath ? path !== excludePath : true))
137
+ .map(([path, description]) => ({ path, description }))
138
+ );
147
139
  }
148
140
 
149
141
  private async processPageEventWithReferences(event: StormEventPage) {
@@ -198,10 +190,17 @@ export class PageQueue extends EventEmitter {
198
190
  path: normalizedPath,
199
191
  method: 'GET',
200
192
  storage_prefix: this.systemId + '_',
201
- prompt:
202
- `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}.\n` +
203
- `The page was referenced from this page: \n### PATH: ${event.payload.path}\n\`\`\`html\n${event.payload.content}\n\`\`\`\n`,
193
+ prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}`,
204
194
  description: reference.description,
195
+ referenced_from: {
196
+ path: event.payload.path,
197
+ content: event.payload.content,
198
+ },
199
+ existing_pages: this.getExistingPages(),
200
+ existing_images: Object.entries(this.images).map(([path, description]) => ({
201
+ path,
202
+ description,
203
+ })),
205
204
  // Only used for matching
206
205
  filename: reference.name + '.ref.html',
207
206
  theme: this.theme,
@@ -293,6 +293,9 @@ router.delete('/ui/serve/:systemId', async (req: KapetaBodyRequest, res: Respons
293
293
  res.status(200).json({ status: 'ok' });
294
294
  });
295
295
 
296
+ /**
297
+ * Edit a single page
298
+ */
296
299
  router.post('/:handle/ui/screen', async (req: KapetaBodyRequest, res: Response) => {
297
300
  try {
298
301
  const handle = req.params.handle as string;
@@ -673,6 +676,9 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
673
676
  }
674
677
  });
675
678
 
679
+ /**
680
+ * Edit all pages
681
+ */
676
682
  router.post('/:handle/ui/edit', async (req: KapetaBodyRequest, res: Response) => {
677
683
  try {
678
684
  const handle = req.params.handle as string;
@@ -732,7 +738,8 @@ router.post('/:handle/ui/edit', async (req: KapetaBodyRequest, res: Response) =>
732
738
  storage_prefix: storagePrefix,
733
739
  },
734
740
  page.conversationId,
735
- true
741
+ true,
742
+ true // this is a global edit
736
743
  );
737
744
  }
738
745
  })
@@ -19,6 +19,7 @@ import {
19
19
  } from './stream';
20
20
  import { Page, StormEventPageUrl } from './events';
21
21
  import createFetch from 'fetch-retry';
22
+ import { Agent, fetch as undiciFetch } from 'undici';
22
23
  const fetchWithRetries = createFetch(global.fetch, { retries: 5, retryDelay: 10 });
23
24
 
24
25
  export const STORM_ID = 'storm';
@@ -51,6 +52,25 @@ export interface UIPagePrompt {
51
52
  shell_page?: string;
52
53
  // contents of theme.md
53
54
  theme?: string;
55
+ // whether this prompt is for a global edit
56
+ global_edit?: boolean;
57
+ // The prompt that was used to start the conversation (user prompt or improved prompt)
58
+ system_prompt?: string;
59
+ // The pages we have already created
60
+ existing_pages?: {
61
+ path: string;
62
+ description: string;
63
+ }[];
64
+ // The images we have already created
65
+ existing_images?: {
66
+ path: string;
67
+ description: string;
68
+ }[];
69
+ // The page that referenced this page
70
+ referenced_from?: {
71
+ path: string;
72
+ content: string;
73
+ };
54
74
  }
55
75
 
56
76
  export interface UIPageSamplePrompt extends UIPagePrompt {
@@ -299,12 +319,18 @@ export class StormClient {
299
319
  headers[ConversationIdHeader] = this._systemId;
300
320
  headers[SystemIdHeader] = this._systemId;
301
321
 
302
- const response = await fetch(u, {
322
+ // Fetch with undici to override the default timeouts
323
+ const response = await undiciFetch(u, {
303
324
  method: 'POST',
304
325
  body: JSON.stringify({
305
326
  pages: input.pages,
306
327
  }),
307
328
  headers: headers,
329
+ dispatcher: new Agent({
330
+ headersTimeout: 0,
331
+ keepAliveTimeout: 0,
332
+ bodyTimeout: 0,
333
+ }),
308
334
  });
309
335
  if (!response.ok) {
310
336
  throw new Error(`HTTP error! Status: ${response.status}`);