@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.
Files changed (152) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/EXTERNAL_DATA_SOURCING.md +484 -0
  3. package/IMPLEMENTATION_GUIDE.md +469 -0
  4. package/INTEGRATION.md +279 -0
  5. package/README.md +1014 -0
  6. package/SETUP_GUIDE.md +577 -0
  7. package/TEST_GUIDE.md +387 -0
  8. package/UI_CUSTOMIZATION.md +395 -0
  9. package/USER_SYNC_GUIDE.md +514 -0
  10. package/client/bootstrap.ts +1 -0
  11. package/client/index.ts +1 -0
  12. package/client/label-studio-label-page.ts +52 -0
  13. package/client/label-studio-project-create.ts +216 -0
  14. package/client/label-studio-project-list.ts +214 -0
  15. package/client/label-studio-wrapper.ts +294 -0
  16. package/client/route.ts +15 -0
  17. package/client/tsconfig.json +13 -0
  18. package/config/config.development.js +124 -0
  19. package/config/config.production.js +182 -0
  20. package/dist-client/bootstrap.d.ts +1 -0
  21. package/dist-client/bootstrap.js +2 -0
  22. package/dist-client/bootstrap.js.map +1 -0
  23. package/dist-client/index.d.ts +1 -0
  24. package/dist-client/index.js +2 -0
  25. package/dist-client/index.js.map +1 -0
  26. package/dist-client/label-studio-label-page.d.ts +8 -0
  27. package/dist-client/label-studio-label-page.js +54 -0
  28. package/dist-client/label-studio-label-page.js.map +1 -0
  29. package/dist-client/label-studio-project-create.d.ts +16 -0
  30. package/dist-client/label-studio-project-create.js +235 -0
  31. package/dist-client/label-studio-project-create.js.map +1 -0
  32. package/dist-client/label-studio-project-list.d.ts +16 -0
  33. package/dist-client/label-studio-project-list.js +222 -0
  34. package/dist-client/label-studio-project-list.js.map +1 -0
  35. package/dist-client/label-studio-wrapper.d.ts +57 -0
  36. package/dist-client/label-studio-wrapper.js +304 -0
  37. package/dist-client/label-studio-wrapper.js.map +1 -0
  38. package/dist-client/route.d.ts +1 -0
  39. package/dist-client/route.js +14 -0
  40. package/dist-client/route.js.map +1 -0
  41. package/dist-client/tsconfig.tsbuildinfo +1 -0
  42. package/dist-server/controller/label-studio-role-mapper.d.ts +35 -0
  43. package/dist-server/controller/label-studio-role-mapper.js +65 -0
  44. package/dist-server/controller/label-studio-role-mapper.js.map +1 -0
  45. package/dist-server/controller/user-provisioning-service.d.ts +66 -0
  46. package/dist-server/controller/user-provisioning-service.js +264 -0
  47. package/dist-server/controller/user-provisioning-service.js.map +1 -0
  48. package/dist-server/index.d.ts +7 -0
  49. package/dist-server/index.js +19 -0
  50. package/dist-server/index.js.map +1 -0
  51. package/dist-server/route/label-studio-sso.d.ts +2 -0
  52. package/dist-server/route/label-studio-sso.js +156 -0
  53. package/dist-server/route/label-studio-sso.js.map +1 -0
  54. package/dist-server/route/webhook.d.ts +65 -0
  55. package/dist-server/route/webhook.js +248 -0
  56. package/dist-server/route/webhook.js.map +1 -0
  57. package/dist-server/route.d.ts +1 -0
  58. package/dist-server/route.js +21 -0
  59. package/dist-server/route.js.map +1 -0
  60. package/dist-server/service/ai-prediction-service.d.ts +27 -0
  61. package/dist-server/service/ai-prediction-service.js +222 -0
  62. package/dist-server/service/ai-prediction-service.js.map +1 -0
  63. package/dist-server/service/dataset-labeling-integration.d.ts +44 -0
  64. package/dist-server/service/dataset-labeling-integration.js +512 -0
  65. package/dist-server/service/dataset-labeling-integration.js.map +1 -0
  66. package/dist-server/service/external-data-source-service.d.ts +78 -0
  67. package/dist-server/service/external-data-source-service.js +415 -0
  68. package/dist-server/service/external-data-source-service.js.map +1 -0
  69. package/dist-server/service/index.d.ts +12 -0
  70. package/dist-server/service/index.js +27 -0
  71. package/dist-server/service/index.js.map +1 -0
  72. package/dist-server/service/label-studio-sso-service.d.ts +38 -0
  73. package/dist-server/service/label-studio-sso-service.js +98 -0
  74. package/dist-server/service/label-studio-sso-service.js.map +1 -0
  75. package/dist-server/service/ml/ml-backend-service.d.ts +23 -0
  76. package/dist-server/service/ml/ml-backend-service.js +153 -0
  77. package/dist-server/service/ml/ml-backend-service.js.map +1 -0
  78. package/dist-server/service/prediction/prediction-management.d.ts +32 -0
  79. package/dist-server/service/prediction/prediction-management.js +299 -0
  80. package/dist-server/service/prediction/prediction-management.js.map +1 -0
  81. package/dist-server/service/project/project-management.d.ts +36 -0
  82. package/dist-server/service/project/project-management.js +309 -0
  83. package/dist-server/service/project/project-management.js.map +1 -0
  84. package/dist-server/service/task/task-management.d.ts +42 -0
  85. package/dist-server/service/task/task-management.js +372 -0
  86. package/dist-server/service/task/task-management.js.map +1 -0
  87. package/dist-server/service/user-provisioning/user-sync-mutation.d.ts +28 -0
  88. package/dist-server/service/user-provisioning/user-sync-mutation.js +111 -0
  89. package/dist-server/service/user-provisioning/user-sync-mutation.js.map +1 -0
  90. package/dist-server/service/webhook/webhook-management.d.ts +21 -0
  91. package/dist-server/service/webhook/webhook-management.js +134 -0
  92. package/dist-server/service/webhook/webhook-management.js.map +1 -0
  93. package/dist-server/tsconfig.tsbuildinfo +1 -0
  94. package/dist-server/types/dataset-labeling-types.d.ts +71 -0
  95. package/dist-server/types/dataset-labeling-types.js +259 -0
  96. package/dist-server/types/dataset-labeling-types.js.map +1 -0
  97. package/dist-server/types/label-studio-types.d.ts +128 -0
  98. package/dist-server/types/label-studio-types.js +494 -0
  99. package/dist-server/types/label-studio-types.js.map +1 -0
  100. package/dist-server/types/prediction-types.d.ts +39 -0
  101. package/dist-server/types/prediction-types.js +121 -0
  102. package/dist-server/types/prediction-types.js.map +1 -0
  103. package/dist-server/utils/annotation-exporter.d.ts +104 -0
  104. package/dist-server/utils/annotation-exporter.js +261 -0
  105. package/dist-server/utils/annotation-exporter.js.map +1 -0
  106. package/dist-server/utils/label-config-builder.d.ts +117 -0
  107. package/dist-server/utils/label-config-builder.js +286 -0
  108. package/dist-server/utils/label-config-builder.js.map +1 -0
  109. package/dist-server/utils/label-studio-api-client.d.ts +180 -0
  110. package/dist-server/utils/label-studio-api-client.js +401 -0
  111. package/dist-server/utils/label-studio-api-client.js.map +1 -0
  112. package/dist-server/utils/media-url-extractor.d.ts +45 -0
  113. package/dist-server/utils/media-url-extractor.js +152 -0
  114. package/dist-server/utils/media-url-extractor.js.map +1 -0
  115. package/dist-server/utils/task-transformer.d.ts +108 -0
  116. package/dist-server/utils/task-transformer.js +260 -0
  117. package/dist-server/utils/task-transformer.js.map +1 -0
  118. package/package.json +47 -0
  119. package/server/SERVER_STRUCTURE.md +351 -0
  120. package/server/controller/label-studio-role-mapper.ts +76 -0
  121. package/server/controller/user-provisioning-service.ts +340 -0
  122. package/server/index.ts +19 -0
  123. package/server/route/label-studio-sso.ts +194 -0
  124. package/server/route/webhook.ts +304 -0
  125. package/server/route.ts +35 -0
  126. package/server/service/ai-prediction-service.ts +239 -0
  127. package/server/service/dataset-labeling-integration.ts +590 -0
  128. package/server/service/external-data-source-service.ts +438 -0
  129. package/server/service/index.ts +24 -0
  130. package/server/service/label-studio-sso-service.ts +108 -0
  131. package/server/service/labeling-scenario-service.ts.deprecated +566 -0
  132. package/server/service/ml/ml-backend-service.ts +127 -0
  133. package/server/service/prediction/prediction-management.ts +281 -0
  134. package/server/service/project/project-management.ts +284 -0
  135. package/server/service/task/task-management.ts +363 -0
  136. package/server/service/user-provisioning/user-sync-mutation.ts +80 -0
  137. package/server/service/webhook/webhook-management.ts +109 -0
  138. package/server/tsconfig.json +11 -0
  139. package/server/types/dataset-labeling-types.ts +181 -0
  140. package/server/types/global.d.ts +23 -0
  141. package/server/types/label-studio-types.ts +346 -0
  142. package/server/types/prediction-types.ts +86 -0
  143. package/server/types/scenario-types.ts.deprecated +362 -0
  144. package/server/utils/annotation-exporter.ts +340 -0
  145. package/server/utils/label-config-builder.ts +340 -0
  146. package/server/utils/label-studio-api-client.ts +487 -0
  147. package/server/utils/media-url-extractor.ts +193 -0
  148. package/server/utils/task-transformer.ts +342 -0
  149. package/test-ai-prediction.js +268 -0
  150. package/test-dataset-integration.js +449 -0
  151. package/test-simple.js +89 -0
  152. 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":";;;AAoEA,wDAKC;AAKD,4DAQC;AAKD,oDAMC;;AAhGD,oEAA+B;AAG/B,MAAM,aAAa,GAAG,IAAI,oBAAM,EAAE,CAAA;AA2SzB,sCAAa;AAzStB;;GAEG;AACH,IAAY,aAQX;AARD,WAAY,aAAa;IACvB,0DAAyC,CAAA;IACzC,0DAAyC,CAAA;IACzC,0DAAyC,CAAA;IACzC,8CAA6B,CAAA;IAC7B,8CAA6B,CAAA;IAC7B,8CAA6B,CAAA;IAC7B,oDAAmC,CAAA;AACrC,CAAC,EARW,aAAa,6BAAb,aAAa,QAQxB;AA8BD;;;GAGG;AACH,MAAM,cAAc,GAAyC,IAAI,GAAG,EAAE,CAAA;AAEtE;;;;;;;;;;;;;;GAcG;AACH,SAAgB,sBAAsB,CAAC,MAAqB,EAAE,OAAuB;IACnF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAChC,CAAC;IACD,cAAc,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AAC3C,CAAC;AAED;;GAEG;AACH,SAAgB,wBAAwB,CAAC,MAAqB,EAAE,OAAuB;IACrF,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;YACf,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAAC,MAAsB;IACzD,IAAI,MAAM,EAAE,CAAC;QACX,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC/B,CAAC;SAAM,CAAC;QACN,cAAc,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,qBAAqB,CAAC,MAAqB,EAAE,OAAuB,EAAE,GAAgB;IACnG,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAM;IACR,CAAC;IAED,mCAAmC;IACnC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,MAAM,GAAG,EAAE,KAAK,CAAC,CAAA;YACrE,sDAAsD;QACxD,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CAAC,OAAuB;IAC5D,OAAO,CAAC,GAAG,CAAC,+BAA+B,EAAE;QAC3C,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;QAC7B,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE;QACxB,YAAY,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE;QACpC,WAAW,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,KAAK;KACrD,CAAC,CAAA;IAEF,iCAAiC;IACjC,iDAAiD;IACjD,yDAAyD;IACzD,wCAAwC;IACxC,4CAA4C;AAC9C,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CAAC,OAAuB;IAC5D,OAAO,CAAC,GAAG,CAAC,+BAA+B,EAAE;QAC3C,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;QAC7B,YAAY,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE;KACrC,CAAC,CAAA;IAEF,sCAAsC;AACxC,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CAAC,OAAuB;IAC5D,OAAO,CAAC,GAAG,CAAC,+BAA+B,EAAE;QAC3C,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;QAC7B,YAAY,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE;KACrC,CAAC,CAAA;IAEF,wCAAwC;AAC1C,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,iBAAiB,CAAC,OAAuB;IACtD,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE;QACrC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;QAC7B,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE;KACzB,CAAC,CAAA;IAEF,6BAA6B;AAC/B,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,oBAAoB,CAAC,OAAuB;IACzD,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE;QACxC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;QAC7B,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK;KACpC,CAAC,CAAA;IAEF,gCAAgC;AAClC,CAAC;AAED;;;GAGG;AACH,aAAa,CAAC,IAAI,CAAC,uBAAuB,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE;IACrE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAsB,CAAA;QAElD,OAAO,CAAC,GAAG,CAAC,6BAA6B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAE1D,iCAAiC;QACjC,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,KAAK,aAAa,CAAC,kBAAkB;gBACnC,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAA;gBACtC,MAAK;YAEP,KAAK,aAAa,CAAC,kBAAkB;gBACnC,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAA;gBACtC,MAAK;YAEP,KAAK,aAAa,CAAC,kBAAkB;gBACnC,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAA;gBACtC,MAAK;YAEP,KAAK,aAAa,CAAC,YAAY;gBAC7B,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAA;gBAChC,MAAK;YAEP,KAAK,aAAa,CAAC,YAAY;gBAC7B,gCAAgC;gBAChC,MAAK;YAEP,KAAK,aAAa,CAAC,YAAY;gBAC7B,kCAAkC;gBAClC,MAAK;YAEP,KAAK,aAAa,CAAC,eAAe;gBAChC,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAA;gBACnC,MAAK;YAEP;gBACE,OAAO,CAAC,IAAI,CAAC,6BAA6B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,0BAA0B;QAC1B,MAAM,qBAAqB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,CAAA;QAEzD,GAAG,CAAC,MAAM,GAAG,GAAG,CAAA;QAChB,GAAG,CAAC,IAAI,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAA;QAC3D,GAAG,CAAC,MAAM,GAAG,GAAG,CAAA;QAChB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;IACrC,CAAC;AACH,CAAC,CAAC,CAAA;AAEF;;;GAGG;AACH,aAAa,CAAC,IAAI,CAAC,gCAAgC,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,IAA6B,CAAA;QAE/D,gCAAgC;QAChC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,uBAAuB,CAAA;QACnE,MAAM,UAAU,GAAG,GAAG,SAAS,uBAAuB,CAAA;QAEtD,0CAA0C;QAC1C,MAAM,EAAE,cAAc,EAAE,GAAG,gEAAa,qCAAqC,GAAC,CAAA;QAE9E,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,aAAa,CAAC;YACjD,OAAO,EAAE,SAAS;YAClB,GAAG,EAAE,UAAU;YACf,YAAY,EAAE,IAAI;YAClB,oBAAoB,EAAE,IAAI;YAC1B,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAA;QAEF,GAAG,CAAC,MAAM,GAAG,GAAG,CAAA;QAChB,GAAG,CAAC,IAAI,GAAG;YACT,OAAO,EAAE,IAAI;YACb,OAAO;SACR,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAA;QAC5D,GAAG,CAAC,MAAM,GAAG,GAAG,CAAA;QAChB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;IACrC,CAAC;AACH,CAAC,CAAC,CAAA;AAEF;;GAEG;AACH,aAAa,CAAC,GAAG,CAAC,kCAAkC,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE;IAC/E,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAEhD,MAAM,EAAE,cAAc,EAAE,GAAG,gEAAa,qCAAqC,GAAC,CAAA;QAC9E,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;QAE5D,GAAG,CAAC,MAAM,GAAG,GAAG,CAAA;QAChB,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAA;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAA;QAC1D,GAAG,CAAC,MAAM,GAAG,GAAG,CAAA;QAChB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;IACrC,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,sCAAsC;AACtC,OAAO,CAAC,EAAE,CAAC,uCAAuC,EAAE,CAAC,IAAS,EAAE,MAAc,EAAE,EAAE;IAChF,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAA;IAClC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,cAAc,EAAE,CAAC,CAAA;AAC5C,CAAC,CAAC,CAAA","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
+ }