Skip to content

Commit 636db40

Browse files
committed
fix: added more controlled rate for code streaming
1 parent 0a6ff76 commit 636db40

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

app/lib/stores/workbench.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import * as nodePath from 'node:path';
1616
import { extractRelativePath } from '~/utils/diff';
1717
import { description } from '~/lib/persistence';
1818
import Cookies from 'js-cookie';
19+
import { createSampler } from '~/utils/sampler';
1920

2021
export interface ArtifactState {
2122
id: string;
@@ -262,9 +263,9 @@ export class WorkbenchStore {
262263
this.artifacts.setKey(messageId, { ...artifact, ...state });
263264
}
264265
addAction(data: ActionCallbackData) {
265-
this._addAction(data);
266+
// this._addAction(data);
266267

267-
// this.addToExecutionQueue(()=>this._addAction(data))
268+
this.addToExecutionQueue(() => this._addAction(data));
268269
}
269270
async _addAction(data: ActionCallbackData) {
270271
const { messageId } = data;
@@ -280,7 +281,7 @@ export class WorkbenchStore {
280281

281282
runAction(data: ActionCallbackData, isStreaming: boolean = false) {
282283
if (isStreaming) {
283-
this._runAction(data, isStreaming);
284+
this.actionStreamSampler(data, isStreaming);
284285
} else {
285286
this.addToExecutionQueue(() => this._runAction(data, isStreaming));
286287
}
@@ -294,6 +295,12 @@ export class WorkbenchStore {
294295
unreachable('Artifact not found');
295296
}
296297

298+
const action = artifact.runner.actions.get()[data.actionId];
299+
300+
if (!action || action.executed) {
301+
return;
302+
}
303+
297304
if (data.action.type === 'file') {
298305
const wc = await webcontainer;
299306
const fullPath = nodePath.join(wc.workdir, data.action.filePath);
@@ -323,6 +330,10 @@ export class WorkbenchStore {
323330
}
324331
}
325332

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+
326337
#getArtifact(id: string) {
327338
const artifacts = this.artifacts.get();
328339
return artifacts[id];

app/utils/sampler.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

0 commit comments

Comments
 (0)