DMCRA Lab: elastic level-set quantisation over real GGUF weights, measured end to end
Browse files
gguf.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* GGUF reader for the HDC engine — browser-native, no dependencies, no server.
|
| 2 |
+
*
|
| 3 |
+
* Same container spec as templates/static/gguf/gguf.js (GGUF v1/v2/v3; v1 uses 32-bit KV
|
| 4 |
+
* string lengths, v2+ use 64-bit) and the same type tables, which mirror
|
| 5 |
+
* templates/gguf_util.py so both implementations agree on every file.
|
| 6 |
+
*
|
| 7 |
+
* What this copy adds, and why the HDC engine needs its own reader:
|
| 8 |
+
*
|
| 9 |
+
* 1. REAL VOCABULARIES. The metadata block carries tokenizer.ggml.tokens — the model's own
|
| 10 |
+
* vocabulary (512 entries for stories260K, 151,936 for Qwen2.5-0.5B). The toolkit reader
|
| 11 |
+
* truncates every array to 16 entries to bound memory, which is right for an inspector
|
| 12 |
+
* and useless for generation. Here, arrays named in `fullArrayKeys` are kept in full.
|
| 13 |
+
*
|
| 14 |
+
* 2. BOUNDED PREFIXES. A page that streams a multi-GB model over HTTP only ever holds a
|
| 15 |
+
* prefix of it, so EOF can arrive in the middle of the tensor index. That is not an
|
| 16 |
+
* error: the metadata block precedes the index, so all KVs are already parsed and the
|
| 17 |
+
* result is returned with truncated:true.
|
| 18 |
+
*
|
| 19 |
+
* 3. ONE DECODER. 151,936 short strings decoder-per-string is pathologically slow; a single
|
| 20 |
+
* TextDecoder instance is reused for every string in the file.
|
| 21 |
+
*/
|
| 22 |
+
(function (root) {
|
| 23 |
+
"use strict";
|
| 24 |
+
|
| 25 |
+
const MAGIC = 0x46554747; // "GGUF" read as a little-endian u32 (0x47,0x47,0x55,0x46)
|
| 26 |
+
|
| 27 |
+
const GGML_TYPES = {
|
| 28 |
+
0: "F32", 1: "F16", 2: "Q4_0", 3: "Q4_1", 6: "Q5_0", 7: "Q5_1",
|
| 29 |
+
8: "Q8_0", 9: "Q8_1", 10: "Q2_K", 11: "Q3_K", 12: "Q4_K", 13: "Q5_K",
|
| 30 |
+
14: "Q6_K", 15: "Q8_K", 16: "IQ2_XXS", 17: "IQ2_XS", 18: "IQ3_XXS",
|
| 31 |
+
19: "IQ1_S", 20: "IQ4_NL", 21: "IQ3_S", 22: "IQ2_S", 23: "IQ4_XS",
|
| 32 |
+
24: "I8", 25: "I16", 26: "I32", 27: "I64", 28: "F64", 29: "IQ1_M",
|
| 33 |
+
30: "BF16", 34: "TQ1_0", 35: "TQ2_0", 39: "MXFP4"
|
| 34 |
+
};
|
| 35 |
+
|
| 36 |
+
const BITS = {
|
| 37 |
+
F32: 32, F16: 16, BF16: 16, F64: 64,
|
| 38 |
+
"Q8_0": 8.5, "Q8_1": 8.5, I8: 8, I16: 16, I32: 32, I64: 64,
|
| 39 |
+
"Q6_K": 6.5625, "Q5_K": 5.5, "Q5_0": 5.5, "Q5_1": 6.0,
|
| 40 |
+
"Q4_K": 4.5, "Q4_0": 4.5, "Q4_1": 5.0, "IQ4_NL": 4.5, "IQ4_XS": 4.25,
|
| 41 |
+
"Q3_K": 3.4375, "IQ3_XXS": 3.06, "IQ3_S": 3.44,
|
| 42 |
+
"Q2_K": 2.5625, "IQ2_XXS": 2.06, "IQ2_XS": 2.31, "IQ2_S": 2.5,
|
| 43 |
+
"IQ1_S": 1.56, "IQ1_M": 1.75, "TQ1_0": 1.69, "TQ2_0": 2.06
|
| 44 |
+
};
|
| 45 |
+
|
| 46 |
+
const T_NAME = { 0: "u8", 1: "i8", 2: "u16", 3: "i16", 4: "u32", 5: "i32", 6: "f32",
|
| 47 |
+
7: "bool", 8: "string", 9: "array", 10: "u64", 11: "i64", 12: "f64" };
|
| 48 |
+
|
| 49 |
+
// Arrays these keys name are the ones generation actually consumes: keep them whole.
|
| 50 |
+
const DEFAULT_FULL = ["tokenizer.ggml.tokens", "tokenizer.ggml.merges",
|
| 51 |
+
"tokenizer.ggml.token_type", "tokenizer.ggml.scores"];
|
| 52 |
+
|
| 53 |
+
const MAX_ARRAY_KEEP = 16; // bound memory on unrelated big arrays, as the toolkit does
|
| 54 |
+
const MAX_FULL_ITEMS = 400000;
|
| 55 |
+
|
| 56 |
+
// tokenizer.ggml.token_type values (llama.cpp)
|
| 57 |
+
const TOKEN_TYPE = { 1: "NORMAL", 2: "UNKNOWN", 3: "CONTROL", 4: "USER_DEFINED",
|
| 58 |
+
5: "UNUSED", 6: "BYTE" };
|
| 59 |
+
|
| 60 |
+
const MARKER = /^\u2026?\.{3} (\d+) items total$/;
|
| 61 |
+
|
| 62 |
+
class GgufError extends Error {}
|
| 63 |
+
|
| 64 |
+
class Reader {
|
| 65 |
+
constructor(buffer, opts) {
|
| 66 |
+
this.view = new DataView(buffer);
|
| 67 |
+
this.bytes = new Uint8Array(buffer);
|
| 68 |
+
this.pos = 0;
|
| 69 |
+
this.sizeBytes = buffer.byteLength;
|
| 70 |
+
this.decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8") : null;
|
| 71 |
+
this.maxArrayItems = opts.maxArrayItems;
|
| 72 |
+
this.maxFullItems = opts.maxFullItems;
|
| 73 |
+
// smallest legal header: magic 4 + version 4 + tensor_count 8 + kv_count 8
|
| 74 |
+
if (buffer.byteLength < 24) throw new GgufError("buffer too small for a GGUF header");
|
| 75 |
+
this.magic = this.view.getUint32(0, true);
|
| 76 |
+
if (this.magic !== MAGIC) throw new GgufError("not a GGUF file (bad magic)");
|
| 77 |
+
this.pos = 4;
|
| 78 |
+
this.version = this.u32();
|
| 79 |
+
if (this.version !== 1 && this.version !== 2 && this.version !== 3) {
|
| 80 |
+
throw new GgufError("unsupported GGUF version " + this.version);
|
| 81 |
+
}
|
| 82 |
+
this.nTensors = this.u64();
|
| 83 |
+
this.nKv = this.u64();
|
| 84 |
+
}
|
| 85 |
+
need(n) {
|
| 86 |
+
if (this.pos + n > this.sizeBytes) throw new GgufError("unexpected end of file");
|
| 87 |
+
}
|
| 88 |
+
u8() { this.need(1); return this.view.getUint8(this.pos++); }
|
| 89 |
+
i8() { this.need(1); return this.view.getInt8(this.pos++); }
|
| 90 |
+
u16() { this.need(2); const v = this.view.getUint16(this.pos, true); this.pos += 2; return v; }
|
| 91 |
+
i16() { this.need(2); const v = this.view.getInt16(this.pos, true); this.pos += 2; return v; }
|
| 92 |
+
u32() { this.need(4); const v = this.view.getUint32(this.pos, true); this.pos += 4; return v; }
|
| 93 |
+
i32() { this.need(4); const v = this.view.getInt32(this.pos, true); this.pos += 4; return v; }
|
| 94 |
+
f32() { this.need(4); const v = this.view.getFloat32(this.pos, true); this.pos += 4; return v; }
|
| 95 |
+
f64() { this.need(8); const v = this.view.getFloat64(this.pos, true); this.pos += 8; return v; }
|
| 96 |
+
u64() {
|
| 97 |
+
this.need(8);
|
| 98 |
+
const v = this.view.getBigUint64(this.pos, true);
|
| 99 |
+
this.pos += 8;
|
| 100 |
+
return v <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(v) : v.toString();
|
| 101 |
+
}
|
| 102 |
+
i64() {
|
| 103 |
+
this.need(8);
|
| 104 |
+
const v = this.view.getBigInt64(this.pos, true);
|
| 105 |
+
this.pos += 8;
|
| 106 |
+
return v <= BigInt(Number.MAX_SAFE_INTEGER) && v >= BigInt(Number.MIN_SAFE_INTEGER)
|
| 107 |
+
? Number(v) : v.toString();
|
| 108 |
+
}
|
| 109 |
+
bool() { return this.u8() !== 0; }
|
| 110 |
+
str() {
|
| 111 |
+
const len = this.version === 1 ? this.u32() : this.u64();
|
| 112 |
+
const n = Number(len);
|
| 113 |
+
this.need(n);
|
| 114 |
+
const at = this.pos;
|
| 115 |
+
this.pos += n;
|
| 116 |
+
const slice = this.bytes.subarray(at, at + n);
|
| 117 |
+
if (this.decoder) return this.decoder.decode(slice);
|
| 118 |
+
let s = "";
|
| 119 |
+
for (let i = 0; i < slice.length; i++) s += String.fromCharCode(slice[i]);
|
| 120 |
+
return s;
|
| 121 |
+
}
|
| 122 |
+
/* mode: "keep" -> at most maxArrayItems entries, then a "... N items total" marker
|
| 123 |
+
* "full" -> every entry (up to maxFullItems), for the tokenizer arrays
|
| 124 |
+
* "drop" -> nested array inside an array: consume it, keep only a marker */
|
| 125 |
+
value(type, mode) {
|
| 126 |
+
switch (type) {
|
| 127 |
+
case 0: return this.u8();
|
| 128 |
+
case 1: return this.i8();
|
| 129 |
+
case 2: return this.u16();
|
| 130 |
+
case 3: return this.i16();
|
| 131 |
+
case 4: return this.u32();
|
| 132 |
+
case 5: return this.i32();
|
| 133 |
+
case 6: return this.f32();
|
| 134 |
+
case 7: return this.bool();
|
| 135 |
+
case 8: return this.str();
|
| 136 |
+
case 9: {
|
| 137 |
+
const elem = this.u32();
|
| 138 |
+
const count = Number(this.u64());
|
| 139 |
+
if (mode === "drop") {
|
| 140 |
+
for (let i = 0; i < count; i++) this.value(elem, "drop");
|
| 141 |
+
return "<" + count + " items>";
|
| 142 |
+
}
|
| 143 |
+
const limit = Math.min(count, mode === "full" ? this.maxFullItems : this.maxArrayItems);
|
| 144 |
+
const vals = new Array(limit);
|
| 145 |
+
for (let i = 0; i < count; i++) {
|
| 146 |
+
const v = this.value(elem, "drop");
|
| 147 |
+
if (i < limit) vals[i] = v;
|
| 148 |
+
}
|
| 149 |
+
if (count > limit) vals.push("... " + count + " items total");
|
| 150 |
+
return vals;
|
| 151 |
+
}
|
| 152 |
+
case 10: return this.u64();
|
| 153 |
+
case 11: return this.i64();
|
| 154 |
+
case 12: return this.f64();
|
| 155 |
+
default: throw new GgufError("unknown metadata type " + type);
|
| 156 |
+
}
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function wantFull(key, fullArrayKeys) {
|
| 161 |
+
for (let i = 0; i < fullArrayKeys.length; i++) {
|
| 162 |
+
const k = fullArrayKeys[i];
|
| 163 |
+
if (k === key) return true;
|
| 164 |
+
if (k.charAt(k.length - 1) === "*" && key.indexOf(k.slice(0, -1)) === 0) return true;
|
| 165 |
+
}
|
| 166 |
+
return false;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
function parse(buffer, opts) {
|
| 170 |
+
opts = opts || {};
|
| 171 |
+
const maxArrayItems = opts.maxArrayItems === undefined ? MAX_ARRAY_KEEP : opts.maxArrayItems;
|
| 172 |
+
const maxFullItems = opts.maxFullItems === undefined ? MAX_FULL_ITEMS : opts.maxFullItems;
|
| 173 |
+
const fullArrayKeys = opts.fullArrayKeys || DEFAULT_FULL;
|
| 174 |
+
const metadataOnly = !!opts.metadataOnly;
|
| 175 |
+
|
| 176 |
+
const r = new Reader(buffer, { maxArrayItems: maxArrayItems, maxFullItems: maxFullItems });
|
| 177 |
+
|
| 178 |
+
// The metadata block comes first, so a prefix stream parses it completely. A truncation
|
| 179 |
+
// *inside* the KV block is genuinely unusable and is allowed to throw.
|
| 180 |
+
const kv = [];
|
| 181 |
+
let declared = {};
|
| 182 |
+
for (let i = 0; i < r.nKv; i++) {
|
| 183 |
+
const key = r.str();
|
| 184 |
+
const type = r.u32();
|
| 185 |
+
const mode = wantFull(key, fullArrayKeys) ? "full" : "keep";
|
| 186 |
+
const value = r.value(type, mode);
|
| 187 |
+
kv.push({ key: key, type: T_NAME[type] || String(type), value: value });
|
| 188 |
+
if (Array.isArray(value)) {
|
| 189 |
+
const m = typeof value[value.length - 1] === "string" ? MARKER.exec(value[value.length - 1]) : null;
|
| 190 |
+
if (m) declared[key] = Number(m[1]);
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
const tensors = [];
|
| 195 |
+
let truncated = false;
|
| 196 |
+
if (!metadataOnly) {
|
| 197 |
+
for (let i = 0; i < r.nTensors; i++) {
|
| 198 |
+
try {
|
| 199 |
+
const name = r.str();
|
| 200 |
+
const nd = r.u32();
|
| 201 |
+
const dims = [];
|
| 202 |
+
for (let d = 0; d < nd; d++) dims.push(r.u64());
|
| 203 |
+
const typeId = r.u32();
|
| 204 |
+
const offset = r.u64();
|
| 205 |
+
let params = 1;
|
| 206 |
+
for (let j = 0; j < dims.length; j++) params *= Number(dims[j]);
|
| 207 |
+
tensors.push({ name: name, dims: dims, type: GGML_TYPES[typeId] || String(typeId),
|
| 208 |
+
typeId: typeId, offset: offset, params: params });
|
| 209 |
+
} catch (e) {
|
| 210 |
+
if (!(e instanceof GgufError)) throw e;
|
| 211 |
+
truncated = true; // a prefix buffer ends mid-index; the KVs above are already good
|
| 212 |
+
break;
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
return { version: r.version, nTensors: r.nTensors, nKv: r.nKv, kv: kv, tensors: tensors,
|
| 218 |
+
sizeBytes: r.sizeBytes, truncated: truncated, headerBytes: r.pos,
|
| 219 |
+
declaredArraySizes: declared };
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
/* Pull a string/number array out of a KV list, reporting the declared size when the reader
|
| 223 |
+
* had to truncate it so the caller can tell "512 tokens" from "first 16 of 151,936". */
|
| 224 |
+
function arrayOf(kvValue) {
|
| 225 |
+
if (!Array.isArray(kvValue)) return { items: [], declared: 0, truncated: false };
|
| 226 |
+
const last = kvValue.length ? kvValue[kvValue.length - 1] : null;
|
| 227 |
+
const m = typeof last === "string" ? MARKER.exec(last) : null;
|
| 228 |
+
if (m) return { items: kvValue.slice(0, kvValue.length - 1), declared: Number(m[1]), truncated: true };
|
| 229 |
+
return { items: kvValue, declared: kvValue.length, truncated: false };
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
function vocab(header) {
|
| 233 |
+
const d = {};
|
| 234 |
+
for (let i = 0; i < header.kv.length; i++) d[header.kv[i].key] = header.kv[i].value;
|
| 235 |
+
const toks = arrayOf(d["tokenizer.ggml.tokens"]);
|
| 236 |
+
const merg = arrayOf(d["tokenizer.ggml.merges"]);
|
| 237 |
+
const ttyp = arrayOf(d["tokenizer.ggml.token_type"]);
|
| 238 |
+
const scor = arrayOf(d["tokenizer.ggml.scores"]);
|
| 239 |
+
const num = function (k) { const v = d[k]; return typeof v === "number" ? v : null; };
|
| 240 |
+
const str = function (k) { const v = d[k]; return typeof v === "string" ? v : null; };
|
| 241 |
+
return {
|
| 242 |
+
tokens: toks.items,
|
| 243 |
+
merges: merg.items,
|
| 244 |
+
tokenTypes: ttyp.items,
|
| 245 |
+
scores: scor.items,
|
| 246 |
+
model: str("tokenizer.ggml.model"),
|
| 247 |
+
pre: str("tokenizer.ggml.pre"),
|
| 248 |
+
architecture: str("general.architecture"),
|
| 249 |
+
name: str("general.name"),
|
| 250 |
+
bosId: num("tokenizer.ggml.bos_token_id"),
|
| 251 |
+
eosId: num("tokenizer.ggml.eos_token_id"),
|
| 252 |
+
unkId: num("tokenizer.ggml.unknown_token_id"),
|
| 253 |
+
padId: num("tokenizer.ggml.padding_token_id"),
|
| 254 |
+
addBos: d["tokenizer.ggml.add_bos_token"] === true,
|
| 255 |
+
vocabSize: toks.items.length,
|
| 256 |
+
declaredVocabSize: toks.declared,
|
| 257 |
+
mergeCount: merg.items.length,
|
| 258 |
+
declaredMergeCount: merg.declared,
|
| 259 |
+
truncated: toks.truncated || merg.truncated
|
| 260 |
+
};
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
/* Human-readable token kinds for a handful of ids — used by the UI to show that the
|
| 264 |
+
* vocabulary really came from the model (BPE pieces, byte fallbacks, control tokens). */
|
| 265 |
+
function tokenKind(v, id) {
|
| 266 |
+
const t = v && v.tokenTypes ? v.tokenTypes[id] : undefined;
|
| 267 |
+
return TOKEN_TYPE[t] || (typeof t === "number" ? String(t) : "?");
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
function quantHistogram(tensors) {
|
| 271 |
+
const h = {};
|
| 272 |
+
for (let i = 0; i < tensors.length; i++) h[tensors[i].type] = (h[tensors[i].type] || 0) + 1;
|
| 273 |
+
return Object.entries(h).sort(function (a, b) { return b[1] - a[1]; });
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
function fmtBytes(n) {
|
| 277 |
+
if (n === null || n === undefined) return "?";
|
| 278 |
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
| 279 |
+
let i = 0, v = n;
|
| 280 |
+
while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; }
|
| 281 |
+
return (i === 0 ? v.toFixed(0) : v.toFixed(2)) + " " + units[i];
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
function estimateSizes(nParams, overhead) {
|
| 285 |
+
overhead = overhead || 0;
|
| 286 |
+
return Object.entries(BITS)
|
| 287 |
+
.map(function (e) {
|
| 288 |
+
return { quant: e[0], bits: e[1], bytes: Math.round(nParams * e[1] / 8 * (1 + overhead)) };
|
| 289 |
+
})
|
| 290 |
+
.sort(function (a, b) { return a.bits - b.bits; });
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
const api = { parse: parse, vocab: vocab, arrayOf: arrayOf, tokenKind: tokenKind,
|
| 294 |
+
quantHistogram: quantHistogram, fmtBytes: fmtBytes, estimateSizes: estimateSizes,
|
| 295 |
+
GGML_TYPES: GGML_TYPES, BITS: BITS, T_NAME: T_NAME, TOKEN_TYPE: TOKEN_TYPE,
|
| 296 |
+
DEFAULT_FULL: DEFAULT_FULL, GgufError: GgufError };
|
| 297 |
+
|
| 298 |
+
if (typeof module !== "undefined" && module.exports) module.exports = api;
|
| 299 |
+
root.HDCGGUF = api;
|
| 300 |
+
})(typeof globalThis !== "undefined" ? globalThis : this);
|