rust-kgdb 0.8.14 → 0.8.16
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 +167 -40
- package/package.json +1 -1
package/hypermind-agent.js
CHANGED
|
@@ -4924,6 +4924,7 @@ class HyperMindAgent {
|
|
|
4924
4924
|
this.rules = config.rules || new DatalogRuleSet()
|
|
4925
4925
|
this.sandbox = new WasmSandbox(config.sandbox || {})
|
|
4926
4926
|
this.name = config.name || 'hypermind-agent'
|
|
4927
|
+
this.answerFormat = config.answerFormat || 'text' // 'text' | 'table' | 'json'
|
|
4927
4928
|
|
|
4928
4929
|
// ThinkingReasoner for deductive reasoning with proof-carrying outputs
|
|
4929
4930
|
// Enabled by default - every HyperMindAgent has deductive reasoning
|
|
@@ -5025,33 +5026,75 @@ class HyperMindAgent {
|
|
|
5025
5026
|
}
|
|
5026
5027
|
|
|
5027
5028
|
// 4. Train RDF2Vec embeddings if requested
|
|
5028
|
-
|
|
5029
|
+
// Uses native Rust loadTtlWithEmbeddings() - all embedding logic in Rust
|
|
5030
|
+
let embeddingsEnabled = false
|
|
5029
5031
|
if (options.rdf2vec) {
|
|
5030
5032
|
try {
|
|
5031
|
-
|
|
5032
|
-
const
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
windowSize: options.rdf2vecWindowSize || 5,
|
|
5041
|
-
minCount: 1,
|
|
5042
|
-
epochs: options.rdf2vecEpochs || 5
|
|
5043
|
-
})
|
|
5033
|
+
// Get RDF2Vec config from options (support both boolean and object)
|
|
5034
|
+
const rdf2vecConfig = typeof options.rdf2vec === 'object' ? options.rdf2vec : {}
|
|
5035
|
+
// Use snake_case for NAPI-RS struct field names
|
|
5036
|
+
const config = {
|
|
5037
|
+
vector_size: rdf2vecConfig.dimensions || options.rdf2vecDimensions || 128,
|
|
5038
|
+
window_size: rdf2vecConfig.window || options.rdf2vecWindowSize || 5,
|
|
5039
|
+
walk_length: rdf2vecConfig.walkLength || options.rdf2vecWalkLength || 5,
|
|
5040
|
+
walks_per_node: rdf2vecConfig.walksPerNode || options.rdf2vecWalksPerEntity || 10
|
|
5041
|
+
}
|
|
5044
5042
|
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5043
|
+
// Re-load data with embeddings using native Rust (efficient, parallel)
|
|
5044
|
+
const loadResult = JSON.parse(db._db.loadTtlWithEmbeddings(
|
|
5045
|
+
options.data,
|
|
5046
|
+
null,
|
|
5047
|
+
config
|
|
5048
|
+
))
|
|
5049
|
+
|
|
5050
|
+
if (loadResult.embeddings?.trained) {
|
|
5051
|
+
embeddingsEnabled = true
|
|
5052
|
+
console.log(`[HyperMindAgent.create] Trained RDF2Vec embeddings:`)
|
|
5053
|
+
console.log(` Entities: ${loadResult.embeddings.entities_embedded}`)
|
|
5054
|
+
console.log(` Dimensions: ${loadResult.embeddings.dimensions}`)
|
|
5055
|
+
console.log(` Walks: ${loadResult.embeddings.walks_generated}`)
|
|
5056
|
+
console.log(` Training time: ${loadResult.embeddings.training_time_secs?.toFixed(2)}s`)
|
|
5048
5057
|
}
|
|
5049
5058
|
} catch (e) {
|
|
5050
5059
|
console.warn(`[HyperMindAgent.create] RDF2Vec training skipped: ${e.message}`)
|
|
5051
|
-
embeddings = null
|
|
5052
5060
|
}
|
|
5053
5061
|
}
|
|
5054
5062
|
|
|
5063
|
+
// Create wrapper for embedding operations if enabled
|
|
5064
|
+
const embeddings = embeddingsEnabled ? {
|
|
5065
|
+
// Delegate to native GraphDB embedding methods
|
|
5066
|
+
getEmbedding: (entity) => db._db.getEmbedding(entity),
|
|
5067
|
+
findSimilar: (entity, k, threshold) => {
|
|
5068
|
+
try {
|
|
5069
|
+
const results = JSON.parse(db._db.findSimilar(entity, k || 10))
|
|
5070
|
+
if (threshold) {
|
|
5071
|
+
return results.filter(r => r.similarity >= threshold)
|
|
5072
|
+
}
|
|
5073
|
+
return results
|
|
5074
|
+
} catch (e) {
|
|
5075
|
+
return []
|
|
5076
|
+
}
|
|
5077
|
+
},
|
|
5078
|
+
search: (text, k, threshold) => {
|
|
5079
|
+
// For text search, find entities that match and return similar
|
|
5080
|
+
// This uses the embedding similarity from native Rust
|
|
5081
|
+
const entities = db.querySelect(`SELECT ?s WHERE { ?s ?p ?o } LIMIT ${k || 10}`)
|
|
5082
|
+
const results = []
|
|
5083
|
+
for (const e of entities) {
|
|
5084
|
+
const entity = e.bindings?.s || e.s
|
|
5085
|
+
if (entity) {
|
|
5086
|
+
const similar = JSON.parse(db._db.findSimilar(entity, 1))
|
|
5087
|
+
if (similar.length > 0 && (!threshold || similar[0].similarity >= threshold)) {
|
|
5088
|
+
results.push({ entity, similarity: similar[0]?.similarity || 0 })
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
return results.slice(0, k || 10)
|
|
5093
|
+
},
|
|
5094
|
+
hasEmbeddings: () => db._db.hasEmbeddings(),
|
|
5095
|
+
getStats: () => JSON.parse(db._db.getEmbeddingStats())
|
|
5096
|
+
} : null
|
|
5097
|
+
|
|
5055
5098
|
// 5. Create agent with all components
|
|
5056
5099
|
const agent = new HyperMindAgent({
|
|
5057
5100
|
name: options.name,
|
|
@@ -5825,36 +5868,120 @@ LIMIT 100`
|
|
|
5825
5868
|
|
|
5826
5869
|
_formatAnswer(results, inferences, intent, deduction = null) {
|
|
5827
5870
|
const sparqlResults = results.filter(r => r.tool === 'kg.sparql.query' && r.success)
|
|
5828
|
-
const
|
|
5871
|
+
const allRows = sparqlResults.flatMap(r => Array.isArray(r.result) ? r.result : [])
|
|
5872
|
+
const totalResults = allRows.length
|
|
5873
|
+
|
|
5874
|
+
// Helper to extract readable name from URI
|
|
5875
|
+
const extractName = (uri) => {
|
|
5876
|
+
if (!uri) return null
|
|
5877
|
+
const str = String(uri)
|
|
5878
|
+
const lastSlash = str.lastIndexOf('/')
|
|
5879
|
+
const lastHash = str.lastIndexOf('#')
|
|
5880
|
+
const pos = Math.max(lastSlash, lastHash)
|
|
5881
|
+
const local = pos >= 0 ? str.substring(pos + 1) : str
|
|
5882
|
+
// Convert "lessort__mathias" to "Mathias Lessort"
|
|
5883
|
+
if (local.includes('__')) {
|
|
5884
|
+
const parts = local.split('__')
|
|
5885
|
+
return parts.map(p => p.charAt(0).toUpperCase() + p.slice(1)).reverse().join(' ')
|
|
5886
|
+
}
|
|
5887
|
+
return local.charAt(0).toUpperCase() + local.slice(1)
|
|
5888
|
+
}
|
|
5889
|
+
|
|
5890
|
+
// Extract unique entities from results
|
|
5891
|
+
const extractEntities = (rows, maxItems = 10) => {
|
|
5892
|
+
const seen = new Set()
|
|
5893
|
+
const entities = []
|
|
5894
|
+
for (const row of rows) {
|
|
5895
|
+
for (const [key, value] of Object.entries(row)) {
|
|
5896
|
+
const name = extractName(value)
|
|
5897
|
+
if (name && !seen.has(name) && !name.match(/^(none|unknown|integer)$/i)) {
|
|
5898
|
+
seen.add(name)
|
|
5899
|
+
entities.push({ key, name, uri: value })
|
|
5900
|
+
if (entities.length >= maxItems) return entities
|
|
5901
|
+
}
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
return entities
|
|
5905
|
+
}
|
|
5829
5906
|
|
|
5830
|
-
|
|
5907
|
+
// Determine output format
|
|
5908
|
+
const format = this.answerFormat || 'text'
|
|
5831
5909
|
|
|
5832
|
-
|
|
5833
|
-
|
|
5910
|
+
// JSON format - return structured data
|
|
5911
|
+
if (format === 'json') {
|
|
5912
|
+
return JSON.stringify({
|
|
5913
|
+
count: totalResults,
|
|
5914
|
+
results: allRows.slice(0, 20).map(row => {
|
|
5915
|
+
const formatted = {}
|
|
5916
|
+
for (const [k, v] of Object.entries(row)) {
|
|
5917
|
+
formatted[k] = extractName(v) || v
|
|
5918
|
+
}
|
|
5919
|
+
return formatted
|
|
5920
|
+
}),
|
|
5921
|
+
reasoning: deduction ? {
|
|
5922
|
+
observations: deduction.observations || 0,
|
|
5923
|
+
derivedFacts: (deduction.derivedFacts || []).length,
|
|
5924
|
+
rulesApplied: deduction.rulesFired || 0
|
|
5925
|
+
} : null
|
|
5926
|
+
}, null, 2)
|
|
5927
|
+
}
|
|
5928
|
+
|
|
5929
|
+
// TABLE format - professional tabular output
|
|
5930
|
+
if (format === 'table') {
|
|
5931
|
+
const entities = extractEntities(allRows, 15)
|
|
5932
|
+
if (entities.length === 0) {
|
|
5933
|
+
return `No results found.`
|
|
5934
|
+
}
|
|
5935
|
+
|
|
5936
|
+
// Build table
|
|
5937
|
+
let table = `\n┌${'─'.repeat(40)}┐\n`
|
|
5938
|
+
table += `│ Results (${totalResults} total)${' '.repeat(Math.max(0, 24 - String(totalResults).length))}│\n`
|
|
5939
|
+
table += `├${'─'.repeat(40)}┤\n`
|
|
5940
|
+
|
|
5941
|
+
for (const entity of entities) {
|
|
5942
|
+
const nameStr = entity.name.substring(0, 36).padEnd(36)
|
|
5943
|
+
table += `│ ${nameStr} │\n`
|
|
5944
|
+
}
|
|
5945
|
+
|
|
5946
|
+
if (totalResults > entities.length) {
|
|
5947
|
+
table += `│ ... and ${totalResults - entities.length} more${' '.repeat(Math.max(0, 23 - String(totalResults - entities.length).length))}│\n`
|
|
5948
|
+
}
|
|
5949
|
+
table += `└${'─'.repeat(40)}┘`
|
|
5950
|
+
|
|
5951
|
+
return table
|
|
5834
5952
|
}
|
|
5835
5953
|
|
|
5836
|
-
//
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5954
|
+
// TEXT format (default) - natural language answer
|
|
5955
|
+
const entities = extractEntities(allRows, 10)
|
|
5956
|
+
|
|
5957
|
+
if (totalResults === 0) {
|
|
5958
|
+
return 'No results found.'
|
|
5959
|
+
}
|
|
5960
|
+
|
|
5961
|
+
// Build natural language answer from actual data
|
|
5962
|
+
let answer = ''
|
|
5963
|
+
|
|
5964
|
+
if (entities.length > 0) {
|
|
5965
|
+
const names = entities.map(e => e.name)
|
|
5966
|
+
if (names.length === 1) {
|
|
5967
|
+
answer = names[0]
|
|
5968
|
+
} else if (names.length <= 5) {
|
|
5969
|
+
answer = names.slice(0, -1).join(', ') + ' and ' + names[names.length - 1]
|
|
5970
|
+
} else {
|
|
5971
|
+
answer = names.slice(0, 5).join(', ') + ` and ${totalResults - 5} more`
|
|
5852
5972
|
}
|
|
5973
|
+
} else {
|
|
5974
|
+
answer = `Found ${totalResults} results`
|
|
5975
|
+
}
|
|
5976
|
+
|
|
5977
|
+
// Add reasoning context if available
|
|
5978
|
+
if (deduction && deduction.derivedFacts && deduction.derivedFacts.length > 0) {
|
|
5979
|
+
answer += `\n\n[Reasoning: ${deduction.observations || 0} observations → ${deduction.derivedFacts.length} derived facts via ${deduction.rulesFired || 0} OWL rules]`
|
|
5853
5980
|
}
|
|
5854
5981
|
|
|
5982
|
+
// Special handling for fraud detection
|
|
5855
5983
|
if (intent.type === 'detect_fraud' && totalResults > 0) {
|
|
5856
|
-
answer = `Detected ${totalResults} potential fraud cases
|
|
5857
|
-
(answer.indexOf('\n\n') >= 0 ? answer.substring(answer.indexOf('\n\n')) : '')
|
|
5984
|
+
answer = `Detected ${totalResults} potential fraud cases: ${answer}`
|
|
5858
5985
|
}
|
|
5859
5986
|
|
|
5860
5987
|
return answer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rust-kgdb",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.16",
|
|
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",
|