@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,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test script for Dataset-Label Studio Integration
|
|
3
|
+
*
|
|
4
|
+
* Tests:
|
|
5
|
+
* 1. Query existing DataSets
|
|
6
|
+
* 2. Query DataSamples from a DataSet
|
|
7
|
+
* 3. Create Label Studio tasks from DataSamples
|
|
8
|
+
* 4. Query labeling status
|
|
9
|
+
* 5. Generate AI predictions for tasks
|
|
10
|
+
* 6. Sync annotations back to DataSamples
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const axios = require('axios')
|
|
14
|
+
|
|
15
|
+
// Configuration
|
|
16
|
+
// NOTE: With subdomain cookie-sharing approach, services run on separate subdomains
|
|
17
|
+
const THINGS_FACTORY_GRAPHQL = process.env.THINGS_FACTORY_GRAPHQL || 'http://dataset.localhost:3000/graphql'
|
|
18
|
+
const LABEL_STUDIO_URL = process.env.LABEL_STUDIO_URL || 'http://label.dataset.localhost:8080'
|
|
19
|
+
const LABEL_STUDIO_PROJECT_ID = process.env.LABEL_STUDIO_PROJECT_ID || 2 // Change to your project ID
|
|
20
|
+
|
|
21
|
+
async function queryDataSets() {
|
|
22
|
+
console.log('\nš Step 1: Querying DataSets...')
|
|
23
|
+
console.log('='.repeat(60))
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const query = `
|
|
27
|
+
query {
|
|
28
|
+
dataSets(params: { pagination: { page: 1, limit: 10 } }) {
|
|
29
|
+
items {
|
|
30
|
+
id
|
|
31
|
+
name
|
|
32
|
+
description
|
|
33
|
+
active
|
|
34
|
+
}
|
|
35
|
+
total
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`
|
|
39
|
+
|
|
40
|
+
const response = await axios.post(THINGS_FACTORY_GRAPHQL, { query })
|
|
41
|
+
|
|
42
|
+
if (response.data.errors) {
|
|
43
|
+
console.error('ā GraphQL errors:', response.data.errors)
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const dataSets = response.data.data.dataSets
|
|
48
|
+
console.log(`ā
Found ${dataSets.total} DataSet(s)`)
|
|
49
|
+
|
|
50
|
+
if (dataSets.items.length > 0) {
|
|
51
|
+
console.log('\nAvailable DataSets:')
|
|
52
|
+
dataSets.items.forEach((ds, i) => {
|
|
53
|
+
console.log(` ${i + 1}. ${ds.name} (${ds.id})`)
|
|
54
|
+
console.log(` Active: ${ds.active}`)
|
|
55
|
+
if (ds.description) console.log(` Description: ${ds.description}`)
|
|
56
|
+
})
|
|
57
|
+
return dataSets.items[0] // Return first dataset for testing
|
|
58
|
+
} else {
|
|
59
|
+
console.log('ā ļø No DataSets found. Please create a DataSet first.')
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('ā Failed to query DataSets:', error.message)
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function queryDataSamples(dataSetId) {
|
|
69
|
+
console.log(`\nš¦ Step 2: Querying DataSamples from DataSet ${dataSetId}...`)
|
|
70
|
+
console.log('='.repeat(60))
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const query = `
|
|
74
|
+
query DataSamplesByDataSet($dataSetId: String!) {
|
|
75
|
+
dataSamplesByDataSet(
|
|
76
|
+
dataSetId: $dataSetId
|
|
77
|
+
params: { pagination: { page: 1, limit: 10 } }
|
|
78
|
+
) {
|
|
79
|
+
items {
|
|
80
|
+
id
|
|
81
|
+
name
|
|
82
|
+
data
|
|
83
|
+
rawData
|
|
84
|
+
collectedAt
|
|
85
|
+
}
|
|
86
|
+
total
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`
|
|
90
|
+
|
|
91
|
+
const response = await axios.post(THINGS_FACTORY_GRAPHQL, {
|
|
92
|
+
query,
|
|
93
|
+
variables: { dataSetId }
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
if (response.data.errors) {
|
|
97
|
+
console.error('ā GraphQL errors:', response.data.errors)
|
|
98
|
+
return []
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const samples = response.data.data.dataSamplesByDataSet
|
|
102
|
+
console.log(`ā
Found ${samples.total} DataSample(s)`)
|
|
103
|
+
|
|
104
|
+
if (samples.items.length > 0) {
|
|
105
|
+
console.log('\nDataSample details:')
|
|
106
|
+
samples.items.forEach((sample, i) => {
|
|
107
|
+
console.log(` ${i + 1}. ${sample.name || sample.id}`)
|
|
108
|
+
console.log(` ID: ${sample.id}`)
|
|
109
|
+
console.log(` Collected: ${sample.collectedAt}`)
|
|
110
|
+
|
|
111
|
+
// Check for image URL
|
|
112
|
+
let imageUrl = null
|
|
113
|
+
if (sample.data && typeof sample.data === 'object' && sample.data.image) {
|
|
114
|
+
imageUrl = sample.data.image
|
|
115
|
+
} else if (sample.rawData) {
|
|
116
|
+
try {
|
|
117
|
+
const rawData = typeof sample.rawData === 'string' ? JSON.parse(sample.rawData) : sample.rawData
|
|
118
|
+
if (rawData.image) imageUrl = rawData.image
|
|
119
|
+
else if (typeof rawData === 'string' && rawData.startsWith('http')) imageUrl = rawData
|
|
120
|
+
} catch (e) {
|
|
121
|
+
if (sample.rawData.startsWith('http')) imageUrl = sample.rawData
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (imageUrl) {
|
|
126
|
+
console.log(` Image: ${imageUrl}`)
|
|
127
|
+
} else {
|
|
128
|
+
console.log(` Image: ā ļø No image URL found`)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
return samples.items
|
|
132
|
+
} else {
|
|
133
|
+
console.log('ā ļø No DataSamples found in this DataSet.')
|
|
134
|
+
console.log('š” Create some DataSamples with image URLs for testing.')
|
|
135
|
+
return []
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('ā Failed to query DataSamples:', error.message)
|
|
139
|
+
return []
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function createLabelingTasks(dataSetId) {
|
|
144
|
+
console.log(`\nš·ļø Step 3: Creating Label Studio tasks from DataSet ${dataSetId}...`)
|
|
145
|
+
console.log('='.repeat(60))
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const mutation = `
|
|
149
|
+
mutation CreateLabelingTasks($input: CreateLabelingTasksRequest!) {
|
|
150
|
+
createLabelingTasksFromDataset(input: $input) {
|
|
151
|
+
totalSamples
|
|
152
|
+
tasksCreated
|
|
153
|
+
tasksFailed
|
|
154
|
+
tasksSkipped
|
|
155
|
+
predictionsCreated
|
|
156
|
+
taskIds
|
|
157
|
+
error
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`
|
|
161
|
+
|
|
162
|
+
const response = await axios.post(THINGS_FACTORY_GRAPHQL, {
|
|
163
|
+
query: mutation,
|
|
164
|
+
variables: {
|
|
165
|
+
input: {
|
|
166
|
+
projectId: LABEL_STUDIO_PROJECT_ID,
|
|
167
|
+
dataSetId: dataSetId,
|
|
168
|
+
imageField: 'image',
|
|
169
|
+
autoGeneratePredictions: true,
|
|
170
|
+
confidenceThreshold: 0.5,
|
|
171
|
+
limit: 10
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
if (response.data.errors) {
|
|
177
|
+
console.error('ā GraphQL errors:', response.data.errors)
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const result = response.data.data.createLabelingTasksFromDataset
|
|
182
|
+
|
|
183
|
+
console.log('ā
Task creation completed!')
|
|
184
|
+
console.log(`\nš Results:`)
|
|
185
|
+
console.log(` Total samples processed: ${result.totalSamples}`)
|
|
186
|
+
console.log(` Tasks created: ${result.tasksCreated}`)
|
|
187
|
+
console.log(` Tasks failed: ${result.tasksFailed}`)
|
|
188
|
+
console.log(` Tasks skipped: ${result.tasksSkipped}`)
|
|
189
|
+
if (result.predictionsCreated !== undefined) {
|
|
190
|
+
console.log(` AI predictions created: ${result.predictionsCreated}`)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (result.taskIds && result.taskIds.length > 0) {
|
|
194
|
+
console.log(`\nšÆ Created task IDs:`)
|
|
195
|
+
result.taskIds.forEach(taskId => {
|
|
196
|
+
console.log(` - Task ${taskId}`)
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (result.error) {
|
|
201
|
+
console.log(`\nā ļø Errors: ${result.error}`)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return result
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error('ā Failed to create labeling tasks:', error.message)
|
|
207
|
+
if (error.response?.data) {
|
|
208
|
+
console.error(' Response:', JSON.stringify(error.response.data, null, 2))
|
|
209
|
+
}
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function queryLabelingStatus(dataSetId) {
|
|
215
|
+
console.log(`\nš Step 4: Querying labeling status for DataSet ${dataSetId}...`)
|
|
216
|
+
console.log('='.repeat(60))
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const query = `
|
|
220
|
+
query DatasetLabelingStatus($input: DatasetLabelingStatusRequest!) {
|
|
221
|
+
datasetLabelingStatus(input: $input) {
|
|
222
|
+
dataSetId
|
|
223
|
+
dataSetName
|
|
224
|
+
totalSamples
|
|
225
|
+
tasksCreated
|
|
226
|
+
withPredictions
|
|
227
|
+
withAnnotations
|
|
228
|
+
annotationsCompleted
|
|
229
|
+
notProcessed
|
|
230
|
+
completionRate
|
|
231
|
+
projectId
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
`
|
|
235
|
+
|
|
236
|
+
const response = await axios.post(THINGS_FACTORY_GRAPHQL, {
|
|
237
|
+
query,
|
|
238
|
+
variables: {
|
|
239
|
+
input: {
|
|
240
|
+
dataSetId: dataSetId,
|
|
241
|
+
projectId: LABEL_STUDIO_PROJECT_ID
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
if (response.data.errors) {
|
|
247
|
+
console.error('ā GraphQL errors:', response.data.errors)
|
|
248
|
+
return null
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const status = response.data.data.datasetLabelingStatus
|
|
252
|
+
|
|
253
|
+
console.log('ā
Labeling status retrieved!')
|
|
254
|
+
console.log(`\nš Status for "${status.dataSetName}":`)
|
|
255
|
+
console.log(` Total samples: ${status.totalSamples}`)
|
|
256
|
+
console.log(` Tasks created: ${status.tasksCreated}`)
|
|
257
|
+
console.log(` With AI predictions: ${status.withPredictions}`)
|
|
258
|
+
console.log(` With annotations: ${status.withAnnotations}`)
|
|
259
|
+
console.log(` Annotations completed: ${status.annotationsCompleted}`)
|
|
260
|
+
console.log(` Not yet processed: ${status.notProcessed}`)
|
|
261
|
+
console.log(` Completion rate: ${(status.completionRate * 100).toFixed(1)}%`)
|
|
262
|
+
|
|
263
|
+
return status
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.error('ā Failed to query labeling status:', error.message)
|
|
266
|
+
return null
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function generatePredictions(dataSetId) {
|
|
271
|
+
console.log(`\nš¤ Step 5: Generating AI predictions for DataSet ${dataSetId}...`)
|
|
272
|
+
console.log('='.repeat(60))
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const mutation = `
|
|
276
|
+
mutation GeneratePredictions($input: GeneratePredictionsForDatasetRequest!) {
|
|
277
|
+
generatePredictionsForDataset(input: $input) {
|
|
278
|
+
total
|
|
279
|
+
succeeded
|
|
280
|
+
failed
|
|
281
|
+
modelVersion
|
|
282
|
+
results {
|
|
283
|
+
taskId
|
|
284
|
+
predictionId
|
|
285
|
+
success
|
|
286
|
+
objectCount
|
|
287
|
+
avgConfidence
|
|
288
|
+
error
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
`
|
|
293
|
+
|
|
294
|
+
const response = await axios.post(THINGS_FACTORY_GRAPHQL, {
|
|
295
|
+
query: mutation,
|
|
296
|
+
variables: {
|
|
297
|
+
input: {
|
|
298
|
+
dataSetId: dataSetId,
|
|
299
|
+
projectId: LABEL_STUDIO_PROJECT_ID,
|
|
300
|
+
confidenceThreshold: 0.5,
|
|
301
|
+
forceRegenerate: false
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
if (response.data.errors) {
|
|
307
|
+
console.error('ā GraphQL errors:', response.data.errors)
|
|
308
|
+
return null
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const result = response.data.data.generatePredictionsForDataset
|
|
312
|
+
|
|
313
|
+
console.log('ā
Prediction generation completed!')
|
|
314
|
+
console.log(`\nš Results:`)
|
|
315
|
+
console.log(` Total tasks: ${result.total}`)
|
|
316
|
+
console.log(` Succeeded: ${result.succeeded}`)
|
|
317
|
+
console.log(` Failed: ${result.failed}`)
|
|
318
|
+
console.log(` Model version: ${result.modelVersion}`)
|
|
319
|
+
|
|
320
|
+
if (result.results && result.results.length > 0) {
|
|
321
|
+
console.log(`\nšÆ Prediction details:`)
|
|
322
|
+
result.results.forEach((res, i) => {
|
|
323
|
+
if (res.success && res.objectCount > 0) {
|
|
324
|
+
console.log(` Task ${res.taskId}: ${res.objectCount} objects (avg conf: ${(res.avgConfidence * 100).toFixed(1)}%)`)
|
|
325
|
+
} else if (res.error) {
|
|
326
|
+
console.log(` Task ${res.taskId}: ${res.error}`)
|
|
327
|
+
}
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return result
|
|
332
|
+
} catch (error) {
|
|
333
|
+
console.error('ā Failed to generate predictions:', error.message)
|
|
334
|
+
return null
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function syncAnnotations(dataSetId) {
|
|
339
|
+
console.log(`\nš Step 6: Syncing annotations back to DataSet ${dataSetId}...`)
|
|
340
|
+
console.log('='.repeat(60))
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
const mutation = `
|
|
344
|
+
mutation SyncAnnotations($input: SyncAnnotationsRequest!) {
|
|
345
|
+
syncAnnotationsToDataset(input: $input) {
|
|
346
|
+
totalAnnotations
|
|
347
|
+
samplesUpdated
|
|
348
|
+
updatesFailed
|
|
349
|
+
skipped
|
|
350
|
+
error
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
`
|
|
354
|
+
|
|
355
|
+
const response = await axios.post(THINGS_FACTORY_GRAPHQL, {
|
|
356
|
+
query: mutation,
|
|
357
|
+
variables: {
|
|
358
|
+
input: {
|
|
359
|
+
projectId: LABEL_STUDIO_PROJECT_ID,
|
|
360
|
+
dataSetId: dataSetId,
|
|
361
|
+
completedOnly: false
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
if (response.data.errors) {
|
|
367
|
+
console.error('ā GraphQL errors:', response.data.errors)
|
|
368
|
+
return null
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const result = response.data.data.syncAnnotationsToDataset
|
|
372
|
+
|
|
373
|
+
console.log('ā
Annotation sync completed!')
|
|
374
|
+
console.log(`\nš Results:`)
|
|
375
|
+
console.log(` Total annotations processed: ${result.totalAnnotations}`)
|
|
376
|
+
console.log(` DataSamples updated: ${result.samplesUpdated}`)
|
|
377
|
+
console.log(` Updates failed: ${result.updatesFailed}`)
|
|
378
|
+
console.log(` Skipped: ${result.skipped}`)
|
|
379
|
+
|
|
380
|
+
if (result.error) {
|
|
381
|
+
console.log(`\nā ļø Errors: ${result.error}`)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return result
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.error('ā Failed to sync annotations:', error.message)
|
|
387
|
+
return null
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async function main() {
|
|
392
|
+
console.log('š Dataset-Label Studio Integration Test\n')
|
|
393
|
+
console.log('='.repeat(60))
|
|
394
|
+
console.log(` Things Factory GraphQL: ${THINGS_FACTORY_GRAPHQL}`)
|
|
395
|
+
console.log(` Label Studio URL: ${LABEL_STUDIO_URL}`)
|
|
396
|
+
console.log(` Label Studio Project ID: ${LABEL_STUDIO_PROJECT_ID}`)
|
|
397
|
+
console.log('='.repeat(60))
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
// Step 1: Query DataSets
|
|
401
|
+
const dataSet = await queryDataSets()
|
|
402
|
+
if (!dataSet) {
|
|
403
|
+
console.log('\nš” Please create a DataSet with DataSamples first.')
|
|
404
|
+
process.exit(1)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Step 2: Query DataSamples
|
|
408
|
+
const samples = await queryDataSamples(dataSet.id)
|
|
409
|
+
if (samples.length === 0) {
|
|
410
|
+
console.log('\nš” Please add DataSamples with image URLs to the DataSet.')
|
|
411
|
+
process.exit(1)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Step 3: Create Label Studio tasks
|
|
415
|
+
const createResult = await createLabelingTasks(dataSet.id)
|
|
416
|
+
if (!createResult || createResult.tasksCreated === 0) {
|
|
417
|
+
console.log('\nā ļø No tasks were created. Check if images are properly configured.')
|
|
418
|
+
process.exit(1)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Step 4: Query labeling status
|
|
422
|
+
await queryLabelingStatus(dataSet.id)
|
|
423
|
+
|
|
424
|
+
// Step 5: Generate predictions (if not already done)
|
|
425
|
+
await generatePredictions(dataSet.id)
|
|
426
|
+
|
|
427
|
+
// Step 6: Sync annotations (if any exist)
|
|
428
|
+
await syncAnnotations(dataSet.id)
|
|
429
|
+
|
|
430
|
+
console.log('\n' + '='.repeat(60))
|
|
431
|
+
console.log('⨠Integration test completed successfully!')
|
|
432
|
+
console.log('\nš Next steps:')
|
|
433
|
+
console.log(` 1. Open Label Studio: ${LABEL_STUDIO_URL}/projects/${LABEL_STUDIO_PROJECT_ID}`)
|
|
434
|
+
console.log(` 2. Review AI-generated predictions`)
|
|
435
|
+
console.log(` 3. Complete annotations`)
|
|
436
|
+
console.log(` 4. Run syncAnnotations again to update DataSamples`)
|
|
437
|
+
console.log('='.repeat(60))
|
|
438
|
+
|
|
439
|
+
} catch (error) {
|
|
440
|
+
console.error('\nš„ Test failed with error:', error)
|
|
441
|
+
process.exit(1)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Run the test
|
|
446
|
+
main().catch(error => {
|
|
447
|
+
console.error('š„ Unexpected error:', error)
|
|
448
|
+
process.exit(1)
|
|
449
|
+
})
|
package/test-simple.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple test for AI inference (without Label Studio API token)
|
|
3
|
+
* Tests just the AI detection functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const axios = require('axios')
|
|
7
|
+
|
|
8
|
+
const THINGS_FACTORY_GRAPHQL = process.env.THINGS_FACTORY_GRAPHQL || 'http://dataset.localhost:3000/graphql'
|
|
9
|
+
|
|
10
|
+
// Sample image URL for testing
|
|
11
|
+
const TEST_IMAGE_URL = 'https://example.com/test.jpg'
|
|
12
|
+
|
|
13
|
+
async function testAIDetection() {
|
|
14
|
+
console.log('š¤ Testing AI Object Detection\n')
|
|
15
|
+
console.log('='.repeat(60))
|
|
16
|
+
console.log(` GraphQL: ${THINGS_FACTORY_GRAPHQL}`)
|
|
17
|
+
console.log(` Test Image: ${TEST_IMAGE_URL}`)
|
|
18
|
+
console.log('='.repeat(60))
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const query = `
|
|
22
|
+
query DetectObjects($input: InferenceRequest!) {
|
|
23
|
+
detectObjects(input: $input) {
|
|
24
|
+
className
|
|
25
|
+
confidence
|
|
26
|
+
bbox {
|
|
27
|
+
x
|
|
28
|
+
y
|
|
29
|
+
width
|
|
30
|
+
height
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`
|
|
35
|
+
|
|
36
|
+
console.log('\nš¤ Sending GraphQL request...')
|
|
37
|
+
|
|
38
|
+
const response = await axios.post(THINGS_FACTORY_GRAPHQL, {
|
|
39
|
+
query,
|
|
40
|
+
variables: {
|
|
41
|
+
input: {
|
|
42
|
+
imageUrl: TEST_IMAGE_URL,
|
|
43
|
+
confidenceThreshold: 0.5
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (response.data.errors) {
|
|
49
|
+
console.error('\nā GraphQL errors:')
|
|
50
|
+
response.data.errors.forEach(err => {
|
|
51
|
+
console.error(` - ${err.message}`)
|
|
52
|
+
})
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const objects = response.data.data.detectObjects
|
|
57
|
+
|
|
58
|
+
console.log('\nā
AI Detection successful!')
|
|
59
|
+
console.log(`\nšÆ Detected ${objects.length} objects:\n`)
|
|
60
|
+
|
|
61
|
+
objects.forEach((obj, i) => {
|
|
62
|
+
console.log(` ${i + 1}. ${obj.className}`)
|
|
63
|
+
console.log(` Confidence: ${(obj.confidence * 100).toFixed(1)}%`)
|
|
64
|
+
console.log(` BBox: x=${obj.bbox.x}, y=${obj.bbox.y}, w=${obj.bbox.width}, h=${obj.bbox.height}`)
|
|
65
|
+
console.log()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
console.log('='.repeat(60))
|
|
69
|
+
console.log('⨠Mock AI is working correctly!')
|
|
70
|
+
console.log('\nš This confirms:')
|
|
71
|
+
console.log(' ā ai-inference module is loaded')
|
|
72
|
+
console.log(' ā GraphQL API is accessible')
|
|
73
|
+
console.log(' ā Mock AI client is functioning')
|
|
74
|
+
console.log('\nš” Next step: Configure Label Studio API token to test full integration')
|
|
75
|
+
|
|
76
|
+
return true
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error('\nā Test failed:', error.message)
|
|
79
|
+
if (error.response?.data) {
|
|
80
|
+
console.error('\n Response:', JSON.stringify(error.response.data, null, 2))
|
|
81
|
+
}
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Run test
|
|
87
|
+
testAIDetection().then(success => {
|
|
88
|
+
process.exit(success ? 0 : 1)
|
|
89
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import route from './dist-client/route'
|
|
2
|
+
import bootstrap from './dist-client/bootstrap'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
route,
|
|
6
|
+
routes: [
|
|
7
|
+
{ tagname: 'label-studio-project-list', page: 'label-studio-project-list' },
|
|
8
|
+
{ tagname: 'label-studio-project-create', page: 'label-studio-project-create' },
|
|
9
|
+
{ tagname: 'label-studio-label-page', page: 'label-studio-label' }
|
|
10
|
+
],
|
|
11
|
+
bootstrap
|
|
12
|
+
}
|