@things-factory/labeling 9.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -0
- package/ENTITY_IMPLEMENTATION.md +351 -0
- package/INTEGRATION_COMPLETE.md +531 -0
- package/MIGRATION_GUIDE.md +310 -0
- package/README.md +551 -0
- package/REFACTORING_SUMMARY.md +212 -0
- package/UI_DOCUMENTATION.md +552 -0
- package/dist-client/index.d.ts +3 -0
- package/dist-client/index.js +9 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/labeling-workflow-builder.d.ts +26 -0
- package/dist-client/pages/labeling-workflow-builder.js +636 -0
- package/dist-client/pages/labeling-workflow-builder.js.map +1 -0
- package/dist-client/pages/labeling-workflow-list.d.ts +24 -0
- package/dist-client/pages/labeling-workflow-list.js +495 -0
- package/dist-client/pages/labeling-workflow-list.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +47 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/entities/index.d.ts +5 -0
- package/dist-server/entities/index.js +11 -0
- package/dist-server/entities/index.js.map +1 -0
- package/dist-server/index.d.ts +3 -0
- package/dist-server/index.js +7 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/route.d.ts +2 -0
- package/dist-server/route.js +6 -0
- package/dist-server/route.js.map +1 -0
- package/dist-server/service/index.d.ts +8 -0
- package/dist-server/service/index.js +21 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/labeling-workflow-service.d.ts +69 -0
- package/dist-server/service/labeling-workflow-service.js +521 -0
- package/dist-server/service/labeling-workflow-service.js.map +1 -0
- package/dist-server/service/labeling-workflow.d.ts +30 -0
- package/dist-server/service/labeling-workflow.js +119 -0
- package/dist-server/service/labeling-workflow.js.map +1 -0
- package/dist-server/service/workflow-execution-step.d.ts +28 -0
- package/dist-server/service/workflow-execution-step.js +115 -0
- package/dist-server/service/workflow-execution-step.js.map +1 -0
- package/dist-server/service/workflow-execution.d.ts +27 -0
- package/dist-server/service/workflow-execution.js +110 -0
- package/dist-server/service/workflow-execution.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/dist-server/types/workflow-types.d.ts +141 -0
- package/dist-server/types/workflow-types.js +488 -0
- package/dist-server/types/workflow-types.js.map +1 -0
- package/package.json +51 -0
- package/things-factory.config.js +11 -0
- package/translations/en.json +6 -0
- package/translations/ja.json +6 -0
- package/translations/ko.json +6 -0
- package/translations/ms.json +6 -0
- package/translations/zh.json +6 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LabelingWorkflowService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const workflow_types_js_1 = require("../types/workflow-types.js");
|
|
8
|
+
const labeling_workflow_js_1 = require("./labeling-workflow.js");
|
|
9
|
+
const workflow_execution_js_1 = require("./workflow-execution.js");
|
|
10
|
+
const workflow_execution_step_js_1 = require("./workflow-execution-step.js");
|
|
11
|
+
const dataset_labeling_integration_js_1 = require("@things-factory/integration-label-studio/dist-server/service/dataset-labeling-integration.js");
|
|
12
|
+
const external_data_source_service_js_1 = require("@things-factory/integration-label-studio/dist-server/service/external-data-source-service.js");
|
|
13
|
+
const ai_prediction_service_js_1 = require("@things-factory/integration-label-studio/dist-server/service/ai-prediction-service.js");
|
|
14
|
+
/**
|
|
15
|
+
* Labeling Workflow Service
|
|
16
|
+
*
|
|
17
|
+
* Orchestrates end-to-end labeling workflows with multiple steps
|
|
18
|
+
*
|
|
19
|
+
* Features:
|
|
20
|
+
* - Define multi-step labeling workflows
|
|
21
|
+
* - Auto-execute workflows based on triggers
|
|
22
|
+
* - Track execution progress
|
|
23
|
+
* - Error handling and retry logic
|
|
24
|
+
* - Conditional execution
|
|
25
|
+
* - Database persistence with TypeORM
|
|
26
|
+
*/
|
|
27
|
+
let LabelingWorkflowService = class LabelingWorkflowService {
|
|
28
|
+
constructor() {
|
|
29
|
+
// Service dependencies (lazy initialization)
|
|
30
|
+
this.datasetIntegration = null;
|
|
31
|
+
this.externalDataSource = null;
|
|
32
|
+
this.aiPredictionService = null;
|
|
33
|
+
}
|
|
34
|
+
getDatasetIntegration() {
|
|
35
|
+
if (!this.datasetIntegration) {
|
|
36
|
+
this.datasetIntegration = new dataset_labeling_integration_js_1.DatasetLabelingIntegration();
|
|
37
|
+
}
|
|
38
|
+
return this.datasetIntegration;
|
|
39
|
+
}
|
|
40
|
+
getExternalDataSource() {
|
|
41
|
+
if (!this.externalDataSource) {
|
|
42
|
+
this.externalDataSource = new external_data_source_service_js_1.ExternalDataSourceService();
|
|
43
|
+
}
|
|
44
|
+
return this.externalDataSource;
|
|
45
|
+
}
|
|
46
|
+
getAIPredictionService() {
|
|
47
|
+
if (!this.aiPredictionService) {
|
|
48
|
+
this.aiPredictionService = new ai_prediction_service_js_1.LabelStudioAIPredictionService();
|
|
49
|
+
}
|
|
50
|
+
return this.aiPredictionService;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Convert Entity to GraphQL Type
|
|
54
|
+
*/
|
|
55
|
+
toWorkflowType(entity) {
|
|
56
|
+
return {
|
|
57
|
+
...entity,
|
|
58
|
+
steps: JSON.parse(entity.steps)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
toExecutionType(entity) {
|
|
62
|
+
return entity;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a new labeling workflow
|
|
66
|
+
*/
|
|
67
|
+
async createLabelingWorkflow(input, context) {
|
|
68
|
+
console.log(`[Workflow] Creating workflow: ${input.name}`);
|
|
69
|
+
const workflowRepo = (0, typeorm_1.getRepository)(labeling_workflow_js_1.LabelingWorkflow);
|
|
70
|
+
const workflow = workflowRepo.create({
|
|
71
|
+
domain: context.state.domain,
|
|
72
|
+
creator: context.state.user,
|
|
73
|
+
name: input.name,
|
|
74
|
+
description: input.description,
|
|
75
|
+
projectId: input.projectId,
|
|
76
|
+
triggerType: input.triggerType,
|
|
77
|
+
triggerConfig: input.triggerConfig,
|
|
78
|
+
steps: JSON.stringify(input.steps.map((step, index) => ({
|
|
79
|
+
name: step.name,
|
|
80
|
+
type: step.type,
|
|
81
|
+
config: step.config,
|
|
82
|
+
condition: step.condition,
|
|
83
|
+
continueOnError: step.continueOnError !== undefined ? step.continueOnError : true,
|
|
84
|
+
maxRetries: step.maxRetries,
|
|
85
|
+
order: index + 1
|
|
86
|
+
}))),
|
|
87
|
+
status: input.autoStart ? workflow_types_js_1.WorkflowStatus.Active : workflow_types_js_1.WorkflowStatus.Draft
|
|
88
|
+
});
|
|
89
|
+
await workflowRepo.save(workflow);
|
|
90
|
+
console.log(`[Workflow] Created workflow ${workflow.id} with ${input.steps.length} steps`);
|
|
91
|
+
return this.toWorkflowType(workflow);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Execute a workflow
|
|
95
|
+
*/
|
|
96
|
+
async executeLabelingWorkflow(input, context) {
|
|
97
|
+
const workflowRepo = (0, typeorm_1.getRepository)(labeling_workflow_js_1.LabelingWorkflow);
|
|
98
|
+
const workflow = await workflowRepo.findOne({
|
|
99
|
+
where: { id: input.workflowId, domain: context.state.domain, deletedAt: (0, typeorm_1.IsNull)() }
|
|
100
|
+
});
|
|
101
|
+
if (!workflow) {
|
|
102
|
+
throw new Error(`Workflow not found: ${input.workflowId}`);
|
|
103
|
+
}
|
|
104
|
+
console.log(`[Workflow] Executing workflow: ${workflow.name} (${workflow.id})`);
|
|
105
|
+
const steps = JSON.parse(workflow.steps);
|
|
106
|
+
const executionRepo = (0, typeorm_1.getRepository)(workflow_execution_js_1.WorkflowExecution);
|
|
107
|
+
const execution = executionRepo.create({
|
|
108
|
+
domain: context.state.domain,
|
|
109
|
+
workflow,
|
|
110
|
+
workflowId: workflow.id,
|
|
111
|
+
workflowName: workflow.name,
|
|
112
|
+
status: 'running',
|
|
113
|
+
startedAt: new Date()
|
|
114
|
+
});
|
|
115
|
+
await executionRepo.save(execution);
|
|
116
|
+
// Create execution steps
|
|
117
|
+
const stepRepo = (0, typeorm_1.getRepository)(workflow_execution_step_js_1.WorkflowExecutionStep);
|
|
118
|
+
await Promise.all(steps.map((step, index) => stepRepo.save(stepRepo.create({
|
|
119
|
+
domain: context.state.domain,
|
|
120
|
+
execution,
|
|
121
|
+
executionId: execution.id,
|
|
122
|
+
stepName: step.name,
|
|
123
|
+
stepType: step.type,
|
|
124
|
+
order: index + 1,
|
|
125
|
+
status: 'pending'
|
|
126
|
+
}))));
|
|
127
|
+
// Execute asynchronously
|
|
128
|
+
this.executeWorkflowAsync(execution.id, workflow.id, steps, context, input.parameters).catch(error => {
|
|
129
|
+
console.error(`[Workflow] Execution failed:`, error);
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
executionId: execution.id,
|
|
133
|
+
status: 'running',
|
|
134
|
+
summary: `Started execution of ${steps.length} steps`
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get workflow details
|
|
139
|
+
*/
|
|
140
|
+
async labelingWorkflow(workflowId, context) {
|
|
141
|
+
const workflowRepo = (0, typeorm_1.getRepository)(labeling_workflow_js_1.LabelingWorkflow);
|
|
142
|
+
const workflow = await workflowRepo.findOne({
|
|
143
|
+
where: { id: workflowId, domain: context.state.domain, deletedAt: (0, typeorm_1.IsNull)() }
|
|
144
|
+
});
|
|
145
|
+
if (!workflow) {
|
|
146
|
+
throw new Error(`Workflow not found: ${workflowId}`);
|
|
147
|
+
}
|
|
148
|
+
return this.toWorkflowType(workflow);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* List all workflows
|
|
152
|
+
*/
|
|
153
|
+
async labelingWorkflows(projectId, context) {
|
|
154
|
+
const workflowRepo = (0, typeorm_1.getRepository)(labeling_workflow_js_1.LabelingWorkflow);
|
|
155
|
+
const queryBuilder = workflowRepo
|
|
156
|
+
.createQueryBuilder('workflow')
|
|
157
|
+
.where('workflow.domain = :domain', { domain: context.state.domain.id })
|
|
158
|
+
.andWhere('workflow.deletedAt IS NULL');
|
|
159
|
+
if (projectId) {
|
|
160
|
+
queryBuilder.andWhere('workflow.projectId = :projectId', { projectId });
|
|
161
|
+
}
|
|
162
|
+
const [entities, total] = await queryBuilder.orderBy('workflow.createdAt', 'DESC').getManyAndCount();
|
|
163
|
+
const items = entities.map(e => this.toWorkflowType(e));
|
|
164
|
+
return { items, total };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get execution status
|
|
168
|
+
*/
|
|
169
|
+
async workflowExecution(executionId, context) {
|
|
170
|
+
const executionRepo = (0, typeorm_1.getRepository)(workflow_execution_js_1.WorkflowExecution);
|
|
171
|
+
const execution = await executionRepo.findOne({
|
|
172
|
+
where: { id: executionId, domain: context.state.domain, deletedAt: (0, typeorm_1.IsNull)() },
|
|
173
|
+
relations: ['steps']
|
|
174
|
+
});
|
|
175
|
+
if (!execution) {
|
|
176
|
+
throw new Error(`Execution not found: ${executionId}`);
|
|
177
|
+
}
|
|
178
|
+
return this.toExecutionType(execution);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* List executions for a workflow
|
|
182
|
+
*/
|
|
183
|
+
async workflowExecutions(workflowId, context) {
|
|
184
|
+
const executionRepo = (0, typeorm_1.getRepository)(workflow_execution_js_1.WorkflowExecution);
|
|
185
|
+
const [entities, total] = await executionRepo.findAndCount({
|
|
186
|
+
where: { workflowId, domain: context.state.domain, deletedAt: (0, typeorm_1.IsNull)() },
|
|
187
|
+
order: { startedAt: 'DESC' }
|
|
188
|
+
});
|
|
189
|
+
const items = entities.map(e => this.toExecutionType(e));
|
|
190
|
+
return { items, total };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Pause a workflow
|
|
194
|
+
*/
|
|
195
|
+
async pauseLabelingWorkflow(workflowId, context) {
|
|
196
|
+
const workflowRepo = (0, typeorm_1.getRepository)(labeling_workflow_js_1.LabelingWorkflow);
|
|
197
|
+
const workflow = await workflowRepo.findOne({
|
|
198
|
+
where: { id: workflowId, domain: context.state.domain, deletedAt: (0, typeorm_1.IsNull)() }
|
|
199
|
+
});
|
|
200
|
+
if (!workflow) {
|
|
201
|
+
throw new Error(`Workflow not found: ${workflowId}`);
|
|
202
|
+
}
|
|
203
|
+
workflow.status = workflow_types_js_1.WorkflowStatus.Paused;
|
|
204
|
+
await workflowRepo.save(workflow);
|
|
205
|
+
console.log(`[Workflow] Paused workflow: ${workflow.name}`);
|
|
206
|
+
return this.toWorkflowType(workflow);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Resume a workflow
|
|
210
|
+
*/
|
|
211
|
+
async resumeLabelingWorkflow(workflowId, context) {
|
|
212
|
+
const workflowRepo = (0, typeorm_1.getRepository)(labeling_workflow_js_1.LabelingWorkflow);
|
|
213
|
+
const workflow = await workflowRepo.findOne({
|
|
214
|
+
where: { id: workflowId, domain: context.state.domain, deletedAt: (0, typeorm_1.IsNull)() }
|
|
215
|
+
});
|
|
216
|
+
if (!workflow) {
|
|
217
|
+
throw new Error(`Workflow not found: ${workflowId}`);
|
|
218
|
+
}
|
|
219
|
+
workflow.status = workflow_types_js_1.WorkflowStatus.Active;
|
|
220
|
+
await workflowRepo.save(workflow);
|
|
221
|
+
console.log(`[Workflow] Resumed workflow: ${workflow.name}`);
|
|
222
|
+
return this.toWorkflowType(workflow);
|
|
223
|
+
}
|
|
224
|
+
// ============================================================================
|
|
225
|
+
// Private Methods - Workflow Execution Engine
|
|
226
|
+
// ============================================================================
|
|
227
|
+
async executeWorkflowAsync(executionId, workflowId, steps, context, parametersJson) {
|
|
228
|
+
const startTime = Date.now();
|
|
229
|
+
const parameters = parametersJson ? JSON.parse(parametersJson) : {};
|
|
230
|
+
console.log(`[Workflow] Starting execution ${executionId}`);
|
|
231
|
+
const executionRepo = (0, typeorm_1.getRepository)(workflow_execution_js_1.WorkflowExecution);
|
|
232
|
+
const stepRepo = (0, typeorm_1.getRepository)(workflow_execution_step_js_1.WorkflowExecutionStep);
|
|
233
|
+
const workflowRepo = (0, typeorm_1.getRepository)(labeling_workflow_js_1.LabelingWorkflow);
|
|
234
|
+
const execution = await executionRepo.findOne({
|
|
235
|
+
where: { id: executionId },
|
|
236
|
+
relations: ['steps', 'workflow']
|
|
237
|
+
});
|
|
238
|
+
if (!execution) {
|
|
239
|
+
throw new Error(`Execution not found: ${executionId}`);
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
for (const [index, workflowStep] of steps.entries()) {
|
|
243
|
+
const executionStep = execution.steps[index];
|
|
244
|
+
executionStep.status = 'running';
|
|
245
|
+
executionStep.startedAt = new Date();
|
|
246
|
+
await stepRepo.save(executionStep);
|
|
247
|
+
console.log(`[Workflow] Executing step ${index + 1}/${steps.length}: ${workflowStep.name}`);
|
|
248
|
+
try {
|
|
249
|
+
// Check condition if specified
|
|
250
|
+
if (workflowStep.condition) {
|
|
251
|
+
const conditionMet = this.evaluateCondition(workflowStep.condition, parameters);
|
|
252
|
+
if (!conditionMet) {
|
|
253
|
+
console.log(`[Workflow] Step ${workflowStep.name} condition not met, skipping`);
|
|
254
|
+
executionStep.status = 'skipped';
|
|
255
|
+
executionStep.completedAt = new Date();
|
|
256
|
+
await stepRepo.save(executionStep);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Execute step
|
|
261
|
+
const stepConfig = JSON.parse(workflowStep.config);
|
|
262
|
+
const stepResult = await this.executeStep(workflowStep.type, stepConfig, execution.workflow.projectId, context, parameters);
|
|
263
|
+
executionStep.status = 'completed';
|
|
264
|
+
executionStep.output = JSON.stringify(stepResult);
|
|
265
|
+
executionStep.completedAt = new Date();
|
|
266
|
+
executionStep.durationMs = executionStep.completedAt.getTime() - executionStep.startedAt.getTime();
|
|
267
|
+
await stepRepo.save(executionStep);
|
|
268
|
+
console.log(`[Workflow] Step ${workflowStep.name} completed in ${executionStep.durationMs}ms`);
|
|
269
|
+
// Store step result in parameters for next steps
|
|
270
|
+
parameters[`step_${index}_result`] = stepResult;
|
|
271
|
+
}
|
|
272
|
+
catch (stepError) {
|
|
273
|
+
console.error(`[Workflow] Step ${workflowStep.name} failed:`, stepError);
|
|
274
|
+
executionStep.status = 'failed';
|
|
275
|
+
executionStep.error = stepError.message;
|
|
276
|
+
executionStep.completedAt = new Date();
|
|
277
|
+
executionStep.durationMs = executionStep.completedAt.getTime() - executionStep.startedAt.getTime();
|
|
278
|
+
await stepRepo.save(executionStep);
|
|
279
|
+
// Check if we should continue on error
|
|
280
|
+
if (!workflowStep.continueOnError) {
|
|
281
|
+
throw new Error(`Step ${workflowStep.name} failed: ${stepError.message}`);
|
|
282
|
+
}
|
|
283
|
+
console.log(`[Workflow] Continuing despite error (continueOnError=true)`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// All steps completed
|
|
287
|
+
execution.status = 'completed';
|
|
288
|
+
execution.summary = this.generateExecutionSummary(execution.steps);
|
|
289
|
+
execution.completedAt = new Date();
|
|
290
|
+
execution.totalDurationMs = Date.now() - startTime;
|
|
291
|
+
await executionRepo.save(execution);
|
|
292
|
+
console.log(`[Workflow] Execution completed in ${execution.totalDurationMs}ms`);
|
|
293
|
+
// Update workflow status
|
|
294
|
+
const workflow = await workflowRepo.findOne({ where: { id: workflowId } });
|
|
295
|
+
if (workflow) {
|
|
296
|
+
workflow.lastExecutedAt = new Date();
|
|
297
|
+
if (workflow.triggerType === workflow_types_js_1.TriggerType.Schedule) {
|
|
298
|
+
// Calculate next execution time (simplified, needs proper cron parser)
|
|
299
|
+
// workflow.nextExecutionAt = ...
|
|
300
|
+
}
|
|
301
|
+
await workflowRepo.save(workflow);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
console.error(`[Workflow] Execution failed:`, error);
|
|
306
|
+
execution.status = 'failed';
|
|
307
|
+
execution.error = error.message;
|
|
308
|
+
execution.summary = `Failed: ${error.message}`;
|
|
309
|
+
execution.completedAt = new Date();
|
|
310
|
+
execution.totalDurationMs = Date.now() - startTime;
|
|
311
|
+
await executionRepo.save(execution);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async executeStep(stepType, config, projectId, context, parameters) {
|
|
315
|
+
switch (stepType) {
|
|
316
|
+
case workflow_types_js_1.WorkflowStepType.ImportData:
|
|
317
|
+
return await this.executeImportDataStep(config, projectId, context, parameters);
|
|
318
|
+
case workflow_types_js_1.WorkflowStepType.GeneratePredictions:
|
|
319
|
+
return await this.executeGeneratePredictionsStep(config, projectId, context, parameters);
|
|
320
|
+
case workflow_types_js_1.WorkflowStepType.WaitForAnnotations:
|
|
321
|
+
return await this.executeWaitForAnnotationsStep(config, projectId, context);
|
|
322
|
+
case workflow_types_js_1.WorkflowStepType.SyncAnnotations:
|
|
323
|
+
return await this.executeSyncAnnotationsStep(config, projectId, context, parameters);
|
|
324
|
+
case workflow_types_js_1.WorkflowStepType.ValidateQuality:
|
|
325
|
+
return await this.executeValidateQualityStep(config, projectId, context, parameters);
|
|
326
|
+
case workflow_types_js_1.WorkflowStepType.Notification:
|
|
327
|
+
return await this.executeNotificationStep(config, parameters);
|
|
328
|
+
default:
|
|
329
|
+
throw new Error(`Unknown step type: ${stepType}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async executeImportDataStep(config, projectId, context, parameters) {
|
|
333
|
+
if (config.sourceType === 'dataset') {
|
|
334
|
+
return await this.getDatasetIntegration().createLabelingTasksFromDataset({
|
|
335
|
+
projectId,
|
|
336
|
+
dataSetId: config.dataSetId,
|
|
337
|
+
imageField: config.imageField,
|
|
338
|
+
autoGeneratePredictions: config.autoGeneratePredictions || false,
|
|
339
|
+
limit: config.limit
|
|
340
|
+
}, context);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
return await this.getExternalDataSource().importFromExternalSource({
|
|
344
|
+
projectId,
|
|
345
|
+
source: {
|
|
346
|
+
sourceType: config.sourceType,
|
|
347
|
+
sourceUrl: config.sourceUrl,
|
|
348
|
+
authHeader: config.authHeader,
|
|
349
|
+
dataPath: config.dataPath
|
|
350
|
+
},
|
|
351
|
+
imageField: config.imageField,
|
|
352
|
+
limit: config.limit
|
|
353
|
+
}, context);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async executeGeneratePredictionsStep(config, projectId, context, parameters) {
|
|
357
|
+
const dataSetId = parameters.dataSetId || config.dataSetId;
|
|
358
|
+
if (!dataSetId) {
|
|
359
|
+
throw new Error('dataSetId required for GeneratePredictions step');
|
|
360
|
+
}
|
|
361
|
+
return await this.getDatasetIntegration().generatePredictionsForDataset({
|
|
362
|
+
dataSetId,
|
|
363
|
+
projectId,
|
|
364
|
+
modelId: config.modelId,
|
|
365
|
+
confidenceThreshold: config.confidenceThreshold,
|
|
366
|
+
forceRegenerate: config.forceRegenerate || false
|
|
367
|
+
}, context);
|
|
368
|
+
}
|
|
369
|
+
async executeWaitForAnnotationsStep(config, projectId, context) {
|
|
370
|
+
// This is a simplified implementation
|
|
371
|
+
// In production, this should poll or use webhooks
|
|
372
|
+
console.log(`[Workflow] WaitForAnnotations step - checking criteria`);
|
|
373
|
+
// TODO: Implement actual waiting/polling logic
|
|
374
|
+
return {
|
|
375
|
+
status: 'waiting',
|
|
376
|
+
message: 'WaitForAnnotations step - manual check required'
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
async executeSyncAnnotationsStep(config, projectId, context, parameters) {
|
|
380
|
+
const dataSetId = parameters.dataSetId || config.dataSetId;
|
|
381
|
+
if (!dataSetId) {
|
|
382
|
+
throw new Error('dataSetId required for SyncAnnotations step');
|
|
383
|
+
}
|
|
384
|
+
return await this.getDatasetIntegration().syncAnnotationsToDataset({
|
|
385
|
+
projectId,
|
|
386
|
+
dataSetId,
|
|
387
|
+
completedOnly: config.completedOnly !== false,
|
|
388
|
+
sinceDate: config.sinceDate
|
|
389
|
+
}, context);
|
|
390
|
+
}
|
|
391
|
+
async executeValidateQualityStep(config, projectId, context, parameters) {
|
|
392
|
+
console.log(`[Workflow] ValidateQuality step`);
|
|
393
|
+
// TODO: Implement quality validation logic
|
|
394
|
+
return {
|
|
395
|
+
status: 'validated',
|
|
396
|
+
message: 'Quality validation - not yet implemented'
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
async executeNotificationStep(config, parameters) {
|
|
400
|
+
console.log(`[Workflow] Notification step: ${config.message}`);
|
|
401
|
+
// TODO: Implement actual notification (email, webhook, etc.)
|
|
402
|
+
return {
|
|
403
|
+
status: 'sent',
|
|
404
|
+
message: config.message,
|
|
405
|
+
recipients: config.recipients
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
evaluateCondition(condition, parameters) {
|
|
409
|
+
// Simple condition evaluation
|
|
410
|
+
// In production, use a proper expression evaluator
|
|
411
|
+
try {
|
|
412
|
+
const conditionObj = JSON.parse(condition);
|
|
413
|
+
// Example: { "step_0_result.tasksCreated": { "$gt": 0 } }
|
|
414
|
+
// For now, always return true
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
console.warn(`[Workflow] Failed to evaluate condition: ${condition}`);
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
generateExecutionSummary(steps) {
|
|
423
|
+
const completed = steps.filter(s => s.status === 'completed').length;
|
|
424
|
+
const failed = steps.filter(s => s.status === 'failed').length;
|
|
425
|
+
const skipped = steps.filter(s => s.status === 'skipped').length;
|
|
426
|
+
return `Completed ${completed}/${steps.length} steps (${failed} failed, ${skipped} skipped)`;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
exports.LabelingWorkflowService = LabelingWorkflowService;
|
|
430
|
+
tslib_1.__decorate([
|
|
431
|
+
(0, type_graphql_1.Mutation)(returns => workflow_types_js_1.LabelingWorkflow, {
|
|
432
|
+
description: 'Create a new labeling workflow'
|
|
433
|
+
}),
|
|
434
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "mutation")'),
|
|
435
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
436
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
437
|
+
tslib_1.__metadata("design:type", Function),
|
|
438
|
+
tslib_1.__metadata("design:paramtypes", [workflow_types_js_1.CreateWorkflowRequest, Object]),
|
|
439
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
440
|
+
], LabelingWorkflowService.prototype, "createLabelingWorkflow", null);
|
|
441
|
+
tslib_1.__decorate([
|
|
442
|
+
(0, type_graphql_1.Mutation)(returns => workflow_types_js_1.WorkflowExecutionResult, {
|
|
443
|
+
description: 'Execute a labeling workflow'
|
|
444
|
+
}),
|
|
445
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "mutation")'),
|
|
446
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
447
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
448
|
+
tslib_1.__metadata("design:type", Function),
|
|
449
|
+
tslib_1.__metadata("design:paramtypes", [workflow_types_js_1.ExecuteWorkflowRequest, Object]),
|
|
450
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
451
|
+
], LabelingWorkflowService.prototype, "executeLabelingWorkflow", null);
|
|
452
|
+
tslib_1.__decorate([
|
|
453
|
+
(0, type_graphql_1.Query)(returns => workflow_types_js_1.LabelingWorkflow, {
|
|
454
|
+
description: 'Get labeling workflow details'
|
|
455
|
+
}),
|
|
456
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "query")'),
|
|
457
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('workflowId')),
|
|
458
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
459
|
+
tslib_1.__metadata("design:type", Function),
|
|
460
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
461
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
462
|
+
], LabelingWorkflowService.prototype, "labelingWorkflow", null);
|
|
463
|
+
tslib_1.__decorate([
|
|
464
|
+
(0, type_graphql_1.Query)(returns => workflow_types_js_1.LabelingWorkflowList, {
|
|
465
|
+
description: 'List all labeling workflows'
|
|
466
|
+
}),
|
|
467
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "query")'),
|
|
468
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('projectId', type => type_graphql_1.Int, { nullable: true })),
|
|
469
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
470
|
+
tslib_1.__metadata("design:type", Function),
|
|
471
|
+
tslib_1.__metadata("design:paramtypes", [Number, Object]),
|
|
472
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
473
|
+
], LabelingWorkflowService.prototype, "labelingWorkflows", null);
|
|
474
|
+
tslib_1.__decorate([
|
|
475
|
+
(0, type_graphql_1.Query)(returns => workflow_types_js_1.WorkflowExecution, {
|
|
476
|
+
description: 'Get workflow execution status'
|
|
477
|
+
}),
|
|
478
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "query")'),
|
|
479
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('executionId')),
|
|
480
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
481
|
+
tslib_1.__metadata("design:type", Function),
|
|
482
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
483
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
484
|
+
], LabelingWorkflowService.prototype, "workflowExecution", null);
|
|
485
|
+
tslib_1.__decorate([
|
|
486
|
+
(0, type_graphql_1.Query)(returns => workflow_types_js_1.WorkflowExecutionList, {
|
|
487
|
+
description: 'List executions for a workflow'
|
|
488
|
+
}),
|
|
489
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "query")'),
|
|
490
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('workflowId')),
|
|
491
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
492
|
+
tslib_1.__metadata("design:type", Function),
|
|
493
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
494
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
495
|
+
], LabelingWorkflowService.prototype, "workflowExecutions", null);
|
|
496
|
+
tslib_1.__decorate([
|
|
497
|
+
(0, type_graphql_1.Mutation)(returns => workflow_types_js_1.LabelingWorkflow, {
|
|
498
|
+
description: 'Pause a workflow'
|
|
499
|
+
}),
|
|
500
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "mutation")'),
|
|
501
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('workflowId')),
|
|
502
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
503
|
+
tslib_1.__metadata("design:type", Function),
|
|
504
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
505
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
506
|
+
], LabelingWorkflowService.prototype, "pauseLabelingWorkflow", null);
|
|
507
|
+
tslib_1.__decorate([
|
|
508
|
+
(0, type_graphql_1.Mutation)(returns => workflow_types_js_1.LabelingWorkflow, {
|
|
509
|
+
description: 'Resume a paused workflow'
|
|
510
|
+
}),
|
|
511
|
+
(0, type_graphql_1.Directive)('@privilege(category: "labeling", privilege: "mutation")'),
|
|
512
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('workflowId')),
|
|
513
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
514
|
+
tslib_1.__metadata("design:type", Function),
|
|
515
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
516
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
517
|
+
], LabelingWorkflowService.prototype, "resumeLabelingWorkflow", null);
|
|
518
|
+
exports.LabelingWorkflowService = LabelingWorkflowService = tslib_1.__decorate([
|
|
519
|
+
(0, type_graphql_1.Resolver)()
|
|
520
|
+
], LabelingWorkflowService);
|
|
521
|
+
//# sourceMappingURL=labeling-workflow-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labeling-workflow-service.js","sourceRoot":"","sources":["../../server/service/labeling-workflow-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Query, Mutation, Arg, Ctx, Int, Directive } from 'type-graphql'\nimport { getRepository, IsNull } from 'typeorm'\nimport {\n CreateWorkflowRequest,\n ExecuteWorkflowRequest,\n WorkflowExecutionResult,\n LabelingWorkflow as LabelingWorkflowType,\n WorkflowExecution as WorkflowExecutionType,\n LabelingWorkflowList,\n WorkflowExecutionList,\n WorkflowStatus,\n WorkflowStepType,\n TriggerType,\n WorkflowStep\n} from '../types/workflow-types.js'\nimport { LabelingWorkflow as LabelingWorkflowEntity } from './labeling-workflow.js'\nimport { WorkflowExecution as WorkflowExecutionEntity } from './workflow-execution.js'\nimport { WorkflowExecutionStep as WorkflowExecutionStepEntity } from './workflow-execution-step.js'\nimport { DatasetLabelingIntegration } from '@things-factory/integration-label-studio/dist-server/service/dataset-labeling-integration.js'\nimport { ExternalDataSourceService } from '@things-factory/integration-label-studio/dist-server/service/external-data-source-service.js'\nimport { LabelStudioAIPredictionService } from '@things-factory/integration-label-studio/dist-server/service/ai-prediction-service.js'\n\n/**\n * Labeling Workflow Service\n *\n * Orchestrates end-to-end labeling workflows with multiple steps\n *\n * Features:\n * - Define multi-step labeling workflows\n * - Auto-execute workflows based on triggers\n * - Track execution progress\n * - Error handling and retry logic\n * - Conditional execution\n * - Database persistence with TypeORM\n */\n@Resolver()\nexport class LabelingWorkflowService {\n // Service dependencies (lazy initialization)\n private datasetIntegration: DatasetLabelingIntegration | null = null\n private externalDataSource: ExternalDataSourceService | null = null\n private aiPredictionService: LabelStudioAIPredictionService | null = null\n\n private getDatasetIntegration(): DatasetLabelingIntegration {\n if (!this.datasetIntegration) {\n this.datasetIntegration = new DatasetLabelingIntegration()\n }\n return this.datasetIntegration\n }\n\n private getExternalDataSource(): ExternalDataSourceService {\n if (!this.externalDataSource) {\n this.externalDataSource = new ExternalDataSourceService()\n }\n return this.externalDataSource\n }\n\n private getAIPredictionService(): LabelStudioAIPredictionService {\n if (!this.aiPredictionService) {\n this.aiPredictionService = new LabelStudioAIPredictionService()\n }\n return this.aiPredictionService\n }\n\n /**\n * Convert Entity to GraphQL Type\n */\n private toWorkflowType(entity: LabelingWorkflowEntity): LabelingWorkflowType {\n return {\n ...entity,\n steps: JSON.parse(entity.steps)\n } as LabelingWorkflowType\n }\n\n private toExecutionType(entity: WorkflowExecutionEntity): WorkflowExecutionType {\n return entity as any as WorkflowExecutionType\n }\n\n /**\n * Create a new labeling workflow\n */\n @Mutation(returns => LabelingWorkflowType, {\n description: 'Create a new labeling workflow'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"mutation\")')\n async createLabelingWorkflow(\n @Arg('input') input: CreateWorkflowRequest,\n @Ctx() context: ResolverContext\n ): Promise<LabelingWorkflowType> {\n console.log(`[Workflow] Creating workflow: ${input.name}`)\n\n const workflowRepo = getRepository(LabelingWorkflowEntity)\n\n const workflow = workflowRepo.create({\n domain: context.state.domain,\n creator: context.state.user,\n name: input.name,\n description: input.description,\n projectId: input.projectId,\n triggerType: input.triggerType,\n triggerConfig: input.triggerConfig,\n steps: JSON.stringify(\n input.steps.map((step, index) => ({\n name: step.name,\n type: step.type,\n config: step.config,\n condition: step.condition,\n continueOnError: step.continueOnError !== undefined ? step.continueOnError : true,\n maxRetries: step.maxRetries,\n order: index + 1\n }))\n ),\n status: input.autoStart ? WorkflowStatus.Active : WorkflowStatus.Draft\n })\n\n await workflowRepo.save(workflow)\n\n console.log(`[Workflow] Created workflow ${workflow.id} with ${input.steps.length} steps`)\n\n return this.toWorkflowType(workflow)\n }\n\n /**\n * Execute a workflow\n */\n @Mutation(returns => WorkflowExecutionResult, {\n description: 'Execute a labeling workflow'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"mutation\")')\n async executeLabelingWorkflow(\n @Arg('input') input: ExecuteWorkflowRequest,\n @Ctx() context: ResolverContext\n ): Promise<WorkflowExecutionResult> {\n const workflowRepo = getRepository(LabelingWorkflowEntity)\n const workflow = await workflowRepo.findOne({\n where: { id: input.workflowId, domain: context.state.domain, deletedAt: IsNull() }\n })\n\n if (!workflow) {\n throw new Error(`Workflow not found: ${input.workflowId}`)\n }\n\n console.log(`[Workflow] Executing workflow: ${workflow.name} (${workflow.id})`)\n\n const steps = JSON.parse(workflow.steps) as WorkflowStep[]\n\n const executionRepo = getRepository(WorkflowExecutionEntity)\n const execution = executionRepo.create({\n domain: context.state.domain,\n workflow,\n workflowId: workflow.id,\n workflowName: workflow.name,\n status: 'running',\n startedAt: new Date()\n })\n\n await executionRepo.save(execution)\n\n // Create execution steps\n const stepRepo = getRepository(WorkflowExecutionStepEntity)\n await Promise.all(\n steps.map((step, index) =>\n stepRepo.save(\n stepRepo.create({\n domain: context.state.domain,\n execution,\n executionId: execution.id,\n stepName: step.name,\n stepType: step.type,\n order: index + 1,\n status: 'pending'\n })\n )\n )\n )\n\n // Execute asynchronously\n this.executeWorkflowAsync(execution.id, workflow.id, steps, context, input.parameters).catch(error => {\n console.error(`[Workflow] Execution failed:`, error)\n })\n\n return {\n executionId: execution.id,\n status: 'running',\n summary: `Started execution of ${steps.length} steps`\n }\n }\n\n /**\n * Get workflow details\n */\n @Query(returns => LabelingWorkflowType, {\n description: 'Get labeling workflow details'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"query\")')\n async labelingWorkflow(\n @Arg('workflowId') workflowId: string,\n @Ctx() context: ResolverContext\n ): Promise<LabelingWorkflowType> {\n const workflowRepo = getRepository(LabelingWorkflowEntity)\n const workflow = await workflowRepo.findOne({\n where: { id: workflowId, domain: context.state.domain, deletedAt: IsNull() }\n })\n\n if (!workflow) {\n throw new Error(`Workflow not found: ${workflowId}`)\n }\n\n return this.toWorkflowType(workflow)\n }\n\n /**\n * List all workflows\n */\n @Query(returns => LabelingWorkflowList, {\n description: 'List all labeling workflows'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"query\")')\n async labelingWorkflows(\n @Arg('projectId', type => Int, { nullable: true }) projectId: number,\n @Ctx() context: ResolverContext\n ): Promise<LabelingWorkflowList> {\n const workflowRepo = getRepository(LabelingWorkflowEntity)\n const queryBuilder = workflowRepo\n .createQueryBuilder('workflow')\n .where('workflow.domain = :domain', { domain: context.state.domain.id })\n .andWhere('workflow.deletedAt IS NULL')\n\n if (projectId) {\n queryBuilder.andWhere('workflow.projectId = :projectId', { projectId })\n }\n\n const [entities, total] = await queryBuilder.orderBy('workflow.createdAt', 'DESC').getManyAndCount()\n const items = entities.map(e => this.toWorkflowType(e))\n\n return { items, total }\n }\n\n /**\n * Get execution status\n */\n @Query(returns => WorkflowExecutionType, {\n description: 'Get workflow execution status'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"query\")')\n async workflowExecution(\n @Arg('executionId') executionId: string,\n @Ctx() context: ResolverContext\n ): Promise<WorkflowExecutionType> {\n const executionRepo = getRepository(WorkflowExecutionEntity)\n const execution = await executionRepo.findOne({\n where: { id: executionId, domain: context.state.domain, deletedAt: IsNull() },\n relations: ['steps']\n })\n\n if (!execution) {\n throw new Error(`Execution not found: ${executionId}`)\n }\n\n return this.toExecutionType(execution)\n }\n\n /**\n * List executions for a workflow\n */\n @Query(returns => WorkflowExecutionList, {\n description: 'List executions for a workflow'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"query\")')\n async workflowExecutions(\n @Arg('workflowId') workflowId: string,\n @Ctx() context: ResolverContext\n ): Promise<WorkflowExecutionList> {\n const executionRepo = getRepository(WorkflowExecutionEntity)\n const [entities, total] = await executionRepo.findAndCount({\n where: { workflowId, domain: context.state.domain, deletedAt: IsNull() },\n order: { startedAt: 'DESC' }\n })\n\n const items = entities.map(e => this.toExecutionType(e))\n return { items, total }\n }\n\n /**\n * Pause a workflow\n */\n @Mutation(returns => LabelingWorkflowType, {\n description: 'Pause a workflow'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"mutation\")')\n async pauseLabelingWorkflow(\n @Arg('workflowId') workflowId: string,\n @Ctx() context: ResolverContext\n ): Promise<LabelingWorkflowType> {\n const workflowRepo = getRepository(LabelingWorkflowEntity)\n const workflow = await workflowRepo.findOne({\n where: { id: workflowId, domain: context.state.domain, deletedAt: IsNull() }\n })\n\n if (!workflow) {\n throw new Error(`Workflow not found: ${workflowId}`)\n }\n\n workflow.status = WorkflowStatus.Paused\n await workflowRepo.save(workflow)\n\n console.log(`[Workflow] Paused workflow: ${workflow.name}`)\n\n return this.toWorkflowType(workflow)\n }\n\n /**\n * Resume a workflow\n */\n @Mutation(returns => LabelingWorkflowType, {\n description: 'Resume a paused workflow'\n })\n @Directive('@privilege(category: \"labeling\", privilege: \"mutation\")')\n async resumeLabelingWorkflow(\n @Arg('workflowId') workflowId: string,\n @Ctx() context: ResolverContext\n ): Promise<LabelingWorkflowType> {\n const workflowRepo = getRepository(LabelingWorkflowEntity)\n const workflow = await workflowRepo.findOne({\n where: { id: workflowId, domain: context.state.domain, deletedAt: IsNull() }\n })\n\n if (!workflow) {\n throw new Error(`Workflow not found: ${workflowId}`)\n }\n\n workflow.status = WorkflowStatus.Active\n await workflowRepo.save(workflow)\n\n console.log(`[Workflow] Resumed workflow: ${workflow.name}`)\n\n return this.toWorkflowType(workflow)\n }\n\n // ============================================================================\n // Private Methods - Workflow Execution Engine\n // ============================================================================\n\n private async executeWorkflowAsync(\n executionId: string,\n workflowId: string,\n steps: WorkflowStep[],\n context: ResolverContext,\n parametersJson?: string\n ): Promise<void> {\n const startTime = Date.now()\n const parameters = parametersJson ? JSON.parse(parametersJson) : {}\n\n console.log(`[Workflow] Starting execution ${executionId}`)\n\n const executionRepo = getRepository(WorkflowExecutionEntity)\n const stepRepo = getRepository(WorkflowExecutionStepEntity)\n const workflowRepo = getRepository(LabelingWorkflowEntity)\n\n const execution = await executionRepo.findOne({\n where: { id: executionId },\n relations: ['steps', 'workflow']\n })\n\n if (!execution) {\n throw new Error(`Execution not found: ${executionId}`)\n }\n\n try {\n for (const [index, workflowStep] of steps.entries()) {\n const executionStep = execution.steps[index]\n executionStep.status = 'running'\n executionStep.startedAt = new Date()\n await stepRepo.save(executionStep)\n\n console.log(`[Workflow] Executing step ${index + 1}/${steps.length}: ${workflowStep.name}`)\n\n try {\n // Check condition if specified\n if (workflowStep.condition) {\n const conditionMet = this.evaluateCondition(workflowStep.condition, parameters)\n if (!conditionMet) {\n console.log(`[Workflow] Step ${workflowStep.name} condition not met, skipping`)\n executionStep.status = 'skipped'\n executionStep.completedAt = new Date()\n await stepRepo.save(executionStep)\n continue\n }\n }\n\n // Execute step\n const stepConfig = JSON.parse(workflowStep.config)\n const stepResult = await this.executeStep(\n workflowStep.type,\n stepConfig,\n execution.workflow.projectId,\n context,\n parameters\n )\n\n executionStep.status = 'completed'\n executionStep.output = JSON.stringify(stepResult)\n executionStep.completedAt = new Date()\n executionStep.durationMs = executionStep.completedAt.getTime() - executionStep.startedAt.getTime()\n await stepRepo.save(executionStep)\n\n console.log(`[Workflow] Step ${workflowStep.name} completed in ${executionStep.durationMs}ms`)\n\n // Store step result in parameters for next steps\n parameters[`step_${index}_result`] = stepResult\n } catch (stepError) {\n console.error(`[Workflow] Step ${workflowStep.name} failed:`, stepError)\n\n executionStep.status = 'failed'\n executionStep.error = stepError.message\n executionStep.completedAt = new Date()\n executionStep.durationMs = executionStep.completedAt.getTime() - executionStep.startedAt.getTime()\n await stepRepo.save(executionStep)\n\n // Check if we should continue on error\n if (!workflowStep.continueOnError) {\n throw new Error(`Step ${workflowStep.name} failed: ${stepError.message}`)\n }\n\n console.log(`[Workflow] Continuing despite error (continueOnError=true)`)\n }\n }\n\n // All steps completed\n execution.status = 'completed'\n execution.summary = this.generateExecutionSummary(execution.steps)\n execution.completedAt = new Date()\n execution.totalDurationMs = Date.now() - startTime\n await executionRepo.save(execution)\n\n console.log(`[Workflow] Execution completed in ${execution.totalDurationMs}ms`)\n\n // Update workflow status\n const workflow = await workflowRepo.findOne({ where: { id: workflowId } })\n if (workflow) {\n workflow.lastExecutedAt = new Date()\n if (workflow.triggerType === TriggerType.Schedule) {\n // Calculate next execution time (simplified, needs proper cron parser)\n // workflow.nextExecutionAt = ...\n }\n await workflowRepo.save(workflow)\n }\n } catch (error) {\n console.error(`[Workflow] Execution failed:`, error)\n\n execution.status = 'failed'\n execution.error = error.message\n execution.summary = `Failed: ${error.message}`\n execution.completedAt = new Date()\n execution.totalDurationMs = Date.now() - startTime\n await executionRepo.save(execution)\n }\n }\n\n private async executeStep(\n stepType: WorkflowStepType,\n config: any,\n projectId: number,\n context: ResolverContext,\n parameters: any\n ): Promise<any> {\n switch (stepType) {\n case WorkflowStepType.ImportData:\n return await this.executeImportDataStep(config, projectId, context, parameters)\n\n case WorkflowStepType.GeneratePredictions:\n return await this.executeGeneratePredictionsStep(config, projectId, context, parameters)\n\n case WorkflowStepType.WaitForAnnotations:\n return await this.executeWaitForAnnotationsStep(config, projectId, context)\n\n case WorkflowStepType.SyncAnnotations:\n return await this.executeSyncAnnotationsStep(config, projectId, context, parameters)\n\n case WorkflowStepType.ValidateQuality:\n return await this.executeValidateQualityStep(config, projectId, context, parameters)\n\n case WorkflowStepType.Notification:\n return await this.executeNotificationStep(config, parameters)\n\n default:\n throw new Error(`Unknown step type: ${stepType}`)\n }\n }\n\n private async executeImportDataStep(\n config: any,\n projectId: number,\n context: ResolverContext,\n parameters: any\n ): Promise<any> {\n if (config.sourceType === 'dataset') {\n return await this.getDatasetIntegration().createLabelingTasksFromDataset(\n {\n projectId,\n dataSetId: config.dataSetId,\n imageField: config.imageField,\n autoGeneratePredictions: config.autoGeneratePredictions || false,\n limit: config.limit\n },\n context\n )\n } else {\n return await this.getExternalDataSource().importFromExternalSource(\n {\n projectId,\n source: {\n sourceType: config.sourceType,\n sourceUrl: config.sourceUrl,\n authHeader: config.authHeader,\n dataPath: config.dataPath\n },\n imageField: config.imageField,\n limit: config.limit\n },\n context\n )\n }\n }\n\n private async executeGeneratePredictionsStep(\n config: any,\n projectId: number,\n context: ResolverContext,\n parameters: any\n ): Promise<any> {\n const dataSetId = parameters.dataSetId || config.dataSetId\n\n if (!dataSetId) {\n throw new Error('dataSetId required for GeneratePredictions step')\n }\n\n return await this.getDatasetIntegration().generatePredictionsForDataset(\n {\n dataSetId,\n projectId,\n modelId: config.modelId,\n confidenceThreshold: config.confidenceThreshold,\n forceRegenerate: config.forceRegenerate || false\n },\n context\n )\n }\n\n private async executeWaitForAnnotationsStep(config: any, projectId: number, context: ResolverContext): Promise<any> {\n // This is a simplified implementation\n // In production, this should poll or use webhooks\n console.log(`[Workflow] WaitForAnnotations step - checking criteria`)\n\n // TODO: Implement actual waiting/polling logic\n return {\n status: 'waiting',\n message: 'WaitForAnnotations step - manual check required'\n }\n }\n\n private async executeSyncAnnotationsStep(\n config: any,\n projectId: number,\n context: ResolverContext,\n parameters: any\n ): Promise<any> {\n const dataSetId = parameters.dataSetId || config.dataSetId\n\n if (!dataSetId) {\n throw new Error('dataSetId required for SyncAnnotations step')\n }\n\n return await this.getDatasetIntegration().syncAnnotationsToDataset(\n {\n projectId,\n dataSetId,\n completedOnly: config.completedOnly !== false,\n sinceDate: config.sinceDate\n },\n context\n )\n }\n\n private async executeValidateQualityStep(\n config: any,\n projectId: number,\n context: ResolverContext,\n parameters: any\n ): Promise<any> {\n console.log(`[Workflow] ValidateQuality step`)\n\n // TODO: Implement quality validation logic\n return {\n status: 'validated',\n message: 'Quality validation - not yet implemented'\n }\n }\n\n private async executeNotificationStep(config: any, parameters: any): Promise<any> {\n console.log(`[Workflow] Notification step: ${config.message}`)\n\n // TODO: Implement actual notification (email, webhook, etc.)\n return {\n status: 'sent',\n message: config.message,\n recipients: config.recipients\n }\n }\n\n private evaluateCondition(condition: string, parameters: any): boolean {\n // Simple condition evaluation\n // In production, use a proper expression evaluator\n try {\n const conditionObj = JSON.parse(condition)\n // Example: { \"step_0_result.tasksCreated\": { \"$gt\": 0 } }\n // For now, always return true\n return true\n } catch (error) {\n console.warn(`[Workflow] Failed to evaluate condition: ${condition}`)\n return true\n }\n }\n\n private generateExecutionSummary(steps: WorkflowExecutionStepEntity[]): string {\n const completed = steps.filter(s => s.status === 'completed').length\n const failed = steps.filter(s => s.status === 'failed').length\n const skipped = steps.filter(s => s.status === 'skipped').length\n\n return `Completed ${completed}/${steps.length} steps (${failed} failed, ${skipped} skipped)`\n }\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
import { User } from '@things-factory/auth-base';
|
|
3
|
+
import { WorkflowExecution } from './workflow-execution.js';
|
|
4
|
+
import { WorkflowStatus, TriggerType } from '../types/workflow-types.js';
|
|
5
|
+
/**
|
|
6
|
+
* LabelingWorkflow Entity
|
|
7
|
+
*
|
|
8
|
+
* Represents a labeling workflow definition with multiple steps.
|
|
9
|
+
* Stores workflow configuration, steps, and trigger settings.
|
|
10
|
+
*/
|
|
11
|
+
export declare class LabelingWorkflow {
|
|
12
|
+
id: string;
|
|
13
|
+
domain?: Domain;
|
|
14
|
+
domainId?: string;
|
|
15
|
+
creator?: User;
|
|
16
|
+
creatorId?: string;
|
|
17
|
+
name: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
projectId: number;
|
|
20
|
+
triggerType: TriggerType;
|
|
21
|
+
triggerConfig?: string;
|
|
22
|
+
steps: string;
|
|
23
|
+
status: WorkflowStatus;
|
|
24
|
+
lastExecutedAt?: Date;
|
|
25
|
+
nextExecutionAt?: Date;
|
|
26
|
+
executions?: WorkflowExecution[];
|
|
27
|
+
createdAt: Date;
|
|
28
|
+
updatedAt: Date;
|
|
29
|
+
deletedAt?: Date;
|
|
30
|
+
}
|