Spaces:
Running
Running
File size: 6,343 Bytes
b39afbe |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
/**
* 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
|