Spaces:
Running
Running
/** | |
* Copyright (c) 2023 MERCENARIES.AI PTE. LTD. | |
* All rights reserved. | |
*/ | |
const script = { | |
name: 'qa', | |
exec: async function (ctx, payload) { | |
try { | |
const dbService = ctx.app.services.get('db'); | |
let workflowIds; | |
if (Array.isArray(payload) && payload.length > 0) { | |
workflowIds = payload; | |
} else { | |
const demoWorkflows = await dbService.find({ 'meta.template': true }, ['id']); | |
if (!demoWorkflows || demoWorkflows.length === 0) { | |
throw new Error('No recipes found with "meta.template=true" flag in the database') | |
} | |
workflowIds = demoWorkflows.map(doc => doc.id); | |
} | |
workflowIds = workflowIds.filter(id => id !== 'c0deba5e-417d-49df-96d3-8aeb8fc15402'); //exclude bugbear | |
if (!workflowIds.length) throw new Error('No workflowIds provided'); | |
const wfIntegration = ctx.app.integrations.get('workflow'); | |
const user = await dbService.get(`user:${ctx.userId}`); | |
ctx.app.events.onAny((event, value) => { | |
if (event.startsWith('jobs.')) { | |
console.log('Received jobs event:', event, value); | |
} | |
}); | |
const workflowPromises = workflowIds.map(async (workflowId) => { | |
console.log(`Starting recipe with ID: ${workflowId}`); | |
try { | |
const workflowName = (await dbService.get(`wf:${workflowId}`))?.meta?.name || 'Unknown'; | |
const jobResult = await wfIntegration.startWorkflow(wfIntegration, workflowId, ctx.session, user, {}); | |
if (jobResult.jobId) { | |
return await new Promise((resolve, reject) => { | |
let hasFinished = false; | |
const timeoutTime = 60000; | |
let timeout; | |
let startTime = Date.now(); | |
const startTimeout = () => { | |
startTime = Date.now(); // Record start time when job_started_ event is received | |
timeout = setTimeout(() => { | |
if (!hasFinished) { | |
const duration = Math.round((Date.now() - startTime) / 1000); // Calculate and round the duration here | |
console.log(`Timeout as no job_finished event received for recipe ID: ${workflowId}. Timeout Time: ${timeoutTime / 1000} seconds. Duration: ${duration} seconds`); | |
// eslint-disable-next-line prefer-promise-reject-errors | |
reject({ | |
status: 'timeout', | |
workflowId, | |
workflowName, | |
jobId: jobResult.jobId, | |
error: `Recipe Timeout reached as no job_finished event received within ${timeoutTime / 1000} seconds` | |
}); | |
} | |
}, timeoutTime); | |
}; | |
// Setting up listener for job_started_ event to start the timeout timer | |
ctx.app.events.once(`jobs.job_started_${jobResult.jobId}`).then(startTimeout); | |
ctx.app.events.once(`jobs.job_finished_${jobResult.jobId}`) | |
.then((jobs) => { | |
clearTimeout(timeout); | |
let job; | |
if (Array.isArray(jobs) && jobs.length > 0) { | |
job = jobs[0]; | |
} else { | |
job = jobs; | |
} | |
const endTime = new Date(); // Record end time | |
const duration = Math.round((endTime - startTime) / 1000); // Calculate the duration | |
const errorDetails = job.errors.length ? JSON.stringify(job.errors) : 'None'; | |
console.log(`Workflow ${workflowId} finished with jobState: ${job._state}. Job details:`, job); | |
if (job._state === 'success') { | |
resolve({ | |
status: 'success', | |
workflowId, | |
workflowName, | |
jobId: jobResult.jobId, | |
jobState: job._state, | |
duration | |
}); | |
} else { | |
// eslint-disable-next-line prefer-promise-reject-errors | |
reject({ | |
status: 'fail', | |
workflowId, | |
workflowName, | |
jobId: jobResult.jobId, | |
jobState: job._state, | |
error: errorDetails, | |
duration | |
}); | |
} | |
hasFinished = true; | |
}); | |
}); | |
} else { | |
throw new Error('Failed to start a job for the workflow'); | |
} | |
} catch (error) { | |
console.error(`Error in recipe ${workflowId}:`, error); | |
return await Promise.reject(error); | |
} | |
}) | |
const results = await Promise.allSettled(workflowPromises); | |
const report = '## QA Report\n' + results.map(result => { | |
if (result.status === 'fulfilled') { | |
return `#### β ${result.value.workflowName}\n - **Workflow ID**: ${result.value.workflowId}\n - **Job ID**: ${result.value.jobId}\n - **Status**: ${result.status} ${result.value.status} ${result.value.jobState}\n - **Error**: ${result.value.error || 'None'}\n - **Duration**: ${result.value.duration} s`; | |
} else { | |
return `#### β ${result.reason.workflowName}\n - **Workflow ID**: ${result.reason.workflowId}\n - **Job ID**: ${result.reason.jobId}\n - **Status**: ${result.status} ${result.reason.status} ${result.reason.jobState}\n - **Error**: ${result.reason?.error || 'None'}\n - **Duration**: ${result.reason.duration} s`; | |
} | |
}).join('\n'); | |
void await ctx.app.blocks.runBlock( | |
ctx, | |
'omnitool.write_document', | |
{ text: report, fileName: 'qa_report.md', textType: 'text/markdown', storageType: 'Temporary' } | |
); | |
void await ctx.app.blocks.runBlock( | |
ctx, | |
'omnitool.chat_output', | |
{ text: 'QA Report has been generated. Please check your file manager for the QA report.'} | |
); | |
} catch (error) { | |
console.error('Error in QA script:', error); | |
await ctx.app.sendMessageToSession(ctx.session.sessionId, error.message, 'text/plain'); | |
throw new Error(error.message); | |
} | |
}, | |
}; | |
export default script | |