@@ -8,7 +8,7 @@ const os = require('bare-os')
88// Lazily loaded: requiring index.js pulls in the native binding, which isn't
99// needed for the model download/integrity helpers and lets those run in unit
1010// tests without a compiled addon.
11- function getGGMLBert ( ) {
11+ function getGGMLBert ( ) {
1212 return require ( '../../index.js' )
1313}
1414
@@ -217,7 +217,7 @@ let _manifestCache
217217// Loads and caches the model manifest (single source of truth for model URLs +
218218// sha256/bytes integrity). Returns null when absent so callers fall back to the
219219// URL declared in MODEL_CONFIGS.
220- function loadManifest ( manifestPath = DEFAULT_MANIFEST_PATH ) {
220+ function loadManifest ( manifestPath = DEFAULT_MANIFEST_PATH ) {
221221 if ( manifestPath === DEFAULT_MANIFEST_PATH && _manifestCache !== undefined ) {
222222 return _manifestCache
223223 }
@@ -231,13 +231,13 @@ function loadManifest (manifestPath = DEFAULT_MANIFEST_PATH) {
231231 return parsed
232232}
233233
234- function resolveModelEntry ( modelName , { manifest } = { } ) {
234+ function resolveModelEntry ( modelName , { manifest } = { } ) {
235235 const m = manifest !== undefined ? manifest : loadManifest ( )
236236 if ( ! m || ! m . models ) return null
237237 return m . models [ modelName ] || null
238238}
239239
240- function entryHasIntegrity ( entry ) {
240+ function entryHasIntegrity ( entry ) {
241241 if ( ! entry ) return false
242242 const hasSha = typeof entry . sha256 === 'string' && entry . sha256 . length === 64
243243 const hasBytes = Number . isInteger ( entry . bytes )
@@ -246,12 +246,20 @@ function entryHasIntegrity (entry) {
246246
247247// Streaming sha256 via bare-crypto. Returns null when bare-crypto is
248248// unavailable so callers can degrade rather than fail.
249- async function sha256File ( filePath ) {
249+ async function sha256File ( filePath ) {
250250 let crypto
251- try { crypto = require ( 'bare-crypto' ) } catch ( _ ) { return null }
251+ try {
252+ crypto = require ( 'bare-crypto' )
253+ } catch ( _ ) {
254+ return null
255+ }
252256 return await new Promise ( ( resolve , reject ) => {
253257 let hash
254- try { hash = crypto . createHash ( 'sha256' ) } catch ( _ ) { return resolve ( null ) }
258+ try {
259+ hash = crypto . createHash ( 'sha256' )
260+ } catch ( _ ) {
261+ return resolve ( null )
262+ }
255263 const stream = fs . createReadStream ( filePath )
256264 stream . on ( 'data' , ( chunk ) => hash . update ( chunk ) )
257265 stream . on ( 'error' , reject )
@@ -262,7 +270,7 @@ async function sha256File (filePath) {
262270// Verifies a model file against a manifest entry. Byte-length is checked first
263271// (cheap) so a size mismatch fails fast before hashing a multi-GB file.
264272// { ok: true } when it passes (or when no integrity value is pinned yet).
265- async function verifyModelFile ( filePath , entry ) {
273+ async function verifyModelFile ( filePath , entry ) {
266274 let stats
267275 try {
268276 stats = fs . statSync ( filePath )
@@ -279,7 +287,9 @@ async function verifyModelFile (filePath, entry) {
279287 if ( hasSha ) {
280288 const got = await sha256File ( filePath )
281289 if ( got === null ) {
282- console . log ( '[download] WARNING: sha256 pinned for this model but bare-crypto is unavailable — integrity NOT verified' )
290+ console . log (
291+ '[download] WARNING: sha256 pinned for this model but bare-crypto is unavailable — integrity NOT verified'
292+ )
283293 return { ok : true , unverified : true }
284294 }
285295 if ( got !== entry . sha256 . toLowerCase ( ) ) {
@@ -293,8 +303,12 @@ async function verifyModelFile (filePath, entry) {
293303
294304// Counts real download attempts so warm-vs-cold behaviour is unit-testable.
295305let _downloadCount = 0
296- function getDownloadCount ( ) { return _downloadCount }
297- function resetDownloadCount ( ) { _downloadCount = 0 }
306+ function getDownloadCount ( ) {
307+ return _downloadCount
308+ }
309+ function resetDownloadCount ( ) {
310+ _downloadCount = 0
311+ }
298312
299313/**
300314 * Model configurations for testing
@@ -356,10 +370,12 @@ function prestagedModelDir(modelName) {
356370
357371/**
358372 * Ensures the model file exists, downloading it if necessary
359- * @param {string } modelName - The model name to ensure
373+ * @param {Object } opts
374+ * @param {string } opts.modelName - The model name to ensure
360375 * @returns {Promise<[string, string]> } Returns [modelName, modelDir]
361376 */
362- async function ensureModel ( modelName , { modelDir = path . resolve ( __dirname , '../model' ) , manifest, download } = { } ) {
377+ async function ensureModel ( { modelName, modelDir, manifest, download } = { } ) {
378+ const dir = modelDir || path . resolve ( __dirname , '../model' )
363379 const modelConfig = getModelConfig ( modelName )
364380
365381 // Model URL + sha256/bytes come from models.manifest.json (by modelName); the
@@ -373,7 +389,7 @@ async function ensureModel (modelName, { modelDir = path.resolve(__dirname, '../
373389 throw new Error ( `Unknown model: ${ modelName } ` )
374390 }
375391
376- const modelPath = path . join ( modelDir , modelName )
392+ const modelPath = path . join ( dir , modelName )
377393 const doDownload = download || downloadFileWithRetries
378394 const urls = hasManifestUrls ? entry . urls : [ modelConfig . downloadUrl ]
379395 const hasIntegrity = entryHasIntegrity ( entry )
@@ -383,17 +399,27 @@ async function ensureModel (modelName, { modelDir = path.resolve(__dirname, '../
383399 const res = await verifyModelFile ( modelPath , entry )
384400 if ( res . ok ) {
385401 console . log ( `[download] ${ modelName } : cached copy verified, skipping download` )
386- return [ modelName , modelDir ]
402+ return [ modelName , dir ]
387403 }
388- console . log ( `[download] ${ modelName } : cached copy failed integrity (${ res . reason } ); deleting and re-downloading` )
389- try { fs . unlinkSync ( modelPath ) } catch ( _ ) { }
404+ console . log (
405+ `[download] ${ modelName } : cached copy failed integrity (${ res . reason } ); deleting and re-downloading`
406+ )
407+ try {
408+ fs . unlinkSync ( modelPath )
409+ } catch ( _ ) { }
390410 } else {
391411 const stat = fs . statSync ( modelPath )
392- if ( stat . size > 0 ) {
393- return [ modelName , modelDir ]
412+ if ( stat . size === 0 ) {
413+ console . log ( `[download] Removing zero-byte cached file: ${ modelName } ` )
414+ fs . unlinkSync ( modelPath )
415+ } else {
416+ if ( entry ) {
417+ console . log (
418+ `[download] ${ modelName } : no sha256/bytes pinned in manifest — integrity check SKIPPED (run scripts/generate-model-manifest.mjs to pin)`
419+ )
420+ }
421+ return [ modelName , dir ]
394422 }
395- console . log ( `[download] Removing zero-byte cached file: ${ modelName } ` )
396- fs . unlinkSync ( modelPath )
397423 }
398424 }
399425
@@ -403,18 +429,18 @@ async function ensureModel (modelName, { modelDir = path.resolve(__dirname, '../
403429 // load() writes sibling files next to the model (e.g. openclCacheDir).
404430 const staged = prestagedModelDir ( modelName )
405431 if ( staged ) {
406- fs . mkdirSync ( modelDir , { recursive : true } )
432+ fs . mkdirSync ( dir , { recursive : true } )
407433 console . log ( `[prestage] Using pre-staged model ${ modelName } (copying into writable modelDir)` )
408434 fs . copyFileSync ( path . join ( staged , modelName ) , modelPath )
409435 const stat = fs . statSync ( modelPath )
410436 if ( stat . size === 0 ) {
411437 fs . unlinkSync ( modelPath )
412438 throw new Error ( `[prestage] copied model ${ modelName } is empty` )
413439 }
414- return [ modelName , modelDir ]
440+ return [ modelName , dir ]
415441 }
416442
417- fs . mkdirSync ( modelDir , { recursive : true } )
443+ fs . mkdirSync ( dir , { recursive : true } )
418444 console . log ( `[download] Downloading test model: ${ modelName } ...` )
419445 _downloadCount ++
420446
@@ -423,15 +449,19 @@ async function ensureModel (modelName, { modelDir = path.resolve(__dirname, '../
423449 if ( hasIntegrity ) {
424450 const res = await verifyModelFile ( modelPath , entry )
425451 if ( ! res . ok ) {
426- try { fs . unlinkSync ( modelPath ) } catch ( _ ) { }
427- throw new Error ( `[download] ${ modelName } : freshly downloaded file failed integrity: ${ res . reason } ` )
452+ try {
453+ fs . unlinkSync ( modelPath )
454+ } catch ( _ ) { }
455+ throw new Error (
456+ `[download] ${ modelName } : freshly downloaded file failed integrity: ${ res . reason } `
457+ )
428458 }
429459 }
430460
431461 const stat = fs . statSync ( modelPath )
432462 console . log ( `[download] Model ready: ${ ( stat . size / 1024 / 1024 ) . toFixed ( 1 ) } MB` )
433463
434- return [ modelName , modelDir ]
464+ return [ modelName , dir ]
435465}
436466
437467/**
@@ -471,7 +501,7 @@ async function createEmbeddingsTestInstance(
471501 gpuLayers = null ,
472502 batchSize = '1024'
473503) {
474- const [ , modelDir ] = await ensureModel ( modelName )
504+ const [ , modelDir ] = await ensureModel ( { modelName } )
475505 const modelPath = path . join ( modelDir , modelName )
476506
477507 t . ok ( fs . existsSync ( modelPath ) , 'Model file should exist' )
0 commit comments