Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/rag/examples/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function ensureModels (keys, diskPath) {
if (!model) {
throw new Error(`Unknown model key: ${key}. Available keys: ${Object.keys(RAG_MODELS).join(', ')}`)
}
return { key, ...model, fullPath: path.join(diskPath, model.filename) }
return { key, ...model, fullPath: path.resolve(diskPath, model.filename) }
})

const missing = requested.filter(m => !fs.existsSync(m.fullPath))
Expand Down
14 changes: 14 additions & 0 deletions packages/rag/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@
"NOTICE"
],
"types": "index.d.ts",
"imports": {
"#crypto": {
"bare": "bare-crypto",
"node": "node:crypto",
"react-native": "./src/shims/crypto.js",
"default": "./src/shims/crypto.js"
},
"#fetch": {
"bare": "bare-fetch",
"node": "./src/shims/fetch.js",
"react-native": "./src/shims/fetch.js",
"default": "./src/shims/fetch.js"
}
},
"devDependencies": {
"@qvac/embed-llamacpp": "^0.14.0",
"@qvac/llm-llamacpp": "^0.16.0",
Expand Down
18 changes: 1 addition & 17 deletions packages/rag/src/adapters/database/HyperDBAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,7 @@ const {
} = require('../../utils/helper')
const QvacLogger = require('@qvac/logging')

let qvacCrypto
try {
qvacCrypto = require('crypto')
} catch (e) {
try {
qvacCrypto = require('bare-crypto')
} catch (e2) {
if (typeof global !== 'undefined' && global.crypto && global.crypto.createHash) {
qvacCrypto = global.crypto
} else {
throw new QvacErrorRAG({
code: ERR_CODES.DEPENDENCY_REQUIRED,
adds: 'No crypto implementation found. Please ensure a crypto module is available in your environment.'
})
}
}
}
const qvacCrypto = require('#crypto')

class HyperDBAdapter extends BaseDBAdapter {
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/rag/src/adapters/llm/HttpLlmAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class HttpLlmAdapter extends BaseLlmAdapter {
*/
async _makeHttpRequest (requestBody) {
try {
const fetch = await import('bare-fetch').then(module => module.default || module)
const fetch = await import('#fetch').then(module => module.default || module)

const response = await fetch(this.httpConfig.apiUrl, {
method: this.httpConfig.method,
Expand All @@ -94,7 +94,7 @@ class HttpLlmAdapter extends BaseLlmAdapter {

return response.json()
} catch (error) {
if ((error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') && error.message.includes('bare-fetch')) {
if ((error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') && (error.message.includes('bare-fetch') || error.message.includes('#fetch'))) {
throw new QvacErrorRAG({ code: ERR_CODES.DEPENDENCY_REQUIRED, adds: 'bare-fetch is required for HttpLlmAdapter.', cause: error })
Comment thread
yuranich marked this conversation as resolved.
Outdated
}
throw error
Expand Down
22 changes: 22 additions & 0 deletions packages/rag/src/shims/crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const { QvacErrorRAG, ERR_CODES } = require('../errors')

function ensureCrypto () {
if (typeof globalThis !== 'undefined' && globalThis.crypto && typeof globalThis.crypto.createHash === 'function') {
return globalThis.crypto
}
throw new QvacErrorRAG({
code: ERR_CODES.DEPENDENCY_REQUIRED,
adds: 'No crypto implementation found. Please ensure a crypto module is available in your environment (Bare: bare-crypto; Node: node:crypto; other: provide a Web Crypto-compatible globalThis.crypto).'
})
}

module.exports = new Proxy({}, {
get (_target, prop) {
return ensureCrypto()[prop]
},
has (_target, prop) {
try { return prop in ensureCrypto() } catch { return false }
}
})
20 changes: 20 additions & 0 deletions packages/rag/src/shims/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict'

const { QvacErrorRAG, ERR_CODES } = require('../errors')

function ensureFetch () {
if (typeof globalThis !== 'undefined' && typeof globalThis.fetch === 'function') {
return globalThis.fetch.bind(globalThis)
}
throw new QvacErrorRAG({
code: ERR_CODES.DEPENDENCY_REQUIRED,
adds: 'No fetch implementation found. Please ensure a Fetch-compatible globalThis.fetch is available (Bare: bare-fetch; Node 18+: built-in; other: provide a polyfill).'
})
}

function fetchProxy (...args) {
return ensureFetch()(...args)
}

module.exports = fetchProxy
module.exports.default = fetchProxy
14 changes: 4 additions & 10 deletions packages/rag/src/utils/helper.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
'use strict'

const { QvacErrorRAG, ERR_CODES } = require('../errors')
// Set up crypto polyfill for uuid-random
try {
const crypto = require('bare-crypto')
const crypto = require('#crypto')

if (typeof global !== 'undefined' && !global.crypto) {
global.crypto = crypto
} catch (e2) {
if (typeof global === 'undefined' || (typeof global !== 'undefined' && !global.crypto)) {
throw new QvacErrorRAG({
code: ERR_CODES.DEPENDENCY_REQUIRED,
adds: 'No crypto implementation found. Please ensure a crypto module is available in your environment.'
})
}
}

const uuid = require('uuid-random')

/**
Expand Down
42 changes: 42 additions & 0 deletions packages/rag/test/unit/shim-crypto.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict'

const test = require('brittle')
const cryptoShim = require('../../src/shims/crypto')
const { QvacErrorRAG, ERR_CODES } = require('../../src/errors')

test('crypto shim: throws QvacErrorRAG when no crypto implementation is available', t => {
const original = globalThis.crypto
// Force the shim's resolver to find no implementation.
delete globalThis.crypto

try {
const probe = cryptoShim.createHash
t.fail(`Expected accessing a property on the shim to throw, got ${typeof probe}`)
} catch (err) {
t.ok(err instanceof QvacErrorRAG, 'Error should be instance of QvacErrorRAG')
t.is(err.code, ERR_CODES.DEPENDENCY_REQUIRED, 'Error code should be DEPENDENCY_REQUIRED')
} finally {
if (original !== undefined) globalThis.crypto = original
}
})

test('crypto shim: delegates property access to globalThis.crypto when available', t => {
const original = globalThis.crypto
const stub = {
createHash: () => 'stub',
anything: 'value'
}
globalThis.crypto = stub

try {
t.is(typeof cryptoShim.createHash, 'function', 'createHash should be delegated as a function')
t.is(cryptoShim.createHash(), 'stub', 'createHash invocation should return stubbed value')
t.is(cryptoShim.anything, 'value', 'arbitrary properties should be delegated to the stub')
} finally {
if (original === undefined) {
delete globalThis.crypto
} else {
globalThis.crypto = original
}
}
})
49 changes: 49 additions & 0 deletions packages/rag/test/unit/shim-fetch.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict'

const test = require('brittle')
const fetchShim = require('../../src/shims/fetch')
const { QvacErrorRAG, ERR_CODES } = require('../../src/errors')

test('fetch shim: throws QvacErrorRAG when no fetch implementation is available', async t => {
const original = globalThis.fetch
// Force the shim's resolver to find no implementation.
delete globalThis.fetch

try {
await fetchShim('https://example.test')
t.fail('Expected calling the shim to throw')
} catch (err) {
t.ok(err instanceof QvacErrorRAG, 'Error should be instance of QvacErrorRAG')
t.is(err.code, ERR_CODES.DEPENDENCY_REQUIRED, 'Error code should be DEPENDENCY_REQUIRED')
} finally {
if (original !== undefined) globalThis.fetch = original
}
})

test('fetch shim: delegates calls to globalThis.fetch when available', async t => {
const original = globalThis.fetch
let receivedArgs
globalThis.fetch = async function stub (...args) {
receivedArgs = args
return { ok: true, url: args[0] }
}

try {
const result = await fetchShim('https://example.test', { method: 'GET' })
t.ok(result.ok, 'Proxy should return the stub response')
t.is(result.url, 'https://example.test', 'Proxy should pass through positional args')
t.is(receivedArgs[0], 'https://example.test', 'First arg forwarded to stub')
t.alike(receivedArgs[1], { method: 'GET' }, 'Second arg forwarded to stub')
} finally {
if (original === undefined) {
delete globalThis.fetch
} else {
globalThis.fetch = original
}
}
})

test('fetch shim: exposes a default export that aliases the same function', t => {
t.is(typeof fetchShim, 'function', 'Module export should be a function')
t.is(fetchShim.default, fetchShim, 'default property should reference the same function')
})
Loading