Skip to content

Commit 4af18c0

Browse files
authored
Merge pull request #655 from thecodacus/streaming-fixed
fix: Add Code Streaming Sampling for Performance Optimization
2 parents 81d2c01 + 02d0be5 commit 4af18c0

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

app/lib/stores/workbench.ts

Lines changed: 8 additions & 2 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;
@@ -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
}
@@ -296,7 +297,8 @@ export class WorkbenchStore {
296297

297298
const action = artifact.runner.actions.get()[data.actionId];
298299

299-
if (action.executed) {
300+
301+
if (!action || action.executed) {
300302
return;
301303
}
302304

@@ -329,6 +331,10 @@ export class WorkbenchStore {
329331
}
330332
}
331333

334+
actionStreamSampler = createSampler(async (data: ActionCallbackData, isStreaming: boolean = false) => {
335+
return await this._runAction(data, isStreaming);
336+
}, 100); // TODO: remove this magic number to have it configurable
337+
332338
#getArtifact(id: string) {
333339
const artifacts = this.artifacts.get();
334340
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)