@unrdf/react 26.4.2
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/package.json +76 -0
- package/src/ai-semantic/index.mjs +31 -0
- package/src/ai-semantic/semantic-analyzer.mjs +781 -0
- package/src/hooks.mjs +50 -0
- package/src/index.mjs +38 -0
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unrdf/react",
|
|
3
|
+
"version": "26.4.2",
|
|
4
|
+
"description": "UNRDF React - AI Semantic Analysis Tools for RDF Knowledge Graphs (Optional Extension)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.mjs",
|
|
9
|
+
"./ai-semantic": "./src/ai-semantic/index.mjs",
|
|
10
|
+
"./semantic-analyzer": "./src/ai-semantic/semantic-analyzer.mjs",
|
|
11
|
+
"./embeddings-manager": "./src/ai-semantic/embeddings-manager.mjs",
|
|
12
|
+
"./nlp-query-builder": "./src/ai-semantic/nlp-query-builder.mjs",
|
|
13
|
+
"./anomaly-detector": "./src/ai-semantic/anomaly-detector.mjs"
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"files": [
|
|
17
|
+
"src/",
|
|
18
|
+
"dist/",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "vitest run --coverage",
|
|
24
|
+
"test:fast": "vitest run --coverage",
|
|
25
|
+
"test:watch": "vitest --coverage",
|
|
26
|
+
"build": "node build.config.mjs",
|
|
27
|
+
"lint": "eslint src/ --max-warnings=0",
|
|
28
|
+
"lint:fix": "eslint src/ --fix",
|
|
29
|
+
"format": "prettier --write src/",
|
|
30
|
+
"format:check": "prettier --check src/",
|
|
31
|
+
"clean": "rm -rf dist/ .nyc_output/ coverage/",
|
|
32
|
+
"dev": "echo 'Development mode for @unrdf/react'"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"rdf",
|
|
36
|
+
"react",
|
|
37
|
+
"ai",
|
|
38
|
+
"semantic",
|
|
39
|
+
"nlp",
|
|
40
|
+
"embeddings",
|
|
41
|
+
"anomaly-detection",
|
|
42
|
+
"knowledge-graph"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@opentelemetry/api": "^1.9.0",
|
|
46
|
+
"@unrdf/core": "workspace:*",
|
|
47
|
+
"@unrdf/oxigraph": "workspace:*",
|
|
48
|
+
"lru-cache": "^11.1.0",
|
|
49
|
+
"zod": "^4.1.13"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"react": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "^24.10.1",
|
|
61
|
+
"vitest": "^4.0.15"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18.0.0"
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "https://github.com/unrdf/unrdf.git",
|
|
69
|
+
"directory": "packages/react"
|
|
70
|
+
},
|
|
71
|
+
"bugs": {
|
|
72
|
+
"url": "https://github.com/unrdf/unrdf/issues"
|
|
73
|
+
},
|
|
74
|
+
"homepage": "https://github.com/unrdf/unrdf#readme",
|
|
75
|
+
"license": "MIT"
|
|
76
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Semantic Analysis Module
|
|
3
|
+
*
|
|
4
|
+
* Provides AI-powered semantic analysis tools for RDF knowledge graphs.
|
|
5
|
+
*
|
|
6
|
+
* @module ai-semantic
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
SemanticAnalyzer,
|
|
11
|
+
createSemanticAnalyzer,
|
|
12
|
+
defaultSemanticAnalyzer,
|
|
13
|
+
} from './semantic-analyzer.mjs';
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
EmbeddingsManager,
|
|
17
|
+
createEmbeddingsManager,
|
|
18
|
+
defaultEmbeddingsManager,
|
|
19
|
+
} from './embeddings-manager.mjs';
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
NLPQueryBuilder,
|
|
23
|
+
createNLPQueryBuilder,
|
|
24
|
+
defaultNLPQueryBuilder,
|
|
25
|
+
} from './nlp-query-builder.mjs';
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
AnomalyDetector,
|
|
29
|
+
createAnomalyDetector,
|
|
30
|
+
defaultAnomalyDetector,
|
|
31
|
+
} from './anomaly-detector.mjs';
|
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file AI Semantic Analyzer for RDF Graph Analysis
|
|
3
|
+
* @module ai-semantic/semantic-analyzer
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Implements AI-powered semantic analysis for RDF knowledge graphs.
|
|
7
|
+
* Analyzes semantic relationships, extracts key concepts, computes similarity,
|
|
8
|
+
* and suggests ontology improvements based on data patterns.
|
|
9
|
+
*
|
|
10
|
+
* Integrates with UNRDF's Knowledge Hook system and provides OTEL observability.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { trace, SpanStatusCode } from '@opentelemetry/api';
|
|
14
|
+
import LRUCache from 'lru-cache';
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
|
|
17
|
+
const tracer = trace.getTracer('unrdf-ai-semantic');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Semantic analysis result schema
|
|
21
|
+
*/
|
|
22
|
+
const SemanticAnalysisResultSchema = z.object({
|
|
23
|
+
concepts: z.array(
|
|
24
|
+
z.object({
|
|
25
|
+
uri: z.string(),
|
|
26
|
+
label: z.string().optional(),
|
|
27
|
+
frequency: z.number(),
|
|
28
|
+
centrality: z.number(),
|
|
29
|
+
type: z.string().optional(),
|
|
30
|
+
})
|
|
31
|
+
),
|
|
32
|
+
relationships: z.array(
|
|
33
|
+
z.object({
|
|
34
|
+
subject: z.string(),
|
|
35
|
+
predicate: z.string(),
|
|
36
|
+
object: z.string(),
|
|
37
|
+
strength: z.number(),
|
|
38
|
+
})
|
|
39
|
+
),
|
|
40
|
+
patterns: z.array(
|
|
41
|
+
z.object({
|
|
42
|
+
pattern: z.string(),
|
|
43
|
+
count: z.number(),
|
|
44
|
+
confidence: z.number(),
|
|
45
|
+
})
|
|
46
|
+
),
|
|
47
|
+
suggestions: z.array(
|
|
48
|
+
z.object({
|
|
49
|
+
type: z.enum([
|
|
50
|
+
'missing_inverse',
|
|
51
|
+
'missing_subclass',
|
|
52
|
+
'inconsistent_domain',
|
|
53
|
+
'redundant_property',
|
|
54
|
+
]),
|
|
55
|
+
description: z.string(),
|
|
56
|
+
priority: z.enum(['high', 'medium', 'low']),
|
|
57
|
+
})
|
|
58
|
+
),
|
|
59
|
+
statistics: z.object({
|
|
60
|
+
totalTriples: z.number(),
|
|
61
|
+
uniqueSubjects: z.number(),
|
|
62
|
+
uniquePredicates: z.number(),
|
|
63
|
+
uniqueObjects: z.number(),
|
|
64
|
+
avgDegree: z.number(),
|
|
65
|
+
density: z.number(),
|
|
66
|
+
}),
|
|
67
|
+
duration: z.number(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Semantic similarity result schema
|
|
72
|
+
*/
|
|
73
|
+
const SimilarityResultSchema = z.object({
|
|
74
|
+
similarity: z.number().min(0).max(1),
|
|
75
|
+
method: z.string(),
|
|
76
|
+
commonProperties: z.array(z.string()),
|
|
77
|
+
commonNeighbors: z.array(z.string()),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Semantic Analyzer Configuration
|
|
82
|
+
*/
|
|
83
|
+
const SemanticAnalyzerConfigSchema = z.object({
|
|
84
|
+
cacheSize: z.number().default(1000),
|
|
85
|
+
enableCache: z.boolean().default(true),
|
|
86
|
+
maxConcepts: z.number().default(100),
|
|
87
|
+
minConceptFrequency: z.number().default(2),
|
|
88
|
+
similarityThreshold: z.number().min(0).max(1).default(0.7),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* AI Semantic Analyzer for RDF graphs
|
|
93
|
+
*/
|
|
94
|
+
export class SemanticAnalyzer {
|
|
95
|
+
/**
|
|
96
|
+
* Create a new semantic analyzer
|
|
97
|
+
* @param {Object} [config] - Analyzer configuration
|
|
98
|
+
*/
|
|
99
|
+
constructor(config = {}) {
|
|
100
|
+
this.config = SemanticAnalyzerConfigSchema.parse(config);
|
|
101
|
+
|
|
102
|
+
// LRU cache for analysis results
|
|
103
|
+
this.cache = this.config.enableCache ? new LRUCache({ max: this.config.cacheSize }) : null;
|
|
104
|
+
|
|
105
|
+
// Statistics
|
|
106
|
+
this.stats = {
|
|
107
|
+
analyses: 0,
|
|
108
|
+
cacheHits: 0,
|
|
109
|
+
cacheMisses: 0,
|
|
110
|
+
avgDuration: 0,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Analyze an RDF graph to understand semantic relationships
|
|
116
|
+
* @param {Store} store - RDF store to analyze
|
|
117
|
+
* @param {Object} [options] - Analysis options
|
|
118
|
+
* @param {boolean} [options.useCache=true] - Use cached results
|
|
119
|
+
* @param {number} [options.maxConcepts] - Maximum concepts to extract
|
|
120
|
+
* @returns {Promise<Object>} Analysis results
|
|
121
|
+
*/
|
|
122
|
+
async analyze(store, options = {}) {
|
|
123
|
+
return tracer.startActiveSpan('semantic.analyze', async span => {
|
|
124
|
+
const startTime = Date.now();
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
span.setAttributes({
|
|
128
|
+
'semantic.store_size': store.size,
|
|
129
|
+
'semantic.cache_enabled': this.config.enableCache,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Check cache
|
|
133
|
+
const cacheKey = this._getCacheKey(store);
|
|
134
|
+
if (options.useCache !== false && this.cache) {
|
|
135
|
+
const cached = this.cache.get(cacheKey);
|
|
136
|
+
if (cached) {
|
|
137
|
+
this.stats.cacheHits++;
|
|
138
|
+
span.setAttribute('semantic.cache_hit', true);
|
|
139
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
140
|
+
return cached;
|
|
141
|
+
}
|
|
142
|
+
this.stats.cacheMisses++;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Extract concepts and entities
|
|
146
|
+
const concepts = await this._extractConcepts(store, options.maxConcepts);
|
|
147
|
+
span.setAttribute('semantic.concepts_count', concepts.length);
|
|
148
|
+
|
|
149
|
+
// Analyze relationships
|
|
150
|
+
const relationships = await this._analyzeRelationships(store);
|
|
151
|
+
span.setAttribute('semantic.relationships_count', relationships.length);
|
|
152
|
+
|
|
153
|
+
// Detect patterns
|
|
154
|
+
const patterns = await this._detectPatterns(store);
|
|
155
|
+
span.setAttribute('semantic.patterns_count', patterns.length);
|
|
156
|
+
|
|
157
|
+
// Generate suggestions
|
|
158
|
+
const suggestions = await this._generateSuggestions(store, concepts, relationships);
|
|
159
|
+
span.setAttribute('semantic.suggestions_count', suggestions.length);
|
|
160
|
+
|
|
161
|
+
// Calculate statistics
|
|
162
|
+
const statistics = await this._calculateStatistics(store);
|
|
163
|
+
|
|
164
|
+
const duration = Date.now() - startTime;
|
|
165
|
+
const result = SemanticAnalysisResultSchema.parse({
|
|
166
|
+
concepts,
|
|
167
|
+
relationships,
|
|
168
|
+
patterns,
|
|
169
|
+
suggestions,
|
|
170
|
+
statistics,
|
|
171
|
+
duration,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Update cache
|
|
175
|
+
if (this.cache) {
|
|
176
|
+
this.cache.set(cacheKey, result);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Update stats
|
|
180
|
+
this.stats.analyses++;
|
|
181
|
+
this.stats.avgDuration =
|
|
182
|
+
(this.stats.avgDuration * (this.stats.analyses - 1) + duration) / this.stats.analyses;
|
|
183
|
+
|
|
184
|
+
span.setAttributes({
|
|
185
|
+
'semantic.duration_ms': duration,
|
|
186
|
+
'semantic.analysis_complete': true,
|
|
187
|
+
});
|
|
188
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
189
|
+
|
|
190
|
+
return result;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
span.recordException(error);
|
|
193
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Extract key concepts and entities from the graph
|
|
201
|
+
* @param {Store} store - RDF store
|
|
202
|
+
* @param {number} [maxConcepts] - Maximum concepts to return
|
|
203
|
+
* @returns {Promise<Array>} Key concepts
|
|
204
|
+
* @private
|
|
205
|
+
*/
|
|
206
|
+
async _extractConcepts(store, maxConcepts) {
|
|
207
|
+
return tracer.startActiveSpan('semantic.extract_concepts', async span => {
|
|
208
|
+
try {
|
|
209
|
+
const conceptMap = new Map();
|
|
210
|
+
|
|
211
|
+
// Count subject occurrences
|
|
212
|
+
for (const quad of store) {
|
|
213
|
+
const subject = quad.subject.value;
|
|
214
|
+
if (!conceptMap.has(subject)) {
|
|
215
|
+
conceptMap.set(subject, {
|
|
216
|
+
uri: subject,
|
|
217
|
+
frequency: 0,
|
|
218
|
+
types: new Set(),
|
|
219
|
+
labels: new Set(),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
const concept = conceptMap.get(subject);
|
|
223
|
+
concept.frequency++;
|
|
224
|
+
|
|
225
|
+
// Track types
|
|
226
|
+
if (quad.predicate.value === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
|
|
227
|
+
concept.types.add(quad.object.value);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Track labels
|
|
231
|
+
if (quad.predicate.value === 'http://www.w3.org/2000/01/rdf-schema#label') {
|
|
232
|
+
concept.labels.add(quad.object.value);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Calculate centrality (simplified PageRank-like metric)
|
|
237
|
+
const centrality = await this._calculateCentrality(store, conceptMap);
|
|
238
|
+
|
|
239
|
+
// Convert to array and filter
|
|
240
|
+
let concepts = Array.from(conceptMap.entries())
|
|
241
|
+
.map(([uri, data]) => ({
|
|
242
|
+
uri,
|
|
243
|
+
label: data.labels.size > 0 ? Array.from(data.labels)[0] : undefined,
|
|
244
|
+
frequency: data.frequency,
|
|
245
|
+
centrality: centrality.get(uri) || 0,
|
|
246
|
+
type: data.types.size > 0 ? Array.from(data.types)[0] : undefined,
|
|
247
|
+
}))
|
|
248
|
+
.filter(c => c.frequency >= this.config.minConceptFrequency)
|
|
249
|
+
.sort((a, b) => b.centrality - a.centrality);
|
|
250
|
+
|
|
251
|
+
// Limit results
|
|
252
|
+
const limit = maxConcepts || this.config.maxConcepts;
|
|
253
|
+
concepts = concepts.slice(0, limit);
|
|
254
|
+
|
|
255
|
+
span.setAttribute('semantic.concepts_extracted', concepts.length);
|
|
256
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
257
|
+
return concepts;
|
|
258
|
+
} catch (error) {
|
|
259
|
+
span.recordException(error);
|
|
260
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Calculate centrality scores for concepts (simplified PageRank)
|
|
268
|
+
* @param {Store} store - RDF store
|
|
269
|
+
* @param {Map} conceptMap - Map of concepts
|
|
270
|
+
* @returns {Promise<Map>} Centrality scores
|
|
271
|
+
* @private
|
|
272
|
+
*/
|
|
273
|
+
async _calculateCentrality(store, conceptMap) {
|
|
274
|
+
const centrality = new Map();
|
|
275
|
+
const damping = 0.85;
|
|
276
|
+
const iterations = 10;
|
|
277
|
+
|
|
278
|
+
// Initialize
|
|
279
|
+
for (const uri of conceptMap.keys()) {
|
|
280
|
+
centrality.set(uri, 1.0);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Build adjacency list
|
|
284
|
+
const outLinks = new Map();
|
|
285
|
+
const inLinks = new Map();
|
|
286
|
+
|
|
287
|
+
for (const quad of store) {
|
|
288
|
+
const subj = quad.subject.value;
|
|
289
|
+
const obj = quad.object.value;
|
|
290
|
+
|
|
291
|
+
if (conceptMap.has(subj) && conceptMap.has(obj)) {
|
|
292
|
+
if (!outLinks.has(subj)) outLinks.set(subj, []);
|
|
293
|
+
if (!inLinks.has(obj)) inLinks.set(obj, []);
|
|
294
|
+
outLinks.get(subj).push(obj);
|
|
295
|
+
inLinks.get(obj).push(subj);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// PageRank iterations
|
|
300
|
+
for (let i = 0; i < iterations; i++) {
|
|
301
|
+
const newCentrality = new Map();
|
|
302
|
+
|
|
303
|
+
for (const uri of conceptMap.keys()) {
|
|
304
|
+
let rank = 1 - damping;
|
|
305
|
+
const incoming = inLinks.get(uri) || [];
|
|
306
|
+
|
|
307
|
+
for (const source of incoming) {
|
|
308
|
+
const outDegree = (outLinks.get(source) || []).length;
|
|
309
|
+
if (outDegree > 0) {
|
|
310
|
+
rank += damping * (centrality.get(source) / outDegree);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
newCentrality.set(uri, rank);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
centrality.clear();
|
|
318
|
+
for (const [uri, rank] of newCentrality) {
|
|
319
|
+
centrality.set(uri, rank);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return centrality;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Analyze relationships between concepts
|
|
328
|
+
* @param {Store} store - RDF store
|
|
329
|
+
* @returns {Promise<Array>} Relationships with strength scores
|
|
330
|
+
* @private
|
|
331
|
+
*/
|
|
332
|
+
async _analyzeRelationships(store) {
|
|
333
|
+
return tracer.startActiveSpan('semantic.analyze_relationships', async span => {
|
|
334
|
+
try {
|
|
335
|
+
const relationshipMap = new Map();
|
|
336
|
+
|
|
337
|
+
for (const quad of store) {
|
|
338
|
+
const key = `${quad.subject.value}|${quad.predicate.value}|${quad.object.value}`;
|
|
339
|
+
if (!relationshipMap.has(key)) {
|
|
340
|
+
relationshipMap.set(key, {
|
|
341
|
+
subject: quad.subject.value,
|
|
342
|
+
predicate: quad.predicate.value,
|
|
343
|
+
object: quad.object.value,
|
|
344
|
+
count: 0,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
relationshipMap.get(key).count++;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Calculate relationship strength (normalized by max count)
|
|
351
|
+
const maxCount = Math.max(...Array.from(relationshipMap.values()).map(r => r.count), 1);
|
|
352
|
+
|
|
353
|
+
const relationships = Array.from(relationshipMap.values())
|
|
354
|
+
.map(r => ({
|
|
355
|
+
subject: r.subject,
|
|
356
|
+
predicate: r.predicate,
|
|
357
|
+
object: r.object,
|
|
358
|
+
strength: r.count / maxCount,
|
|
359
|
+
}))
|
|
360
|
+
.sort((a, b) => b.strength - a.strength)
|
|
361
|
+
.slice(0, 100); // Top 100 relationships
|
|
362
|
+
|
|
363
|
+
span.setAttribute('semantic.relationships_analyzed', relationships.length);
|
|
364
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
365
|
+
return relationships;
|
|
366
|
+
} catch (error) {
|
|
367
|
+
span.recordException(error);
|
|
368
|
+
span.setStatus({
|
|
369
|
+
code: SpanStatusCode.ERROR,
|
|
370
|
+
message: error.message,
|
|
371
|
+
});
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Detect common patterns in the graph
|
|
379
|
+
* @param {Store} store - RDF store
|
|
380
|
+
* @returns {Promise<Array>} Detected patterns
|
|
381
|
+
* @private
|
|
382
|
+
*/
|
|
383
|
+
async _detectPatterns(store) {
|
|
384
|
+
return tracer.startActiveSpan('semantic.detect_patterns', async span => {
|
|
385
|
+
try {
|
|
386
|
+
const patterns = [];
|
|
387
|
+
const predicateCount = new Map();
|
|
388
|
+
|
|
389
|
+
// Count predicate usage
|
|
390
|
+
for (const quad of store) {
|
|
391
|
+
const pred = quad.predicate.value;
|
|
392
|
+
predicateCount.set(pred, (predicateCount.get(pred) || 0) + 1);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Identify common predicates
|
|
396
|
+
const totalTriples = store.size;
|
|
397
|
+
for (const [predicate, count] of predicateCount.entries()) {
|
|
398
|
+
const confidence = count / totalTriples;
|
|
399
|
+
if (count >= 3) {
|
|
400
|
+
patterns.push({
|
|
401
|
+
pattern: `Common predicate: ${predicate}`,
|
|
402
|
+
count,
|
|
403
|
+
confidence,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Detect type patterns
|
|
409
|
+
const typePatterns = await this._detectTypePatterns(store);
|
|
410
|
+
patterns.push(...typePatterns);
|
|
411
|
+
|
|
412
|
+
// Sort by confidence
|
|
413
|
+
patterns.sort((a, b) => b.confidence - a.confidence);
|
|
414
|
+
|
|
415
|
+
span.setAttribute('semantic.patterns_detected', patterns.length);
|
|
416
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
417
|
+
return patterns.slice(0, 20); // Top 20 patterns
|
|
418
|
+
} catch (error) {
|
|
419
|
+
span.recordException(error);
|
|
420
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Detect type-based patterns
|
|
428
|
+
* @param {Store} store - RDF store
|
|
429
|
+
* @returns {Promise<Array>} Type patterns
|
|
430
|
+
* @private
|
|
431
|
+
*/
|
|
432
|
+
async _detectTypePatterns(store) {
|
|
433
|
+
const typeCount = new Map();
|
|
434
|
+
const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
|
|
435
|
+
|
|
436
|
+
for (const quad of store) {
|
|
437
|
+
if (quad.predicate.value === RDF_TYPE) {
|
|
438
|
+
const type = quad.object.value;
|
|
439
|
+
typeCount.set(type, (typeCount.get(type) || 0) + 1);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const totalEntities = typeCount.size;
|
|
444
|
+
return Array.from(typeCount.entries())
|
|
445
|
+
.filter(([_, count]) => count >= 2)
|
|
446
|
+
.map(([type, count]) => ({
|
|
447
|
+
pattern: `Entity type: ${type}`,
|
|
448
|
+
count,
|
|
449
|
+
confidence: count / Math.max(totalEntities, 1),
|
|
450
|
+
}));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Generate ontology improvement suggestions
|
|
455
|
+
* @param {Store} store - RDF store
|
|
456
|
+
* @param {Array} concepts - Extracted concepts
|
|
457
|
+
* @param {Array} relationships - Analyzed relationships
|
|
458
|
+
* @returns {Promise<Array>} Suggestions
|
|
459
|
+
* @private
|
|
460
|
+
*/
|
|
461
|
+
async _generateSuggestions(store, concepts, relationships) {
|
|
462
|
+
return tracer.startActiveSpan('semantic.generate_suggestions', async span => {
|
|
463
|
+
try {
|
|
464
|
+
const suggestions = [];
|
|
465
|
+
|
|
466
|
+
// Check for missing inverse properties
|
|
467
|
+
const inverseCheck = await this._checkMissingInverses(store, relationships);
|
|
468
|
+
suggestions.push(...inverseCheck);
|
|
469
|
+
|
|
470
|
+
// Check for inconsistent domains/ranges
|
|
471
|
+
const domainCheck = await this._checkInconsistentDomains(store);
|
|
472
|
+
suggestions.push(...domainCheck);
|
|
473
|
+
|
|
474
|
+
// Check for potential subclass relationships
|
|
475
|
+
const subclassCheck = await this._checkPotentialSubclasses(store, concepts);
|
|
476
|
+
suggestions.push(...subclassCheck);
|
|
477
|
+
|
|
478
|
+
span.setAttribute('semantic.suggestions_generated', suggestions.length);
|
|
479
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
480
|
+
return suggestions;
|
|
481
|
+
} catch (error) {
|
|
482
|
+
span.recordException(error);
|
|
483
|
+
span.setStatus({
|
|
484
|
+
code: SpanStatusCode.ERROR,
|
|
485
|
+
message: error.message,
|
|
486
|
+
});
|
|
487
|
+
throw error;
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Check for missing inverse properties
|
|
494
|
+
* @param {Store} store - RDF store
|
|
495
|
+
* @param {Array} relationships - Relationships
|
|
496
|
+
* @returns {Promise<Array>} Suggestions
|
|
497
|
+
* @private
|
|
498
|
+
*/
|
|
499
|
+
async _checkMissingInverses(store, relationships) {
|
|
500
|
+
const suggestions = [];
|
|
501
|
+
const predicates = new Set(relationships.map(r => r.predicate));
|
|
502
|
+
|
|
503
|
+
for (const pred of predicates) {
|
|
504
|
+
// Simple heuristic: check if there's a reverse relationship pattern
|
|
505
|
+
const forward = relationships.filter(r => r.predicate === pred);
|
|
506
|
+
const reverse = relationships.filter(r =>
|
|
507
|
+
forward.some(f => f.subject === r.object && f.object === r.subject)
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
if (forward.length > 5 && reverse.length === 0) {
|
|
511
|
+
suggestions.push({
|
|
512
|
+
type: 'missing_inverse',
|
|
513
|
+
description: `Consider adding inverse property for ${pred}`,
|
|
514
|
+
priority: 'medium',
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return suggestions;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Check for inconsistent domain/range definitions
|
|
524
|
+
* @param {Store} store - RDF store
|
|
525
|
+
* @returns {Promise<Array>} Suggestions
|
|
526
|
+
* @private
|
|
527
|
+
*/
|
|
528
|
+
async _checkInconsistentDomains(store) {
|
|
529
|
+
const suggestions = [];
|
|
530
|
+
const RDFS_DOMAIN = 'http://www.w3.org/2000/01/rdf-schema#domain';
|
|
531
|
+
const _RDFS_RANGE = 'http://www.w3.org/2000/01/rdf-schema#range';
|
|
532
|
+
|
|
533
|
+
// This is a simplified check - a full implementation would be more comprehensive
|
|
534
|
+
const domainMap = new Map();
|
|
535
|
+
|
|
536
|
+
for (const quad of store) {
|
|
537
|
+
if (quad.predicate.value === RDFS_DOMAIN) {
|
|
538
|
+
domainMap.set(quad.subject.value, quad.object.value);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Check for predicates used with entities outside their declared domain
|
|
543
|
+
for (const quad of store) {
|
|
544
|
+
const expectedDomain = domainMap.get(quad.predicate.value);
|
|
545
|
+
if (expectedDomain) {
|
|
546
|
+
// Would need to check actual types - simplified here
|
|
547
|
+
// In practice, query for rdf:type of subjects
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return suggestions;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Check for potential subclass relationships
|
|
556
|
+
* @param {Store} store - RDF store
|
|
557
|
+
* @param {Array} concepts - Concepts
|
|
558
|
+
* @returns {Promise<Array>} Suggestions
|
|
559
|
+
* @private
|
|
560
|
+
*/
|
|
561
|
+
async _checkPotentialSubclasses(store, _concepts) {
|
|
562
|
+
const suggestions = [];
|
|
563
|
+
const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
|
|
564
|
+
|
|
565
|
+
// Find entities with multiple types that might indicate subclass relationships
|
|
566
|
+
const entityTypes = new Map();
|
|
567
|
+
|
|
568
|
+
for (const quad of store) {
|
|
569
|
+
if (quad.predicate.value === RDF_TYPE) {
|
|
570
|
+
if (!entityTypes.has(quad.subject.value)) {
|
|
571
|
+
entityTypes.set(quad.subject.value, new Set());
|
|
572
|
+
}
|
|
573
|
+
entityTypes.get(quad.subject.value).add(quad.object.value);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Entities with multiple types might suggest missing subclass axioms
|
|
578
|
+
for (const [entity, types] of entityTypes.entries()) {
|
|
579
|
+
if (types.size > 1) {
|
|
580
|
+
const typeArray = Array.from(types);
|
|
581
|
+
suggestions.push({
|
|
582
|
+
type: 'missing_subclass',
|
|
583
|
+
description: `Entity ${entity} has multiple types ${typeArray.join(', ')} - consider adding subclass relationships`,
|
|
584
|
+
priority: 'low',
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return suggestions.slice(0, 5); // Limit suggestions
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Calculate graph statistics
|
|
594
|
+
* @param {Store} store - RDF store
|
|
595
|
+
* @returns {Promise<Object>} Statistics
|
|
596
|
+
* @private
|
|
597
|
+
*/
|
|
598
|
+
async _calculateStatistics(store) {
|
|
599
|
+
const subjects = new Set();
|
|
600
|
+
const predicates = new Set();
|
|
601
|
+
const objects = new Set();
|
|
602
|
+
|
|
603
|
+
for (const quad of store) {
|
|
604
|
+
subjects.add(quad.subject.value);
|
|
605
|
+
predicates.add(quad.predicate.value);
|
|
606
|
+
objects.add(quad.object.value);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const uniqueSubjects = subjects.size;
|
|
610
|
+
const uniquePredicates = predicates.size;
|
|
611
|
+
const uniqueObjects = objects.size;
|
|
612
|
+
const totalTriples = store.size;
|
|
613
|
+
|
|
614
|
+
// Calculate average degree (average number of edges per node)
|
|
615
|
+
const nodes = new Set([...subjects, ...objects]);
|
|
616
|
+
const avgDegree = totalTriples / Math.max(nodes.size, 1);
|
|
617
|
+
|
|
618
|
+
// Calculate density (actual edges / possible edges)
|
|
619
|
+
const maxPossibleEdges = nodes.size * (nodes.size - 1);
|
|
620
|
+
const density = maxPossibleEdges > 0 ? totalTriples / maxPossibleEdges : 0;
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
totalTriples,
|
|
624
|
+
uniqueSubjects,
|
|
625
|
+
uniquePredicates,
|
|
626
|
+
uniqueObjects,
|
|
627
|
+
avgDegree,
|
|
628
|
+
density,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Compute semantic similarity between two concepts
|
|
634
|
+
* @param {Store} store - RDF store
|
|
635
|
+
* @param {string} concept1 - First concept URI
|
|
636
|
+
* @param {string} concept2 - Second concept URI
|
|
637
|
+
* @param {Object} [options] - Similarity options
|
|
638
|
+
* @returns {Promise<Object>} Similarity result
|
|
639
|
+
*/
|
|
640
|
+
async computeSimilarity(store, concept1, concept2, _options = {}) {
|
|
641
|
+
return tracer.startActiveSpan('semantic.compute_similarity', async span => {
|
|
642
|
+
try {
|
|
643
|
+
span.setAttributes({
|
|
644
|
+
'semantic.concept1': concept1,
|
|
645
|
+
'semantic.concept2': concept2,
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
// Get properties for both concepts
|
|
649
|
+
const props1 = this._getConceptProperties(store, concept1);
|
|
650
|
+
const props2 = this._getConceptProperties(store, concept2);
|
|
651
|
+
|
|
652
|
+
// Get neighbors
|
|
653
|
+
const neighbors1 = this._getConceptNeighbors(store, concept1);
|
|
654
|
+
const neighbors2 = this._getConceptNeighbors(store, concept2);
|
|
655
|
+
|
|
656
|
+
// Jaccard similarity on properties
|
|
657
|
+
const commonProps = props1.filter(p => props2.includes(p));
|
|
658
|
+
const propSimilarity =
|
|
659
|
+
commonProps.length / Math.max(new Set([...props1, ...props2]).size, 1);
|
|
660
|
+
|
|
661
|
+
// Jaccard similarity on neighbors
|
|
662
|
+
const commonNeighbors = neighbors1.filter(n => neighbors2.includes(n));
|
|
663
|
+
const neighborSimilarity =
|
|
664
|
+
commonNeighbors.length / Math.max(new Set([...neighbors1, ...neighbors2]).size, 1);
|
|
665
|
+
|
|
666
|
+
// Combined similarity (weighted average)
|
|
667
|
+
const similarity = propSimilarity * 0.6 + neighborSimilarity * 0.4;
|
|
668
|
+
|
|
669
|
+
const result = SimilarityResultSchema.parse({
|
|
670
|
+
similarity,
|
|
671
|
+
method: 'jaccard',
|
|
672
|
+
commonProperties: commonProps,
|
|
673
|
+
commonNeighbors: commonNeighbors,
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
span.setAttributes({
|
|
677
|
+
'semantic.similarity_score': similarity,
|
|
678
|
+
'semantic.common_properties': commonProps.length,
|
|
679
|
+
'semantic.common_neighbors': commonNeighbors.length,
|
|
680
|
+
});
|
|
681
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
682
|
+
|
|
683
|
+
return result;
|
|
684
|
+
} catch (error) {
|
|
685
|
+
span.recordException(error);
|
|
686
|
+
span.setStatus({
|
|
687
|
+
code: SpanStatusCode.ERROR,
|
|
688
|
+
message: error.message,
|
|
689
|
+
});
|
|
690
|
+
throw error;
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Get properties of a concept
|
|
697
|
+
* @param {Store} store - RDF store
|
|
698
|
+
* @param {string} conceptUri - Concept URI
|
|
699
|
+
* @returns {Array<string>} Property URIs
|
|
700
|
+
* @private
|
|
701
|
+
*/
|
|
702
|
+
_getConceptProperties(store, conceptUri) {
|
|
703
|
+
const properties = [];
|
|
704
|
+
for (const quad of store) {
|
|
705
|
+
if (quad.subject.value === conceptUri) {
|
|
706
|
+
properties.push(quad.predicate.value);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return properties;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Get neighbors of a concept
|
|
714
|
+
* @param {Store} store - RDF store
|
|
715
|
+
* @param {string} conceptUri - Concept URI
|
|
716
|
+
* @returns {Array<string>} Neighbor URIs
|
|
717
|
+
* @private
|
|
718
|
+
*/
|
|
719
|
+
_getConceptNeighbors(store, conceptUri) {
|
|
720
|
+
const neighbors = [];
|
|
721
|
+
for (const quad of store) {
|
|
722
|
+
if (quad.subject.value === conceptUri) {
|
|
723
|
+
neighbors.push(quad.object.value);
|
|
724
|
+
}
|
|
725
|
+
if (quad.object.value === conceptUri) {
|
|
726
|
+
neighbors.push(quad.subject.value);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return neighbors;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Generate cache key for a store
|
|
734
|
+
* @param {Store} store - RDF store
|
|
735
|
+
* @returns {string} Cache key
|
|
736
|
+
* @private
|
|
737
|
+
*/
|
|
738
|
+
_getCacheKey(store) {
|
|
739
|
+
// Simple hash based on size and a sample of quads
|
|
740
|
+
const sample = Array.from(store)
|
|
741
|
+
.slice(0, 10)
|
|
742
|
+
.map(q => `${q.subject.value}|${q.predicate.value}|${q.object.value}`)
|
|
743
|
+
.join('::');
|
|
744
|
+
return `${store.size}:${sample}`;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Clear the analysis cache
|
|
749
|
+
*/
|
|
750
|
+
clearCache() {
|
|
751
|
+
if (this.cache) {
|
|
752
|
+
this.cache.clear();
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Get analyzer statistics
|
|
758
|
+
* @returns {Object} Statistics
|
|
759
|
+
*/
|
|
760
|
+
getStats() {
|
|
761
|
+
return {
|
|
762
|
+
...this.stats,
|
|
763
|
+
cacheSize: this.cache ? this.cache.size : 0,
|
|
764
|
+
cacheHitRate: this.stats.analyses > 0 ? this.stats.cacheHits / this.stats.analyses : 0,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Create a semantic analyzer instance
|
|
771
|
+
* @param {Object} [config] - Configuration
|
|
772
|
+
* @returns {SemanticAnalyzer} Semantic analyzer
|
|
773
|
+
*/
|
|
774
|
+
export function createSemanticAnalyzer(config = {}) {
|
|
775
|
+
return new SemanticAnalyzer(config);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Default semantic analyzer instance
|
|
780
|
+
*/
|
|
781
|
+
export const defaultSemanticAnalyzer = createSemanticAnalyzer();
|
package/src/hooks.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview All hooks export (for tree-shaking)
|
|
3
|
+
* @module react-hooks/hooks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Core
|
|
7
|
+
export { useKnowledgeEngine } from './core/useKnowledgeEngine.mjs';
|
|
8
|
+
export { useStore } from './core/useStore.mjs';
|
|
9
|
+
export { useTriples } from './core/useTriples.mjs';
|
|
10
|
+
export { useGraphs } from './core/useGraphs.mjs';
|
|
11
|
+
export { useTerms } from './core/useTerms.mjs';
|
|
12
|
+
|
|
13
|
+
// Query
|
|
14
|
+
export { useSPARQLQuery } from './query/useSPARQLQuery.mjs';
|
|
15
|
+
export { useQueryAsync } from './query/useQueryAsync.mjs';
|
|
16
|
+
export { useShapeValidation } from './query/useShapeValidation.mjs';
|
|
17
|
+
export { useReasoning } from './query/useReasoning.mjs';
|
|
18
|
+
export { useDeltaQuery } from './query/useDeltaQuery.mjs';
|
|
19
|
+
|
|
20
|
+
// Knowledge Hooks
|
|
21
|
+
export { useKnowledgeHook } from './knowledge-hooks/useKnowledgeHook.mjs';
|
|
22
|
+
export { useHookManager } from './knowledge-hooks/useHookManager.mjs';
|
|
23
|
+
export { useHookRegistry } from './knowledge-hooks/useHookRegistry.mjs';
|
|
24
|
+
export { useHookExecution } from './knowledge-hooks/useHookExecution.mjs';
|
|
25
|
+
|
|
26
|
+
// Storage
|
|
27
|
+
// useIndexedDBStore removed - use useOfflineStore from './composition/use-offline-store.mjs' instead
|
|
28
|
+
export { useQuadStore } from './storage/useQuadStore.mjs';
|
|
29
|
+
export { useTransaction } from './storage/useTransaction.mjs';
|
|
30
|
+
export { useAuditTrail } from './storage/useAuditTrail.mjs';
|
|
31
|
+
|
|
32
|
+
// Cache
|
|
33
|
+
export { useQueryCache } from './cache/useQueryCache.mjs';
|
|
34
|
+
export { useMemoizedQuery } from './cache/useMemoizedQuery.mjs';
|
|
35
|
+
export { useCacheStats } from './cache/useCacheStats.mjs';
|
|
36
|
+
|
|
37
|
+
// Effects
|
|
38
|
+
export { useKnowledgeEffect } from './effects/useKnowledgeEffect.mjs';
|
|
39
|
+
export { useDeltaTracking } from './effects/useDeltaTracking.mjs';
|
|
40
|
+
export { useGraphListener } from './effects/useGraphListener.mjs';
|
|
41
|
+
|
|
42
|
+
// Utils
|
|
43
|
+
export { useNamespaces } from './utils/useNamespaces.mjs';
|
|
44
|
+
export { useValidation } from './utils/useValidation.mjs';
|
|
45
|
+
export { useDebug } from './utils/useDebug.mjs';
|
|
46
|
+
export { usePerformanceTracking } from './utils/usePerformanceTracking.mjs';
|
|
47
|
+
|
|
48
|
+
// Batch
|
|
49
|
+
export { useBatchOperations } from './batch/useBatchOperations.mjs';
|
|
50
|
+
export { useOptimizedBatch } from './batch/useOptimizedBatch.mjs';
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unrdf/react
|
|
3
|
+
*
|
|
4
|
+
* AI Semantic Analysis Tools for RDF Knowledge Graphs
|
|
5
|
+
*
|
|
6
|
+
* Provides AI-powered analysis capabilities for:
|
|
7
|
+
* - Semantic analysis and concept extraction (SemanticAnalyzer)
|
|
8
|
+
* - Graph embeddings with TransE/ComplEx/RotatE (EmbeddingsManager)
|
|
9
|
+
* - Natural language to SPARQL query building (NLPQueryBuilder)
|
|
10
|
+
* - Anomaly detection for data quality (AnomalyDetector)
|
|
11
|
+
*
|
|
12
|
+
* @module @unrdf/react
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// AI Semantic Analysis
|
|
16
|
+
export {
|
|
17
|
+
SemanticAnalyzer,
|
|
18
|
+
createSemanticAnalyzer,
|
|
19
|
+
defaultSemanticAnalyzer,
|
|
20
|
+
} from './ai-semantic/semantic-analyzer.mjs';
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
EmbeddingsManager,
|
|
24
|
+
createEmbeddingsManager,
|
|
25
|
+
defaultEmbeddingsManager,
|
|
26
|
+
} from './ai-semantic/embeddings-manager.mjs';
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
NLPQueryBuilder,
|
|
30
|
+
createNLPQueryBuilder,
|
|
31
|
+
defaultNLPQueryBuilder,
|
|
32
|
+
} from './ai-semantic/nlp-query-builder.mjs';
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
AnomalyDetector,
|
|
36
|
+
createAnomalyDetector,
|
|
37
|
+
defaultAnomalyDetector,
|
|
38
|
+
} from './ai-semantic/anomaly-detector.mjs';
|