@things-factory/integration-label-studio 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 +85 -0
- package/EXTERNAL_DATA_SOURCING.md +484 -0
- package/IMPLEMENTATION_GUIDE.md +469 -0
- package/INTEGRATION.md +279 -0
- package/README.md +1014 -0
- package/SETUP_GUIDE.md +577 -0
- package/TEST_GUIDE.md +387 -0
- package/UI_CUSTOMIZATION.md +395 -0
- package/USER_SYNC_GUIDE.md +514 -0
- package/client/bootstrap.ts +1 -0
- package/client/index.ts +1 -0
- package/client/label-studio-label-page.ts +52 -0
- package/client/label-studio-project-create.ts +216 -0
- package/client/label-studio-project-list.ts +214 -0
- package/client/label-studio-wrapper.ts +294 -0
- package/client/route.ts +15 -0
- package/client/tsconfig.json +13 -0
- package/config/config.development.js +124 -0
- package/config/config.production.js +182 -0
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +2 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/label-studio-label-page.d.ts +8 -0
- package/dist-client/label-studio-label-page.js +54 -0
- package/dist-client/label-studio-label-page.js.map +1 -0
- package/dist-client/label-studio-project-create.d.ts +16 -0
- package/dist-client/label-studio-project-create.js +235 -0
- package/dist-client/label-studio-project-create.js.map +1 -0
- package/dist-client/label-studio-project-list.d.ts +16 -0
- package/dist-client/label-studio-project-list.js +222 -0
- package/dist-client/label-studio-project-list.js.map +1 -0
- package/dist-client/label-studio-wrapper.d.ts +57 -0
- package/dist-client/label-studio-wrapper.js +304 -0
- package/dist-client/label-studio-wrapper.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +14 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/controller/label-studio-role-mapper.d.ts +35 -0
- package/dist-server/controller/label-studio-role-mapper.js +65 -0
- package/dist-server/controller/label-studio-role-mapper.js.map +1 -0
- package/dist-server/controller/user-provisioning-service.d.ts +66 -0
- package/dist-server/controller/user-provisioning-service.js +264 -0
- package/dist-server/controller/user-provisioning-service.js.map +1 -0
- package/dist-server/index.d.ts +7 -0
- package/dist-server/index.js +19 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/route/label-studio-sso.d.ts +2 -0
- package/dist-server/route/label-studio-sso.js +156 -0
- package/dist-server/route/label-studio-sso.js.map +1 -0
- package/dist-server/route/webhook.d.ts +65 -0
- package/dist-server/route/webhook.js +248 -0
- package/dist-server/route/webhook.js.map +1 -0
- package/dist-server/route.d.ts +1 -0
- package/dist-server/route.js +21 -0
- package/dist-server/route.js.map +1 -0
- package/dist-server/service/ai-prediction-service.d.ts +27 -0
- package/dist-server/service/ai-prediction-service.js +222 -0
- package/dist-server/service/ai-prediction-service.js.map +1 -0
- package/dist-server/service/dataset-labeling-integration.d.ts +44 -0
- package/dist-server/service/dataset-labeling-integration.js +512 -0
- package/dist-server/service/dataset-labeling-integration.js.map +1 -0
- package/dist-server/service/external-data-source-service.d.ts +78 -0
- package/dist-server/service/external-data-source-service.js +415 -0
- package/dist-server/service/external-data-source-service.js.map +1 -0
- package/dist-server/service/index.d.ts +12 -0
- package/dist-server/service/index.js +27 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/label-studio-sso-service.d.ts +38 -0
- package/dist-server/service/label-studio-sso-service.js +98 -0
- package/dist-server/service/label-studio-sso-service.js.map +1 -0
- package/dist-server/service/ml/ml-backend-service.d.ts +23 -0
- package/dist-server/service/ml/ml-backend-service.js +153 -0
- package/dist-server/service/ml/ml-backend-service.js.map +1 -0
- package/dist-server/service/prediction/prediction-management.d.ts +32 -0
- package/dist-server/service/prediction/prediction-management.js +299 -0
- package/dist-server/service/prediction/prediction-management.js.map +1 -0
- package/dist-server/service/project/project-management.d.ts +36 -0
- package/dist-server/service/project/project-management.js +309 -0
- package/dist-server/service/project/project-management.js.map +1 -0
- package/dist-server/service/task/task-management.d.ts +42 -0
- package/dist-server/service/task/task-management.js +372 -0
- package/dist-server/service/task/task-management.js.map +1 -0
- package/dist-server/service/user-provisioning/user-sync-mutation.d.ts +28 -0
- package/dist-server/service/user-provisioning/user-sync-mutation.js +111 -0
- package/dist-server/service/user-provisioning/user-sync-mutation.js.map +1 -0
- package/dist-server/service/webhook/webhook-management.d.ts +21 -0
- package/dist-server/service/webhook/webhook-management.js +134 -0
- package/dist-server/service/webhook/webhook-management.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/dist-server/types/dataset-labeling-types.d.ts +71 -0
- package/dist-server/types/dataset-labeling-types.js +259 -0
- package/dist-server/types/dataset-labeling-types.js.map +1 -0
- package/dist-server/types/label-studio-types.d.ts +128 -0
- package/dist-server/types/label-studio-types.js +494 -0
- package/dist-server/types/label-studio-types.js.map +1 -0
- package/dist-server/types/prediction-types.d.ts +39 -0
- package/dist-server/types/prediction-types.js +121 -0
- package/dist-server/types/prediction-types.js.map +1 -0
- package/dist-server/utils/annotation-exporter.d.ts +104 -0
- package/dist-server/utils/annotation-exporter.js +261 -0
- package/dist-server/utils/annotation-exporter.js.map +1 -0
- package/dist-server/utils/label-config-builder.d.ts +117 -0
- package/dist-server/utils/label-config-builder.js +286 -0
- package/dist-server/utils/label-config-builder.js.map +1 -0
- package/dist-server/utils/label-studio-api-client.d.ts +180 -0
- package/dist-server/utils/label-studio-api-client.js +401 -0
- package/dist-server/utils/label-studio-api-client.js.map +1 -0
- package/dist-server/utils/media-url-extractor.d.ts +45 -0
- package/dist-server/utils/media-url-extractor.js +152 -0
- package/dist-server/utils/media-url-extractor.js.map +1 -0
- package/dist-server/utils/task-transformer.d.ts +108 -0
- package/dist-server/utils/task-transformer.js +260 -0
- package/dist-server/utils/task-transformer.js.map +1 -0
- package/package.json +47 -0
- package/server/SERVER_STRUCTURE.md +351 -0
- package/server/controller/label-studio-role-mapper.ts +76 -0
- package/server/controller/user-provisioning-service.ts +340 -0
- package/server/index.ts +19 -0
- package/server/route/label-studio-sso.ts +194 -0
- package/server/route/webhook.ts +304 -0
- package/server/route.ts +35 -0
- package/server/service/ai-prediction-service.ts +239 -0
- package/server/service/dataset-labeling-integration.ts +590 -0
- package/server/service/external-data-source-service.ts +438 -0
- package/server/service/index.ts +24 -0
- package/server/service/label-studio-sso-service.ts +108 -0
- package/server/service/labeling-scenario-service.ts.deprecated +566 -0
- package/server/service/ml/ml-backend-service.ts +127 -0
- package/server/service/prediction/prediction-management.ts +281 -0
- package/server/service/project/project-management.ts +284 -0
- package/server/service/task/task-management.ts +363 -0
- package/server/service/user-provisioning/user-sync-mutation.ts +80 -0
- package/server/service/webhook/webhook-management.ts +109 -0
- package/server/tsconfig.json +11 -0
- package/server/types/dataset-labeling-types.ts +181 -0
- package/server/types/global.d.ts +23 -0
- package/server/types/label-studio-types.ts +346 -0
- package/server/types/prediction-types.ts +86 -0
- package/server/types/scenario-types.ts.deprecated +362 -0
- package/server/utils/annotation-exporter.ts +340 -0
- package/server/utils/label-config-builder.ts +340 -0
- package/server/utils/label-studio-api-client.ts +487 -0
- package/server/utils/media-url-extractor.ts +193 -0
- package/server/utils/task-transformer.ts +342 -0
- package/test-ai-prediction.js +268 -0
- package/test-dataset-integration.js +449 -0
- package/test-simple.js +89 -0
- package/things-factory.config.js +12 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.webhookRouter = exports.WebhookAction = void 0;
|
|
4
|
+
exports.registerWebhookHandler = registerWebhookHandler;
|
|
5
|
+
exports.unregisterWebhookHandler = unregisterWebhookHandler;
|
|
6
|
+
exports.clearWebhookHandlers = clearWebhookHandlers;
|
|
7
|
+
const tslib_1 = require("tslib");
|
|
8
|
+
const koa_router_1 = tslib_1.__importDefault(require("koa-router"));
|
|
9
|
+
const webhookRouter = new koa_router_1.default();
|
|
10
|
+
exports.webhookRouter = webhookRouter;
|
|
11
|
+
/**
|
|
12
|
+
* Label Studio Webhook Event Types
|
|
13
|
+
*/
|
|
14
|
+
var WebhookAction;
|
|
15
|
+
(function (WebhookAction) {
|
|
16
|
+
WebhookAction["ANNOTATION_CREATED"] = "ANNOTATION_CREATED";
|
|
17
|
+
WebhookAction["ANNOTATION_UPDATED"] = "ANNOTATION_UPDATED";
|
|
18
|
+
WebhookAction["ANNOTATION_DELETED"] = "ANNOTATION_DELETED";
|
|
19
|
+
WebhookAction["TASK_CREATED"] = "TASK_CREATED";
|
|
20
|
+
WebhookAction["TASK_UPDATED"] = "TASK_UPDATED";
|
|
21
|
+
WebhookAction["TASK_DELETED"] = "TASK_DELETED";
|
|
22
|
+
WebhookAction["PROJECT_UPDATED"] = "PROJECT_UPDATED";
|
|
23
|
+
})(WebhookAction || (exports.WebhookAction = WebhookAction = {}));
|
|
24
|
+
/**
|
|
25
|
+
* Registry for custom webhook handlers
|
|
26
|
+
* Multiple handlers can be registered per action
|
|
27
|
+
*/
|
|
28
|
+
const customHandlers = new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Register a custom webhook handler
|
|
31
|
+
* Handlers are executed in registration order
|
|
32
|
+
*
|
|
33
|
+
* @param action - Webhook action to handle
|
|
34
|
+
* @param handler - Handler function
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* registerWebhookHandler(WebhookAction.ANNOTATION_CREATED, async (payload, ctx) => {
|
|
38
|
+
* console.log('Custom handler:', payload.annotation?.id)
|
|
39
|
+
* // Store annotation in database
|
|
40
|
+
* // Trigger ML training
|
|
41
|
+
* // Send notifications
|
|
42
|
+
* })
|
|
43
|
+
*/
|
|
44
|
+
function registerWebhookHandler(action, handler) {
|
|
45
|
+
if (!customHandlers.has(action)) {
|
|
46
|
+
customHandlers.set(action, []);
|
|
47
|
+
}
|
|
48
|
+
customHandlers.get(action).push(handler);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Unregister a webhook handler
|
|
52
|
+
*/
|
|
53
|
+
function unregisterWebhookHandler(action, handler) {
|
|
54
|
+
const handlers = customHandlers.get(action);
|
|
55
|
+
if (handlers) {
|
|
56
|
+
const index = handlers.indexOf(handler);
|
|
57
|
+
if (index > -1) {
|
|
58
|
+
handlers.splice(index, 1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Clear all custom handlers for an action
|
|
64
|
+
*/
|
|
65
|
+
function clearWebhookHandlers(action) {
|
|
66
|
+
if (action) {
|
|
67
|
+
customHandlers.delete(action);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
customHandlers.clear();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Execute all registered handlers for an action
|
|
75
|
+
*/
|
|
76
|
+
async function executeCustomHandlers(action, payload, ctx) {
|
|
77
|
+
const handlers = customHandlers.get(action);
|
|
78
|
+
if (!handlers || handlers.length === 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Execute all handlers in sequence
|
|
82
|
+
for (const handler of handlers) {
|
|
83
|
+
try {
|
|
84
|
+
await handler(payload, ctx);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error(`[Webhook] Custom handler error for ${action}:`, error);
|
|
88
|
+
// Continue executing other handlers even if one fails
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Handle annotation created event
|
|
94
|
+
*/
|
|
95
|
+
async function handleAnnotationCreated(payload) {
|
|
96
|
+
console.log(`[Webhook] Annotation created:`, {
|
|
97
|
+
projectId: payload.project.id,
|
|
98
|
+
taskId: payload.task?.id,
|
|
99
|
+
annotationId: payload.annotation?.id,
|
|
100
|
+
completedBy: payload.annotation?.completed_by?.email
|
|
101
|
+
});
|
|
102
|
+
// TODO: Implement business logic
|
|
103
|
+
// 1. Store annotation in Things-Factory database
|
|
104
|
+
// 2. Trigger downstream processes (e.g., model training)
|
|
105
|
+
// 3. Send notifications to stakeholders
|
|
106
|
+
// 4. Update task status in external systems
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Handle annotation updated event
|
|
110
|
+
*/
|
|
111
|
+
async function handleAnnotationUpdated(payload) {
|
|
112
|
+
console.log(`[Webhook] Annotation updated:`, {
|
|
113
|
+
projectId: payload.project.id,
|
|
114
|
+
annotationId: payload.annotation?.id
|
|
115
|
+
});
|
|
116
|
+
// TODO: Update annotation in database
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Handle annotation deleted event
|
|
120
|
+
*/
|
|
121
|
+
async function handleAnnotationDeleted(payload) {
|
|
122
|
+
console.log(`[Webhook] Annotation deleted:`, {
|
|
123
|
+
projectId: payload.project.id,
|
|
124
|
+
annotationId: payload.annotation?.id
|
|
125
|
+
});
|
|
126
|
+
// TODO: Remove annotation from database
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Handle task created event
|
|
130
|
+
*/
|
|
131
|
+
async function handleTaskCreated(payload) {
|
|
132
|
+
console.log(`[Webhook] Task created:`, {
|
|
133
|
+
projectId: payload.project.id,
|
|
134
|
+
taskId: payload.task?.id
|
|
135
|
+
});
|
|
136
|
+
// TODO: Store task reference
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Handle project updated event
|
|
140
|
+
*/
|
|
141
|
+
async function handleProjectUpdated(payload) {
|
|
142
|
+
console.log(`[Webhook] Project updated:`, {
|
|
143
|
+
projectId: payload.project.id,
|
|
144
|
+
projectTitle: payload.project.title
|
|
145
|
+
});
|
|
146
|
+
// TODO: Update project metadata
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Main webhook endpoint
|
|
150
|
+
* Label Studio will POST events here
|
|
151
|
+
*/
|
|
152
|
+
webhookRouter.post('/label-studio/webhook', async (ctx) => {
|
|
153
|
+
try {
|
|
154
|
+
const payload = ctx.request.body;
|
|
155
|
+
console.log(`[Webhook] Received event: ${payload.action}`);
|
|
156
|
+
// Execute default handlers first
|
|
157
|
+
switch (payload.action) {
|
|
158
|
+
case WebhookAction.ANNOTATION_CREATED:
|
|
159
|
+
await handleAnnotationCreated(payload);
|
|
160
|
+
break;
|
|
161
|
+
case WebhookAction.ANNOTATION_UPDATED:
|
|
162
|
+
await handleAnnotationUpdated(payload);
|
|
163
|
+
break;
|
|
164
|
+
case WebhookAction.ANNOTATION_DELETED:
|
|
165
|
+
await handleAnnotationDeleted(payload);
|
|
166
|
+
break;
|
|
167
|
+
case WebhookAction.TASK_CREATED:
|
|
168
|
+
await handleTaskCreated(payload);
|
|
169
|
+
break;
|
|
170
|
+
case WebhookAction.TASK_UPDATED:
|
|
171
|
+
// Optional: handle task updates
|
|
172
|
+
break;
|
|
173
|
+
case WebhookAction.TASK_DELETED:
|
|
174
|
+
// Optional: handle task deletions
|
|
175
|
+
break;
|
|
176
|
+
case WebhookAction.PROJECT_UPDATED:
|
|
177
|
+
await handleProjectUpdated(payload);
|
|
178
|
+
break;
|
|
179
|
+
default:
|
|
180
|
+
console.warn(`[Webhook] Unknown action: ${payload.action}`);
|
|
181
|
+
}
|
|
182
|
+
// Execute custom handlers
|
|
183
|
+
await executeCustomHandlers(payload.action, payload, ctx);
|
|
184
|
+
ctx.status = 200;
|
|
185
|
+
ctx.body = { success: true };
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error('[Webhook] Error processing webhook:', error);
|
|
189
|
+
ctx.status = 500;
|
|
190
|
+
ctx.body = { error: error.message };
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
/**
|
|
194
|
+
* Webhook registration helper
|
|
195
|
+
* Call this to register webhook with Label Studio
|
|
196
|
+
*/
|
|
197
|
+
webhookRouter.post('/label-studio/webhook/register', async (ctx) => {
|
|
198
|
+
try {
|
|
199
|
+
const { projectId } = ctx.request.body;
|
|
200
|
+
// Get Things-Factory server URL
|
|
201
|
+
const serverUrl = process.env.SERVER_URL || 'http://localhost:3000';
|
|
202
|
+
const webhookUrl = `${serverUrl}/label-studio/webhook`;
|
|
203
|
+
// Register webhook using Label Studio API
|
|
204
|
+
const { labelStudioApi } = await Promise.resolve().then(() => tslib_1.__importStar(require('../utils/label-studio-api-client.js')));
|
|
205
|
+
const webhook = await labelStudioApi.createWebhook({
|
|
206
|
+
project: projectId,
|
|
207
|
+
url: webhookUrl,
|
|
208
|
+
send_payload: true,
|
|
209
|
+
send_for_all_actions: true,
|
|
210
|
+
headers: {
|
|
211
|
+
'Content-Type': 'application/json'
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
ctx.status = 200;
|
|
215
|
+
ctx.body = {
|
|
216
|
+
success: true,
|
|
217
|
+
webhook
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error('[Webhook] Error registering webhook:', error);
|
|
222
|
+
ctx.status = 500;
|
|
223
|
+
ctx.body = { error: error.message };
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
/**
|
|
227
|
+
* Get webhooks for a project
|
|
228
|
+
*/
|
|
229
|
+
webhookRouter.get('/label-studio/webhook/:projectId', async (ctx) => {
|
|
230
|
+
try {
|
|
231
|
+
const projectId = parseInt(ctx.params.projectId);
|
|
232
|
+
const { labelStudioApi } = await Promise.resolve().then(() => tslib_1.__importStar(require('../utils/label-studio-api-client.js')));
|
|
233
|
+
const webhooks = await labelStudioApi.getWebhooks(projectId);
|
|
234
|
+
ctx.status = 200;
|
|
235
|
+
ctx.body = webhooks;
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
console.error('[Webhook] Error fetching webhooks:', error);
|
|
239
|
+
ctx.status = 500;
|
|
240
|
+
ctx.body = { error: error.message };
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
// Register routes with Things-Factory
|
|
244
|
+
process.on('bootstrap-module-domain-private-route', (_app, router) => {
|
|
245
|
+
router.use(webhookRouter.routes());
|
|
246
|
+
router.use(webhookRouter.allowedMethods());
|
|
247
|
+
});
|
|
248
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../server/route/webhook.ts"],"names":[],"mappings":"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","sourcesContent":["import Koa from 'koa'\nimport Router from 'koa-router'\nimport { getRepository } from 'typeorm'\n\nconst webhookRouter = new Router()\n\n/**\n * Label Studio Webhook Event Types\n */\nexport enum WebhookAction {\n ANNOTATION_CREATED = 'ANNOTATION_CREATED',\n ANNOTATION_UPDATED = 'ANNOTATION_UPDATED',\n ANNOTATION_DELETED = 'ANNOTATION_DELETED',\n TASK_CREATED = 'TASK_CREATED',\n TASK_UPDATED = 'TASK_UPDATED',\n TASK_DELETED = 'TASK_DELETED',\n PROJECT_UPDATED = 'PROJECT_UPDATED'\n}\n\nexport interface WebhookPayload {\n action: WebhookAction\n project: {\n id: number\n title: string\n }\n task?: {\n id: number\n data: any\n annotations: any[]\n }\n annotation?: {\n id: number\n result: any[]\n completed_by: {\n id: number\n email: string\n }\n lead_time: number\n }\n}\n\n/**\n * Webhook handler function type\n * Applications can register custom handlers for any webhook action\n */\nexport type WebhookHandler = (payload: WebhookPayload, context: Koa.Context) => Promise<void>\n\n/**\n * Registry for custom webhook handlers\n * Multiple handlers can be registered per action\n */\nconst customHandlers: Map<WebhookAction, WebhookHandler[]> = new Map()\n\n/**\n * Register a custom webhook handler\n * Handlers are executed in registration order\n *\n * @param action - Webhook action to handle\n * @param handler - Handler function\n *\n * @example\n * registerWebhookHandler(WebhookAction.ANNOTATION_CREATED, async (payload, ctx) => {\n * console.log('Custom handler:', payload.annotation?.id)\n * // Store annotation in database\n * // Trigger ML training\n * // Send notifications\n * })\n */\nexport function registerWebhookHandler(action: WebhookAction, handler: WebhookHandler): void {\n if (!customHandlers.has(action)) {\n customHandlers.set(action, [])\n }\n customHandlers.get(action)!.push(handler)\n}\n\n/**\n * Unregister a webhook handler\n */\nexport function unregisterWebhookHandler(action: WebhookAction, handler: WebhookHandler): void {\n const handlers = customHandlers.get(action)\n if (handlers) {\n const index = handlers.indexOf(handler)\n if (index > -1) {\n handlers.splice(index, 1)\n }\n }\n}\n\n/**\n * Clear all custom handlers for an action\n */\nexport function clearWebhookHandlers(action?: WebhookAction): void {\n if (action) {\n customHandlers.delete(action)\n } else {\n customHandlers.clear()\n }\n}\n\n/**\n * Execute all registered handlers for an action\n */\nasync function executeCustomHandlers(action: WebhookAction, payload: WebhookPayload, ctx: Koa.Context): Promise<void> {\n const handlers = customHandlers.get(action)\n if (!handlers || handlers.length === 0) {\n return\n }\n\n // Execute all handlers in sequence\n for (const handler of handlers) {\n try {\n await handler(payload, ctx)\n } catch (error) {\n console.error(`[Webhook] Custom handler error for ${action}:`, error)\n // Continue executing other handlers even if one fails\n }\n }\n}\n\n/**\n * Handle annotation created event\n */\nasync function handleAnnotationCreated(payload: WebhookPayload) {\n console.log(`[Webhook] Annotation created:`, {\n projectId: payload.project.id,\n taskId: payload.task?.id,\n annotationId: payload.annotation?.id,\n completedBy: payload.annotation?.completed_by?.email\n })\n\n // TODO: Implement business logic\n // 1. Store annotation in Things-Factory database\n // 2. Trigger downstream processes (e.g., model training)\n // 3. Send notifications to stakeholders\n // 4. Update task status in external systems\n}\n\n/**\n * Handle annotation updated event\n */\nasync function handleAnnotationUpdated(payload: WebhookPayload) {\n console.log(`[Webhook] Annotation updated:`, {\n projectId: payload.project.id,\n annotationId: payload.annotation?.id\n })\n\n // TODO: Update annotation in database\n}\n\n/**\n * Handle annotation deleted event\n */\nasync function handleAnnotationDeleted(payload: WebhookPayload) {\n console.log(`[Webhook] Annotation deleted:`, {\n projectId: payload.project.id,\n annotationId: payload.annotation?.id\n })\n\n // TODO: Remove annotation from database\n}\n\n/**\n * Handle task created event\n */\nasync function handleTaskCreated(payload: WebhookPayload) {\n console.log(`[Webhook] Task created:`, {\n projectId: payload.project.id,\n taskId: payload.task?.id\n })\n\n // TODO: Store task reference\n}\n\n/**\n * Handle project updated event\n */\nasync function handleProjectUpdated(payload: WebhookPayload) {\n console.log(`[Webhook] Project updated:`, {\n projectId: payload.project.id,\n projectTitle: payload.project.title\n })\n\n // TODO: Update project metadata\n}\n\n/**\n * Main webhook endpoint\n * Label Studio will POST events here\n */\nwebhookRouter.post('/label-studio/webhook', async (ctx: Koa.Context) => {\n try {\n const payload = ctx.request.body as WebhookPayload\n\n console.log(`[Webhook] Received event: ${payload.action}`)\n\n // Execute default handlers first\n switch (payload.action) {\n case WebhookAction.ANNOTATION_CREATED:\n await handleAnnotationCreated(payload)\n break\n\n case WebhookAction.ANNOTATION_UPDATED:\n await handleAnnotationUpdated(payload)\n break\n\n case WebhookAction.ANNOTATION_DELETED:\n await handleAnnotationDeleted(payload)\n break\n\n case WebhookAction.TASK_CREATED:\n await handleTaskCreated(payload)\n break\n\n case WebhookAction.TASK_UPDATED:\n // Optional: handle task updates\n break\n\n case WebhookAction.TASK_DELETED:\n // Optional: handle task deletions\n break\n\n case WebhookAction.PROJECT_UPDATED:\n await handleProjectUpdated(payload)\n break\n\n default:\n console.warn(`[Webhook] Unknown action: ${payload.action}`)\n }\n\n // Execute custom handlers\n await executeCustomHandlers(payload.action, payload, ctx)\n\n ctx.status = 200\n ctx.body = { success: true }\n } catch (error) {\n console.error('[Webhook] Error processing webhook:', error)\n ctx.status = 500\n ctx.body = { error: error.message }\n }\n})\n\n/**\n * Webhook registration helper\n * Call this to register webhook with Label Studio\n */\nwebhookRouter.post('/label-studio/webhook/register', async (ctx: Koa.Context) => {\n try {\n const { projectId } = ctx.request.body as { projectId: number }\n\n // Get Things-Factory server URL\n const serverUrl = process.env.SERVER_URL || 'http://localhost:3000'\n const webhookUrl = `${serverUrl}/label-studio/webhook`\n\n // Register webhook using Label Studio API\n const { labelStudioApi } = await import('../utils/label-studio-api-client.js')\n\n const webhook = await labelStudioApi.createWebhook({\n project: projectId,\n url: webhookUrl,\n send_payload: true,\n send_for_all_actions: true,\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n\n ctx.status = 200\n ctx.body = {\n success: true,\n webhook\n }\n } catch (error) {\n console.error('[Webhook] Error registering webhook:', error)\n ctx.status = 500\n ctx.body = { error: error.message }\n }\n})\n\n/**\n * Get webhooks for a project\n */\nwebhookRouter.get('/label-studio/webhook/:projectId', async (ctx: Koa.Context) => {\n try {\n const projectId = parseInt(ctx.params.projectId)\n\n const { labelStudioApi } = await import('../utils/label-studio-api-client.js')\n const webhooks = await labelStudioApi.getWebhooks(projectId)\n\n ctx.status = 200\n ctx.body = webhooks\n } catch (error) {\n console.error('[Webhook] Error fetching webhooks:', error)\n ctx.status = 500\n ctx.body = { error: error.message }\n }\n})\n\n// Register routes with Things-Factory\nprocess.on('bootstrap-module-domain-private-route', (_app: Koa, router: Router) => {\n router.use(webhookRouter.routes())\n router.use(webhookRouter.allowedMethods())\n})\n\nexport { webhookRouter }\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const webhook_1 = require("./route/webhook");
|
|
4
|
+
const label_studio_sso_1 = require("./route/label-studio-sso");
|
|
5
|
+
// Public routes - No authentication required
|
|
6
|
+
process.on('bootstrap-module-global-public-route', (_app, routes) => {
|
|
7
|
+
/*
|
|
8
|
+
* Register webhook routes (Label Studio → Things Factory)
|
|
9
|
+
* These must be public so Label Studio can send events
|
|
10
|
+
*/
|
|
11
|
+
routes.use(webhook_1.webhookRouter.routes(), webhook_1.webhookRouter.allowedMethods());
|
|
12
|
+
});
|
|
13
|
+
// Private routes - Authentication required
|
|
14
|
+
process.on('bootstrap-module-domain-private-route', (_app, routes) => {
|
|
15
|
+
/*
|
|
16
|
+
* Register Label Studio SSO routes
|
|
17
|
+
* This requires authentication - ctx.state.user will be available
|
|
18
|
+
*/
|
|
19
|
+
routes.use(label_studio_sso_1.ssoRouter.routes(), label_studio_sso_1.ssoRouter.allowedMethods());
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../server/route.ts"],"names":[],"mappings":";;AAeA,6CAA+C;AAC/C,+DAAoD;AAEpD,6CAA6C;AAC7C,OAAO,CAAC,EAAE,CAAC,sCAA6C,EAAE,CAAC,IAAS,EAAE,MAAc,EAAE,EAAE;IACtF;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,uBAAa,CAAC,MAAM,EAAE,EAAE,uBAAa,CAAC,cAAc,EAAE,CAAC,CAAA;AACpE,CAAC,CAAC,CAAA;AAEF,2CAA2C;AAC3C,OAAO,CAAC,EAAE,CAAC,uCAA8C,EAAE,CAAC,IAAS,EAAE,MAAc,EAAE,EAAE;IACvF;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,4BAAS,CAAC,MAAM,EAAE,EAAE,4BAAS,CAAC,cAAc,EAAE,CAAC,CAAA;AAC5D,CAAC,CAAC,CAAA","sourcesContent":["/**\n * Label Studio Integration Routes\n *\n * This module registers all Label Studio-related routes:\n * - Webhook handler for Label Studio events (PUBLIC)\n * - SSO authentication endpoint (PRIVATE - requires authentication)\n *\n * ## Subdomain Cookie Sharing Architecture:\n * This integration uses subdomain-based cookie sharing instead of proxying.\n * - Backend gets JWT token from Label Studio and sets shared domain cookie\n * - Frontend loads Label Studio directly (not through proxy)\n * - Cookie is automatically sent with all subdomain requests\n */\nimport Koa from 'koa'\nimport Router from 'koa-router'\nimport { webhookRouter } from './route/webhook'\nimport { ssoRouter } from './route/label-studio-sso'\n\n// Public routes - No authentication required\nprocess.on('bootstrap-module-global-public-route' as any, (_app: Koa, routes: Router) => {\n /*\n * Register webhook routes (Label Studio → Things Factory)\n * These must be public so Label Studio can send events\n */\n routes.use(webhookRouter.routes(), webhookRouter.allowedMethods())\n})\n\n// Private routes - Authentication required\nprocess.on('bootstrap-module-domain-private-route' as any, (_app: Koa, routes: Router) => {\n /*\n * Register Label Studio SSO routes\n * This requires authentication - ctx.state.user will be available\n */\n routes.use(ssoRouter.routes(), ssoRouter.allowedMethods())\n})\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GeneratePredictionRequest, BatchGeneratePredictionRequest, PredictionGenerationResult, BatchPredictionResult } from '../types/prediction-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Label Studio AI Prediction Service
|
|
4
|
+
*
|
|
5
|
+
* Integrates AI inference with Label Studio prediction system
|
|
6
|
+
* Uses ai-inference module for pure AI operations
|
|
7
|
+
*/
|
|
8
|
+
export declare class LabelStudioAIPredictionService {
|
|
9
|
+
/**
|
|
10
|
+
* Generate Label Studio prediction for a task
|
|
11
|
+
* Runs AI model and creates prediction in Label Studio
|
|
12
|
+
*/
|
|
13
|
+
generatePrediction(input: GeneratePredictionRequest, context: ResolverContext): Promise<PredictionGenerationResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Generate predictions for multiple tasks in batch
|
|
16
|
+
*/
|
|
17
|
+
generateBatchPredictions(input: BatchGeneratePredictionRequest, context: ResolverContext): Promise<BatchPredictionResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Auto-generate predictions for all unlabeled tasks in a project
|
|
20
|
+
*/
|
|
21
|
+
autoGeneratePredictions(projectId: number, modelId: string, confidenceThreshold: number, context: ResolverContext): Promise<BatchPredictionResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Convert AI detection results to Label Studio format
|
|
24
|
+
* This handles the Label Studio-specific data structure
|
|
25
|
+
*/
|
|
26
|
+
private convertToLabelStudioFormat;
|
|
27
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LabelStudioAIPredictionService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const ai_inference_1 = require("@things-factory/ai-inference");
|
|
7
|
+
const label_studio_api_client_js_1 = require("../utils/label-studio-api-client.js");
|
|
8
|
+
const prediction_types_js_1 = require("../types/prediction-types.js");
|
|
9
|
+
/**
|
|
10
|
+
* Label Studio AI Prediction Service
|
|
11
|
+
*
|
|
12
|
+
* Integrates AI inference with Label Studio prediction system
|
|
13
|
+
* Uses ai-inference module for pure AI operations
|
|
14
|
+
*/
|
|
15
|
+
let LabelStudioAIPredictionService = class LabelStudioAIPredictionService {
|
|
16
|
+
/**
|
|
17
|
+
* Generate Label Studio prediction for a task
|
|
18
|
+
* Runs AI model and creates prediction in Label Studio
|
|
19
|
+
*/
|
|
20
|
+
async generatePrediction(input, context) {
|
|
21
|
+
try {
|
|
22
|
+
// 1. Run AI model using ai-inference module
|
|
23
|
+
const modelClient = input.modelId
|
|
24
|
+
? ai_inference_1.AIModelClientFactory.getClient(input.modelId)
|
|
25
|
+
: ai_inference_1.AIModelClientFactory.getDefaultClient();
|
|
26
|
+
const objects = await modelClient.detectObjects(input.imageUrl, {
|
|
27
|
+
confidenceThreshold: input.confidenceThreshold
|
|
28
|
+
});
|
|
29
|
+
if (objects.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
taskId: input.taskId,
|
|
32
|
+
success: true,
|
|
33
|
+
objectCount: 0,
|
|
34
|
+
error: 'No objects detected'
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// 2. Convert AI results to Label Studio format
|
|
38
|
+
const labelStudioResult = this.convertToLabelStudioFormat(objects);
|
|
39
|
+
// 3. Calculate average confidence
|
|
40
|
+
const avgConfidence = objects.reduce((sum, obj) => sum + obj.confidence, 0) / objects.length;
|
|
41
|
+
// 4. Create prediction in Label Studio
|
|
42
|
+
const prediction = await label_studio_api_client_js_1.labelStudioApi.createPrediction({
|
|
43
|
+
task: input.taskId,
|
|
44
|
+
result: labelStudioResult,
|
|
45
|
+
score: avgConfidence,
|
|
46
|
+
model_version: input.modelId || 'default-model-v1.0'
|
|
47
|
+
});
|
|
48
|
+
console.log(`[LS AI Prediction] Created prediction ${prediction.id} for task ${input.taskId} with ${objects.length} objects`);
|
|
49
|
+
return {
|
|
50
|
+
taskId: input.taskId,
|
|
51
|
+
predictionId: prediction.id,
|
|
52
|
+
success: true,
|
|
53
|
+
objectCount: objects.length,
|
|
54
|
+
avgConfidence
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error(`[LS AI Prediction] Failed for task ${input.taskId}:`, error);
|
|
59
|
+
return {
|
|
60
|
+
taskId: input.taskId,
|
|
61
|
+
success: false,
|
|
62
|
+
objectCount: 0,
|
|
63
|
+
error: error.message
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate predictions for multiple tasks in batch
|
|
69
|
+
*/
|
|
70
|
+
async generateBatchPredictions(input, context) {
|
|
71
|
+
const results = [];
|
|
72
|
+
let succeeded = 0;
|
|
73
|
+
let failed = 0;
|
|
74
|
+
const modelId = input.modelId || 'default-model-v1.0';
|
|
75
|
+
try {
|
|
76
|
+
// Process tasks in parallel (limit concurrency to avoid overload)
|
|
77
|
+
const BATCH_SIZE = 5;
|
|
78
|
+
for (let i = 0; i < input.taskIds.length; i += BATCH_SIZE) {
|
|
79
|
+
const batch = input.taskIds.slice(i, i + BATCH_SIZE);
|
|
80
|
+
const batchPromises = batch.map(async (taskId) => {
|
|
81
|
+
try {
|
|
82
|
+
// Get task data from Label Studio
|
|
83
|
+
const task = await label_studio_api_client_js_1.labelStudioApi.getTask(taskId);
|
|
84
|
+
if (!task || !task.data || !task.data.image) {
|
|
85
|
+
failed++;
|
|
86
|
+
return {
|
|
87
|
+
taskId,
|
|
88
|
+
success: false,
|
|
89
|
+
objectCount: 0,
|
|
90
|
+
error: 'Task has no image data'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Generate prediction
|
|
94
|
+
const result = await this.generatePrediction({
|
|
95
|
+
taskId,
|
|
96
|
+
imageUrl: task.data.image,
|
|
97
|
+
modelId: input.modelId,
|
|
98
|
+
confidenceThreshold: input.confidenceThreshold
|
|
99
|
+
}, context);
|
|
100
|
+
if (result.success) {
|
|
101
|
+
succeeded++;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
failed++;
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
failed++;
|
|
110
|
+
return {
|
|
111
|
+
taskId,
|
|
112
|
+
success: false,
|
|
113
|
+
objectCount: 0,
|
|
114
|
+
error: error.message
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const batchResults = await Promise.all(batchPromises);
|
|
119
|
+
results.push(...batchResults);
|
|
120
|
+
}
|
|
121
|
+
console.log(`[LS AI Prediction] Batch completed: ${succeeded} succeeded, ${failed} failed out of ${input.taskIds.length} tasks`);
|
|
122
|
+
return {
|
|
123
|
+
total: input.taskIds.length,
|
|
124
|
+
succeeded,
|
|
125
|
+
failed,
|
|
126
|
+
results,
|
|
127
|
+
modelVersion: modelId
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error('[LS AI Prediction] Batch processing failed:', error);
|
|
132
|
+
throw new Error(`Batch prediction generation failed: ${error.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Auto-generate predictions for all unlabeled tasks in a project
|
|
137
|
+
*/
|
|
138
|
+
async autoGeneratePredictions(projectId, modelId, confidenceThreshold, context) {
|
|
139
|
+
try {
|
|
140
|
+
// Get all tasks from project
|
|
141
|
+
const tasksResponse = await label_studio_api_client_js_1.labelStudioApi.getTasks(projectId, {
|
|
142
|
+
page_size: 1000 // Adjust as needed
|
|
143
|
+
});
|
|
144
|
+
const tasks = tasksResponse.tasks || tasksResponse.results || [];
|
|
145
|
+
// Filter unlabeled tasks (no annotations)
|
|
146
|
+
const unlabeledTasks = tasks.filter((task) => {
|
|
147
|
+
const annotationCount = task.annotations?.length || task.total_annotations || 0;
|
|
148
|
+
return annotationCount === 0;
|
|
149
|
+
});
|
|
150
|
+
console.log(`[LS AI Prediction] Auto-generating predictions for ${unlabeledTasks.length} unlabeled tasks in project ${projectId}`);
|
|
151
|
+
// Generate predictions in batch
|
|
152
|
+
return await this.generateBatchPredictions({
|
|
153
|
+
projectId,
|
|
154
|
+
taskIds: unlabeledTasks.map((t) => t.id),
|
|
155
|
+
modelId,
|
|
156
|
+
confidenceThreshold
|
|
157
|
+
}, context);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error(`[LS AI Prediction] Auto-generation failed for project ${projectId}:`, error);
|
|
161
|
+
throw new Error(`Auto prediction generation failed: ${error.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Convert AI detection results to Label Studio format
|
|
166
|
+
* This handles the Label Studio-specific data structure
|
|
167
|
+
*/
|
|
168
|
+
convertToLabelStudioFormat(objects) {
|
|
169
|
+
return objects.map(obj => ({
|
|
170
|
+
from_name: 'label',
|
|
171
|
+
to_name: 'image',
|
|
172
|
+
type: 'rectanglelabels',
|
|
173
|
+
value: {
|
|
174
|
+
x: obj.bbox.x,
|
|
175
|
+
y: obj.bbox.y,
|
|
176
|
+
width: obj.bbox.width,
|
|
177
|
+
height: obj.bbox.height,
|
|
178
|
+
rectanglelabels: [obj.className]
|
|
179
|
+
}
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
exports.LabelStudioAIPredictionService = LabelStudioAIPredictionService;
|
|
184
|
+
tslib_1.__decorate([
|
|
185
|
+
(0, type_graphql_1.Mutation)(returns => prediction_types_js_1.PredictionGenerationResult, {
|
|
186
|
+
description: 'Generate Label Studio prediction for a task using AI model'
|
|
187
|
+
}),
|
|
188
|
+
(0, type_graphql_1.Directive)('@privilege(category: "label-studio", privilege: "mutation")'),
|
|
189
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
190
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
191
|
+
tslib_1.__metadata("design:type", Function),
|
|
192
|
+
tslib_1.__metadata("design:paramtypes", [prediction_types_js_1.GeneratePredictionRequest, Object]),
|
|
193
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
194
|
+
], LabelStudioAIPredictionService.prototype, "generatePrediction", null);
|
|
195
|
+
tslib_1.__decorate([
|
|
196
|
+
(0, type_graphql_1.Mutation)(returns => prediction_types_js_1.BatchPredictionResult, {
|
|
197
|
+
description: 'Generate Label Studio predictions for multiple tasks in batch'
|
|
198
|
+
}),
|
|
199
|
+
(0, type_graphql_1.Directive)('@privilege(category: "label-studio", privilege: "mutation")'),
|
|
200
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
201
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
202
|
+
tslib_1.__metadata("design:type", Function),
|
|
203
|
+
tslib_1.__metadata("design:paramtypes", [prediction_types_js_1.BatchGeneratePredictionRequest, Object]),
|
|
204
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
205
|
+
], LabelStudioAIPredictionService.prototype, "generateBatchPredictions", null);
|
|
206
|
+
tslib_1.__decorate([
|
|
207
|
+
(0, type_graphql_1.Mutation)(returns => prediction_types_js_1.BatchPredictionResult, {
|
|
208
|
+
description: 'Auto-generate predictions for all unlabeled tasks in a Label Studio project'
|
|
209
|
+
}),
|
|
210
|
+
(0, type_graphql_1.Directive)('@privilege(category: "label-studio", privilege: "mutation")'),
|
|
211
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('projectId', type => type_graphql_1.Int)),
|
|
212
|
+
tslib_1.__param(1, (0, type_graphql_1.Arg)('modelId', { nullable: true })),
|
|
213
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('confidenceThreshold', type => type_graphql_1.Float, { nullable: true })),
|
|
214
|
+
tslib_1.__param(3, (0, type_graphql_1.Ctx)()),
|
|
215
|
+
tslib_1.__metadata("design:type", Function),
|
|
216
|
+
tslib_1.__metadata("design:paramtypes", [Number, String, Number, Object]),
|
|
217
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
218
|
+
], LabelStudioAIPredictionService.prototype, "autoGeneratePredictions", null);
|
|
219
|
+
exports.LabelStudioAIPredictionService = LabelStudioAIPredictionService = tslib_1.__decorate([
|
|
220
|
+
(0, type_graphql_1.Resolver)()
|
|
221
|
+
], LabelStudioAIPredictionService);
|
|
222
|
+
//# sourceMappingURL=ai-prediction-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-prediction-service.js","sourceRoot":"","sources":["../../server/service/ai-prediction-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Int, Float, Directive } from 'type-graphql'\nimport { AIModelClientFactory, DetectedObject } from '@things-factory/ai-inference'\nimport { labelStudioApi } from '../utils/label-studio-api-client.js'\nimport {\n GeneratePredictionRequest,\n BatchGeneratePredictionRequest,\n PredictionGenerationResult,\n BatchPredictionResult\n} from '../types/prediction-types.js'\n\n/**\n * Label Studio AI Prediction Service\n *\n * Integrates AI inference with Label Studio prediction system\n * Uses ai-inference module for pure AI operations\n */\n@Resolver()\nexport class LabelStudioAIPredictionService {\n /**\n * Generate Label Studio prediction for a task\n * Runs AI model and creates prediction in Label Studio\n */\n @Mutation(returns => PredictionGenerationResult, {\n description: 'Generate Label Studio prediction for a task using AI model'\n })\n @Directive('@privilege(category: \"label-studio\", privilege: \"mutation\")')\n async generatePrediction(\n @Arg('input') input: GeneratePredictionRequest,\n @Ctx() context: ResolverContext\n ): Promise<PredictionGenerationResult> {\n try {\n // 1. Run AI model using ai-inference module\n const modelClient = input.modelId\n ? AIModelClientFactory.getClient(input.modelId)\n : AIModelClientFactory.getDefaultClient()\n\n const objects = await modelClient.detectObjects(input.imageUrl, {\n confidenceThreshold: input.confidenceThreshold\n })\n\n if (objects.length === 0) {\n return {\n taskId: input.taskId,\n success: true,\n objectCount: 0,\n error: 'No objects detected'\n }\n }\n\n // 2. Convert AI results to Label Studio format\n const labelStudioResult = this.convertToLabelStudioFormat(objects)\n\n // 3. Calculate average confidence\n const avgConfidence = objects.reduce((sum, obj) => sum + obj.confidence, 0) / objects.length\n\n // 4. Create prediction in Label Studio\n const prediction = await labelStudioApi.createPrediction({\n task: input.taskId,\n result: labelStudioResult,\n score: avgConfidence,\n model_version: input.modelId || 'default-model-v1.0'\n })\n\n console.log(\n `[LS AI Prediction] Created prediction ${prediction.id} for task ${input.taskId} with ${objects.length} objects`\n )\n\n return {\n taskId: input.taskId,\n predictionId: prediction.id,\n success: true,\n objectCount: objects.length,\n avgConfidence\n }\n } catch (error) {\n console.error(`[LS AI Prediction] Failed for task ${input.taskId}:`, error)\n return {\n taskId: input.taskId,\n success: false,\n objectCount: 0,\n error: error.message\n }\n }\n }\n\n /**\n * Generate predictions for multiple tasks in batch\n */\n @Mutation(returns => BatchPredictionResult, {\n description: 'Generate Label Studio predictions for multiple tasks in batch'\n })\n @Directive('@privilege(category: \"label-studio\", privilege: \"mutation\")')\n async generateBatchPredictions(\n @Arg('input') input: BatchGeneratePredictionRequest,\n @Ctx() context: ResolverContext\n ): Promise<BatchPredictionResult> {\n const results: PredictionGenerationResult[] = []\n let succeeded = 0\n let failed = 0\n\n const modelId = input.modelId || 'default-model-v1.0'\n\n try {\n // Process tasks in parallel (limit concurrency to avoid overload)\n const BATCH_SIZE = 5\n for (let i = 0; i < input.taskIds.length; i += BATCH_SIZE) {\n const batch = input.taskIds.slice(i, i + BATCH_SIZE)\n\n const batchPromises = batch.map(async taskId => {\n try {\n // Get task data from Label Studio\n const task = await labelStudioApi.getTask(taskId)\n\n if (!task || !task.data || !task.data.image) {\n failed++\n return {\n taskId,\n success: false,\n objectCount: 0,\n error: 'Task has no image data'\n }\n }\n\n // Generate prediction\n const result = await this.generatePrediction(\n {\n taskId,\n imageUrl: task.data.image,\n modelId: input.modelId,\n confidenceThreshold: input.confidenceThreshold\n },\n context\n )\n\n if (result.success) {\n succeeded++\n } else {\n failed++\n }\n\n return result\n } catch (error) {\n failed++\n return {\n taskId,\n success: false,\n objectCount: 0,\n error: error.message\n }\n }\n })\n\n const batchResults = await Promise.all(batchPromises)\n results.push(...batchResults)\n }\n\n console.log(\n `[LS AI Prediction] Batch completed: ${succeeded} succeeded, ${failed} failed out of ${input.taskIds.length} tasks`\n )\n\n return {\n total: input.taskIds.length,\n succeeded,\n failed,\n results,\n modelVersion: modelId\n }\n } catch (error) {\n console.error('[LS AI Prediction] Batch processing failed:', error)\n throw new Error(`Batch prediction generation failed: ${error.message}`)\n }\n }\n\n /**\n * Auto-generate predictions for all unlabeled tasks in a project\n */\n @Mutation(returns => BatchPredictionResult, {\n description: 'Auto-generate predictions for all unlabeled tasks in a Label Studio project'\n })\n @Directive('@privilege(category: \"label-studio\", privilege: \"mutation\")')\n async autoGeneratePredictions(\n @Arg('projectId', type => Int) projectId: number,\n @Arg('modelId', { nullable: true }) modelId: string,\n @Arg('confidenceThreshold', type => Float, { nullable: true }) confidenceThreshold: number,\n @Ctx() context: ResolverContext\n ): Promise<BatchPredictionResult> {\n try {\n // Get all tasks from project\n const tasksResponse = await labelStudioApi.getTasks(projectId, {\n page_size: 1000 // Adjust as needed\n })\n\n const tasks = tasksResponse.tasks || tasksResponse.results || []\n\n // Filter unlabeled tasks (no annotations)\n const unlabeledTasks = tasks.filter((task: any) => {\n const annotationCount = task.annotations?.length || task.total_annotations || 0\n return annotationCount === 0\n })\n\n console.log(\n `[LS AI Prediction] Auto-generating predictions for ${unlabeledTasks.length} unlabeled tasks in project ${projectId}`\n )\n\n // Generate predictions in batch\n return await this.generateBatchPredictions(\n {\n projectId,\n taskIds: unlabeledTasks.map((t: any) => t.id),\n modelId,\n confidenceThreshold\n },\n context\n )\n } catch (error) {\n console.error(`[LS AI Prediction] Auto-generation failed for project ${projectId}:`, error)\n throw new Error(`Auto prediction generation failed: ${error.message}`)\n }\n }\n\n /**\n * Convert AI detection results to Label Studio format\n * This handles the Label Studio-specific data structure\n */\n private convertToLabelStudioFormat(objects: DetectedObject[]) {\n return objects.map(obj => ({\n from_name: 'label',\n to_name: 'image',\n type: 'rectanglelabels',\n value: {\n x: obj.bbox.x,\n y: obj.bbox.y,\n width: obj.bbox.width,\n height: obj.bbox.height,\n rectanglelabels: [obj.className]\n }\n }))\n }\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CreateLabelingTasksRequest, TaskCreationResult, SyncAnnotationsRequest, SyncAnnotationsResult, DatasetLabelingStatus, DatasetLabelingStatusRequest, GeneratePredictionsForDatasetRequest } from '../types/dataset-labeling-types.js';
|
|
2
|
+
import { BatchPredictionResult } from '../types/prediction-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Dataset Labeling Integration Service
|
|
5
|
+
*
|
|
6
|
+
* Integrates Things-Factory Dataset module with Label Studio for AI-assisted labeling
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Create Label Studio tasks from DataSamples
|
|
10
|
+
* - Auto-generate AI predictions for labeling tasks
|
|
11
|
+
* - Sync human annotations back to DataSamples
|
|
12
|
+
* - Track labeling progress and status
|
|
13
|
+
*/
|
|
14
|
+
export declare class DatasetLabelingIntegration {
|
|
15
|
+
/**
|
|
16
|
+
* Create Label Studio labeling tasks from DataSamples in a DataSet
|
|
17
|
+
* Optionally generates AI predictions automatically
|
|
18
|
+
*/
|
|
19
|
+
createLabelingTasksFromDataset(input: CreateLabelingTasksRequest, context: ResolverContext): Promise<TaskCreationResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Sync completed annotations from Label Studio back to DataSamples
|
|
22
|
+
*/
|
|
23
|
+
syncAnnotationsToDataset(input: SyncAnnotationsRequest, context: ResolverContext): Promise<SyncAnnotationsResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Generate AI predictions for existing DataSet samples
|
|
26
|
+
*/
|
|
27
|
+
generatePredictionsForDataset(input: GeneratePredictionsForDatasetRequest, context: ResolverContext): Promise<BatchPredictionResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Query labeling status for a DataSet
|
|
30
|
+
*/
|
|
31
|
+
datasetLabelingStatus(input: DatasetLabelingStatusRequest, context: ResolverContext): Promise<DatasetLabelingStatus>;
|
|
32
|
+
/**
|
|
33
|
+
* Helper: Extract image URL from DataSample
|
|
34
|
+
*/
|
|
35
|
+
private extractImageUrl;
|
|
36
|
+
/**
|
|
37
|
+
* Helper: Convert AI detection results to Label Studio format
|
|
38
|
+
*/
|
|
39
|
+
private convertToLabelStudioFormat;
|
|
40
|
+
/**
|
|
41
|
+
* Helper: Convert Label Studio annotation to DataSample judgment format
|
|
42
|
+
*/
|
|
43
|
+
private convertAnnotationToJudgment;
|
|
44
|
+
}
|