rust-kgdb 0.8.21 → 0.8.23
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/hypermind-agent.js +193 -190
- package/package.json +1 -1
- package/rust-kgdb-napi.darwin-arm64.node +0 -0
- package/rust-kgdb-napi.darwin-x64.node +0 -0
- package/rust-kgdb-napi.node +0 -0
package/hypermind-agent.js
CHANGED
|
@@ -46,26 +46,42 @@ function loadNativeBindingDirect() {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const native = loadNativeBindingDirect()
|
|
49
|
+
|
|
50
|
+
// Native Rust bindings - SDK is THIN, only pure functions
|
|
51
|
+
// GraphDB handles all RDF/SPARQL operations
|
|
52
|
+
// tokenizeIdentifier and computeSimilarity are pure utility functions
|
|
49
53
|
const {
|
|
50
|
-
OlogSchema,
|
|
51
|
-
PredicateResolverService,
|
|
52
|
-
SchemaValidatorService,
|
|
53
|
-
ThinkingReasoner: NativeThinkingReasoner,
|
|
54
|
-
computeSimilarity,
|
|
55
54
|
tokenizeIdentifier,
|
|
56
|
-
|
|
57
|
-
extractKeywords: nativeExtractKeywords
|
|
55
|
+
computeSimilarity
|
|
58
56
|
} = native
|
|
59
57
|
|
|
60
58
|
/**
|
|
61
|
-
* Extract keywords from natural language prompt
|
|
62
|
-
*
|
|
59
|
+
* Extract keywords from natural language prompt
|
|
60
|
+
* Uses schema predicates for domain-aware extraction
|
|
63
61
|
* @param {string} prompt - Natural language prompt
|
|
62
|
+
* @param {string[]} schemaPredicates - Optional schema predicates for domain hints
|
|
64
63
|
* @returns {string[]} Extracted keywords
|
|
65
64
|
*/
|
|
66
|
-
function extractKeywords(prompt) {
|
|
65
|
+
function extractKeywords(prompt, schemaPredicates = []) {
|
|
67
66
|
if (!prompt) return []
|
|
68
|
-
|
|
67
|
+
// Split on whitespace and filter short words
|
|
68
|
+
const words = prompt.toLowerCase()
|
|
69
|
+
.replace(/[^\w\s]/g, ' ')
|
|
70
|
+
.split(/\s+/)
|
|
71
|
+
.filter(w => w.length > 2)
|
|
72
|
+
|
|
73
|
+
// If schema predicates provided, boost domain-relevant keywords
|
|
74
|
+
if (schemaPredicates.length > 0) {
|
|
75
|
+
const predicateWords = new Set()
|
|
76
|
+
for (const pred of schemaPredicates) {
|
|
77
|
+
const tokens = tokenizeIdentifier ? tokenizeIdentifier(pred.split('/').pop().split('#').pop()) : []
|
|
78
|
+
tokens.forEach(t => predicateWords.add(t.toLowerCase()))
|
|
79
|
+
}
|
|
80
|
+
// Return words that match schema or are content words
|
|
81
|
+
return words.filter(w => predicateWords.has(w) || w.length > 3)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return words
|
|
69
85
|
}
|
|
70
86
|
|
|
71
87
|
// ============================================================================
|
|
@@ -838,12 +854,12 @@ class SchemaContext {
|
|
|
838
854
|
// STRATEGY 2: Extract RDFS/OWL explicit schema (if VoID incomplete)
|
|
839
855
|
if (ctx.classes.size < 10) {
|
|
840
856
|
const classQuery = `
|
|
841
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
842
|
-
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
843
857
|
SELECT DISTINCT ?class ?super ?label WHERE {
|
|
844
|
-
{ ?class
|
|
845
|
-
|
|
846
|
-
|
|
858
|
+
{ ?class <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> }
|
|
859
|
+
UNION
|
|
860
|
+
{ ?class <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> }
|
|
861
|
+
OPTIONAL { ?class <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?super }
|
|
862
|
+
OPTIONAL { ?class <http://www.w3.org/2000/01/rdf-schema#label> ?label }
|
|
847
863
|
} LIMIT ${config.maxClasses}
|
|
848
864
|
`
|
|
849
865
|
const classResults = kg.querySelect(classQuery)
|
|
@@ -860,14 +876,15 @@ class SchemaContext {
|
|
|
860
876
|
// STRATEGY 3: Extract property morphisms with domain/range
|
|
861
877
|
if (ctx.properties.size < 10) {
|
|
862
878
|
const propQuery = `
|
|
863
|
-
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
864
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
865
|
-
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
866
879
|
SELECT DISTINCT ?prop ?domain ?range ?label WHERE {
|
|
867
|
-
{ ?prop
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
880
|
+
{ ?prop <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> }
|
|
881
|
+
UNION
|
|
882
|
+
{ ?prop <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#ObjectProperty> }
|
|
883
|
+
UNION
|
|
884
|
+
{ ?prop <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#DatatypeProperty> }
|
|
885
|
+
OPTIONAL { ?prop <http://www.w3.org/2000/01/rdf-schema#domain> ?domain }
|
|
886
|
+
OPTIONAL { ?prop <http://www.w3.org/2000/01/rdf-schema#range> ?range }
|
|
887
|
+
OPTIONAL { ?prop <http://www.w3.org/2000/01/rdf-schema#label> ?label }
|
|
871
888
|
} LIMIT ${config.maxProperties}
|
|
872
889
|
`
|
|
873
890
|
const propResults = kg.querySelect(propQuery)
|
|
@@ -922,11 +939,9 @@ class SchemaContext {
|
|
|
922
939
|
//
|
|
923
940
|
// ALWAYS extract entities - they are essential for entity resolution
|
|
924
941
|
const entityQuery = `
|
|
925
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
926
|
-
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
|
|
927
942
|
SELECT DISTINCT ?entity ?label ?type WHERE {
|
|
928
|
-
?entity
|
|
929
|
-
OPTIONAL { ?entity
|
|
943
|
+
?entity <http://www.w3.org/2000/01/rdf-schema#label> ?label .
|
|
944
|
+
OPTIONAL { ?entity <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ?type }
|
|
930
945
|
} LIMIT ${config.maxEntities || 1000}
|
|
931
946
|
`
|
|
932
947
|
try {
|
|
@@ -1037,15 +1052,13 @@ class SchemaContext {
|
|
|
1037
1052
|
try {
|
|
1038
1053
|
// Extract classes (Objects in schema category)
|
|
1039
1054
|
const classQuery = `
|
|
1040
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
1041
|
-
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
1042
|
-
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
1043
1055
|
SELECT DISTINCT ?class ?super ?label ?comment WHERE {
|
|
1044
|
-
{ ?class
|
|
1045
|
-
UNION
|
|
1046
|
-
|
|
1047
|
-
OPTIONAL { ?class
|
|
1048
|
-
OPTIONAL { ?class
|
|
1056
|
+
{ ?class <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2000/01/rdf-schema#Class> }
|
|
1057
|
+
UNION
|
|
1058
|
+
{ ?class <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> }
|
|
1059
|
+
OPTIONAL { ?class <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?super }
|
|
1060
|
+
OPTIONAL { ?class <http://www.w3.org/2000/01/rdf-schema#label> ?label }
|
|
1061
|
+
OPTIONAL { ?class <http://www.w3.org/2000/01/rdf-schema#comment> ?comment }
|
|
1049
1062
|
} LIMIT ${CONFIG.schema.maxClasses}
|
|
1050
1063
|
`
|
|
1051
1064
|
const classResults = loadedKg.querySelect(classQuery)
|
|
@@ -1069,17 +1082,15 @@ class SchemaContext {
|
|
|
1069
1082
|
|
|
1070
1083
|
// Extract properties (Morphisms with domain/range)
|
|
1071
1084
|
const propQuery = `
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
OPTIONAL { ?prop
|
|
1080
|
-
OPTIONAL { ?prop
|
|
1081
|
-
OPTIONAL { ?prop rdfs:label ?label }
|
|
1082
|
-
OPTIONAL { ?prop a owl:FunctionalProperty . BIND(true AS ?functional) }
|
|
1085
|
+
SELECT DISTINCT ?prop ?domain ?range ?label WHERE {
|
|
1086
|
+
{ ?prop <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Property> }
|
|
1087
|
+
UNION
|
|
1088
|
+
{ ?prop <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#ObjectProperty> }
|
|
1089
|
+
UNION
|
|
1090
|
+
{ ?prop <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#DatatypeProperty> }
|
|
1091
|
+
OPTIONAL { ?prop <http://www.w3.org/2000/01/rdf-schema#domain> ?domain }
|
|
1092
|
+
OPTIONAL { ?prop <http://www.w3.org/2000/01/rdf-schema#range> ?range }
|
|
1093
|
+
OPTIONAL { ?prop <http://www.w3.org/2000/01/rdf-schema#label> ?label }
|
|
1083
1094
|
} LIMIT ${CONFIG.schema.maxProperties}
|
|
1084
1095
|
`
|
|
1085
1096
|
const propResults = loadedKg.querySelect(propQuery)
|
|
@@ -1088,14 +1099,13 @@ class SchemaContext {
|
|
|
1088
1099
|
const domain = r.bindings?.domain || r.domain
|
|
1089
1100
|
const range = r.bindings?.range || r.range
|
|
1090
1101
|
const label = r.bindings?.label || r.label
|
|
1091
|
-
const functional = r.bindings?.functional || r.functional
|
|
1092
1102
|
if (prop) {
|
|
1093
1103
|
ctx.properties.set(prop, {
|
|
1094
1104
|
uri: prop,
|
|
1095
1105
|
domain: domain || null,
|
|
1096
1106
|
range: range || null,
|
|
1097
1107
|
label: label || null,
|
|
1098
|
-
functional:
|
|
1108
|
+
functional: false,
|
|
1099
1109
|
source
|
|
1100
1110
|
})
|
|
1101
1111
|
}
|
|
@@ -2942,100 +2952,18 @@ class LLMPlanner {
|
|
|
2942
2952
|
o: r.bindings?.o || r.o
|
|
2943
2953
|
}))
|
|
2944
2954
|
|
|
2945
|
-
//
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
const localName = cls.split('/').pop().split('#').pop()
|
|
2957
|
-
olog.addClass(localName)
|
|
2958
|
-
} catch (e) { /* skip invalid class */ }
|
|
2959
|
-
}
|
|
2960
|
-
|
|
2961
|
-
// Add properties with aliases extracted from local names
|
|
2962
|
-
for (const prop of (schema.predicates || [])) {
|
|
2963
|
-
try {
|
|
2964
|
-
const localName = prop.split('/').pop().split('#').pop()
|
|
2965
|
-
// Generate aliases from tokenized form
|
|
2966
|
-
const tokens = native.tokenizeIdentifier(localName)
|
|
2967
|
-
const aliases = tokens.length > 1 ? [tokens.join(''), tokens.join('_')] : []
|
|
2968
|
-
olog.addProperty(prop, 'Thing', 'Thing', [localName, ...aliases])
|
|
2969
|
-
} catch (e) { /* skip invalid property */ }
|
|
2970
|
-
}
|
|
2971
|
-
|
|
2972
|
-
olog.build()
|
|
2973
|
-
|
|
2974
|
-
// ============================================================
|
|
2975
|
-
// ENTITY RESOLUTION: Populate Olog with entities from RDF data
|
|
2976
|
-
// This enables NL entity references like "Fifth Amendment" to
|
|
2977
|
-
// resolve to canonical URIs like "legal:FifthAmendment"
|
|
2978
|
-
//
|
|
2979
|
-
// CRITICAL FIX (2025-12-23): Use SchemaContext's extracted entities
|
|
2980
|
-
// The entities extracted with Strategy 6 (rdfs:label) are now used
|
|
2981
|
-
// to populate the Olog's label_to_entity map for O(1) lookup.
|
|
2982
|
-
// ============================================================
|
|
2983
|
-
try {
|
|
2984
|
-
let entityCount = 0
|
|
2985
|
-
|
|
2986
|
-
// PRIMARY: Use SchemaContext entities (extracted via Strategy 6)
|
|
2987
|
-
// These are the entities with rdfs:label that we need for resolution
|
|
2988
|
-
// Use this._schemaContext if available (from getSchemaContext())
|
|
2989
|
-
const schemaCtx = this._schemaContext || await this.getSchemaContext?.()
|
|
2990
|
-
if (schemaCtx && schemaCtx.entities && schemaCtx.entities.size > 0) {
|
|
2991
|
-
const entityTriples = []
|
|
2992
|
-
for (const [uri, info] of schemaCtx.entities) {
|
|
2993
|
-
// Create triples for entity labels
|
|
2994
|
-
if (info.label) {
|
|
2995
|
-
entityTriples.push([uri, 'http://www.w3.org/2000/01/rdf-schema#label', info.label])
|
|
2996
|
-
}
|
|
2997
|
-
if (info.type) {
|
|
2998
|
-
entityTriples.push([uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', info.type])
|
|
2999
|
-
}
|
|
3000
|
-
}
|
|
3001
|
-
if (entityTriples.length > 0 && olog.populateEntitiesFromTriples) {
|
|
3002
|
-
entityCount = olog.populateEntitiesFromTriples(JSON.stringify(entityTriples))
|
|
3003
|
-
schema._entityCount = entityCount
|
|
3004
|
-
schema._entitySource = 'schemaContext'
|
|
3005
|
-
}
|
|
3006
|
-
}
|
|
3007
|
-
|
|
3008
|
-
// FALLBACK: Query triples if SchemaContext has no entities
|
|
3009
|
-
if (entityCount === 0 && this.kg && typeof this.kg.querySelect === 'function') {
|
|
3010
|
-
const allTriples = this.kg.querySelect('SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10000')
|
|
3011
|
-
if (allTriples && allTriples.length > 0) {
|
|
3012
|
-
// Convert to triple array format expected by Rust
|
|
3013
|
-
const triplesArray = allTriples.map(result => [
|
|
3014
|
-
result.bindings?.s || result.bindings?.subject || '',
|
|
3015
|
-
result.bindings?.p || result.bindings?.predicate || '',
|
|
3016
|
-
result.bindings?.o || result.bindings?.object || ''
|
|
3017
|
-
]).filter(t => t[0] && t[1] && t[2])
|
|
3018
|
-
|
|
3019
|
-
// Populate entities in Olog using Rust extraction
|
|
3020
|
-
if (triplesArray.length > 0 && olog.populateEntitiesFromTriples) {
|
|
3021
|
-
entityCount = olog.populateEntitiesFromTriples(JSON.stringify(triplesArray))
|
|
3022
|
-
schema._entityCount = entityCount
|
|
3023
|
-
schema._entitySource = 'triples'
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
} catch (entityErr) {
|
|
3028
|
-
// Entity extraction is optional - continue without it
|
|
3029
|
-
schema._entityExtractionError = entityErr.message
|
|
2955
|
+
// Build predicate index for fast keyword→URI lookup
|
|
2956
|
+
// NO STATE - computed fresh from schema.predicates
|
|
2957
|
+
schema._predicateIndex = new Map()
|
|
2958
|
+
for (const pred of (schema.predicates || [])) {
|
|
2959
|
+
const localName = pred.split('/').pop().split('#').pop()
|
|
2960
|
+
const tokens = tokenizeIdentifier ? tokenizeIdentifier(localName) : [localName.toLowerCase()]
|
|
2961
|
+
// Index by local name and tokens
|
|
2962
|
+
schema._predicateIndex.set(localName.toLowerCase(), pred)
|
|
2963
|
+
for (const token of tokens) {
|
|
2964
|
+
if (token.length > 2) {
|
|
2965
|
+
schema._predicateIndex.set(token.toLowerCase(), pred)
|
|
3030
2966
|
}
|
|
3031
|
-
|
|
3032
|
-
schema._nativeResolver = new native.PredicateResolverService(olog, threshold)
|
|
3033
|
-
schema._nativeOlog = olog
|
|
3034
|
-
} catch (e) {
|
|
3035
|
-
// NO FALLBACKS - propagate error with context
|
|
3036
|
-
console.error('[extractSchema] Native resolver initialization failed:', e.message)
|
|
3037
|
-
schema._nativeResolverError = e.message
|
|
3038
|
-
schema._nativeResolver = null
|
|
3039
2967
|
}
|
|
3040
2968
|
}
|
|
3041
2969
|
|
|
@@ -3214,10 +3142,15 @@ ${schemaText}
|
|
|
3214
3142
|
${memoryText}
|
|
3215
3143
|
|
|
3216
3144
|
RULES:
|
|
3217
|
-
-
|
|
3218
|
-
-
|
|
3219
|
-
-
|
|
3220
|
-
-
|
|
3145
|
+
- Use predicates from the schema to construct SPARQL queries
|
|
3146
|
+
- For pattern queries (fraud rings, collusion, networks, relationships):
|
|
3147
|
+
- Map semantic intent to relationship predicates (e.g., 'knows', 'referredBy', 'claimsWith')
|
|
3148
|
+
- Generate triangle/cycle patterns: ?a :knows ?b . ?b :knows ?c . ?c :knows ?a
|
|
3149
|
+
- A "fraud ring" = entities connected in cycles via relationship predicates
|
|
3150
|
+
- For risk queries, use 'riskScore' with FILTER (e.g., FILTER(?score > 0.7))
|
|
3151
|
+
- For similarity queries, look for shared attributes (same address, overlapping claims)
|
|
3152
|
+
- Always return valid SPARQL using actual schema predicates
|
|
3153
|
+
- Use proper SPARQL 1.1 syntax with correct prefixes
|
|
3221
3154
|
|
|
3222
3155
|
Respond in JSON:
|
|
3223
3156
|
{
|
|
@@ -3332,14 +3265,26 @@ Intent types: detect_fraud, find_similar, explain, find_patterns, aggregate, gen
|
|
|
3332
3265
|
|
|
3333
3266
|
// Generate SPARQL based on intent and schema
|
|
3334
3267
|
if (intent.query || intent.compliance || intent.aggregate) {
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3268
|
+
let sparql = null
|
|
3269
|
+
|
|
3270
|
+
// Try schema-driven SPARQL generation first (fast, deterministic)
|
|
3271
|
+
try {
|
|
3272
|
+
sparql = this._generateSchemaSparql(intent, schema, context)
|
|
3273
|
+
} catch (schemaErr) {
|
|
3274
|
+
// Keyword matching failed - return empty steps (let LLM handle in call())
|
|
3275
|
+
// This is NOT a fallback - complex queries go through LLM path
|
|
3276
|
+
sparql = null
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
if (sparql) {
|
|
3280
|
+
steps.push({
|
|
3281
|
+
id: stepId++,
|
|
3282
|
+
tool: 'kg.sparql.query',
|
|
3283
|
+
input_type: 'Query',
|
|
3284
|
+
output_type: 'BindingSet',
|
|
3285
|
+
args: { sparql }
|
|
3286
|
+
})
|
|
3287
|
+
}
|
|
3343
3288
|
}
|
|
3344
3289
|
|
|
3345
3290
|
if (intent.pattern) {
|
|
@@ -3426,39 +3371,77 @@ Intent types: detect_fraud, find_similar, explain, find_patterns, aggregate, gen
|
|
|
3426
3371
|
}
|
|
3427
3372
|
|
|
3428
3373
|
// ============================================================
|
|
3429
|
-
//
|
|
3430
|
-
//
|
|
3431
|
-
//
|
|
3432
|
-
// The Rust PredicateResolver.generate_federated_sql() handles:
|
|
3433
|
-
// - Entity resolution (NL → URI)
|
|
3434
|
-
// - Predicate resolution (NL → schema morphism)
|
|
3435
|
-
// - SPARQL pattern generation (category theory composition)
|
|
3436
|
-
// - SQL wrapping with HyperFederate UDFs
|
|
3374
|
+
// SCHEMA-DRIVEN SPARQL GENERATION
|
|
3375
|
+
// Uses schema._predicateIndex built during extractSchema
|
|
3376
|
+
// NO FALLBACKS - requires valid schema match
|
|
3437
3377
|
// ============================================================
|
|
3438
3378
|
|
|
3439
|
-
|
|
3379
|
+
const predicateIndex = schema._predicateIndex || new Map()
|
|
3380
|
+
const predicates = schema.predicates || []
|
|
3381
|
+
|
|
3382
|
+
if (predicates.length === 0) {
|
|
3440
3383
|
throw new Error(JSON.stringify({
|
|
3441
|
-
type: '
|
|
3442
|
-
message: '
|
|
3443
|
-
suggestion: '
|
|
3444
|
-
recoverable:
|
|
3384
|
+
type: 'SchemaError',
|
|
3385
|
+
message: 'No schema predicates available.',
|
|
3386
|
+
suggestion: 'Load data with valid RDF predicates first.',
|
|
3387
|
+
recoverable: true
|
|
3445
3388
|
}))
|
|
3446
3389
|
}
|
|
3447
3390
|
|
|
3448
|
-
|
|
3449
|
-
|
|
3391
|
+
// Extract keywords from prompt
|
|
3392
|
+
const keywords = extractKeywords(prompt, predicates)
|
|
3393
|
+
|
|
3394
|
+
// Find ALL matching predicates using tokenized comparison
|
|
3395
|
+
const matches = []
|
|
3396
|
+
for (const keyword of keywords) {
|
|
3397
|
+
const kwLower = keyword.toLowerCase()
|
|
3450
3398
|
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
pattern: result.pattern,
|
|
3456
|
-
resolved_predicates: result.resolved_predicates,
|
|
3457
|
-
suggestion: 'Entities/predicates may not exist in schema. Check data population.'
|
|
3399
|
+
// Direct index lookup (index has tokenized predicate names)
|
|
3400
|
+
if (predicateIndex.has(kwLower)) {
|
|
3401
|
+
matches.push({ predicate: predicateIndex.get(kwLower), score: 1.0, keyword })
|
|
3402
|
+
continue
|
|
3458
3403
|
}
|
|
3404
|
+
|
|
3405
|
+
// Token-based matching: tokenize predicate and check for substring/exact match
|
|
3406
|
+
for (const pred of predicates) {
|
|
3407
|
+
const localName = pred.split('/').pop().split('#').pop()
|
|
3408
|
+
const tokens = tokenizeIdentifier ? tokenizeIdentifier(localName) : [localName.toLowerCase()]
|
|
3409
|
+
|
|
3410
|
+
for (const token of tokens) {
|
|
3411
|
+
if (token === kwLower) {
|
|
3412
|
+
matches.push({ predicate: pred, score: 1.0, keyword })
|
|
3413
|
+
break
|
|
3414
|
+
} else if (token.includes(kwLower) || kwLower.includes(token)) {
|
|
3415
|
+
matches.push({ predicate: pred, score: 0.7, keyword })
|
|
3416
|
+
break
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
if (matches.length === 0) {
|
|
3423
|
+
throw new Error(JSON.stringify({
|
|
3424
|
+
type: 'NoMatchError',
|
|
3425
|
+
message: `No schema predicates match prompt: "${prompt}"`,
|
|
3426
|
+
keywords,
|
|
3427
|
+
availablePredicates: predicates.slice(0, 10),
|
|
3428
|
+
suggestion: 'Rephrase query using predicates from schema.',
|
|
3429
|
+
recoverable: true
|
|
3430
|
+
}))
|
|
3459
3431
|
}
|
|
3460
3432
|
|
|
3461
|
-
|
|
3433
|
+
// Sort by score and get best match
|
|
3434
|
+
matches.sort((a, b) => b.score - a.score)
|
|
3435
|
+
const bestMatch = matches[0]
|
|
3436
|
+
|
|
3437
|
+
// Build SPARQL from schema predicate
|
|
3438
|
+
const sparql = `SELECT ?subject ?object WHERE { ?subject <${bestMatch.predicate}> ?object } LIMIT ${limit}`
|
|
3439
|
+
context._matchedPredicate = bestMatch.predicate
|
|
3440
|
+
context._matchConfidence = bestMatch.score
|
|
3441
|
+
context._matchKeyword = bestMatch.keyword
|
|
3442
|
+
context._allMatches = matches.slice(0, 5)
|
|
3443
|
+
|
|
3444
|
+
return sparql
|
|
3462
3445
|
}
|
|
3463
3446
|
|
|
3464
3447
|
/**
|
|
@@ -4491,19 +4474,9 @@ class ThinkingReasoner {
|
|
|
4491
4474
|
this.contextId = config.contextId || `thinking-${Date.now()}`
|
|
4492
4475
|
this.actorId = config.actorId || 'hypermind-agent'
|
|
4493
4476
|
|
|
4494
|
-
//
|
|
4495
|
-
//
|
|
4496
|
-
|
|
4497
|
-
if (NativeThinkingReasoner) {
|
|
4498
|
-
try {
|
|
4499
|
-
this._native = new NativeThinkingReasoner()
|
|
4500
|
-
this._hasNative = true
|
|
4501
|
-
} catch (e) {
|
|
4502
|
-
this._hasNative = false
|
|
4503
|
-
}
|
|
4504
|
-
} else {
|
|
4505
|
-
this._hasNative = false
|
|
4506
|
-
}
|
|
4477
|
+
// Native reasoning not available in thin SDK
|
|
4478
|
+
// All reasoning handled via SPARQL and schema-driven approach
|
|
4479
|
+
this._hasNative = false
|
|
4507
4480
|
|
|
4508
4481
|
// Fallback stores (only used if native not available)
|
|
4509
4482
|
this.events = []
|
|
@@ -5774,7 +5747,37 @@ class HyperMindAgent {
|
|
|
5774
5747
|
trace.addStep({ type: 'intent_classification', intent })
|
|
5775
5748
|
|
|
5776
5749
|
// 3. Generate typed execution plan
|
|
5777
|
-
|
|
5750
|
+
let plan
|
|
5751
|
+
try {
|
|
5752
|
+
plan = this._generatePlan(intent, prompt)
|
|
5753
|
+
} catch (planErr) {
|
|
5754
|
+
// Schema-based SPARQL generation failed - try LLM for semantic understanding
|
|
5755
|
+
// This is NOT a fallback, it's the proper path for complex queries
|
|
5756
|
+
if (this.apiKey && this.planner && this.planner.model) {
|
|
5757
|
+
const schema = this.planner._schemaCache || { predicates: [], classes: [] }
|
|
5758
|
+
const llmResult = await this.planner._planWithLLM(prompt, schema, memories)
|
|
5759
|
+
if (llmResult && llmResult.sparql) {
|
|
5760
|
+
// Create plan with LLM-generated SPARQL
|
|
5761
|
+
plan = {
|
|
5762
|
+
id: `plan_llm_${Date.now()}`,
|
|
5763
|
+
intent: llmResult.type || intent.type,
|
|
5764
|
+
steps: [{
|
|
5765
|
+
id: 1,
|
|
5766
|
+
tool: 'kg.sparql.query',
|
|
5767
|
+
args: { sparql: llmResult.sparql }
|
|
5768
|
+
}],
|
|
5769
|
+
type_chain: 'kg.sparql.query',
|
|
5770
|
+
_llmGenerated: true
|
|
5771
|
+
}
|
|
5772
|
+
trace.addStep({ type: 'llm_sparql_generation', sparql: llmResult.sparql })
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
|
|
5776
|
+
// If still no plan, throw the original error
|
|
5777
|
+
if (!plan) {
|
|
5778
|
+
throw planErr
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5778
5781
|
trace.addStep({ type: 'execution_plan', plan })
|
|
5779
5782
|
|
|
5780
5783
|
// 4. Execute plan in WASM sandbox
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rust-kgdb",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.23",
|
|
4
4
|
"description": "High-performance RDF/SPARQL database with AI agent framework and cross-database federation. GraphDB (449ns lookups, 5-11x faster than RDFox), HyperFederate (KGDB + Snowflake + BigQuery), GraphFrames analytics, Datalog reasoning, HNSW vector embeddings. HyperMindAgent for schema-aware query generation with audit trails. W3C SPARQL 1.1 compliant. Native performance via Rust + NAPI-RS.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
Binary file
|
|
Binary file
|
package/rust-kgdb-napi.node
CHANGED
|
Binary file
|