-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat: allow training multiple capture actions in one recording session #562
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
7fe982a
c6266fd
cd4820f
bd5087e
35e7778
00ef3ba
6243563
6376fd6
b5c5ed7
42b56d7
82d6f70
a00e69e
a7771cf
f975862
8d06146
4ed3160
2ffbdc7
109afff
882b25c
01ab958
c7e3a66
f1d0cbd
b4e3ccd
f1c1488
0c5e98c
d0f284c
f65dda0
302ec00
e5f63be
9eeb367
458392d
db890e0
e6a7fdf
2b04634
c202a50
cf5be61
47ed5ce
7c7116a
1f06bcd
392a5fd
624d7fc
9bc9815
94fecc1
9c57824
decf14a
b72d0dc
43b7a7d
02a150d
b83fcb2
db25627
5cd756c
3b618d8
6419d31
daa9779
eed8ff3
7b6de7d
f5df4c9
e09e794
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -255,7 +255,6 @@ async function processRunExecution(job: Job<ExecuteRunData>) { | |
return { success: true }; | ||
} | ||
|
||
// Process the results | ||
const binaryOutputService = new BinaryOutputService('maxun-run-screenshots'); | ||
const uploadedBinaryOutput = await binaryOutputService.uploadAndStoreBinaryOutput(run, interpretationInfo.binaryOutput); | ||
|
||
|
@@ -264,36 +263,57 @@ async function processRunExecution(job: Job<ExecuteRunData>) { | |
return { success: true }; | ||
} | ||
|
||
// Update the run record with results | ||
const categorizedOutput = { | ||
scrapeSchema: interpretationInfo.scrapeSchemaOutput || {}, | ||
scrapeList: interpretationInfo.scrapeListOutput || {}, | ||
other: interpretationInfo.otherOutput || {} | ||
}; | ||
|
||
await run.update({ | ||
...run, | ||
status: 'success', | ||
finishedAt: new Date().toLocaleString(), | ||
browserId: plainRun.browserId, | ||
log: interpretationInfo.log.join('\n'), | ||
serializableOutput: interpretationInfo.serializableOutput, | ||
serializableOutput: { | ||
scrapeSchema: Object.values(categorizedOutput.scrapeSchema), | ||
scrapeList: Object.values(categorizedOutput.scrapeList), | ||
other: Object.values(categorizedOutput.other), | ||
}, | ||
binaryOutput: uploadedBinaryOutput, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainStoring
- serializableOutput: {
- scrapeSchema: Object.values(categorizedOutput.scrapeSchema),
- scrapeList: Object.values(categorizedOutput.scrapeList),
- other: Object.values(categorizedOutput.other),
- },
+ // Keep the keyed structure to preserve traceability
+ serializableOutput: categorizedOutput, Are downstream readers (e.g. analytics, UI) prepared for this schema change? Please double-check before merging. I’d like to inspect how 🏁 Script executed: #!/bin/bash
set -e
echo "Searching for collectDataByType usage/definition..."
rg "collectDataByType" -n .
echo ""
echo "Searching for categorizedOutput references..."
rg "categorizedOutput" -n .
echo ""
echo "Searching for serializableOutput usages..."
rg "serializableOutput" -n .
echo ""
echo "Displaying lines 250–340 in server/src/pgboss-worker.ts for context..."
sed -n '250,340p' server/src/pgboss-worker.ts Length of output: 9378 I want to confirm how items are keyed and how the UI expects 🏁 Script executed: #!/bin/bash
set -e
echo "1) collectDataByType definition (around line 566):"
sed -n '550,620p' server/src/pgboss-worker.ts
echo ""
echo "2) RunContent.tsx usage (first 200 lines):"
sed -n '1,200p' src/components/run/RunContent.tsx Length of output: 10556 Review serializableOutput schema and downstream compatibility Affected locations: Next steps:
Until all of these are updated, please do not swap in
|
||
}); | ||
|
||
// Track extraction metrics | ||
let totalRowsExtracted = 0; | ||
let totalSchemaItemsExtracted = 0; | ||
let totalListItemsExtracted = 0; | ||
let extractedScreenshotsCount = 0; | ||
let extractedItemsCount = 0; | ||
|
||
if (run.dataValues.binaryOutput && run.dataValues.binaryOutput["item-0"]) { | ||
extractedScreenshotsCount = 1; | ||
|
||
if (categorizedOutput.scrapeSchema) { | ||
Object.values(categorizedOutput.scrapeSchema).forEach((schemaResult: any) => { | ||
if (Array.isArray(schemaResult)) { | ||
totalSchemaItemsExtracted += schemaResult.length; | ||
} else if (schemaResult && typeof schemaResult === 'object') { | ||
totalSchemaItemsExtracted += 1; | ||
} | ||
}); | ||
} | ||
|
||
if (run.dataValues.serializableOutput && run.dataValues.serializableOutput["item-0"]) { | ||
const itemsArray = run.dataValues.serializableOutput["item-0"]; | ||
extractedItemsCount = itemsArray.length; | ||
|
||
totalRowsExtracted = itemsArray.reduce((total, item) => { | ||
return total + Object.keys(item).length; | ||
}, 0); | ||
|
||
if (categorizedOutput.scrapeList) { | ||
Object.values(categorizedOutput.scrapeList).forEach((listResult: any) => { | ||
if (Array.isArray(listResult)) { | ||
totalListItemsExtracted += listResult.length; | ||
} | ||
}); | ||
} | ||
|
||
console.log(`Extracted Items Count: ${extractedItemsCount}`); | ||
|
||
if (uploadedBinaryOutput) { | ||
extractedScreenshotsCount = Object.keys(uploadedBinaryOutput).length; | ||
} | ||
|
||
const totalRowsExtracted = totalSchemaItemsExtracted + totalListItemsExtracted; | ||
|
||
console.log(`Extracted Schema Items Count: ${totalSchemaItemsExtracted}`); | ||
console.log(`Extracted List Items Count: ${totalListItemsExtracted}`); | ||
console.log(`Extracted Screenshots Count: ${extractedScreenshotsCount}`); | ||
console.log(`Total Rows Extracted: ${totalRowsExtracted}`); | ||
|
||
|
@@ -306,7 +326,8 @@ async function processRunExecution(job: Job<ExecuteRunData>) { | |
created_at: new Date().toISOString(), | ||
status: 'success', | ||
totalRowsExtracted, | ||
extractedItemsCount, | ||
schemaItemsExtracted: totalSchemaItemsExtracted, | ||
listItemsExtracted: totalListItemsExtracted, | ||
extractedScreenshotsCount, | ||
} | ||
); | ||
|
@@ -339,7 +360,7 @@ async function processRunExecution(job: Job<ExecuteRunData>) { | |
robotName: recording.recording_meta.name, | ||
status: 'success', | ||
finishedAt: new Date().toLocaleString() | ||
});; | ||
}); | ||
|
||
// Check for and process queued runs before destroying the browser | ||
const queuedRunProcessed = await checkAndProcessQueuedRun(data.userId, plainRun.browserId); | ||
|
@@ -458,7 +479,11 @@ async function abortRun(runId: string, userId: string): Promise<boolean> { | |
} | ||
|
||
let currentLog = 'Run aborted by user'; | ||
let serializableOutput: Record<string, any> = {}; | ||
let categorizedOutput = { | ||
scrapeSchema: {}, | ||
scrapeList: {}, | ||
other: {} | ||
}; | ||
let binaryOutput: Record<string, any> = {}; | ||
|
||
try { | ||
|
@@ -467,16 +492,16 @@ async function abortRun(runId: string, userId: string): Promise<boolean> { | |
currentLog = browser.interpreter.debugMessages.join('\n') || currentLog; | ||
} | ||
|
||
if (browser.interpreter.serializableData) { | ||
browser.interpreter.serializableData.forEach((item, index) => { | ||
serializableOutput[`item-${index}`] = item; | ||
}); | ||
if (browser.interpreter.serializableDataByType) { | ||
categorizedOutput = { | ||
scrapeSchema: collectDataByType(browser.interpreter.serializableDataByType.scrapeSchema || []), | ||
scrapeList: collectDataByType(browser.interpreter.serializableDataByType.scrapeList || []), | ||
other: collectDataByType(browser.interpreter.serializableDataByType.other || []) | ||
}; | ||
} | ||
|
||
if (browser.interpreter.binaryData) { | ||
browser.interpreter.binaryData.forEach((item, index) => { | ||
binaryOutput[`item-${index}`] = item; | ||
}); | ||
binaryOutput = collectBinaryData(browser.interpreter.binaryData); | ||
} | ||
} | ||
} catch (interpreterError) { | ||
|
@@ -488,7 +513,11 @@ async function abortRun(runId: string, userId: string): Promise<boolean> { | |
finishedAt: new Date().toLocaleString(), | ||
browserId: plainRun.browserId, | ||
log: currentLog, | ||
serializableOutput, | ||
serializableOutput: { | ||
scrapeSchema: Object.values(categorizedOutput.scrapeSchema), | ||
scrapeList: Object.values(categorizedOutput.scrapeList), | ||
other: Object.values(categorizedOutput.other), | ||
}, | ||
binaryOutput, | ||
}); | ||
|
||
|
@@ -529,6 +558,30 @@ async function abortRun(runId: string, userId: string): Promise<boolean> { | |
} | ||
} | ||
|
||
/** | ||
* Helper function to collect data from arrays into indexed objects | ||
* @param dataArray Array of data to be transformed into an object with indexed keys | ||
* @returns Object with indexed keys | ||
*/ | ||
function collectDataByType(dataArray: any[]): Record<string, any> { | ||
return dataArray.reduce((result: Record<string, any>, item, index) => { | ||
result[`item-${index}`] = item; | ||
return result; | ||
}, {}); | ||
} | ||
|
||
/** | ||
* Helper function to collect binary data (like screenshots) | ||
* @param binaryDataArray Array of binary data objects to be transformed | ||
* @returns Object with indexed keys | ||
*/ | ||
function collectBinaryData(binaryDataArray: { mimetype: string, data: string, type?: string }[]): Record<string, any> { | ||
return binaryDataArray.reduce((result: Record<string, any>, item, index) => { | ||
result[`item-${index}`] = item; | ||
return result; | ||
}, {}); | ||
} | ||
|
||
async function registerRunExecutionWorker() { | ||
try { | ||
const registeredUserQueues = new Map(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify that the UI correctly handles action state throughout the capture flow
These changes support the action state management in the UI layer. Let's ensure that the
setActionType
callback integrates correctly with the UI's action lifecycle management.Also applies to: 381-383, 391-393, 421-423, 432-434, 478-480, 493-495, 507-509, 520-522, 532-534, 566-568
🏁 Script executed:
Length of output: 138
🏁 Script executed:
Length of output: 2760
Ensure the UI wires up the new
setActionType
callbackWe found calls to
debugChannel.setActionType
inmaxun-core/src/interpret.ts
but no corresponding handling in the UI. The recorder panel only subscribes tostartAction
/finishAction
, so your new action-type updates won’t propagate unless you explicitly pass and handlesetActionType
in your React components.Locations to update:
• Include
setActionType
in the debug channel props alongsidestartAction
/finishAction
.• Add logic (e.g., a state setter or effect) to respond to
setActionType(type)
calls and update the UI accordingly.Without these changes, the UI won’t reflect the action‐type changes emitted by
interpret.ts
.🧰 Tools
🪛 Biome (1.9.4)
[error] 48-48: Don't use 'Function' as a type.
Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.
(lint/complexity/noBannedTypes)