508 lines
16 KiB
JavaScript
508 lines
16 KiB
JavaScript
/* tslint:disable */
|
|
/* eslint-disable */
|
|
/* prettier-ignore */
|
|
|
|
/* auto-generated by NAPI-RS */
|
|
|
|
const { existsSync, readFileSync } = require('fs')
|
|
const { join } = require('path')
|
|
|
|
const { platform, arch } = process
|
|
|
|
let nativeBinding = null
|
|
let localFileExisted = false
|
|
let loadError = null
|
|
|
|
function isMusl() {
|
|
// For Node 10
|
|
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
try {
|
|
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
|
return readFileSync(lddPath, 'utf8').includes('musl')
|
|
} catch (e) {
|
|
return true
|
|
}
|
|
} else {
|
|
const { glibcVersionRuntime } = process.report.getReport().header
|
|
return !glibcVersionRuntime
|
|
}
|
|
}
|
|
|
|
switch (platform) {
|
|
case 'android':
|
|
switch (arch) {
|
|
case 'arm64':
|
|
localFileExisted = existsSync(join(__dirname, 'ruvector-gnn.android-arm64.node'))
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.android-arm64.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-android-arm64')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
case 'arm':
|
|
localFileExisted = existsSync(join(__dirname, 'ruvector-gnn.android-arm-eabi.node'))
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.android-arm-eabi.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-android-arm-eabi')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
default:
|
|
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
}
|
|
break
|
|
case 'win32':
|
|
switch (arch) {
|
|
case 'x64':
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.win32-x64-msvc.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.win32-x64-msvc.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-win32-x64-msvc')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
case 'ia32':
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.win32-ia32-msvc.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.win32-ia32-msvc.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-win32-ia32-msvc')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
case 'arm64':
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.win32-arm64-msvc.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.win32-arm64-msvc.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-win32-arm64-msvc')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
default:
|
|
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
|
}
|
|
break
|
|
case 'darwin':
|
|
localFileExisted = existsSync(join(__dirname, 'ruvector-gnn.darwin-universal.node'))
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.darwin-universal.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-darwin-universal')
|
|
}
|
|
break
|
|
} catch {}
|
|
switch (arch) {
|
|
case 'x64':
|
|
localFileExisted = existsSync(join(__dirname, 'ruvector-gnn.darwin-x64.node'))
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.darwin-x64.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-darwin-x64')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
case 'arm64':
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.darwin-arm64.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.darwin-arm64.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-darwin-arm64')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
default:
|
|
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
|
}
|
|
break
|
|
case 'freebsd':
|
|
if (arch !== 'x64') {
|
|
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
|
}
|
|
localFileExisted = existsSync(join(__dirname, 'ruvector-gnn.freebsd-x64.node'))
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.freebsd-x64.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-freebsd-x64')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
case 'linux':
|
|
switch (arch) {
|
|
case 'x64':
|
|
if (isMusl()) {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-x64-musl.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-x64-musl.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-x64-musl')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
} else {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-x64-gnu.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-x64-gnu.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-x64-gnu')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
}
|
|
break
|
|
case 'arm64':
|
|
if (isMusl()) {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-arm64-musl.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-arm64-musl.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-arm64-musl')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
} else {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-arm64-gnu.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-arm64-gnu.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-arm64-gnu')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
}
|
|
break
|
|
case 'arm':
|
|
if (isMusl()) {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-arm-musleabihf.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-arm-musleabihf.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-arm-musleabihf')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
} else {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-arm-gnueabihf.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-arm-gnueabihf.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-arm-gnueabihf')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
}
|
|
break
|
|
case 'riscv64':
|
|
if (isMusl()) {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-riscv64-musl.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-riscv64-musl.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-riscv64-musl')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
} else {
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-riscv64-gnu.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-riscv64-gnu.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-riscv64-gnu')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
}
|
|
break
|
|
case 's390x':
|
|
localFileExisted = existsSync(
|
|
join(__dirname, 'ruvector-gnn.linux-s390x-gnu.node')
|
|
)
|
|
try {
|
|
if (localFileExisted) {
|
|
nativeBinding = require('./ruvector-gnn.linux-s390x-gnu.node')
|
|
} else {
|
|
nativeBinding = require('@ruvector/gnn-linux-s390x-gnu')
|
|
}
|
|
} catch (e) {
|
|
loadError = e
|
|
}
|
|
break
|
|
default:
|
|
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
|
}
|
|
break
|
|
default:
|
|
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
|
}
|
|
|
|
if (!nativeBinding) {
|
|
if (loadError) {
|
|
throw loadError
|
|
}
|
|
throw new Error(`Failed to load native binding`)
|
|
}
|
|
|
|
const { RuvectorLayer: NativeRuvectorLayer, TensorCompress: NativeTensorCompress, differentiableSearch: nativeDifferentiableSearch, hierarchicalForward: nativeHierarchicalForward, getCompressionLevel, init } = nativeBinding
|
|
|
|
// ============================================================================
|
|
// Optimized Type Conversion Helpers
|
|
// The native Rust code expects Float32Array for maximum performance.
|
|
// These helpers convert any array-like input to Float32Array efficiently.
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Convert any array-like to Float32Array (native Rust expects Float32Array)
|
|
* Optimized paths:
|
|
* - Float32Array: zero-copy return
|
|
* - Float64Array: efficient typed array copy
|
|
* - Array: direct Float32Array construction
|
|
* @param {number[] | Float32Array | Float64Array} input
|
|
* @returns {Float32Array}
|
|
*/
|
|
function toFloat32Array(input) {
|
|
// Fast path: already Float32Array - zero copy
|
|
if (input instanceof Float32Array) return input
|
|
// Float64Array: efficient typed array conversion
|
|
if (input instanceof Float64Array) return new Float32Array(input)
|
|
// Regular array: direct construction
|
|
if (Array.isArray(input)) return new Float32Array(input)
|
|
// Fallback for other array-likes
|
|
return new Float32Array(Array.from(input))
|
|
}
|
|
|
|
/**
|
|
* Convert array of arrays to array of Float32Arrays
|
|
* @param {(number[] | Float32Array | Float64Array)[]} input
|
|
* @returns {Float32Array[]}
|
|
*/
|
|
function toFloat32ArrayBatch(input) {
|
|
const result = new Array(input.length)
|
|
for (let i = 0; i < input.length; i++) {
|
|
result[i] = toFloat32Array(input[i])
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Batch convert with pre-allocated buffer for maximum performance
|
|
* Use when all vectors have the same dimension
|
|
* @param {(number[] | Float32Array | Float64Array)[]} input
|
|
* @param {number} dim - Vector dimension
|
|
* @returns {Float32Array[]}
|
|
*/
|
|
function toFloat32ArrayBatchOptimized(input, dim) {
|
|
const count = input.length
|
|
const result = new Array(count)
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const arr = input[i]
|
|
if (arr instanceof Float32Array) {
|
|
result[i] = arr
|
|
} else if (arr instanceof Float64Array) {
|
|
result[i] = new Float32Array(arr)
|
|
} else {
|
|
result[i] = new Float32Array(arr)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// ============================================================================
|
|
// Wrapper Functions - Accept any array type, convert to Float32Array
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Differentiable search using soft attention mechanism
|
|
* Accepts regular arrays or typed arrays, converts to Float32Array internally
|
|
* @param {number[] | Float32Array} query - Query vector
|
|
* @param {(number[] | Float32Array)[]} candidateEmbeddings - Candidate vectors
|
|
* @param {number} k - Number of results
|
|
* @param {number} temperature - Softmax temperature
|
|
* @returns {{indices: number[], weights: number[]}}
|
|
*/
|
|
function differentiableSearch(query, candidateEmbeddings, k, temperature) {
|
|
return nativeDifferentiableSearch(
|
|
toFloat32Array(query),
|
|
toFloat32ArrayBatch(candidateEmbeddings),
|
|
k,
|
|
temperature
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Hierarchical forward pass through GNN layers
|
|
* @param {number[] | Float32Array} query - Query vector
|
|
* @param {(number[] | Float32Array)[][]} layerEmbeddings - Embeddings by layer
|
|
* @param {string[]} gnnLayersJson - Serialized GNN layers
|
|
* @returns {Float32Array}
|
|
*/
|
|
function hierarchicalForward(query, layerEmbeddings, gnnLayersJson) {
|
|
return nativeHierarchicalForward(
|
|
toFloat32Array(query),
|
|
layerEmbeddings.map(layer => toFloat32ArrayBatch(layer)),
|
|
gnnLayersJson
|
|
)
|
|
}
|
|
|
|
// ============================================================================
|
|
// Wrapped Classes
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Graph Neural Network layer for HNSW topology
|
|
*/
|
|
class RuvectorLayer {
|
|
constructor(inputDim, hiddenDim, heads, dropout) {
|
|
this._native = new NativeRuvectorLayer(inputDim, hiddenDim, heads, dropout)
|
|
}
|
|
|
|
/**
|
|
* Forward pass through the GNN layer
|
|
* @param {number[] | Float32Array} nodeEmbedding - Node embedding
|
|
* @param {(number[] | Float32Array)[]} neighborEmbeddings - Neighbor embeddings
|
|
* @param {number[] | Float32Array} edgeWeights - Edge weights
|
|
* @returns {Float32Array}
|
|
*/
|
|
forward(nodeEmbedding, neighborEmbeddings, edgeWeights) {
|
|
return this._native.forward(
|
|
toFloat32Array(nodeEmbedding),
|
|
toFloat32ArrayBatch(neighborEmbeddings),
|
|
toFloat32Array(edgeWeights)
|
|
)
|
|
}
|
|
|
|
toJson() {
|
|
return this._native.toJson()
|
|
}
|
|
|
|
static fromJson(json) {
|
|
const layer = Object.create(RuvectorLayer.prototype)
|
|
layer._native = NativeRuvectorLayer.fromJson(json)
|
|
return layer
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tensor compressor with adaptive level selection
|
|
*/
|
|
class TensorCompress {
|
|
constructor() {
|
|
this._native = new NativeTensorCompress()
|
|
}
|
|
|
|
/**
|
|
* Compress embedding based on access frequency
|
|
* @param {number[] | Float32Array} embedding - Input embedding
|
|
* @param {number} accessFreq - Access frequency (0.0 - 1.0)
|
|
* @returns {string} Compressed JSON
|
|
*/
|
|
compress(embedding, accessFreq) {
|
|
return this._native.compress(toFloat32Array(embedding), accessFreq)
|
|
}
|
|
|
|
/**
|
|
* Compress with explicit compression level
|
|
* @param {number[] | Float32Array} embedding - Input embedding
|
|
* @param {object} level - Compression level config
|
|
* @returns {string} Compressed JSON
|
|
*/
|
|
compressWithLevel(embedding, level) {
|
|
return this._native.compressWithLevel(toFloat32Array(embedding), level)
|
|
}
|
|
|
|
/**
|
|
* Decompress a compressed tensor
|
|
* @param {string} compressedJson - Compressed JSON
|
|
* @returns {Float32Array}
|
|
*/
|
|
decompress(compressedJson) {
|
|
return this._native.decompress(compressedJson)
|
|
}
|
|
}
|
|
|
|
module.exports.RuvectorLayer = RuvectorLayer
|
|
module.exports.TensorCompress = TensorCompress
|
|
module.exports.differentiableSearch = differentiableSearch
|
|
module.exports.hierarchicalForward = hierarchicalForward
|
|
module.exports.getCompressionLevel = getCompressionLevel
|
|
module.exports.init = init
|
|
|
|
// Export conversion helpers for users who want to pre-convert their data
|
|
module.exports.toFloat32Array = toFloat32Array
|
|
module.exports.toFloat32ArrayBatch = toFloat32ArrayBatch
|
|
module.exports.toFloat32ArrayBatchOptimized = toFloat32ArrayBatchOptimized
|
|
|
|
// Also export native versions for advanced users (require Float32Array directly)
|
|
module.exports.NativeRuvectorLayer = NativeRuvectorLayer
|
|
module.exports.NativeTensorCompress = NativeTensorCompress
|
|
module.exports.nativeDifferentiableSearch = nativeDifferentiableSearch
|
|
module.exports.nativeHierarchicalForward = nativeHierarchicalForward
|