@fortemi/core 2026.7.3 → 2026.7.4
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/README.md +3 -3
- package/dist/aiwg-index-C2G7iy-b.d.ts +826 -0
- package/dist/aiwg-index.d.ts +1 -478
- package/dist/aiwg-index.js +992 -80
- package/dist/aiwg-index.js.map +1 -1
- package/dist/index.d.ts +223 -328
- package/dist/index.js +2763 -222
- package/dist/index.js.map +1 -1
- package/package.json +15 -3
- package/tools/verify-fortemi-compatibility.mjs +82 -0
package/dist/aiwg-index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { sha256 } from '@noble/hashes/sha256';
|
|
2
|
+
import '@noble/hashes/blake3';
|
|
2
3
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
4
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
3
5
|
|
|
4
6
|
// src/hash.ts
|
|
5
7
|
function computeHash(data) {
|
|
@@ -7,6 +9,720 @@ function computeHash(data) {
|
|
|
7
9
|
return `sha256:${bytesToHex(digest)}`;
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
// schemas/aiwg-fortemi-index-export.schema.json
|
|
13
|
+
var aiwg_fortemi_index_export_schema_default = {
|
|
14
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
15
|
+
$id: "https://aiwg.io/schemas/aiwg-fortemi-index-export.json",
|
|
16
|
+
title: "AIWG Fortemi Index Export",
|
|
17
|
+
type: "object",
|
|
18
|
+
additionalProperties: false,
|
|
19
|
+
required: ["schema_version", "generated_at", "source", "items"],
|
|
20
|
+
properties: {
|
|
21
|
+
schema_version: {
|
|
22
|
+
enum: ["aiwg.fortemi.index.export.v1", "aiwg.fortemi.index.export.v2"]
|
|
23
|
+
},
|
|
24
|
+
generated_at: {
|
|
25
|
+
type: "string",
|
|
26
|
+
format: "date-time"
|
|
27
|
+
},
|
|
28
|
+
source: {
|
|
29
|
+
type: "object",
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
required: ["repo", "privacy"],
|
|
32
|
+
properties: {
|
|
33
|
+
repo: {
|
|
34
|
+
type: "string",
|
|
35
|
+
minLength: 1
|
|
36
|
+
},
|
|
37
|
+
privacy: {
|
|
38
|
+
$ref: "#/$defs/privacy"
|
|
39
|
+
},
|
|
40
|
+
graph: {
|
|
41
|
+
type: "string",
|
|
42
|
+
minLength: 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
compatibility: {
|
|
47
|
+
type: "object",
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
required: ["previous_schema_version", "strategy"],
|
|
50
|
+
properties: {
|
|
51
|
+
previous_schema_version: {
|
|
52
|
+
const: "aiwg.fortemi.index.export.v1"
|
|
53
|
+
},
|
|
54
|
+
strategy: {
|
|
55
|
+
const: "supported"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
items: {
|
|
60
|
+
type: "array",
|
|
61
|
+
items: {
|
|
62
|
+
$ref: "#/$defs/record"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
allOf: [
|
|
67
|
+
{
|
|
68
|
+
if: {
|
|
69
|
+
properties: {
|
|
70
|
+
schema_version: {
|
|
71
|
+
const: "aiwg.fortemi.index.export.v1"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
then: {
|
|
76
|
+
not: {
|
|
77
|
+
required: ["compatibility"]
|
|
78
|
+
},
|
|
79
|
+
properties: {
|
|
80
|
+
source: {
|
|
81
|
+
not: {
|
|
82
|
+
required: ["graph"]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
items: {
|
|
86
|
+
items: {
|
|
87
|
+
allOf: [
|
|
88
|
+
{
|
|
89
|
+
properties: {
|
|
90
|
+
schema_version: {
|
|
91
|
+
const: "aiwg.fortemi.index.record.v1"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
$ref: "#/$defs/v1RecordCompatibility"
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
if: {
|
|
106
|
+
properties: {
|
|
107
|
+
schema_version: {
|
|
108
|
+
const: "aiwg.fortemi.index.export.v2"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
then: {
|
|
113
|
+
required: ["compatibility"],
|
|
114
|
+
properties: {
|
|
115
|
+
source: {
|
|
116
|
+
required: ["graph"]
|
|
117
|
+
},
|
|
118
|
+
items: {
|
|
119
|
+
items: {
|
|
120
|
+
properties: {
|
|
121
|
+
schema_version: {
|
|
122
|
+
const: "aiwg.fortemi.index.record.v2"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
$defs: {
|
|
132
|
+
privacy: {
|
|
133
|
+
enum: ["private", "sanitized", "public"]
|
|
134
|
+
},
|
|
135
|
+
recordType: {
|
|
136
|
+
type: "string",
|
|
137
|
+
minLength: 1
|
|
138
|
+
},
|
|
139
|
+
stringArray: {
|
|
140
|
+
type: "array",
|
|
141
|
+
items: {
|
|
142
|
+
type: "string"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
numberArray: {
|
|
146
|
+
type: "array",
|
|
147
|
+
items: {
|
|
148
|
+
type: "number"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
v1RecordCompatibility: {
|
|
152
|
+
type: "object",
|
|
153
|
+
not: {
|
|
154
|
+
anyOf: [
|
|
155
|
+
{
|
|
156
|
+
required: ["name"]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
required: ["summary"]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
required: ["search"]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
required: ["chunks"]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
required: ["embeddings"]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
required: ["compatibility"]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
required: ["skos_concepts"]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
required: ["skos_relations"]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
required: ["provenance_events"]
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
properties: {
|
|
185
|
+
source: {
|
|
186
|
+
not: {
|
|
187
|
+
anyOf: [
|
|
188
|
+
{
|
|
189
|
+
required: ["origin"]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
required: ["generated"]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
required: ["checksum"]
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
required: ["updated_at"]
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
relationships: {
|
|
204
|
+
items: {
|
|
205
|
+
not: {
|
|
206
|
+
anyOf: [
|
|
207
|
+
{
|
|
208
|
+
required: ["target_path"]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
required: ["direction"]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
required: ["label"]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
required: ["confidence"]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
required: ["privacy"]
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
required: ["metadata"]
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
privacy: {
|
|
230
|
+
not: {
|
|
231
|
+
required: ["locality"]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
record: {
|
|
237
|
+
type: "object",
|
|
238
|
+
additionalProperties: false,
|
|
239
|
+
required: [
|
|
240
|
+
"schema_version",
|
|
241
|
+
"id",
|
|
242
|
+
"type",
|
|
243
|
+
"source",
|
|
244
|
+
"title",
|
|
245
|
+
"text",
|
|
246
|
+
"facets",
|
|
247
|
+
"tags",
|
|
248
|
+
"concepts",
|
|
249
|
+
"relationships",
|
|
250
|
+
"provenance",
|
|
251
|
+
"privacy",
|
|
252
|
+
"updated_at"
|
|
253
|
+
],
|
|
254
|
+
properties: {
|
|
255
|
+
schema_version: {
|
|
256
|
+
enum: [
|
|
257
|
+
"aiwg.fortemi.index.record.v1",
|
|
258
|
+
"aiwg.fortemi.index.record.v2"
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
id: {
|
|
262
|
+
type: "string",
|
|
263
|
+
minLength: 1
|
|
264
|
+
},
|
|
265
|
+
type: {
|
|
266
|
+
$ref: "#/$defs/recordType"
|
|
267
|
+
},
|
|
268
|
+
source: {
|
|
269
|
+
type: "object",
|
|
270
|
+
additionalProperties: false,
|
|
271
|
+
required: ["path", "repo_relative_path", "locator"],
|
|
272
|
+
properties: {
|
|
273
|
+
path: {
|
|
274
|
+
type: "string",
|
|
275
|
+
minLength: 1
|
|
276
|
+
},
|
|
277
|
+
repo_relative_path: {
|
|
278
|
+
type: "string",
|
|
279
|
+
minLength: 1
|
|
280
|
+
},
|
|
281
|
+
locator: {
|
|
282
|
+
type: "string",
|
|
283
|
+
minLength: 1
|
|
284
|
+
},
|
|
285
|
+
origin: {
|
|
286
|
+
type: "string",
|
|
287
|
+
minLength: 1
|
|
288
|
+
},
|
|
289
|
+
generated: {
|
|
290
|
+
type: "boolean"
|
|
291
|
+
},
|
|
292
|
+
checksum: {
|
|
293
|
+
type: "string"
|
|
294
|
+
},
|
|
295
|
+
updated_at: {
|
|
296
|
+
type: "string",
|
|
297
|
+
format: "date-time"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
title: {
|
|
302
|
+
type: "string"
|
|
303
|
+
},
|
|
304
|
+
name: {
|
|
305
|
+
type: "string"
|
|
306
|
+
},
|
|
307
|
+
summary: {
|
|
308
|
+
type: "string"
|
|
309
|
+
},
|
|
310
|
+
text: {
|
|
311
|
+
type: "string"
|
|
312
|
+
},
|
|
313
|
+
search: {
|
|
314
|
+
type: "object",
|
|
315
|
+
additionalProperties: false,
|
|
316
|
+
required: [
|
|
317
|
+
"title",
|
|
318
|
+
"body",
|
|
319
|
+
"triggers",
|
|
320
|
+
"aliases",
|
|
321
|
+
"tags",
|
|
322
|
+
"frontmatter"
|
|
323
|
+
],
|
|
324
|
+
properties: {
|
|
325
|
+
title: {
|
|
326
|
+
type: "string"
|
|
327
|
+
},
|
|
328
|
+
name: {
|
|
329
|
+
type: "string"
|
|
330
|
+
},
|
|
331
|
+
summary: {
|
|
332
|
+
type: "string"
|
|
333
|
+
},
|
|
334
|
+
body: {
|
|
335
|
+
type: "string"
|
|
336
|
+
},
|
|
337
|
+
triggers: {
|
|
338
|
+
$ref: "#/$defs/stringArray"
|
|
339
|
+
},
|
|
340
|
+
aliases: {
|
|
341
|
+
$ref: "#/$defs/stringArray"
|
|
342
|
+
},
|
|
343
|
+
capability: {
|
|
344
|
+
type: "string"
|
|
345
|
+
},
|
|
346
|
+
tags: {
|
|
347
|
+
$ref: "#/$defs/stringArray"
|
|
348
|
+
},
|
|
349
|
+
phase: {
|
|
350
|
+
type: "string"
|
|
351
|
+
},
|
|
352
|
+
type: {
|
|
353
|
+
type: "string"
|
|
354
|
+
},
|
|
355
|
+
frontmatter: {
|
|
356
|
+
type: "object"
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
facets: {
|
|
361
|
+
type: "object",
|
|
362
|
+
additionalProperties: {
|
|
363
|
+
$ref: "#/$defs/stringArray"
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
tags: {
|
|
367
|
+
$ref: "#/$defs/stringArray"
|
|
368
|
+
},
|
|
369
|
+
concepts: {
|
|
370
|
+
$ref: "#/$defs/stringArray"
|
|
371
|
+
},
|
|
372
|
+
relationships: {
|
|
373
|
+
type: "array",
|
|
374
|
+
items: {
|
|
375
|
+
type: "object",
|
|
376
|
+
additionalProperties: false,
|
|
377
|
+
required: ["type", "target_id"],
|
|
378
|
+
properties: {
|
|
379
|
+
type: {
|
|
380
|
+
type: "string",
|
|
381
|
+
minLength: 1
|
|
382
|
+
},
|
|
383
|
+
target_id: {
|
|
384
|
+
type: "string",
|
|
385
|
+
minLength: 1
|
|
386
|
+
},
|
|
387
|
+
source_path: {
|
|
388
|
+
type: "string"
|
|
389
|
+
},
|
|
390
|
+
target_path: {
|
|
391
|
+
type: "string"
|
|
392
|
+
},
|
|
393
|
+
direction: {
|
|
394
|
+
enum: ["upstream", "downstream", "related"]
|
|
395
|
+
},
|
|
396
|
+
label: {
|
|
397
|
+
type: "string"
|
|
398
|
+
},
|
|
399
|
+
confidence: {
|
|
400
|
+
type: "number"
|
|
401
|
+
},
|
|
402
|
+
privacy: {
|
|
403
|
+
$ref: "#/$defs/privacy"
|
|
404
|
+
},
|
|
405
|
+
metadata: {
|
|
406
|
+
type: "object"
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
provenance: {
|
|
412
|
+
type: "array",
|
|
413
|
+
minItems: 1,
|
|
414
|
+
items: {
|
|
415
|
+
type: "object",
|
|
416
|
+
additionalProperties: false,
|
|
417
|
+
required: ["field", "source", "path", "confidence", "privacy"],
|
|
418
|
+
properties: {
|
|
419
|
+
field: {
|
|
420
|
+
type: "string",
|
|
421
|
+
minLength: 1
|
|
422
|
+
},
|
|
423
|
+
source: {
|
|
424
|
+
type: "string",
|
|
425
|
+
minLength: 1
|
|
426
|
+
},
|
|
427
|
+
path: {
|
|
428
|
+
type: "string",
|
|
429
|
+
minLength: 1
|
|
430
|
+
},
|
|
431
|
+
confidence: {
|
|
432
|
+
enum: ["source", "candidate", "reviewed", "rejected"]
|
|
433
|
+
},
|
|
434
|
+
privacy: {
|
|
435
|
+
$ref: "#/$defs/privacy"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
privacy: {
|
|
441
|
+
type: "object",
|
|
442
|
+
additionalProperties: false,
|
|
443
|
+
required: ["classification", "pii"],
|
|
444
|
+
properties: {
|
|
445
|
+
classification: {
|
|
446
|
+
$ref: "#/$defs/privacy"
|
|
447
|
+
},
|
|
448
|
+
pii: {
|
|
449
|
+
type: "boolean"
|
|
450
|
+
},
|
|
451
|
+
locality: {
|
|
452
|
+
enum: ["project", "framework", "external"]
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
chunks: {
|
|
457
|
+
type: "array",
|
|
458
|
+
items: {
|
|
459
|
+
type: "object",
|
|
460
|
+
additionalProperties: false,
|
|
461
|
+
properties: {
|
|
462
|
+
id: {
|
|
463
|
+
type: "string",
|
|
464
|
+
minLength: 1
|
|
465
|
+
},
|
|
466
|
+
text: {
|
|
467
|
+
type: "string"
|
|
468
|
+
},
|
|
469
|
+
body: {
|
|
470
|
+
type: "string"
|
|
471
|
+
},
|
|
472
|
+
summary: {
|
|
473
|
+
type: "string"
|
|
474
|
+
},
|
|
475
|
+
source_path: {
|
|
476
|
+
type: "string"
|
|
477
|
+
},
|
|
478
|
+
metadata: {
|
|
479
|
+
type: "object"
|
|
480
|
+
},
|
|
481
|
+
checksum: {
|
|
482
|
+
type: "string"
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
embeddings: {
|
|
488
|
+
type: "array",
|
|
489
|
+
items: {
|
|
490
|
+
type: "object",
|
|
491
|
+
additionalProperties: false,
|
|
492
|
+
properties: {
|
|
493
|
+
id: {
|
|
494
|
+
type: "string"
|
|
495
|
+
},
|
|
496
|
+
model: {
|
|
497
|
+
type: "string"
|
|
498
|
+
},
|
|
499
|
+
embedding: {
|
|
500
|
+
$ref: "#/$defs/numberArray"
|
|
501
|
+
},
|
|
502
|
+
vector: {
|
|
503
|
+
$ref: "#/$defs/numberArray"
|
|
504
|
+
},
|
|
505
|
+
granularity: {
|
|
506
|
+
type: "string"
|
|
507
|
+
},
|
|
508
|
+
source_path: {
|
|
509
|
+
type: "string"
|
|
510
|
+
},
|
|
511
|
+
metadata: {
|
|
512
|
+
type: "object"
|
|
513
|
+
},
|
|
514
|
+
chunk_id: {
|
|
515
|
+
type: "string"
|
|
516
|
+
},
|
|
517
|
+
vector_ref: {
|
|
518
|
+
type: "string"
|
|
519
|
+
},
|
|
520
|
+
input_hash: {
|
|
521
|
+
type: "string"
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
compatibility: {
|
|
527
|
+
type: "object"
|
|
528
|
+
},
|
|
529
|
+
skos_concepts: {
|
|
530
|
+
type: "array",
|
|
531
|
+
items: {
|
|
532
|
+
type: "object",
|
|
533
|
+
additionalProperties: false,
|
|
534
|
+
required: ["id", "prefLabel"],
|
|
535
|
+
properties: {
|
|
536
|
+
id: {
|
|
537
|
+
type: "string",
|
|
538
|
+
minLength: 1
|
|
539
|
+
},
|
|
540
|
+
prefLabel: {
|
|
541
|
+
type: "string",
|
|
542
|
+
minLength: 1
|
|
543
|
+
},
|
|
544
|
+
definition: {
|
|
545
|
+
type: "string"
|
|
546
|
+
},
|
|
547
|
+
scheme: {
|
|
548
|
+
type: "string"
|
|
549
|
+
},
|
|
550
|
+
notation: {
|
|
551
|
+
type: "string"
|
|
552
|
+
},
|
|
553
|
+
uri: {
|
|
554
|
+
type: "string"
|
|
555
|
+
},
|
|
556
|
+
altLabels: {
|
|
557
|
+
$ref: "#/$defs/stringArray"
|
|
558
|
+
},
|
|
559
|
+
metadata: {
|
|
560
|
+
type: "object"
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
skos_relations: {
|
|
566
|
+
type: "array",
|
|
567
|
+
items: {
|
|
568
|
+
type: "object",
|
|
569
|
+
additionalProperties: false,
|
|
570
|
+
required: ["type", "source_id", "target_id"],
|
|
571
|
+
properties: {
|
|
572
|
+
type: {
|
|
573
|
+
type: "string",
|
|
574
|
+
minLength: 1
|
|
575
|
+
},
|
|
576
|
+
source_id: {
|
|
577
|
+
type: "string",
|
|
578
|
+
minLength: 1
|
|
579
|
+
},
|
|
580
|
+
target_id: {
|
|
581
|
+
type: "string",
|
|
582
|
+
minLength: 1
|
|
583
|
+
},
|
|
584
|
+
source_path: {
|
|
585
|
+
type: "string"
|
|
586
|
+
},
|
|
587
|
+
metadata: {
|
|
588
|
+
type: "object"
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
provenance_events: {
|
|
594
|
+
type: "array",
|
|
595
|
+
items: {
|
|
596
|
+
type: "object",
|
|
597
|
+
additionalProperties: false,
|
|
598
|
+
required: ["activity"],
|
|
599
|
+
properties: {
|
|
600
|
+
id: {
|
|
601
|
+
type: "string"
|
|
602
|
+
},
|
|
603
|
+
activity: {
|
|
604
|
+
type: "string",
|
|
605
|
+
minLength: 1
|
|
606
|
+
},
|
|
607
|
+
agent: {
|
|
608
|
+
type: "string"
|
|
609
|
+
},
|
|
610
|
+
started_at: {
|
|
611
|
+
type: "string",
|
|
612
|
+
format: "date-time"
|
|
613
|
+
},
|
|
614
|
+
ended_at: {
|
|
615
|
+
type: "string",
|
|
616
|
+
format: "date-time"
|
|
617
|
+
},
|
|
618
|
+
source: {
|
|
619
|
+
type: "string"
|
|
620
|
+
},
|
|
621
|
+
path: {
|
|
622
|
+
type: "string"
|
|
623
|
+
},
|
|
624
|
+
confidence: {
|
|
625
|
+
enum: ["source", "candidate", "reviewed", "rejected"]
|
|
626
|
+
},
|
|
627
|
+
privacy: {
|
|
628
|
+
$ref: "#/$defs/privacy"
|
|
629
|
+
},
|
|
630
|
+
attributes: {
|
|
631
|
+
type: "object"
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
updated_at: {
|
|
637
|
+
type: "string",
|
|
638
|
+
format: "date-time"
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// src/aiwg-index-schema.ts
|
|
646
|
+
var PROJECTED_FIELDS = [
|
|
647
|
+
"schema_version",
|
|
648
|
+
"id",
|
|
649
|
+
"type",
|
|
650
|
+
"title",
|
|
651
|
+
"text",
|
|
652
|
+
"facets",
|
|
653
|
+
"tags",
|
|
654
|
+
"concepts",
|
|
655
|
+
"privacy"
|
|
656
|
+
];
|
|
657
|
+
var ajvInstance;
|
|
658
|
+
var exportValidator;
|
|
659
|
+
var projectedRecordValidator;
|
|
660
|
+
function getAjv() {
|
|
661
|
+
if (!ajvInstance) {
|
|
662
|
+
ajvInstance = new Ajv2020({
|
|
663
|
+
allErrors: true,
|
|
664
|
+
strict: false,
|
|
665
|
+
validateFormats: false
|
|
666
|
+
});
|
|
667
|
+
ajvInstance.addSchema(aiwg_fortemi_index_export_schema_default);
|
|
668
|
+
}
|
|
669
|
+
return ajvInstance;
|
|
670
|
+
}
|
|
671
|
+
function formatErrors(errors) {
|
|
672
|
+
return (errors ?? []).map((error) => {
|
|
673
|
+
const path = error.instancePath || "(root)";
|
|
674
|
+
return `${path} ${error.message ?? "is invalid"}`;
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
function getExportValidator() {
|
|
678
|
+
exportValidator ??= getAjv().getSchema(aiwg_fortemi_index_export_schema_default.$id) ?? getAjv().compile(aiwg_fortemi_index_export_schema_default);
|
|
679
|
+
return exportValidator;
|
|
680
|
+
}
|
|
681
|
+
function getProjectedRecordValidator() {
|
|
682
|
+
if (!projectedRecordValidator) {
|
|
683
|
+
const properties = Object.fromEntries(Object.keys(aiwg_fortemi_index_export_schema_default.$defs.record.properties).map((field) => [
|
|
684
|
+
field,
|
|
685
|
+
{ $ref: `${aiwg_fortemi_index_export_schema_default.$id}#/$defs/record/properties/${field}` }
|
|
686
|
+
]));
|
|
687
|
+
projectedRecordValidator = getAjv().compile({
|
|
688
|
+
type: "object",
|
|
689
|
+
required: PROJECTED_FIELDS,
|
|
690
|
+
properties,
|
|
691
|
+
additionalProperties: true,
|
|
692
|
+
allOf: [
|
|
693
|
+
{
|
|
694
|
+
if: {
|
|
695
|
+
properties: {
|
|
696
|
+
schema_version: { const: "aiwg.fortemi.index.record.v1" }
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
then: { $ref: `${aiwg_fortemi_index_export_schema_default.$id}#/$defs/v1RecordCompatibility` }
|
|
700
|
+
}
|
|
701
|
+
]
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
return projectedRecordValidator;
|
|
705
|
+
}
|
|
706
|
+
function validateAiwgFortemiIndexExportSchema(value) {
|
|
707
|
+
const validate = getExportValidator();
|
|
708
|
+
const schemaValue = value && typeof value === "object" && Array.isArray(value.items) ? {
|
|
709
|
+
...value,
|
|
710
|
+
items: value.items.map((item) => {
|
|
711
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return item;
|
|
712
|
+
const schemaRecord = { ...item };
|
|
713
|
+
Reflect.deleteProperty(schemaRecord, "binary_sources");
|
|
714
|
+
return schemaRecord;
|
|
715
|
+
})
|
|
716
|
+
} : value;
|
|
717
|
+
const valid = validate(schemaValue);
|
|
718
|
+
return { valid, errors: formatErrors(validate.errors) };
|
|
719
|
+
}
|
|
720
|
+
function validateAiwgFortemiProjectedRecordSchema(value) {
|
|
721
|
+
const validate = getProjectedRecordValidator();
|
|
722
|
+
const valid = validate(value);
|
|
723
|
+
return { valid, errors: formatErrors(validate.errors) };
|
|
724
|
+
}
|
|
725
|
+
|
|
10
726
|
// src/aiwg-index.ts
|
|
11
727
|
var AIWG_SCAN_REQUIRED_FIELDS = [
|
|
12
728
|
"schema_version",
|
|
@@ -21,7 +737,7 @@ var AIWG_SCAN_REQUIRED_FIELDS = [
|
|
|
21
737
|
];
|
|
22
738
|
function isPrivacyExcluded(record, options) {
|
|
23
739
|
const privacy = record.privacy;
|
|
24
|
-
if (!privacy) return
|
|
740
|
+
if (!privacy || !isPrivacyClassification(privacy.classification) || typeof privacy.pii !== "boolean") return true;
|
|
25
741
|
if (privacy.classification === "private" && !options?.includePrivate) return true;
|
|
26
742
|
if (privacy.pii && !options?.includePii) return true;
|
|
27
743
|
return false;
|
|
@@ -90,6 +806,10 @@ function validateOptionalRichMetadata(item, index, errors) {
|
|
|
90
806
|
errors.push("items[" + index + "].skos_concepts must be an array when present");
|
|
91
807
|
} else {
|
|
92
808
|
for (const [conceptIndex, concept] of item.skos_concepts.entries()) {
|
|
809
|
+
if (!isPlainRecord(concept)) {
|
|
810
|
+
errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "] must be an object");
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
93
813
|
if (!hasString(concept.id)) errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "].id is required");
|
|
94
814
|
if (!hasString(concept.prefLabel)) errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "].prefLabel is required");
|
|
95
815
|
if (!isOptionalStringArray(concept.altLabels)) errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "].altLabels must be a string array");
|
|
@@ -104,6 +824,10 @@ function validateOptionalRichMetadata(item, index, errors) {
|
|
|
104
824
|
errors.push("items[" + index + "].skos_relations must be an array when present");
|
|
105
825
|
} else {
|
|
106
826
|
for (const [relationIndex, relation] of item.skos_relations.entries()) {
|
|
827
|
+
if (!isPlainRecord(relation)) {
|
|
828
|
+
errors.push("items[" + index + "].skos_relations[" + relationIndex + "] must be an object");
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
107
831
|
if (!hasString(relation.type)) errors.push("items[" + index + "].skos_relations[" + relationIndex + "].type is required");
|
|
108
832
|
if (!hasString(relation.source_id)) errors.push("items[" + index + "].skos_relations[" + relationIndex + "].source_id is required");
|
|
109
833
|
if (!hasString(relation.target_id)) errors.push("items[" + index + "].skos_relations[" + relationIndex + "].target_id is required");
|
|
@@ -118,6 +842,10 @@ function validateOptionalRichMetadata(item, index, errors) {
|
|
|
118
842
|
errors.push("items[" + index + "].provenance_events must be an array when present");
|
|
119
843
|
} else {
|
|
120
844
|
for (const [eventIndex, event] of item.provenance_events.entries()) {
|
|
845
|
+
if (!isPlainRecord(event)) {
|
|
846
|
+
errors.push("items[" + index + "].provenance_events[" + eventIndex + "] must be an object");
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
121
849
|
if (!hasString(event.activity)) errors.push("items[" + index + "].provenance_events[" + eventIndex + "].activity is required");
|
|
122
850
|
if (event.attributes !== void 0 && !isPlainRecord(event.attributes)) {
|
|
123
851
|
errors.push("items[" + index + "].provenance_events[" + eventIndex + "].attributes must be an object");
|
|
@@ -127,6 +855,10 @@ function validateOptionalRichMetadata(item, index, errors) {
|
|
|
127
855
|
}
|
|
128
856
|
if (Array.isArray(item.relationships)) {
|
|
129
857
|
for (const [relationshipIndex, relationship] of item.relationships.entries()) {
|
|
858
|
+
if (!isPlainRecord(relationship)) {
|
|
859
|
+
errors.push("items[" + index + "].relationships[" + relationshipIndex + "] must be an object");
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
130
862
|
if (relationship.metadata !== void 0 && !isPlainRecord(relationship.metadata)) {
|
|
131
863
|
errors.push("items[" + index + "].relationships[" + relationshipIndex + "].metadata must be an object");
|
|
132
864
|
}
|
|
@@ -155,7 +887,10 @@ function validateOptionalRichMetadata(item, index, errors) {
|
|
|
155
887
|
errors.push("items[" + index + "].chunks must be an array when present");
|
|
156
888
|
} else {
|
|
157
889
|
for (const [chunkIndex, chunk] of item.chunks.entries()) {
|
|
158
|
-
if (!isPlainRecord(chunk))
|
|
890
|
+
if (!isPlainRecord(chunk)) {
|
|
891
|
+
errors.push("items[" + index + "].chunks[" + chunkIndex + "] must be an object");
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
159
894
|
if (chunk.metadata !== void 0 && !isPlainRecord(chunk.metadata)) {
|
|
160
895
|
errors.push("items[" + index + "].chunks[" + chunkIndex + "].metadata must be an object");
|
|
161
896
|
}
|
|
@@ -167,7 +902,10 @@ function validateOptionalRichMetadata(item, index, errors) {
|
|
|
167
902
|
errors.push("items[" + index + "].embeddings must be an array when present");
|
|
168
903
|
} else {
|
|
169
904
|
for (const [embeddingIndex, embedding] of item.embeddings.entries()) {
|
|
170
|
-
if (!isPlainRecord(embedding))
|
|
905
|
+
if (!isPlainRecord(embedding)) {
|
|
906
|
+
errors.push("items[" + index + "].embeddings[" + embeddingIndex + "] must be an object");
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
171
909
|
const vector = embedding.embedding ?? embedding.vector;
|
|
172
910
|
if (vector !== void 0 && (!Array.isArray(vector) || !vector.every((entry) => typeof entry === "number"))) {
|
|
173
911
|
errors.push("items[" + index + "].embeddings[" + embeddingIndex + "].embedding/vector must be a number array");
|
|
@@ -234,9 +972,10 @@ function forbidV2FieldsOnV1Record(item, index, errors) {
|
|
|
234
972
|
}
|
|
235
973
|
}
|
|
236
974
|
function validateAiwgFortemiIndexExport(value) {
|
|
237
|
-
const errors =
|
|
238
|
-
const counts =
|
|
239
|
-
const data = value;
|
|
975
|
+
const errors = validateAiwgFortemiIndexExportSchema(value).errors;
|
|
976
|
+
const counts = /* @__PURE__ */ Object.create(null);
|
|
977
|
+
const data = isPlainRecord(value) ? value : {};
|
|
978
|
+
if (!isPlainRecord(value)) errors.push("index export must be an object");
|
|
240
979
|
if (!isSupportedIndexSchemaVersion(data?.schema_version)) {
|
|
241
980
|
errors.push("schema_version must be aiwg.fortemi.index.export.v1 or aiwg.fortemi.index.export.v2");
|
|
242
981
|
}
|
|
@@ -260,7 +999,12 @@ function validateAiwgFortemiIndexExport(value) {
|
|
|
260
999
|
}
|
|
261
1000
|
const ids = /* @__PURE__ */ new Set();
|
|
262
1001
|
let previousId = "";
|
|
263
|
-
|
|
1002
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
1003
|
+
for (const [index, item] of items.entries()) {
|
|
1004
|
+
if (!isPlainRecord(item)) {
|
|
1005
|
+
errors.push("items[" + index + "] must be an object");
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
264
1008
|
for (const field of REQUIRED_RECORD_FIELDS) {
|
|
265
1009
|
if (!(field in item)) errors.push("items[" + index + "]." + field + " is required");
|
|
266
1010
|
}
|
|
@@ -270,7 +1014,7 @@ function validateAiwgFortemiIndexExport(value) {
|
|
|
270
1014
|
if (!hasString(item.id)) errors.push("items[" + index + "].id is required");
|
|
271
1015
|
if (hasString(item.id) && ids.has(item.id)) errors.push("duplicate id: " + item.id);
|
|
272
1016
|
if (hasString(item.id)) ids.add(item.id);
|
|
273
|
-
if (previousId && hasString(item.id) && previousId
|
|
1017
|
+
if (previousId && hasString(item.id) && previousId > item.id) {
|
|
274
1018
|
errors.push("items must be sorted by id: " + previousId + " before " + item.id);
|
|
275
1019
|
}
|
|
276
1020
|
if (hasString(item.id)) previousId = item.id;
|
|
@@ -311,7 +1055,8 @@ function assertAiwgFortemiIndexExport(value) {
|
|
|
311
1055
|
}
|
|
312
1056
|
function validateAiwgFortemiChunkManifest(value) {
|
|
313
1057
|
const errors = [];
|
|
314
|
-
const data = value;
|
|
1058
|
+
const data = isPlainRecord(value) ? value : {};
|
|
1059
|
+
if (!isPlainRecord(value)) errors.push("chunk manifest must be an object");
|
|
315
1060
|
if (data?.schema_version !== "aiwg.fortemi.index.chunk-manifest.v1") {
|
|
316
1061
|
errors.push("schema_version must be aiwg.fortemi.index.chunk-manifest.v1");
|
|
317
1062
|
}
|
|
@@ -336,7 +1081,9 @@ function validateAiwgFortemiChunkManifest(value) {
|
|
|
336
1081
|
}
|
|
337
1082
|
}
|
|
338
1083
|
}
|
|
339
|
-
if (data.detail !== void 0) {
|
|
1084
|
+
if (data.detail !== void 0 && !isPlainRecord(data.detail)) {
|
|
1085
|
+
errors.push("detail must be an object");
|
|
1086
|
+
} else if (data.detail !== void 0) {
|
|
340
1087
|
if (!hasString(data.detail.href)) errors.push("detail.href is required");
|
|
341
1088
|
else if (!data.detail.href.includes("{id}")) errors.push("detail.href must contain the {id} placeholder");
|
|
342
1089
|
if (data.detail.encoding !== void 0 && data.detail.encoding !== "uri" && data.detail.encoding !== "base64url") {
|
|
@@ -347,6 +1094,10 @@ function validateAiwgFortemiChunkManifest(value) {
|
|
|
347
1094
|
let expectedOffset = 0;
|
|
348
1095
|
const parts = Array.isArray(data?.parts) ? data.parts : [];
|
|
349
1096
|
for (const [index, part] of parts.entries()) {
|
|
1097
|
+
if (!isPlainRecord(part)) {
|
|
1098
|
+
errors.push("parts[" + index + "] must be an object");
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
350
1101
|
if (!hasString(part.href)) errors.push("parts[" + index + "].href is required");
|
|
351
1102
|
if (!hasNonNegativeInteger(part.offset)) errors.push("parts[" + index + "].offset must be a non-negative integer");
|
|
352
1103
|
if (!hasNonNegativeInteger(part.count)) errors.push("parts[" + index + "].count must be a non-negative integer");
|
|
@@ -367,18 +1118,28 @@ function assertAiwgFortemiChunkManifest(value) {
|
|
|
367
1118
|
}
|
|
368
1119
|
return value;
|
|
369
1120
|
}
|
|
370
|
-
function validateProjectedRecords(items) {
|
|
1121
|
+
function validateProjectedRecords(items, sourceSchemaVersion) {
|
|
371
1122
|
const errors = [];
|
|
372
1123
|
const ids = /* @__PURE__ */ new Set();
|
|
373
1124
|
let previousId = "";
|
|
374
1125
|
for (const [index, item] of items.entries()) {
|
|
1126
|
+
if (!isPlainRecord(item)) {
|
|
1127
|
+
errors.push("items[" + index + "] must be an object");
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
1130
|
+
const schemaValidation = validateAiwgFortemiProjectedRecordSchema(item);
|
|
1131
|
+
errors.push(...schemaValidation.errors.map((error) => `items[${index}]${error}`));
|
|
1132
|
+
const expectedRecordVersion = sourceSchemaVersion === "aiwg.fortemi.index.export.v2" ? "aiwg.fortemi.index.record.v2" : "aiwg.fortemi.index.record.v1";
|
|
1133
|
+
if (item.schema_version !== expectedRecordVersion) {
|
|
1134
|
+
errors.push(`items[${index}].schema_version must match ${sourceSchemaVersion}`);
|
|
1135
|
+
}
|
|
375
1136
|
if (!isSupportedRecordSchemaVersion(item.schema_version)) {
|
|
376
1137
|
errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1 or aiwg.fortemi.index.record.v2");
|
|
377
1138
|
}
|
|
378
1139
|
if (!hasString(item.id)) errors.push("items[" + index + "].id is required");
|
|
379
1140
|
if (hasString(item.id) && ids.has(item.id)) errors.push("duplicate id: " + item.id);
|
|
380
1141
|
if (hasString(item.id)) ids.add(item.id);
|
|
381
|
-
if (previousId && hasString(item.id) && previousId
|
|
1142
|
+
if (previousId && hasString(item.id) && previousId > item.id) {
|
|
382
1143
|
errors.push("items must be sorted by id: " + previousId + " before " + item.id);
|
|
383
1144
|
}
|
|
384
1145
|
if (hasString(item.id)) previousId = item.id;
|
|
@@ -394,15 +1155,13 @@ function validateProjectedRecords(items) {
|
|
|
394
1155
|
}
|
|
395
1156
|
if (!Array.isArray(item.tags)) errors.push("items[" + index + "].tags must be an array");
|
|
396
1157
|
if (!Array.isArray(item.concepts)) errors.push("items[" + index + "].concepts must be an array");
|
|
397
|
-
if (!item.privacy || !hasString(item.privacy.classification)) {
|
|
398
|
-
errors.push("items[" + index + "].privacy.classification is required");
|
|
399
|
-
}
|
|
400
1158
|
}
|
|
401
1159
|
return errors;
|
|
402
1160
|
}
|
|
403
1161
|
function validateAiwgFortemiChunkPart(value, partRef, manifest) {
|
|
404
1162
|
const errors = [];
|
|
405
|
-
const data = value;
|
|
1163
|
+
const data = isPlainRecord(value) ? value : {};
|
|
1164
|
+
if (!isPlainRecord(value)) errors.push("chunk part must be an object");
|
|
406
1165
|
if (data?.schema_version !== "aiwg.fortemi.index.chunk.v1") {
|
|
407
1166
|
errors.push("schema_version must be aiwg.fortemi.index.chunk.v1");
|
|
408
1167
|
}
|
|
@@ -419,12 +1178,16 @@ function validateAiwgFortemiChunkPart(value, partRef, manifest) {
|
|
|
419
1178
|
}
|
|
420
1179
|
if (Array.isArray(data?.items)) {
|
|
421
1180
|
if (manifest?.projection) {
|
|
422
|
-
errors.push(...validateProjectedRecords(
|
|
1181
|
+
errors.push(...validateProjectedRecords(
|
|
1182
|
+
data.items,
|
|
1183
|
+
manifest.source_export_schema_version ?? "aiwg.fortemi.index.export.v1"
|
|
1184
|
+
).map((error) => "items." + error));
|
|
423
1185
|
} else {
|
|
424
1186
|
const validation = validateAiwgFortemiIndexExport({
|
|
425
|
-
schema_version: "aiwg.fortemi.index.export.v1",
|
|
1187
|
+
schema_version: manifest?.source_export_schema_version ?? "aiwg.fortemi.index.export.v1",
|
|
426
1188
|
generated_at: manifest?.generated_at ?? "1970-01-01T00:00:00.000Z",
|
|
427
1189
|
source: manifest?.source ?? { repo: "chunk", privacy: "public" },
|
|
1190
|
+
...(manifest?.source_export_schema_version ?? "aiwg.fortemi.index.export.v1") === "aiwg.fortemi.index.export.v2" ? { compatibility: { previous_schema_version: "aiwg.fortemi.index.export.v1", strategy: "supported" } } : {},
|
|
428
1191
|
items: data.items
|
|
429
1192
|
});
|
|
430
1193
|
errors.push(...validation.errors.map((error) => "items." + error));
|
|
@@ -511,15 +1274,17 @@ function recordTitle(item) {
|
|
|
511
1274
|
}
|
|
512
1275
|
function recordText(item) {
|
|
513
1276
|
const base = item.text ?? item.search?.body ?? item.search?.summary ?? item.chunks?.map((chunk) => chunk.text ?? chunk.body ?? chunk.summary ?? "").filter(Boolean).join("\n") ?? "";
|
|
514
|
-
const extractedText =
|
|
1277
|
+
const extractedText = binarySourceText(item);
|
|
515
1278
|
return [base, extractedText].filter(Boolean).join("\n");
|
|
516
1279
|
}
|
|
1280
|
+
function binarySourceText(item) {
|
|
1281
|
+
return "binary_sources" in item ? item.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join("\n") ?? "" : "";
|
|
1282
|
+
}
|
|
517
1283
|
function defaultEmbeddingInput(record, granularity) {
|
|
518
1284
|
const title = recordTitle(record);
|
|
519
1285
|
const text = recordText(record);
|
|
520
1286
|
if (granularity === "title-summary") {
|
|
521
|
-
|
|
522
|
-
return [title, record.search?.summary ?? "", extractedText].filter(Boolean).join("\n");
|
|
1287
|
+
return [title, record.search?.summary ?? "", binarySourceText(record)].filter(Boolean).join("\n");
|
|
523
1288
|
}
|
|
524
1289
|
return [title, text].filter(Boolean).join("\n");
|
|
525
1290
|
}
|
|
@@ -587,6 +1352,7 @@ function recordSearchValues(item) {
|
|
|
587
1352
|
return values.filter((value) => typeof value === "string" && value.length > 0);
|
|
588
1353
|
}
|
|
589
1354
|
function buildAiwgChunkedIndex(index, options = {}) {
|
|
1355
|
+
assertAiwgFortemiIndexExport(index);
|
|
590
1356
|
const partSize = hasPositiveInteger(options.partSize) ? options.partSize : 500;
|
|
591
1357
|
const projection = options.projection;
|
|
592
1358
|
const idEncoding = options.idEncoding ?? "base64url";
|
|
@@ -666,35 +1432,88 @@ function queryMatches(item, q) {
|
|
|
666
1432
|
return matches;
|
|
667
1433
|
}
|
|
668
1434
|
var DISCOVERY_STOPWORDS = /* @__PURE__ */ new Set([
|
|
1435
|
+
"the",
|
|
669
1436
|
"a",
|
|
670
1437
|
"an",
|
|
671
1438
|
"and",
|
|
672
|
-
"
|
|
673
|
-
"
|
|
1439
|
+
"or",
|
|
1440
|
+
"of",
|
|
674
1441
|
"for",
|
|
675
|
-
"
|
|
676
|
-
"how",
|
|
677
|
-
"i",
|
|
1442
|
+
"to",
|
|
678
1443
|
"in",
|
|
1444
|
+
"on",
|
|
1445
|
+
"with",
|
|
1446
|
+
"into",
|
|
1447
|
+
"from",
|
|
679
1448
|
"is",
|
|
1449
|
+
"are",
|
|
1450
|
+
"be",
|
|
1451
|
+
"i",
|
|
1452
|
+
"we",
|
|
1453
|
+
"my",
|
|
1454
|
+
"it",
|
|
1455
|
+
"you",
|
|
680
1456
|
"me",
|
|
681
|
-
"
|
|
682
|
-
"
|
|
683
|
-
"
|
|
1457
|
+
"us",
|
|
1458
|
+
"your",
|
|
1459
|
+
"our",
|
|
1460
|
+
"this",
|
|
1461
|
+
"that",
|
|
1462
|
+
"these",
|
|
1463
|
+
"those",
|
|
1464
|
+
"there",
|
|
1465
|
+
"here",
|
|
1466
|
+
"some",
|
|
1467
|
+
"any",
|
|
1468
|
+
"all",
|
|
1469
|
+
"also",
|
|
684
1470
|
"please",
|
|
685
|
-
"
|
|
686
|
-
"
|
|
687
|
-
"
|
|
688
|
-
"
|
|
1471
|
+
"about",
|
|
1472
|
+
"how",
|
|
1473
|
+
"what",
|
|
1474
|
+
"which",
|
|
1475
|
+
"where",
|
|
1476
|
+
"when",
|
|
1477
|
+
"who",
|
|
1478
|
+
"why",
|
|
1479
|
+
"find",
|
|
1480
|
+
"give",
|
|
1481
|
+
"show",
|
|
1482
|
+
"need",
|
|
1483
|
+
"want",
|
|
1484
|
+
"looking",
|
|
1485
|
+
"look",
|
|
1486
|
+
"help",
|
|
1487
|
+
"do",
|
|
1488
|
+
"does",
|
|
1489
|
+
"did",
|
|
1490
|
+
"can",
|
|
1491
|
+
"could",
|
|
1492
|
+
"should",
|
|
1493
|
+
"would",
|
|
1494
|
+
"will",
|
|
1495
|
+
"handle",
|
|
1496
|
+
"handles",
|
|
1497
|
+
"handling",
|
|
1498
|
+
"aiwg",
|
|
1499
|
+
"skill",
|
|
1500
|
+
"skills",
|
|
1501
|
+
"agent",
|
|
1502
|
+
"agents",
|
|
1503
|
+
"command",
|
|
1504
|
+
"commands",
|
|
1505
|
+
"rule",
|
|
1506
|
+
"rules",
|
|
1507
|
+
"flow",
|
|
1508
|
+
"flows",
|
|
1509
|
+
"workflow",
|
|
1510
|
+
"workflows"
|
|
689
1511
|
]);
|
|
690
|
-
function
|
|
691
|
-
return value.toLowerCase().replace(/[_
|
|
692
|
-
}
|
|
693
|
-
function canonicalDiscoveryName(value) {
|
|
694
|
-
return normalizeDiscoveryText(value).replace(/[\s.-]+/g, "");
|
|
1512
|
+
function normalizeDiscoveryName(value) {
|
|
1513
|
+
return value.toLowerCase().replace(/[-_\s]+/g, " ").trim();
|
|
695
1514
|
}
|
|
696
1515
|
function discoveryTokens(value) {
|
|
697
|
-
return
|
|
1516
|
+
return value.toLowerCase().split(/[^a-z0-9-]+/).filter((token) => token.length > 1 && !DISCOVERY_STOPWORDS.has(token));
|
|
698
1517
|
}
|
|
699
1518
|
function facetValues(item, names) {
|
|
700
1519
|
return names.flatMap((name) => item.facets[name] ?? []);
|
|
@@ -704,17 +1523,58 @@ function addDiscoveryMatch(matches, match) {
|
|
|
704
1523
|
matches.push(match);
|
|
705
1524
|
}
|
|
706
1525
|
}
|
|
707
|
-
function
|
|
708
|
-
if (
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
1526
|
+
function damerauLevenshteinAtMostOne(left, right) {
|
|
1527
|
+
if (left === right) return true;
|
|
1528
|
+
if (Math.abs(left.length - right.length) > 1) return false;
|
|
1529
|
+
if (left.length === right.length) {
|
|
1530
|
+
let firstDiff = -1;
|
|
1531
|
+
let diffCount = 0;
|
|
1532
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
1533
|
+
if (left[index] !== right[index]) {
|
|
1534
|
+
if (firstDiff < 0) firstDiff = index;
|
|
1535
|
+
diffCount += 1;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
if (diffCount === 1) return true;
|
|
1539
|
+
return diffCount === 2 && firstDiff + 1 < left.length && left[firstDiff] === right[firstDiff + 1] && left[firstDiff + 1] === right[firstDiff];
|
|
1540
|
+
}
|
|
1541
|
+
const shorter = left.length < right.length ? left : right;
|
|
1542
|
+
const longer = left.length < right.length ? right : left;
|
|
1543
|
+
let shorterIndex = 0;
|
|
1544
|
+
let longerIndex = 0;
|
|
1545
|
+
let edits = 0;
|
|
1546
|
+
while (shorterIndex < shorter.length && longerIndex < longer.length) {
|
|
1547
|
+
if (shorter[shorterIndex] === longer[longerIndex]) {
|
|
1548
|
+
shorterIndex += 1;
|
|
1549
|
+
longerIndex += 1;
|
|
1550
|
+
} else {
|
|
1551
|
+
edits += 1;
|
|
1552
|
+
if (edits > 1) return false;
|
|
1553
|
+
longerIndex += 1;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
return true;
|
|
712
1557
|
}
|
|
713
|
-
function
|
|
1558
|
+
function nearDiscoveryNameMatch(query, name) {
|
|
1559
|
+
const queryParts = normalizeDiscoveryName(query).split(/\s+/).filter(Boolean);
|
|
1560
|
+
const nameParts = normalizeDiscoveryName(name).split(/\s+/).filter(Boolean);
|
|
1561
|
+
if (queryParts.length !== nameParts.length) return false;
|
|
1562
|
+
return queryParts.every((part, index) => {
|
|
1563
|
+
const target = nameParts[index];
|
|
1564
|
+
if (part === target) return true;
|
|
1565
|
+
if (part.length < 5 || target.length < 5) return false;
|
|
1566
|
+
return damerauLevenshteinAtMostOne(part, target);
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
function lowerValues(values) {
|
|
1570
|
+
return values.map((value) => value.toLowerCase());
|
|
1571
|
+
}
|
|
1572
|
+
function discoveryMatches(item, query, options = {}) {
|
|
714
1573
|
if (!query) return [];
|
|
715
1574
|
const matches = [];
|
|
716
1575
|
const tokens = discoveryTokens(query);
|
|
717
|
-
const
|
|
1576
|
+
const lower = tokens.length > 0 ? tokens.join(" ") : query.toLowerCase().trim();
|
|
1577
|
+
const rawLower = query.toLowerCase().trim();
|
|
718
1578
|
const idParts = item.id.split(/[:/]/);
|
|
719
1579
|
const names = [
|
|
720
1580
|
item.id,
|
|
@@ -740,33 +1600,66 @@ function discoveryMatches(item, query) {
|
|
|
740
1600
|
...item.tags
|
|
741
1601
|
].filter((value) => hasString(value));
|
|
742
1602
|
const sourceValues = [item.source?.path, item.source?.repo_relative_path, item.source?.locator].filter((value) => hasString(value));
|
|
1603
|
+
const title = recordTitle(item);
|
|
1604
|
+
const summary = [item.search?.summary, recordText(item)].filter((value) => hasString(value)).join("\n");
|
|
1605
|
+
const type = item.search?.type ?? item.type;
|
|
1606
|
+
const tags = [...item.search?.tags ?? [], ...item.tags];
|
|
1607
|
+
const useMultiToken = tokens.length > 1;
|
|
1608
|
+
const minHits = useMultiToken ? options.relaxOverlap ? 1 : Math.ceil(tokens.length / 2) : 1;
|
|
1609
|
+
const overlapOK = (hits) => useMultiToken && hits >= minHits;
|
|
1610
|
+
const addInfo = (match) => addDiscoveryMatch(matches, { ...match, score: 0 });
|
|
1611
|
+
const addScore = (score2) => {
|
|
1612
|
+
if (score2 > 0) addDiscoveryMatch(matches, { field: "id", value: item.id, score: score2, reason: "aiwg discovery score" });
|
|
1613
|
+
};
|
|
743
1614
|
for (const name of names) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
}
|
|
749
|
-
|
|
1615
|
+
if (normalizeDiscoveryName(query) === normalizeDiscoveryName(name)) {
|
|
1616
|
+
addInfo({ field: "id", value: name, reason: "exact canonical name" });
|
|
1617
|
+
addScore(1.001);
|
|
1618
|
+
return matches;
|
|
1619
|
+
}
|
|
1620
|
+
if (nearDiscoveryNameMatch(query, name)) {
|
|
1621
|
+
addInfo({ field: "id", value: name, reason: "near canonical name" });
|
|
1622
|
+
addScore(0.951);
|
|
1623
|
+
return matches;
|
|
750
1624
|
}
|
|
751
1625
|
}
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1626
|
+
let score = 0;
|
|
1627
|
+
const scoreText = (field, value, reason, exactScore, overlapScore, weight, exactBonus = 0) => {
|
|
1628
|
+
const normalized = value.toLowerCase();
|
|
1629
|
+
if (normalized.includes(lower)) {
|
|
1630
|
+
score += exactScore * weight;
|
|
1631
|
+
if (normalized === lower) score += exactBonus;
|
|
1632
|
+
addInfo({ field, value, reason });
|
|
1633
|
+
} else if (useMultiToken) {
|
|
1634
|
+
const hits = tokens.filter((token) => normalized.includes(token)).length;
|
|
1635
|
+
if (overlapOK(hits)) {
|
|
1636
|
+
score += overlapScore * weight * (hits / tokens.length);
|
|
1637
|
+
addInfo({ field, value, reason });
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
for (const trigger of lowerValues(triggers)) {
|
|
1642
|
+
if (trigger === lower || trigger === rawLower) {
|
|
1643
|
+
addInfo({ field: "facet", value: trigger, reason: "trigger phrase" });
|
|
1644
|
+
addScore(1.0008);
|
|
1645
|
+
return matches;
|
|
1646
|
+
}
|
|
758
1647
|
}
|
|
759
|
-
for (const
|
|
760
|
-
|
|
761
|
-
if (overlap > 0) addDiscoveryMatch(matches, { field: "concept", value: capability, score: 22 * overlap, reason: "capability overlap" });
|
|
1648
|
+
for (const trigger of triggers) {
|
|
1649
|
+
scoreText("facet", trigger, "trigger phrase", 0.25, 0.06, 4);
|
|
762
1650
|
}
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
1651
|
+
for (const capability of capabilities) scoreText("concept", capability, "capability overlap", 0.2, 0.1, 2);
|
|
1652
|
+
scoreText("title", title, "title token overlap", 0.3, 0.08, 3, 0.2);
|
|
1653
|
+
for (const tag of tags) scoreText("tag", tag, "tag token overlap", 0.2, 0.05, 2);
|
|
1654
|
+
if (summary) scoreText("text", summary, "body token overlap", 0.15, 0.04, 1);
|
|
766
1655
|
for (const source of sourceValues) {
|
|
767
|
-
|
|
768
|
-
if (overlap > 0) addDiscoveryMatch(matches, { field: "source", value: source, score: 2 * overlap, reason: "path overlap" });
|
|
1656
|
+
scoreText("source", source, "path overlap", 0.1, 0.03, 1);
|
|
769
1657
|
}
|
|
1658
|
+
if (type.toLowerCase().includes(lower)) {
|
|
1659
|
+
score += 0.1;
|
|
1660
|
+
addInfo({ field: "facet", value: type, reason: "type overlap" });
|
|
1661
|
+
}
|
|
1662
|
+
addScore(Math.min(score, 1));
|
|
770
1663
|
return matches;
|
|
771
1664
|
}
|
|
772
1665
|
function rankMatches(matches, weights) {
|
|
@@ -792,13 +1685,13 @@ function createSnippet(item, matches, q, maxLength) {
|
|
|
792
1685
|
const firstMatch = textMatch ?? titleMatch ?? matches[0];
|
|
793
1686
|
return clipSnippet(firstMatch?.value ?? recordText(item), q, maxLength);
|
|
794
1687
|
}
|
|
795
|
-
function createRankedEntries(items, q, options, ordinalBase = 0) {
|
|
1688
|
+
function createRankedEntries(items, q, options, ordinalBase = 0, discoveryOptions = {}) {
|
|
796
1689
|
const weights = { ...DEFAULT_QUERY_WEIGHTS, ...options.weights };
|
|
797
1690
|
const profile = options.searchProfile ?? "default";
|
|
798
1691
|
return items.map((item, ordinal) => ({
|
|
799
1692
|
item,
|
|
800
1693
|
ordinal: ordinalBase + ordinal,
|
|
801
|
-
matches: profile === "aiwg-discovery" ? discoveryMatches(item, q) : queryMatches(item, q)
|
|
1694
|
+
matches: profile === "aiwg-discovery" ? discoveryMatches(item, q, discoveryOptions) : queryMatches(item, q)
|
|
802
1695
|
})).filter(({ item, matches }) => {
|
|
803
1696
|
if (q && matches.length === 0) return false;
|
|
804
1697
|
if (options.types && !options.types.includes(item.type)) return false;
|
|
@@ -848,8 +1741,7 @@ function queryAiwgFortemiIndex(index, query = "", options = {}) {
|
|
|
848
1741
|
const q = query.trim().toLowerCase();
|
|
849
1742
|
const entries = createRankedEntries(index.items, q, options);
|
|
850
1743
|
if (entries.length === 0 && q && options.searchProfile === "aiwg-discovery") {
|
|
851
|
-
|
|
852
|
-
return createQueryResultFromRankedEntries(createRankedEntries(index.items, relaxed, options), relaxed, options);
|
|
1744
|
+
return createQueryResultFromRankedEntries(createRankedEntries(index.items, q, options, 0, { relaxOverlap: true }), q, options);
|
|
853
1745
|
}
|
|
854
1746
|
return createQueryResultFromRankedEntries(entries, q, options);
|
|
855
1747
|
}
|
|
@@ -870,7 +1762,8 @@ function cosineSimilarity(left, right) {
|
|
|
870
1762
|
}
|
|
871
1763
|
function validateAiwgStaticEmbeddingSet(value) {
|
|
872
1764
|
const errors = [];
|
|
873
|
-
const data = value;
|
|
1765
|
+
const data = isPlainRecord(value) ? value : {};
|
|
1766
|
+
if (!isPlainRecord(value)) errors.push("embedding set must be an object");
|
|
874
1767
|
if (data?.schema_version !== "aiwg.fortemi.embedding.set.v1") errors.push("schema_version must be aiwg.fortemi.embedding.set.v1");
|
|
875
1768
|
if (!hasString(data?.id)) errors.push("id is required");
|
|
876
1769
|
if (!hasString(data?.model)) errors.push("model is required");
|
|
@@ -878,7 +1771,12 @@ function validateAiwgStaticEmbeddingSet(value) {
|
|
|
878
1771
|
if (!hasString(data?.generated_at)) errors.push("generated_at is required");
|
|
879
1772
|
if (!hasString(data?.granularity)) errors.push("granularity is required");
|
|
880
1773
|
if (!Array.isArray(data?.embeddings)) errors.push("embeddings must be an array");
|
|
881
|
-
|
|
1774
|
+
const embeddings = Array.isArray(data.embeddings) ? data.embeddings : [];
|
|
1775
|
+
for (const [index, embedding] of embeddings.entries()) {
|
|
1776
|
+
if (!isPlainRecord(embedding)) {
|
|
1777
|
+
errors.push("embeddings[" + index + "] must be an object");
|
|
1778
|
+
continue;
|
|
1779
|
+
}
|
|
882
1780
|
if (!hasString(embedding.record_id)) errors.push("embeddings[" + index + "].record_id is required");
|
|
883
1781
|
if (!hasString(embedding.input_hash)) errors.push("embeddings[" + index + "].input_hash is required");
|
|
884
1782
|
if (!Array.isArray(embedding.embedding)) errors.push("embeddings[" + index + "].embedding must be an array");
|
|
@@ -908,7 +1806,10 @@ function queryAiwgSemanticIndex(index, embeddingSet, queryEmbedding, options = {
|
|
|
908
1806
|
}).filter((result) => result !== null && result.score >= (options.minScore ?? -1)).sort((left, right) => right.score - left.score || left.item.id.localeCompare(right.item.id)).slice(offset, offset + limit);
|
|
909
1807
|
}
|
|
910
1808
|
function queryAiwgHybridIndex(index, embeddingSet, query, queryEmbedding, options = {}) {
|
|
911
|
-
const
|
|
1809
|
+
const lexicalOptions = { ...options };
|
|
1810
|
+
delete lexicalOptions.limit;
|
|
1811
|
+
delete lexicalOptions.offset;
|
|
1812
|
+
const lexical = queryAiwgFortemiIndex(index, query, { ...lexicalOptions, rank: true });
|
|
912
1813
|
const semantic = queryAiwgSemanticIndex(index, embeddingSet, queryEmbedding, { limit: index.items.length });
|
|
913
1814
|
const lexicalWeight = options.lexicalWeight ?? 0.5;
|
|
914
1815
|
const semanticWeight = options.semanticWeight ?? 0.5;
|
|
@@ -978,6 +1879,7 @@ function matchSetCacheKey(q, options) {
|
|
|
978
1879
|
concepts: options.concepts ?? null,
|
|
979
1880
|
privacy: options.privacy ?? null,
|
|
980
1881
|
rel: options.relationshipTargetId ?? null,
|
|
1882
|
+
searchProfile: options.searchProfile ?? "default",
|
|
981
1883
|
weights: { ...DEFAULT_QUERY_WEIGHTS, ...options.weights }
|
|
982
1884
|
});
|
|
983
1885
|
}
|
|
@@ -1066,15 +1968,21 @@ function edgeFromRelationship(sourceId, relationship) {
|
|
|
1066
1968
|
}
|
|
1067
1969
|
function relationshipMatches(edge, options = {}) {
|
|
1068
1970
|
const type = relationshipTypeFilter(options);
|
|
1069
|
-
const direction = options.direction ?? "both";
|
|
1070
1971
|
if (type && edge.type !== type) return false;
|
|
1071
1972
|
if (options.relationshipDirection && edge.direction !== options.relationshipDirection) return false;
|
|
1072
1973
|
if (options.sourceId && edge.source_id !== options.sourceId) return false;
|
|
1073
1974
|
if (options.targetId && edge.target_id !== options.targetId) return false;
|
|
1074
|
-
if (
|
|
1075
|
-
if (direction === "in" && options.sourceId && edge.source_id !== options.sourceId) return false;
|
|
1975
|
+
if (options.endpointId && edge.source_id !== options.endpointId && edge.target_id !== options.endpointId) return false;
|
|
1076
1976
|
return true;
|
|
1077
1977
|
}
|
|
1978
|
+
function assertRelationshipDirectionAnchor(options) {
|
|
1979
|
+
if (options.direction === "out" && !options.sourceId) {
|
|
1980
|
+
throw new Error("relationship direction 'out' requires sourceId");
|
|
1981
|
+
}
|
|
1982
|
+
if (options.direction === "in" && !options.targetId) {
|
|
1983
|
+
throw new Error("relationship direction 'in' requires targetId");
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1078
1986
|
function nodeSummary(item) {
|
|
1079
1987
|
return { id: item.id, type: item.type, title: recordTitle(item) };
|
|
1080
1988
|
}
|
|
@@ -1082,6 +1990,7 @@ function addNode(nodes, item) {
|
|
|
1082
1990
|
if (item) nodes.set(item.id, nodeSummary(item));
|
|
1083
1991
|
}
|
|
1084
1992
|
function relationshipResultFromRecords(records, options = {}) {
|
|
1993
|
+
assertRelationshipDirectionAnchor(options);
|
|
1085
1994
|
const byId = new Map(records.map((record) => [record.id, record]));
|
|
1086
1995
|
const edges = [];
|
|
1087
1996
|
for (const record of records) {
|
|
@@ -1091,7 +2000,8 @@ function relationshipResultFromRecords(records, options = {}) {
|
|
|
1091
2000
|
edges.push(edge);
|
|
1092
2001
|
}
|
|
1093
2002
|
}
|
|
1094
|
-
const
|
|
2003
|
+
const sortedEdges = edges.sort((left, right) => left.source_id.localeCompare(right.source_id) || left.target_id.localeCompare(right.target_id) || left.type.localeCompare(right.type));
|
|
2004
|
+
const limitedEdges = options.limit ? sortedEdges.slice(0, options.limit) : sortedEdges;
|
|
1095
2005
|
const nodes = /* @__PURE__ */ new Map();
|
|
1096
2006
|
for (const edge of limitedEdges) {
|
|
1097
2007
|
addNode(nodes, byId.get(edge.source_id));
|
|
@@ -1108,7 +2018,8 @@ function neighborQueryOptions(id, options = {}) {
|
|
|
1108
2018
|
return {
|
|
1109
2019
|
...options,
|
|
1110
2020
|
...direction === "out" ? { sourceId: id } : {},
|
|
1111
|
-
...direction === "in" ? { targetId: id } : {}
|
|
2021
|
+
...direction === "in" ? { targetId: id } : {},
|
|
2022
|
+
...direction === "both" ? { endpointId: id } : {}
|
|
1112
2023
|
};
|
|
1113
2024
|
}
|
|
1114
2025
|
function filterNeighborResult(id, result, options = {}) {
|
|
@@ -1417,13 +2328,14 @@ function createAiwgIndexController(initialIndex) {
|
|
|
1417
2328
|
}
|
|
1418
2329
|
function aiwgFortemiIndexToCommunityGraph(index, options = {}) {
|
|
1419
2330
|
const ids = new Set(index.items.map((item) => item.id));
|
|
1420
|
-
const relationshipWeights = options.relationshipWeights ??
|
|
2331
|
+
const relationshipWeights = options.relationshipWeights ?? /* @__PURE__ */ Object.create(null);
|
|
1421
2332
|
const edgeCounts = /* @__PURE__ */ new Map();
|
|
1422
2333
|
for (const item of index.items) {
|
|
1423
2334
|
for (const relationship of item.relationships) {
|
|
1424
2335
|
if (!ids.has(relationship.target_id) && !options.includeDanglingRelationships) continue;
|
|
1425
2336
|
const kind = relationship.type;
|
|
1426
|
-
const
|
|
2337
|
+
const configuredWeight = Object.prototype.hasOwnProperty.call(relationshipWeights, kind) ? relationshipWeights[kind] : void 0;
|
|
2338
|
+
const baseWeight = typeof configuredWeight === "number" && Number.isFinite(configuredWeight) ? configuredWeight : 1;
|
|
1427
2339
|
const key = `${item.id}\0${relationship.target_id}\0${kind}`;
|
|
1428
2340
|
const existing = edgeCounts.get(key);
|
|
1429
2341
|
if (existing) existing.weight += baseWeight;
|