@tryhamster/gerbil 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/browser/index.d.ts +8 -0
  2. package/dist/browser/index.d.ts.map +1 -1
  3. package/dist/cli.mjs +7 -7
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/frameworks/express.d.mts +1 -1
  6. package/dist/frameworks/express.mjs +1 -1
  7. package/dist/frameworks/fastify.d.mts +1 -1
  8. package/dist/frameworks/fastify.mjs +1 -1
  9. package/dist/frameworks/hono.d.mts +1 -1
  10. package/dist/frameworks/hono.mjs +1 -1
  11. package/dist/frameworks/next.d.mts +3 -3
  12. package/dist/frameworks/next.mjs +1 -1
  13. package/dist/frameworks/react.d.mts +1 -1
  14. package/dist/frameworks/trpc.d.mts +1 -1
  15. package/dist/frameworks/trpc.mjs +1 -1
  16. package/dist/gerbil-BFk5jV0h.mjs +4 -0
  17. package/dist/{gerbil-CJjj7BD_.mjs → gerbil-Cgmb4Dit.mjs} +38 -2
  18. package/dist/gerbil-Cgmb4Dit.mjs.map +1 -0
  19. package/dist/{gerbil-D7gfxZXW.d.mts → gerbil-DU1aRO6v.d.mts} +24 -3
  20. package/dist/gerbil-DU1aRO6v.d.mts.map +1 -0
  21. package/dist/gpu/hooks.d.mts +1 -1
  22. package/dist/gpu/index.d.mts +2 -2
  23. package/dist/gpu/index.mjs +4 -4
  24. package/dist/{gpu-DOK8RgIN.mjs → gpu-kQVLpV3n.mjs} +2 -2
  25. package/dist/{gpu-DOK8RgIN.mjs.map → gpu-kQVLpV3n.mjs.map} +1 -1
  26. package/dist/{index-HgdjgsLb.d.mts → index-ElJKy9i9.d.mts} +118 -25
  27. package/dist/index-ElJKy9i9.d.mts.map +1 -0
  28. package/dist/index.d.mts +3 -3
  29. package/dist/index.mjs +4 -4
  30. package/dist/integrations/ai-sdk.d.mts +1 -1
  31. package/dist/integrations/ai-sdk.mjs +1 -1
  32. package/dist/integrations/langchain.d.mts +1 -1
  33. package/dist/integrations/langchain.mjs +1 -1
  34. package/dist/integrations/llamaindex.d.mts +1 -1
  35. package/dist/integrations/llamaindex.mjs +1 -1
  36. package/dist/integrations/mcp.d.mts +3 -3
  37. package/dist/integrations/mcp.mjs +4 -4
  38. package/dist/{mcp-DaQJ2bie.mjs → mcp-DTusP2m5.mjs} +3 -3
  39. package/dist/{mcp-DaQJ2bie.mjs.map → mcp-DTusP2m5.mjs.map} +1 -1
  40. package/dist/{moonshine-stt-Teic6SbK.mjs → moonshine-stt-9wc6t11v.mjs} +340 -126
  41. package/dist/moonshine-stt-9wc6t11v.mjs.map +1 -0
  42. package/dist/moonshine-stt-B7rDn6Q9.mjs +4 -0
  43. package/dist/{one-liner-BnBYesLF.mjs → one-liner-Bk5x7gYH.mjs} +2 -2
  44. package/dist/{one-liner-BnBYesLF.mjs.map → one-liner-Bk5x7gYH.mjs.map} +1 -1
  45. package/dist/{repl-DMur1t4G.mjs → repl-DV6l-jT8.mjs} +3 -3
  46. package/dist/skills/index.d.mts +3 -3
  47. package/dist/skills/index.mjs +3 -3
  48. package/dist/{skills-BfJI3GNK.mjs → skills-BhcwnL2l.mjs} +2 -2
  49. package/dist/{skills-BfJI3GNK.mjs.map → skills-BhcwnL2l.mjs.map} +1 -1
  50. package/dist/tune/index.mjs +1 -1
  51. package/dist/{types-DW-OBuAC.d.mts → types-BzbBDoaP.d.mts} +9 -1
  52. package/dist/types-BzbBDoaP.d.mts.map +1 -0
  53. package/package.json +1 -1
  54. package/dist/gerbil-BKCknsZ6.mjs +0 -4
  55. package/dist/gerbil-CJjj7BD_.mjs.map +0 -1
  56. package/dist/gerbil-D7gfxZXW.d.mts.map +0 -1
  57. package/dist/index-HgdjgsLb.d.mts.map +0 -1
  58. package/dist/moonshine-stt-CrTu77ps.mjs +0 -4
  59. package/dist/moonshine-stt-Teic6SbK.mjs.map +0 -1
  60. package/dist/types-DW-OBuAC.d.mts.map +0 -1
@@ -10961,6 +10961,330 @@ function idbCacheAvailable() {
10961
10961
  return idbAvailable();
10962
10962
  }
10963
10963
 
10964
+ //#endregion
10965
+ //#region src/gpu/safetensors.ts
10966
+ /**
10967
+ * Parse safetensors header from an ArrayBuffer.
10968
+ *
10969
+ * Can be called with just the header bytes (for streaming — parse header first,
10970
+ * then fetch tensor data by offset) or with the entire file.
10971
+ */
10972
+ function parseSafetensorsHeader(buffer) {
10973
+ if (buffer.byteLength < 8) throw new Error(`Buffer too small to contain safetensors header length (need 8 bytes, got ${buffer.byteLength}).`);
10974
+ const view = new DataView(buffer);
10975
+ const headerLength = Number(view.getBigUint64(0, true));
10976
+ if (headerLength > buffer.byteLength - 8) throw new Error(`Safetensors header length (${headerLength}) exceeds buffer size (${buffer.byteLength - 8}). Pass at least the first ${headerLength + 8} bytes.`);
10977
+ const headerBytes = new Uint8Array(buffer, 8, headerLength);
10978
+ const headerStr = new TextDecoder().decode(headerBytes);
10979
+ const header = JSON.parse(headerStr);
10980
+ const dataStart = 8 + headerLength;
10981
+ const entries = [];
10982
+ let metadata = null;
10983
+ for (const [name, info] of Object.entries(header)) {
10984
+ if (name === "__metadata__") {
10985
+ metadata = info;
10986
+ continue;
10987
+ }
10988
+ const { dtype, shape, data_offsets } = info;
10989
+ entries.push({
10990
+ name,
10991
+ dtype,
10992
+ shape,
10993
+ dataOffset: data_offsets[0],
10994
+ dataLength: data_offsets[1] - data_offsets[0]
10995
+ });
10996
+ }
10997
+ entries.sort((a, b) => a.dataOffset - b.dataOffset);
10998
+ return {
10999
+ headerLength,
11000
+ dataStart,
11001
+ entries,
11002
+ metadata
11003
+ };
11004
+ }
11005
+ /** Byte alignment required for each dtype. */
11006
+ function dtypeAlignment(dtype) {
11007
+ switch (dtype) {
11008
+ case "F64":
11009
+ case "I64":
11010
+ case "U64": return 8;
11011
+ case "F32":
11012
+ case "I32":
11013
+ case "U32": return 4;
11014
+ case "F16":
11015
+ case "BF16":
11016
+ case "I16":
11017
+ case "U16": return 2;
11018
+ case "I8":
11019
+ case "U8":
11020
+ case "BOOL": return 1;
11021
+ default: return 1;
11022
+ }
11023
+ }
11024
+ /**
11025
+ * Create a typed array for the given dtype.
11026
+ *
11027
+ * If `offset` is aligned to the element size, returns a zero-copy view
11028
+ * into `buffer`. Otherwise, copies the relevant slice into a new
11029
+ * properly-aligned ArrayBuffer.
11030
+ */
11031
+ function makeTypedView(buffer, offset, byteLength, dtype) {
11032
+ const aligned = offset % dtypeAlignment(dtype) === 0;
11033
+ const src = aligned ? buffer : buffer.slice(offset, offset + byteLength);
11034
+ const base = aligned ? offset : 0;
11035
+ switch (dtype) {
11036
+ case "F32": return new Float32Array(src, base, byteLength / 4);
11037
+ case "F16": return new Uint16Array(src, base, byteLength / 2);
11038
+ case "BF16": return new Uint16Array(src, base, byteLength / 2);
11039
+ case "I32": return new Int32Array(src, base, byteLength / 4);
11040
+ case "U32": return new Uint32Array(src, base, byteLength / 4);
11041
+ case "I8": return new Int8Array(src, base, byteLength);
11042
+ case "U8": return new Uint8Array(src, base, byteLength);
11043
+ case "I16": return new Int16Array(src, base, byteLength / 2);
11044
+ case "U16": return new Uint16Array(src, base, byteLength / 2);
11045
+ case "F64": return new Float64Array(src, base, byteLength / 8);
11046
+ case "I64": return new BigInt64Array(src, base, byteLength / 8);
11047
+ case "U64": return new BigUint64Array(src, base, byteLength / 8);
11048
+ case "BOOL": return new Uint8Array(src, base, byteLength);
11049
+ default: throw new Error(`Unsupported safetensors dtype: ${dtype}`);
11050
+ }
11051
+ }
11052
+ /**
11053
+ * Get a typed array view for a tensor entry from a complete safetensors buffer.
11054
+ * Zero-copy when the byte offset is properly aligned; copies otherwise.
11055
+ */
11056
+ function getTensorData(buffer, file, entry) {
11057
+ return makeTypedView(buffer, file.dataStart + entry.dataOffset, entry.dataLength, entry.dtype);
11058
+ }
11059
+ /**
11060
+ * Convert BF16 (bfloat16) data to F32.
11061
+ * BF16 is the upper 16 bits of an IEEE 754 float32, so conversion is a simple left-shift.
11062
+ */
11063
+ function bf16ToF32(bf16Bytes) {
11064
+ const bf16 = new Uint16Array(bf16Bytes.buffer, bf16Bytes.byteOffset, bf16Bytes.byteLength / 2);
11065
+ const f32 = new Float32Array(bf16.length);
11066
+ const u32 = new Uint32Array(f32.buffer);
11067
+ for (let i = 0; i < bf16.length; i++) u32[i] = bf16[i] << 16;
11068
+ return f32;
11069
+ }
11070
+ /**
11071
+ * Convert F16 (IEEE 754 half-precision) data to F32.
11072
+ */
11073
+ function f16ToF32(f16View) {
11074
+ const u16 = f16View instanceof Uint16Array ? f16View : new Uint16Array(f16View.buffer, f16View.byteOffset, f16View.byteLength / 2);
11075
+ const f32 = new Float32Array(u16.length);
11076
+ for (let i = 0; i < u16.length; i++) {
11077
+ const h = u16[i];
11078
+ const sign = h >> 15 & 1;
11079
+ const exp = h >> 10 & 31;
11080
+ const frac = h & 1023;
11081
+ if (exp === 0) f32[i] = (sign ? -1 : 1) * 2 ** -14 * (frac / 1024);
11082
+ else if (exp === 31) f32[i] = frac === 0 ? sign ? -Infinity : Infinity : NaN;
11083
+ else f32[i] = (sign ? -1 : 1) * 2 ** (exp - 15) * (1 + frac / 1024);
11084
+ }
11085
+ return f32;
11086
+ }
11087
+
11088
+ //#endregion
11089
+ //#region src/gpu/lora.ts
11090
+ function resolveAdapterBase(spec, revision) {
11091
+ if (spec.startsWith("file:")) return {
11092
+ base: spec.slice(5),
11093
+ local: true
11094
+ };
11095
+ if (spec.startsWith("http://") || spec.startsWith("https://")) return {
11096
+ base: spec,
11097
+ local: false
11098
+ };
11099
+ return {
11100
+ base: `https://huggingface.co/${spec.startsWith("hf:") ? spec.slice(3) : spec}/resolve/${revision}`,
11101
+ local: false
11102
+ };
11103
+ }
11104
+ async function readLocalFile(dir, name) {
11105
+ try {
11106
+ const fs = await import("node:fs");
11107
+ const p = (await import("node:path")).join(dir, name);
11108
+ if (!fs.existsSync(p)) return null;
11109
+ const buf = fs.readFileSync(p);
11110
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
11111
+ } catch {
11112
+ return null;
11113
+ }
11114
+ }
11115
+ async function fetchFile(base, local, name, hfToken) {
11116
+ if (local) return readLocalFile(base, name);
11117
+ const headers = {};
11118
+ if (hfToken) headers.Authorization = `Bearer ${hfToken}`;
11119
+ const res = await fetch(`${base}/${name}`, { headers });
11120
+ if (!res.ok) return null;
11121
+ return res.arrayBuffer();
11122
+ }
11123
+ /** Convert a safetensors tensor slice to f32 (adapters are tiny; eager is fine). */
11124
+ function sliceToF32(buf, dataStart, entry) {
11125
+ const start = dataStart + entry.dataOffset;
11126
+ switch (entry.dtype) {
11127
+ case "F32": return new Float32Array(buf.slice(start, start + entry.dataLength));
11128
+ case "BF16": return bf16ToF32(new Uint8Array(buf, start, entry.dataLength));
11129
+ case "F16": return f16ToF32(new Uint8Array(buf, start, entry.dataLength));
11130
+ default: throw new Error(`LoRA: unsupported adapter tensor dtype ${entry.dtype}`);
11131
+ }
11132
+ }
11133
+ /**
11134
+ * Download + parse an adapter (`adapter_config.json` + `adapter_model.safetensors`).
11135
+ * Returns null when the source has no adapter (so callers can no-op cleanly).
11136
+ */
11137
+ async function fetchAdapter(spec, opts = {}) {
11138
+ const { base, local } = resolveAdapterBase(spec, opts.revision ?? "main");
11139
+ opts.onProgress?.(`Loading adapter ${spec}…`);
11140
+ const configBuf = await fetchFile(base, local, "adapter_config.json", opts.hfToken);
11141
+ if (!configBuf) throw new Error(`LoRA: no adapter_config.json at ${spec}. A flavor needs a PEFT adapter (adapter_config.json + adapter_model.safetensors).`);
11142
+ const config = JSON.parse(new TextDecoder().decode(configBuf));
11143
+ const weightsBuf = await fetchFile(base, local, "adapter_model.safetensors", opts.hfToken);
11144
+ if (!weightsBuf) throw new Error(`LoRA: no adapter_model.safetensors at ${spec} (only .safetensors adapters are supported, not legacy .bin).`);
11145
+ const file = parseSafetensorsHeader(weightsBuf);
11146
+ const tensors = /* @__PURE__ */ new Map();
11147
+ for (const entry of file.entries) {
11148
+ if (!entry.name.includes("lora_A") && !entry.name.includes("lora_B")) continue;
11149
+ tensors.set(entry.name, {
11150
+ data: sliceToF32(weightsBuf, file.dataStart, entry),
11151
+ shape: entry.shape
11152
+ });
11153
+ }
11154
+ if (tensors.size === 0) throw new Error(`LoRA: adapter at ${spec} has no lora_A/lora_B tensors.`);
11155
+ return {
11156
+ config,
11157
+ tensors,
11158
+ label: spec
11159
+ };
11160
+ }
11161
+ /**
11162
+ * Strip a PEFT tensor key down to its module path and canonicalize it to the
11163
+ * base weight key the engine uses.
11164
+ *
11165
+ * PEFT names a target like
11166
+ * base_model.model.model.layers.0.self_attn.q_proj.lora_A.weight
11167
+ * We drop the PEFT wrapper prefix + the lora_A/B suffix to get the module path
11168
+ * (…layers.0.self_attn.q_proj), append ".weight", and run the SAME key mapper
11169
+ * the base load used — so Qwen ("model." strip), Gemma, and LFM2 (out_proj→
11170
+ * o_proj) all canonicalize identically to their base tensors.
11171
+ */
11172
+ function canonicalWeightKey(peftKey, keyMapper) {
11173
+ const marker = peftKey.includes(".lora_A") ? ".lora_A" : ".lora_B";
11174
+ let stem = peftKey.slice(0, peftKey.indexOf(marker));
11175
+ stem = stem.replace(/^base_model\.model\./, "").replace(/^base_model\./, "");
11176
+ return keyMapper(`${stem.startsWith("model.") ? stem : `model.${stem}`}.weight`);
11177
+ }
11178
+ /** Longest matching suffix in a {moduleName: value} pattern map. */
11179
+ function patternLookup(patterns, peftStem) {
11180
+ if (!patterns) return void 0;
11181
+ let best;
11182
+ let bestLen = -1;
11183
+ for (const [name, val] of Object.entries(patterns)) if (peftStem.includes(name) && name.length > bestLen) {
11184
+ best = val;
11185
+ bestLen = name.length;
11186
+ }
11187
+ return best;
11188
+ }
11189
+ /**
11190
+ * Pair lora_A/lora_B tensors and resolve each to a canonical `LoRADelta`.
11191
+ * `keyMapper` must be the same mapper the base model load used.
11192
+ */
11193
+ function buildLoRADeltas(adapter, keyMapper) {
11194
+ const { config, tensors } = adapter;
11195
+ const stems = /* @__PURE__ */ new Set();
11196
+ for (const name of tensors.keys()) {
11197
+ const marker = name.includes(".lora_A") ? ".lora_A" : ".lora_B";
11198
+ stems.add(name.slice(0, name.indexOf(marker)));
11199
+ }
11200
+ const deltas = [];
11201
+ for (const stem of stems) {
11202
+ const A = tensors.get(`${stem}.lora_A.weight`) ?? tensors.get(`${stem}.lora_A`);
11203
+ const B = tensors.get(`${stem}.lora_B.weight`) ?? tensors.get(`${stem}.lora_B`);
11204
+ if (!A || !B) continue;
11205
+ const key = canonicalWeightKey(`${stem}.lora_A.weight`, keyMapper);
11206
+ if (!key) continue;
11207
+ const r = A.shape[0];
11208
+ const inFeatures = A.shape[1];
11209
+ const outFeatures = B.shape[0];
11210
+ if (B.shape[1] !== r) {
11211
+ console.warn(`[gerbil] LoRA: rank mismatch on ${stem} (A r=${r}, B r=${B.shape[1]}); skipping.`);
11212
+ continue;
11213
+ }
11214
+ const alpha = patternLookup(config.alpha_pattern, stem) ?? config.lora_alpha;
11215
+ const effR = patternLookup(config.rank_pattern, stem) ?? r;
11216
+ const scaling = config.use_rslora ? alpha / Math.sqrt(effR) : alpha / effR;
11217
+ deltas.push({
11218
+ key,
11219
+ A: A.data,
11220
+ B: B.data,
11221
+ r,
11222
+ inFeatures,
11223
+ outFeatures,
11224
+ scaling,
11225
+ fanInFanOut: config.fan_in_fan_out === true
11226
+ });
11227
+ }
11228
+ return deltas;
11229
+ }
11230
+ /**
11231
+ * Fold every resolved delta into the store's base weights in place
11232
+ * (W += scaling · B·A). Runs after weights are loaded and before architecture
11233
+ * transforms/quantization. Returns the count of tensors merged (for logging).
11234
+ *
11235
+ * Skips — loudly — any target whose base tensor is missing, shape-mismatched, or
11236
+ * not a float pack (a pre-quantized MLX/GPTQ base can't be merged this way).
11237
+ */
11238
+ async function applyLoRAToStore(store, deltas, onProgress) {
11239
+ let merged = 0;
11240
+ let skipped = 0;
11241
+ for (const d of deltas) {
11242
+ const base = await store.get(d.key);
11243
+ if (!base) {
11244
+ skipped++;
11245
+ continue;
11246
+ }
11247
+ if (!(base.data instanceof Float32Array)) {
11248
+ console.warn(`[gerbil] LoRA: base tensor ${d.key} is not float (pre-quantized base?); skipping merge.`);
11249
+ skipped++;
11250
+ continue;
11251
+ }
11252
+ const [outF, inF] = base.shape;
11253
+ const rows = d.fanInFanOut ? inF : outF;
11254
+ const cols = d.fanInFanOut ? outF : inF;
11255
+ if (rows !== d.outFeatures || cols !== d.inFeatures) {
11256
+ console.warn(`[gerbil] LoRA: shape mismatch on ${d.key} (base ${base.shape}, delta ${d.outFeatures}×${d.inFeatures}); skipping.`);
11257
+ skipped++;
11258
+ continue;
11259
+ }
11260
+ const W = new Float32Array(base.data);
11261
+ const { A, B, r, scaling, fanInFanOut } = d;
11262
+ for (let o = 0; o < d.outFeatures; o++) {
11263
+ const bRow = o * r;
11264
+ for (let k = 0; k < r; k++) {
11265
+ const bScaled = scaling * B[bRow + k];
11266
+ if (bScaled === 0) continue;
11267
+ const aRow = k * d.inFeatures;
11268
+ if (fanInFanOut) for (let i = 0; i < d.inFeatures; i++) W[i * outF + o] += bScaled * A[aRow + i];
11269
+ else {
11270
+ const wRow = o * inF;
11271
+ for (let i = 0; i < d.inFeatures; i++) W[wRow + i] += bScaled * A[aRow + i];
11272
+ }
11273
+ }
11274
+ }
11275
+ await store.set(d.key, {
11276
+ data: W,
11277
+ shape: base.shape
11278
+ });
11279
+ merged++;
11280
+ }
11281
+ onProgress?.(`Adapter merged into ${merged} tensors${skipped ? ` (${skipped} skipped)` : ""}.`);
11282
+ return {
11283
+ merged,
11284
+ skipped
11285
+ };
11286
+ }
11287
+
10964
11288
  //#endregion
10965
11289
  //#region src/gpu/mlx-adapter.ts
10966
11290
  /**
@@ -11342,130 +11666,6 @@ async function opfsEvictStale() {
11342
11666
  } catch {}
11343
11667
  }
11344
11668
 
11345
- //#endregion
11346
- //#region src/gpu/safetensors.ts
11347
- /**
11348
- * Parse safetensors header from an ArrayBuffer.
11349
- *
11350
- * Can be called with just the header bytes (for streaming — parse header first,
11351
- * then fetch tensor data by offset) or with the entire file.
11352
- */
11353
- function parseSafetensorsHeader(buffer) {
11354
- if (buffer.byteLength < 8) throw new Error(`Buffer too small to contain safetensors header length (need 8 bytes, got ${buffer.byteLength}).`);
11355
- const view = new DataView(buffer);
11356
- const headerLength = Number(view.getBigUint64(0, true));
11357
- if (headerLength > buffer.byteLength - 8) throw new Error(`Safetensors header length (${headerLength}) exceeds buffer size (${buffer.byteLength - 8}). Pass at least the first ${headerLength + 8} bytes.`);
11358
- const headerBytes = new Uint8Array(buffer, 8, headerLength);
11359
- const headerStr = new TextDecoder().decode(headerBytes);
11360
- const header = JSON.parse(headerStr);
11361
- const dataStart = 8 + headerLength;
11362
- const entries = [];
11363
- let metadata = null;
11364
- for (const [name, info] of Object.entries(header)) {
11365
- if (name === "__metadata__") {
11366
- metadata = info;
11367
- continue;
11368
- }
11369
- const { dtype, shape, data_offsets } = info;
11370
- entries.push({
11371
- name,
11372
- dtype,
11373
- shape,
11374
- dataOffset: data_offsets[0],
11375
- dataLength: data_offsets[1] - data_offsets[0]
11376
- });
11377
- }
11378
- entries.sort((a, b) => a.dataOffset - b.dataOffset);
11379
- return {
11380
- headerLength,
11381
- dataStart,
11382
- entries,
11383
- metadata
11384
- };
11385
- }
11386
- /** Byte alignment required for each dtype. */
11387
- function dtypeAlignment(dtype) {
11388
- switch (dtype) {
11389
- case "F64":
11390
- case "I64":
11391
- case "U64": return 8;
11392
- case "F32":
11393
- case "I32":
11394
- case "U32": return 4;
11395
- case "F16":
11396
- case "BF16":
11397
- case "I16":
11398
- case "U16": return 2;
11399
- case "I8":
11400
- case "U8":
11401
- case "BOOL": return 1;
11402
- default: return 1;
11403
- }
11404
- }
11405
- /**
11406
- * Create a typed array for the given dtype.
11407
- *
11408
- * If `offset` is aligned to the element size, returns a zero-copy view
11409
- * into `buffer`. Otherwise, copies the relevant slice into a new
11410
- * properly-aligned ArrayBuffer.
11411
- */
11412
- function makeTypedView(buffer, offset, byteLength, dtype) {
11413
- const aligned = offset % dtypeAlignment(dtype) === 0;
11414
- const src = aligned ? buffer : buffer.slice(offset, offset + byteLength);
11415
- const base = aligned ? offset : 0;
11416
- switch (dtype) {
11417
- case "F32": return new Float32Array(src, base, byteLength / 4);
11418
- case "F16": return new Uint16Array(src, base, byteLength / 2);
11419
- case "BF16": return new Uint16Array(src, base, byteLength / 2);
11420
- case "I32": return new Int32Array(src, base, byteLength / 4);
11421
- case "U32": return new Uint32Array(src, base, byteLength / 4);
11422
- case "I8": return new Int8Array(src, base, byteLength);
11423
- case "U8": return new Uint8Array(src, base, byteLength);
11424
- case "I16": return new Int16Array(src, base, byteLength / 2);
11425
- case "U16": return new Uint16Array(src, base, byteLength / 2);
11426
- case "F64": return new Float64Array(src, base, byteLength / 8);
11427
- case "I64": return new BigInt64Array(src, base, byteLength / 8);
11428
- case "U64": return new BigUint64Array(src, base, byteLength / 8);
11429
- case "BOOL": return new Uint8Array(src, base, byteLength);
11430
- default: throw new Error(`Unsupported safetensors dtype: ${dtype}`);
11431
- }
11432
- }
11433
- /**
11434
- * Get a typed array view for a tensor entry from a complete safetensors buffer.
11435
- * Zero-copy when the byte offset is properly aligned; copies otherwise.
11436
- */
11437
- function getTensorData(buffer, file, entry) {
11438
- return makeTypedView(buffer, file.dataStart + entry.dataOffset, entry.dataLength, entry.dtype);
11439
- }
11440
- /**
11441
- * Convert BF16 (bfloat16) data to F32.
11442
- * BF16 is the upper 16 bits of an IEEE 754 float32, so conversion is a simple left-shift.
11443
- */
11444
- function bf16ToF32(bf16Bytes) {
11445
- const bf16 = new Uint16Array(bf16Bytes.buffer, bf16Bytes.byteOffset, bf16Bytes.byteLength / 2);
11446
- const f32 = new Float32Array(bf16.length);
11447
- const u32 = new Uint32Array(f32.buffer);
11448
- for (let i = 0; i < bf16.length; i++) u32[i] = bf16[i] << 16;
11449
- return f32;
11450
- }
11451
- /**
11452
- * Convert F16 (IEEE 754 half-precision) data to F32.
11453
- */
11454
- function f16ToF32(f16View) {
11455
- const u16 = f16View instanceof Uint16Array ? f16View : new Uint16Array(f16View.buffer, f16View.byteOffset, f16View.byteLength / 2);
11456
- const f32 = new Float32Array(u16.length);
11457
- for (let i = 0; i < u16.length; i++) {
11458
- const h = u16[i];
11459
- const sign = h >> 15 & 1;
11460
- const exp = h >> 10 & 31;
11461
- const frac = h & 1023;
11462
- if (exp === 0) f32[i] = (sign ? -1 : 1) * 2 ** -14 * (frac / 1024);
11463
- else if (exp === 31) f32[i] = frac === 0 ? sign ? -Infinity : Infinity : NaN;
11464
- else f32[i] = (sign ? -1 : 1) * 2 ** (exp - 15) * (1 + frac / 1024);
11465
- }
11466
- return f32;
11467
- }
11468
-
11469
11669
  //#endregion
11470
11670
  //#region src/gpu/tokenizer.ts
11471
11671
  /**
@@ -13054,6 +13254,20 @@ async function loadModel(options) {
13054
13254
  filesLoaded++;
13055
13255
  }
13056
13256
  onProgress?.(95, 100, "Weights loaded.");
13257
+ if (options.adapter) {
13258
+ onProgress?.(95, 100, "Loading adapter…");
13259
+ const adapter = await fetchAdapter(options.adapter, {
13260
+ hfToken,
13261
+ revision,
13262
+ onProgress: (m) => onProgress?.(95, 100, m)
13263
+ });
13264
+ if (adapter) {
13265
+ const deltas = buildLoRADeltas(adapter, keyMapper);
13266
+ if (deltas.length === 0) console.warn(`[gerbil] LoRA: adapter ${adapter.label} resolved to 0 mergeable targets for ${architectureName}.`);
13267
+ const { merged, skipped } = await applyLoRAToStore(store, deltas, (m) => onProgress?.(96, 100, m));
13268
+ console.log(`[gerbil] adapter ${adapter.label}: merged ${merged} tensor(s)${skipped ? `, skipped ${skipped}` : ""}.`);
13269
+ }
13270
+ }
13057
13271
  if (architectureName === "Qwen3_5ForConditionalGeneration") {
13058
13272
  const textCfg = rawConfig.text_config ?? rawConfig;
13059
13273
  const numLayers = textCfg.num_hidden_layers;
@@ -14282,5 +14496,5 @@ function selectGraphWeights(graph, weights) {
14282
14496
  }
14283
14497
 
14284
14498
  //#endregion
14285
- export { verifyGPU as S, createStorageBuffer as _, loadMoonshine as a, getOrCreatePipeline as b, quantizeBackboneInt4 as c, Tokenizer as d, Executor as f, createBindGroup as g, clearPipelineCache as h, loadModel as i, quantizeKaniBackbone as l, MATMUL_BIAS_F16C_SPEC as m, MoonshineEncoderExecutor as n, loadOuteTTS as o, KERNEL_REGISTRY as p, loadKaniTTS as r, loadParlerTTS as s, MoonshineSTT as t, remapPrunedToken as u, createUniformBuffer as v, initGPU as x, destroyBuffers as y };
14286
- //# sourceMappingURL=moonshine-stt-Teic6SbK.mjs.map
14499
+ export { getOrCreatePipeline as C, destroyBuffers as S, verifyGPU as T, MATMUL_BIAS_F16C_SPEC as _, loadMoonshine as a, createStorageBuffer as b, quantizeBackboneInt4 as c, Tokenizer as d, applyLoRAToStore as f, KERNEL_REGISTRY as g, Executor as h, loadModel as i, quantizeKaniBackbone as l, fetchAdapter as m, MoonshineEncoderExecutor as n, loadOuteTTS as o, buildLoRADeltas as p, loadKaniTTS as r, loadParlerTTS as s, MoonshineSTT as t, remapPrunedToken as u, clearPipelineCache as v, initGPU as w, createUniformBuffer as x, createBindGroup as y };
14500
+ //# sourceMappingURL=moonshine-stt-9wc6t11v.mjs.map