/** * Diagnose a GGUF on disk after load failure (magic / size / sha256). */ import b4a from 'b4a' /** @type {Record} */ export const BARE_OS_QVAC_KNOWN_GGUF = { 'Qwen3-0.6B-Q4_0.gguf': { size: 382156480, sha256: '33bcc57074ec7b6eada5a90651ee546ec0c2b271002c22baf9f1b2dd1e8f75cb' }, 'Qwen3-1.7B-Q4_0.gguf': { size: 1056782912, sha256: 'c876f159707a4e4f70e045106c69db15bfc935a4981706fd4f65c6e7ea1e81c5' } } /** * @param {string} filePath * @returns {Promise} */ export async function bareOsQvacDiagnoseGguf(filePath) { const p = String(filePath || '').trim() if (!p) return 'gguf: (no path)' /** @type {string[]} */ const lines = ['gguf path: ' + p] try { const fs = await import('bare-fs') const fsp = fs.promises || fs const st = await fsp.stat(p) lines.push('size: ' + st.size + ' bytes') const base = p.split(/[/\\]/).pop() || '' const known = BARE_OS_QVAC_KNOWN_GGUF[base.replace(/^[0-9a-f]+_/i, '')] || Object.entries(BARE_OS_QVAC_KNOWN_GGUF).find(([name]) => base.endsWith(name) )?.[1] if (known) { lines.push( 'expectedSize: ' + known.size + (st.size === known.size ? ' (ok)' : ' (MISMATCH)') ) } const buf = new Uint8Array(4) let fh try { if (typeof fsp.open === 'function') { fh = await fsp.open(p, 'r') if (typeof fh.read === 'function') { await fh.read(buf, 0, 4, 0) } else if (typeof fs.read === 'function' && fh.fd != null) { await new Promise((resolve, reject) => { fs.read(fh.fd, buf, 0, 4, 0, (err) => (err ? reject(err) : resolve())) }) } } else { const fd = fs.openSync(p, 'r') fs.readSync(fd, buf, 0, 4, 0) fs.closeSync(fd) } } finally { try { if (fh && typeof fh.close === 'function') await fh.close() } catch { /* ignore */ } } const magic = b4a.toString(buf, 'utf8') lines.push('magic: ' + JSON.stringify(magic) + (magic === 'GGUF' ? ' (ok)' : ' (BAD)')) if (known && st.size === known.size && st.size <= 2 * 1024 * 1024 * 1024) { try { const crypto = await import('bare-crypto') const createHash = crypto.createHash || crypto.default?.createHash if (typeof createHash === 'function') { const hash = createHash('sha256') const stream = fs.createReadStream ? fs.createReadStream(p) : null if (stream) { await new Promise((resolve, reject) => { stream.on('data', (c) => hash.update(c)) stream.on('error', reject) stream.on('end', resolve) }) const dig = hash.digest('hex') lines.push( 'sha256: ' + dig + (dig === known.sha256 ? ' (ok)' : ' (MISMATCH want ' + known.sha256 + ')') ) } } } catch (e) { lines.push('sha256: (skip ' + String(e && e.message ? e.message : e) + ')') } } } catch (e) { lines.push('diagnose error: ' + String(e && e.message ? e.message : e)) } return lines.join('; ') } /** * Extract a GGUF path from a QVAC / llama error string. * @param {string} msg * @returns {string} */ export function bareOsQvacExtractGgufPath(msg) { const m = /failed to load model '([^']+\.gguf)'/i.exec(String(msg || '')) if (m) return m[1] const m2 = /(\/[^\s']+\.gguf)/i.exec(String(msg || '')) return m2 ? m2[1] : '' }