feat(core): real time progress update for runs#937
Conversation
|
Warning Rate limit exceeded@amhsirak has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 32 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughAdds progress reporting to the Interpreter via a new Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant ServerInterpreter as Server Interpreter
participant CoreInterpreter as Core Interpreter
participant Socket as SocketEmitter
Client->>ServerInterpreter: Start workflow
ServerInterpreter->>CoreInterpreter: run(workflow, options with progressUpdate)
Note right of CoreInterpreter: initialize totalActions = N\nemit progressUpdate(0,N,0)
loop for each action
CoreInterpreter->>CoreInterpreter: execute action & remove from queue
CoreInterpreter-->>ServerInterpreter: progressUpdate(current, total, pct)
ServerInterpreter->>Socket: emit workflowProgress
Socket->>Client: workflowProgress update
end
Note right of CoreInterpreter: final emit => 100%
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
maxun-core/src/interpret.ts
🔇 Additional comments (2)
maxun-core/src/interpret.ts (2)
51-51: LGTM! Progress callback interface is well-defined.The
progressUpdatecallback follows the existing pattern and provides clear semantics for progress reporting.
1705-1710: Verify state cleanup for progress counters.The initialization logic is correct, but the
cleanup()method (lines 1737-1772) doesn't resettotalActionsandexecutedActions. While the check at lines 1697-1699 prevents concurrent runs, adding resets tocleanup()would ensure consistent state management.🔎 Suggested addition to cleanup() method
Add this to the
cleanup()method after line 1765:// Reset state this.isAborted = false; this.initializedWorkflow = null; + this.totalActions = 0; + this.executedActions = 0;
| private totalActions: number = 0; | ||
|
|
||
| private executedActions: number = 0; |
There was a problem hiding this comment.
Critical: Race condition on shared progress counters.
The executedActions and totalActions fields are accessed by multiple concurrent jobs (via enqueueLinks, popup handlers, and the concurrency manager) without synchronization. The increment at line 1605 is not atomic, leading to race conditions where updates can be lost between await points.
Impact:
- Progress counts will be inaccurate or corrupted
- Progress percentages may exceed 100% or never reach it
- User-facing progress UI will display incorrect values
Recommendation: Use atomic operations or introduce a lock/mutex around progress updates, or track progress per-job and aggregate at completion.
🔎 Proposed fix using a simple lock pattern
+ private progressLock: Promise<void> = Promise.resolve();
+
private totalActions: number = 0;
private executedActions: number = 0;Then wrap the progress update in line 1604-1614:
- this.executedActions++;
- const percentage = Math.round((this.executedActions / this.totalActions) * 100);
-
- if (this.options.debugChannel?.progressUpdate) {
- this.options.debugChannel.progressUpdate(
- this.executedActions,
- this.totalActions,
- percentage
- );
- }
+ // Serialize progress updates to prevent race conditions
+ this.progressLock = this.progressLock.then(async () => {
+ this.executedActions++;
+ const percentage = Math.round((this.executedActions / this.totalActions) * 100);
+
+ if (this.options.debugChannel?.progressUpdate) {
+ this.options.debugChannel.progressUpdate(
+ this.executedActions,
+ this.totalActions,
+ percentage
+ );
+ }
+ });Committable suggestion skipped: line range outside the PR's diff.
|
|
||
| this.executedActions++; | ||
| const percentage = Math.round((this.executedActions / this.totalActions) * 100); | ||
|
|
||
| if (this.options.debugChannel?.progressUpdate) { | ||
| this.options.debugChannel.progressUpdate( | ||
| this.executedActions, | ||
| this.totalActions, | ||
| percentage | ||
| ); | ||
| } |
There was a problem hiding this comment.
Consider counting failed actions in progress tracking.
Currently, when an action fails (line 1627-1629), the code continues without incrementing executedActions. This means:
- Failed actions are not reflected in progress
- Progress may never reach 100% if any actions fail
- Users won't know if the workflow is stuck or still progressing
If this is intentional (only counting successful actions), consider documenting this behavior. Otherwise, consider incrementing progress for failed actions as well.
| progressUpdate: (current: number, total: number, percentage: number) => { | ||
| this.socket.nsp.emit('workflowProgress', { | ||
| current, | ||
| total, | ||
| percentage | ||
| }); | ||
| }, |
There was a problem hiding this comment.
Add progressUpdate callback to editor mode for consistency.
The progressUpdate callback is added to InterpretRecording but is missing from interpretRecordingInEditor (line 251-263). This creates an inconsistent experience where progress updates work in run mode but not in editor mode.
🔎 Proposed fix to add progressUpdate to editor mode
Add the progressUpdate callback to the debugChannel options in interpretRecordingInEditor:
debugChannel: {
activeId: (id: any) => {
this.activeId = id;
this.socket.emit('activePairId', id);
},
debugMessage: (msg: any) => {
this.debugMessages.push(`[${new Date().toLocaleString()}] ` + msg);
this.socket.emit('log', msg)
},
setActionType: (type: string) => {
this.currentActionType = type;
- }
+ },
+ progressUpdate: (current: number, total: number, percentage: number) => {
+ this.socket.emit('workflowProgress', {
+ current,
+ total,
+ percentage
+ });
+ }
},Note: For editor mode, use socket.emit instead of socket.nsp.emit to send updates only to the connected client.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In server/src/workflow-management/classes/Interpreter.ts (around lines 583-589
and in the interpretRecordingInEditor block at ~251-263), the debugChannel
options are missing a progressUpdate callback for editor mode; add a
progressUpdate: (current: number, total: number, percentage: number) => {
this.socket.emit('workflowProgress', { current, total, percentage }); } entry to
the debugChannel/options passed into interpretRecordingInEditor so editor-mode
sends the same progress events but only to the connected client via socket.emit
instead of socket.nsp.emit.
| this.currentActionName = name; | ||
| }, | ||
| progressUpdate: (current: number, total: number, percentage: number) => { | ||
| this.socket.nsp.emit('workflowProgress', { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the context around line 584 in Interpreter.ts
cat -n server/src/workflow-management/classes/Interpreter.ts | sed -n '570,600p'Repository: getmaxun/maxun
Length of output: 1340
🏁 Script executed:
# Also, let's get a broader view of the progressUpdate function and its context
cat -n server/src/workflow-management/classes/Interpreter.ts | sed -n '550,610p'Repository: getmaxun/maxun
Length of output: 2455
🏁 Script executed:
# Check if there are any comments about the socket emit patterns
rg -n "socket\.nsp\.emit|socket\.emit" server/src/workflow-management/classes/Interpreter.ts -A 3 -B 3Repository: getmaxun/maxun
Length of output: 5187
🏁 Script executed:
# Let's also check other files in the workflow-management folder to understand the pattern
rg -n "socket\.nsp\.emit|socket\.emit" server/src/workflow-management/ -lRepository: getmaxun/maxun
Length of output: 163
🏁 Script executed:
# Check Generator.ts for socket emit patterns
rg -n "socket\.nsp\.emit|socket\.emit" server/src/workflow-management/classes/Generator.ts -A 2 -B 2 | head -100Repository: getmaxun/maxun
Length of output: 2287
Change socket.nsp.emit to socket.emit for progress updates.
socket.nsp.emit('workflowProgress', ...) at line 584 broadcasts the progress update to all clients in the namespace. This is inconsistent with the rest of the codebase and will cause all concurrent users to receive each other's progress updates.
All other event emissions in this file (and throughout the codebase) use this.socket.emit() to send to the individual client. The progressUpdate callback should do the same:
Fix
progressUpdate: (current: number, total: number, percentage: number) => {
- this.socket.nsp.emit('workflowProgress', {
+ this.socket.emit('workflowProgress', {
current,
total,
percentage
});
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| this.socket.nsp.emit('workflowProgress', { | |
| progressUpdate: (current: number, total: number, percentage: number) => { | |
| this.socket.emit('workflowProgress', { | |
| current, | |
| total, | |
| percentage | |
| }); | |
| }, |
🤖 Prompt for AI Agents
In server/src/workflow-management/classes/Interpreter.ts around line 584, the
progress update currently uses this.socket.nsp.emit which broadcasts to the
entire namespace; change it to this.socket.emit to send the update only to the
connected client. Replace the this.socket.nsp.emit call in the progressUpdate
callback with this.socket.emit keeping the same event name ('workflowProgress')
and payload structure so it matches the rest of the file's client-specific
emissions.
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.