codacus commited on
Commit
97b886a
·
1 Parent(s): bfaa3f7

fix: added more controlled rate for code streaming

Browse files
Files changed (2) hide show
  1. app/lib/stores/workbench.ts +14 -3
  2. app/utils/sampler.ts +49 -0
app/lib/stores/workbench.ts CHANGED
@@ -16,6 +16,7 @@ import * as nodePath from 'node:path';
16
  import { extractRelativePath } from '~/utils/diff';
17
  import { description } from '~/lib/persistence';
18
  import Cookies from 'js-cookie';
 
19
 
20
  export interface ArtifactState {
21
  id: string;
@@ -262,9 +263,9 @@ export class WorkbenchStore {
262
  this.artifacts.setKey(messageId, { ...artifact, ...state });
263
  }
264
  addAction(data: ActionCallbackData) {
265
- this._addAction(data);
266
 
267
- // this.addToExecutionQueue(()=>this._addAction(data))
268
  }
269
  async _addAction(data: ActionCallbackData) {
270
  const { messageId } = data;
@@ -280,7 +281,7 @@ export class WorkbenchStore {
280
 
281
  runAction(data: ActionCallbackData, isStreaming: boolean = false) {
282
  if (isStreaming) {
283
- this._runAction(data, isStreaming);
284
  } else {
285
  this.addToExecutionQueue(() => this._runAction(data, isStreaming));
286
  }
@@ -294,6 +295,12 @@ export class WorkbenchStore {
294
  unreachable('Artifact not found');
295
  }
296
 
 
 
 
 
 
 
297
  if (data.action.type === 'file') {
298
  const wc = await webcontainer;
299
  const fullPath = nodePath.join(wc.workdir, data.action.filePath);
@@ -323,6 +330,10 @@ export class WorkbenchStore {
323
  }
324
  }
325
 
 
 
 
 
326
  #getArtifact(id: string) {
327
  const artifacts = this.artifacts.get();
328
  return artifacts[id];
 
16
  import { extractRelativePath } from '~/utils/diff';
17
  import { description } from '~/lib/persistence';
18
  import Cookies from 'js-cookie';
19
+ import { createSampler } from '~/utils/sampler';
20
 
21
  export interface ArtifactState {
22
  id: string;
 
263
  this.artifacts.setKey(messageId, { ...artifact, ...state });
264
  }
265
  addAction(data: ActionCallbackData) {
266
+ // this._addAction(data);
267
 
268
+ this.addToExecutionQueue(() => this._addAction(data));
269
  }
270
  async _addAction(data: ActionCallbackData) {
271
  const { messageId } = data;
 
281
 
282
  runAction(data: ActionCallbackData, isStreaming: boolean = false) {
283
  if (isStreaming) {
284
+ this.actionStreamSampler(data, isStreaming);
285
  } else {
286
  this.addToExecutionQueue(() => this._runAction(data, isStreaming));
287
  }
 
295
  unreachable('Artifact not found');
296
  }
297
 
298
+ const action = artifact.runner.actions.get()[data.actionId];
299
+
300
+ if (!action || action.executed) {
301
+ return;
302
+ }
303
+
304
  if (data.action.type === 'file') {
305
  const wc = await webcontainer;
306
  const fullPath = nodePath.join(wc.workdir, data.action.filePath);
 
330
  }
331
  }
332
 
333
+ actionStreamSampler = createSampler(async (data: ActionCallbackData, isStreaming: boolean = false) => {
334
+ return await this._runAction(data, isStreaming);
335
+ }, 100); // TODO: remove this magic number to have it configurable
336
+
337
  #getArtifact(id: string) {
338
  const artifacts = this.artifacts.get();
339
  return artifacts[id];
app/utils/sampler.ts ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Creates a function that samples calls at regular intervals and captures trailing calls.
3
+ * - Drops calls that occur between sampling intervals
4
+ * - Takes one call per sampling interval if available
5
+ * - Captures the last call if no call was made during the interval
6
+ *
7
+ * @param fn The function to sample
8
+ * @param sampleInterval How often to sample calls (in ms)
9
+ * @returns The sampled function
10
+ */
11
+ export function createSampler<T extends (...args: any[]) => any>(fn: T, sampleInterval: number): T {
12
+ let lastArgs: Parameters<T> | null = null;
13
+ let lastTime = 0;
14
+ let timeout: NodeJS.Timeout | null = null;
15
+
16
+ // Create a function with the same type as the input function
17
+ const sampled = function (this: any, ...args: Parameters<T>) {
18
+ const now = Date.now();
19
+ lastArgs = args;
20
+
21
+ // If we're within the sample interval, just store the args
22
+ if (now - lastTime < sampleInterval) {
23
+ // Set up trailing call if not already set
24
+ if (!timeout) {
25
+ timeout = setTimeout(
26
+ () => {
27
+ timeout = null;
28
+ lastTime = Date.now();
29
+
30
+ if (lastArgs) {
31
+ fn.apply(this, lastArgs);
32
+ lastArgs = null;
33
+ }
34
+ },
35
+ sampleInterval - (now - lastTime),
36
+ );
37
+ }
38
+
39
+ return;
40
+ }
41
+
42
+ // If we're outside the interval, execute immediately
43
+ lastTime = now;
44
+ fn.apply(this, args);
45
+ lastArgs = null;
46
+ } as T;
47
+
48
+ return sampled;
49
+ }