@seanhogg/builderforce-memory-engine 2026.7.12 → 2026.9.27

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 (196) hide show
  1. package/README.md +14 -0
  2. package/dist/bench/adaptation.d.ts +57 -0
  3. package/dist/bench/adaptation.d.ts.map +1 -0
  4. package/dist/bench/adaptation.js +167 -0
  5. package/dist/bench/adaptation.js.map +1 -0
  6. package/dist/bench/harness.d.ts +2 -0
  7. package/dist/bench/harness.d.ts.map +1 -1
  8. package/dist/bench/harness.js +1 -1
  9. package/dist/bench/harness.js.map +1 -1
  10. package/dist/bench/index.d.ts +5 -3
  11. package/dist/bench/index.d.ts.map +1 -1
  12. package/dist/bench/index.js +4 -2
  13. package/dist/bench/index.js.map +1 -1
  14. package/dist/bench/types.d.ts +88 -0
  15. package/dist/bench/types.d.ts.map +1 -1
  16. package/dist/evermind/adapt.d.ts +40 -0
  17. package/dist/evermind/adapt.d.ts.map +1 -0
  18. package/dist/evermind/adapt.js +25 -0
  19. package/dist/evermind/adapt.js.map +1 -0
  20. package/dist/evermind/delta_wire.d.ts +43 -0
  21. package/dist/evermind/delta_wire.d.ts.map +1 -0
  22. package/dist/evermind/delta_wire.js +94 -0
  23. package/dist/evermind/delta_wire.js.map +1 -0
  24. package/dist/evermind/eval.d.ts +31 -0
  25. package/dist/evermind/eval.d.ts.map +1 -0
  26. package/dist/evermind/eval.js +36 -0
  27. package/dist/evermind/eval.js.map +1 -0
  28. package/dist/evermind/index.d.ts +16 -0
  29. package/dist/evermind/index.d.ts.map +1 -0
  30. package/dist/evermind/index.js +11 -0
  31. package/dist/evermind/index.js.map +1 -0
  32. package/dist/evermind/merge.d.ts +21 -0
  33. package/dist/evermind/merge.d.ts.map +1 -0
  34. package/dist/evermind/merge.js +79 -0
  35. package/dist/evermind/merge.js.map +1 -0
  36. package/dist/{moe/moe_package.d.ts → evermind/package.d.ts} +55 -5
  37. package/dist/evermind/package.d.ts.map +1 -0
  38. package/dist/{moe/moe_package.js → evermind/package.js} +118 -19
  39. package/dist/evermind/package.js.map +1 -0
  40. package/dist/import/foreign/adapter.d.ts +59 -0
  41. package/dist/import/foreign/adapter.d.ts.map +1 -0
  42. package/dist/import/foreign/adapter.js +76 -0
  43. package/dist/import/foreign/adapter.js.map +1 -0
  44. package/dist/import/foreign/apply.d.ts +23 -0
  45. package/dist/import/foreign/apply.d.ts.map +1 -0
  46. package/dist/import/foreign/apply.js +43 -0
  47. package/dist/import/foreign/apply.js.map +1 -0
  48. package/dist/import/foreign/codestral_mamba.d.ts +57 -0
  49. package/dist/import/foreign/codestral_mamba.d.ts.map +1 -0
  50. package/dist/import/foreign/codestral_mamba.js +174 -0
  51. package/dist/import/foreign/codestral_mamba.js.map +1 -0
  52. package/dist/import/foreign/falcon_mamba.d.ts +51 -0
  53. package/dist/import/foreign/falcon_mamba.d.ts.map +1 -0
  54. package/dist/import/foreign/falcon_mamba.js +131 -0
  55. package/dist/import/foreign/falcon_mamba.js.map +1 -0
  56. package/dist/import/foreign/index.d.ts +21 -0
  57. package/dist/import/foreign/index.d.ts.map +1 -0
  58. package/dist/import/foreign/index.js +18 -0
  59. package/dist/import/foreign/index.js.map +1 -0
  60. package/dist/import/foreign/plan.d.ts +122 -0
  61. package/dist/import/foreign/plan.d.ts.map +1 -0
  62. package/dist/import/foreign/plan.js +171 -0
  63. package/dist/import/foreign/plan.js.map +1 -0
  64. package/dist/import/foreign/registry.d.ts +35 -0
  65. package/dist/import/foreign/registry.d.ts.map +1 -0
  66. package/dist/import/foreign/registry.js +89 -0
  67. package/dist/import/foreign/registry.js.map +1 -0
  68. package/dist/import/index.d.ts +7 -0
  69. package/dist/import/index.d.ts.map +1 -1
  70. package/dist/import/index.js +7 -0
  71. package/dist/import/index.js.map +1 -1
  72. package/dist/index.d.ts +29 -12
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +24 -9
  75. package/dist/index.js.map +1 -1
  76. package/dist/kernels/selective_scan.d.ts.map +1 -1
  77. package/dist/kernels/selective_scan.js +15 -9
  78. package/dist/kernels/selective_scan.js.map +1 -1
  79. package/dist/kernels/slice.d.ts +26 -0
  80. package/dist/kernels/slice.d.ts.map +1 -0
  81. package/dist/kernels/slice.js +52 -0
  82. package/dist/kernels/slice.js.map +1 -0
  83. package/dist/kernels/weight_update.d.ts.map +1 -1
  84. package/dist/kernels/weight_update.js +30 -4
  85. package/dist/kernels/weight_update.js.map +1 -1
  86. package/dist/lm/evermind_lm.d.ts +180 -4
  87. package/dist/lm/evermind_lm.d.ts.map +1 -1
  88. package/dist/lm/evermind_lm.js +433 -116
  89. package/dist/lm/evermind_lm.js.map +1 -1
  90. package/dist/lm/index.d.ts +8 -2
  91. package/dist/lm/index.d.ts.map +1 -1
  92. package/dist/lm/index.js +6 -1
  93. package/dist/lm/index.js.map +1 -1
  94. package/dist/lm/text_embedder.d.ts +55 -0
  95. package/dist/lm/text_embedder.d.ts.map +1 -0
  96. package/dist/lm/text_embedder.js +62 -0
  97. package/dist/lm/text_embedder.js.map +1 -0
  98. package/dist/model/attention_block.d.ts.map +1 -1
  99. package/dist/model/attention_block.js +9 -5
  100. package/dist/model/attention_block.js.map +1 -1
  101. package/dist/model/mamba1_block.d.ts.map +1 -1
  102. package/dist/model/mamba1_block.js +28 -12
  103. package/dist/model/mamba1_block.js.map +1 -1
  104. package/dist/model/mamba2_block.d.ts.map +1 -1
  105. package/dist/model/mamba2_block.js +14 -9
  106. package/dist/model/mamba2_block.js.map +1 -1
  107. package/dist/model/mamba3_block.d.ts.map +1 -1
  108. package/dist/model/mamba3_block.js +14 -9
  109. package/dist/model/mamba3_block.js.map +1 -1
  110. package/dist/moe/index.d.ts +0 -2
  111. package/dist/moe/index.d.ts.map +1 -1
  112. package/dist/moe/index.js +0 -1
  113. package/dist/moe/index.js.map +1 -1
  114. package/dist/optim/adamw.d.ts +58 -1
  115. package/dist/optim/adamw.d.ts.map +1 -1
  116. package/dist/optim/adamw.js +69 -4
  117. package/dist/optim/adamw.js.map +1 -1
  118. package/dist/tokenizer/bpe.d.ts +28 -0
  119. package/dist/tokenizer/bpe.d.ts.map +1 -1
  120. package/dist/tokenizer/bpe.js +47 -0
  121. package/dist/tokenizer/bpe.js.map +1 -1
  122. package/dist/training/cpu_ops.d.ts +44 -0
  123. package/dist/training/cpu_ops.d.ts.map +1 -0
  124. package/dist/training/cpu_ops.js +111 -0
  125. package/dist/training/cpu_ops.js.map +1 -0
  126. package/dist/training/lora.d.ts +153 -0
  127. package/dist/training/lora.d.ts.map +1 -0
  128. package/dist/training/lora.js +324 -0
  129. package/dist/training/lora.js.map +1 -0
  130. package/dist/training/mamba1_cpu.d.ts +109 -0
  131. package/dist/training/mamba1_cpu.d.ts.map +1 -0
  132. package/dist/training/mamba1_cpu.js +316 -0
  133. package/dist/training/mamba1_cpu.js.map +1 -0
  134. package/dist/training/mixed_precision.d.ts +67 -0
  135. package/dist/training/mixed_precision.d.ts.map +1 -0
  136. package/dist/training/mixed_precision.js +101 -0
  137. package/dist/training/mixed_precision.js.map +1 -0
  138. package/dist/training/model_cpu.d.ts +100 -0
  139. package/dist/training/model_cpu.d.ts.map +1 -0
  140. package/dist/training/model_cpu.js +252 -0
  141. package/dist/training/model_cpu.js.map +1 -0
  142. package/dist/training/trainer.d.ts +156 -16
  143. package/dist/training/trainer.d.ts.map +1 -1
  144. package/dist/training/trainer.js +327 -101
  145. package/dist/training/trainer.js.map +1 -1
  146. package/dist/utils/base64.d.ts +12 -0
  147. package/dist/utils/base64.d.ts.map +1 -0
  148. package/dist/utils/base64.js +27 -0
  149. package/dist/utils/base64.js.map +1 -0
  150. package/package.json +3 -1
  151. package/src/bench/adaptation.ts +180 -0
  152. package/src/bench/harness.ts +1 -1
  153. package/src/bench/index.ts +13 -2
  154. package/src/bench/types.ts +93 -0
  155. package/src/evermind/adapt.ts +61 -0
  156. package/src/evermind/delta_wire.ts +114 -0
  157. package/src/evermind/eval.ts +62 -0
  158. package/src/evermind/index.ts +33 -0
  159. package/src/evermind/merge.ts +106 -0
  160. package/src/{moe/moe_package.ts → evermind/package.ts} +148 -17
  161. package/src/import/foreign/adapter.ts +106 -0
  162. package/src/import/foreign/apply.ts +54 -0
  163. package/src/import/foreign/codestral_mamba.ts +224 -0
  164. package/src/import/foreign/falcon_mamba.ts +173 -0
  165. package/src/import/foreign/index.ts +37 -0
  166. package/src/import/foreign/plan.ts +294 -0
  167. package/src/import/foreign/registry.ts +126 -0
  168. package/src/import/index.ts +30 -0
  169. package/src/index.ts +104 -13
  170. package/src/kernels/selective_scan.ts +15 -9
  171. package/src/kernels/slice.ts +64 -0
  172. package/src/kernels/weight_update.ts +30 -4
  173. package/src/lm/evermind_lm.ts +470 -116
  174. package/src/lm/index.ts +12 -2
  175. package/src/lm/text_embedder.ts +84 -0
  176. package/src/model/attention_block.ts +9 -5
  177. package/src/model/mamba1_block.ts +28 -12
  178. package/src/model/mamba2_block.ts +14 -9
  179. package/src/model/mamba3_block.ts +14 -9
  180. package/src/moe/index.ts +0 -9
  181. package/src/optim/adamw.ts +115 -7
  182. package/src/tokenizer/bpe.ts +55 -0
  183. package/src/training/cpu_ops.ts +127 -0
  184. package/src/training/lora.ts +365 -0
  185. package/src/training/mamba1_cpu.ts +421 -0
  186. package/src/training/mixed_precision.ts +119 -0
  187. package/src/training/model_cpu.ts +360 -0
  188. package/src/training/trainer.ts +397 -154
  189. package/src/utils/base64.ts +28 -0
  190. package/dist/model/mamba_block.d.ts +0 -64
  191. package/dist/model/mamba_block.d.ts.map +0 -1
  192. package/dist/model/mamba_block.js +0 -303
  193. package/dist/model/mamba_block.js.map +0 -1
  194. package/dist/moe/moe_package.d.ts.map +0 -1
  195. package/dist/moe/moe_package.js.map +0 -1
  196. package/src/model/mamba_block.ts +0 -401
@@ -0,0 +1,294 @@
1
+ /**
2
+ * import/foreign/plan.ts — the ONE executor for a foreign-checkpoint weight port.
3
+ *
4
+ * A port is expressed declaratively: every target parameter of this engine's
5
+ * {@link HybridMambaModel} gets exactly one {@link PortRule} saying which source
6
+ * tensor feeds it and how the bytes are reshaped. Architecture adapters
7
+ * (`falcon_mamba.ts`, `codestral_mamba.ts`) only *describe* their rules — the
8
+ * matching, validation and transform logic lives here once, so a new
9
+ * architecture never re-implements it.
10
+ *
11
+ * The contract that makes a port trustworthy: NOTHING is silently dropped.
12
+ * • a target with no source in the checkpoint is a `fill` rule and is reported
13
+ * in {@link PortedCheckpoint.synthesisedTargets};
14
+ * • a source tensor no rule consumed is reported in
15
+ * {@link PortedCheckpoint.unmappedSources};
16
+ * • a source tensor only PARTLY consumed (e.g. Mamba-2's gate rows, which this
17
+ * engine's block has no branch for) is declared as a {@link PortDiscard};
18
+ * • a shape that doesn't line up throws, naming the tensor and both shapes.
19
+ */
20
+
21
+ import type { NamedTensor } from "../../export/tensors.js";
22
+
23
+ // ── Transform operations ─────────────────────────────────────────────────────
24
+
25
+ /**
26
+ * How one source tensor becomes one target buffer.
27
+ *
28
+ * • `copy` — element-for-element; source and target shapes must
29
+ * agree once size-1 axes are squeezed out. Linear
30
+ * weights need no transpose: HF stores
31
+ * `[out_features, in_features]` row-major and this
32
+ * engine's LINEAR_FORWARD_WGSL reads `W` as `(N, K)`
33
+ * = `[out, in]` row-major — the same layout.
34
+ * • `convTaps` — a depthwise conv kernel `[C, 1, K]` (PyTorch
35
+ * `nn.Conv1d(groups=C)`) → `[C, K]` with the taps
36
+ * REVERSED. PyTorch pads left by `K-1` and cross-
37
+ * correlates: `y[t] = Σ_j w[j]·x[t + j - (K-1)]`,
38
+ * while conv1d.ts computes
39
+ * `y[t] = Σ_k w[k]·x[t - k]`. Substituting
40
+ * `k = K-1-j` shows the two agree only when the tap
41
+ * axis is reversed.
42
+ * • `rowSlice` — keep rows `[start, start+count)` of a rank-2
43
+ * source. Used for Mamba-2's fused `in_proj`.
44
+ * • `fill` — no source: fill the target with a constant.
45
+ * • `negExpToSoftplusInv` — `A_log` convention change. HF Mamba-2 uses
46
+ * `A = -exp(A_log)`; this engine's SSD kernel uses
47
+ * `A = -softplus(A_log)`. Re-express the same `A`:
48
+ * `target = softplus⁻¹(exp(source))`.
49
+ */
50
+ export type PortOp =
51
+ | { kind: "copy" }
52
+ | { kind: "convTaps" }
53
+ | { kind: "rowSlice"; start: number; count: number }
54
+ | { kind: "fill"; value: number }
55
+ | { kind: "negExpToSoftplusInv" };
56
+
57
+ // ── Rules & plans ────────────────────────────────────────────────────────────
58
+
59
+ /** How ONE target parameter of this engine is produced. */
60
+ export interface PortRule {
61
+ /** Target parameter name, exactly as `HybridMambaModel.parameters()` names it. */
62
+ target: string;
63
+ /** Expected target shape (row-major), for validation and reporting. */
64
+ shape: number[];
65
+ /**
66
+ * Candidate source tensor names, in preference order — the first one present
67
+ * in the checkpoint is used. Empty means the target is synthesised (`fill`),
68
+ * because this architecture has no such tensor.
69
+ */
70
+ from: string[];
71
+ op: PortOp;
72
+ /** Why this isn't a plain copy / why there is no source. Required for `fill`. */
73
+ note?: string;
74
+ }
75
+
76
+ /** A source tensor deliberately consumed only in part. */
77
+ export interface PortDiscard {
78
+ source: string;
79
+ /** How many source elements are left behind. */
80
+ elements: number;
81
+ reason: string;
82
+ }
83
+
84
+ /** The complete, architecture-specific description of a port. */
85
+ export interface PortPlan {
86
+ /** Adapter id, e.g. `falcon_mamba`. */
87
+ adapter: string;
88
+ rules: PortRule[];
89
+ discards: PortDiscard[];
90
+ }
91
+
92
+ /** A target that no source tensor feeds, and the constant used instead. */
93
+ export interface SynthesisedTarget {
94
+ target: string;
95
+ value: number;
96
+ reason: string;
97
+ }
98
+
99
+ /** The result of running a {@link PortPlan} over a checkpoint's tensors. */
100
+ export interface PortedTensors {
101
+ adapter: string;
102
+ /** Target parameter name → buffer, sized exactly as the model expects. */
103
+ weights: Map<string, Float32Array>;
104
+ /** Checkpoint tensors that no rule consumed (e.g. an untied `lm_head.weight`). */
105
+ unmappedSources: string[];
106
+ /** Targets this architecture has no tensor for. */
107
+ synthesisedTargets: SynthesisedTarget[];
108
+ /** Source tensors consumed only in part. */
109
+ discardedSources: PortDiscard[];
110
+ }
111
+
112
+ // ── Source-name normalisation ────────────────────────────────────────────────
113
+
114
+ /**
115
+ * Mistral publishes the same Mamba-2 weights twice: the HF conversion
116
+ * (`backbone.…`) and its native `consolidated.safetensors`, whose names carry a
117
+ * `model.` prefix. Strip it so one rule table serves both. The
118
+ * `embedding`/`embeddings` spelling difference is handled by the rules' `from`
119
+ * alternatives, not here.
120
+ */
121
+ export function normaliseSourceName(name: string): string {
122
+ return name.startsWith("model.") ? name.slice("model.".length) : name;
123
+ }
124
+
125
+ // ── Execution ────────────────────────────────────────────────────────────────
126
+
127
+ /** Product of a shape's dimensions (`[]` ⇒ 1). */
128
+ function numel(shape: readonly number[]): number {
129
+ return shape.reduce((n, d) => n * d, 1);
130
+ }
131
+
132
+ /** Shape with size-1 axes removed — `[C, 1, K]` and `[C, K]` compare equal. */
133
+ function squeeze(shape: readonly number[]): number[] {
134
+ const kept = shape.filter((d) => d !== 1);
135
+ return kept.length > 0 ? kept : [1];
136
+ }
137
+
138
+ function sameShape(a: readonly number[], b: readonly number[]): boolean {
139
+ return a.length === b.length && a.every((d, i) => d === b[i]);
140
+ }
141
+
142
+ /**
143
+ * `softplus⁻¹(y) = log(e^y − 1)`, evaluated so it neither overflows nor loses the
144
+ * small-`y` tail. Above ~30 `log(expm1(y))` and `y` agree to well inside f32
145
+ * precision, and `expm1` would have overflowed; below it, `expm1` keeps the
146
+ * precision `exp(y) - 1` would have thrown away.
147
+ */
148
+ function softplusInverse(y: number): number {
149
+ if (!(y > 0)) {
150
+ throw new Error(`import/foreign: softplus⁻¹ needs a positive input, got ${y}`);
151
+ }
152
+ if (y > 30) return y;
153
+ return Math.log(Math.expm1(y));
154
+ }
155
+
156
+ /** Run one rule, returning the target buffer. Throws on any shape disagreement. */
157
+ function applyRule(rule: PortRule, source: NamedTensor | null): Float32Array {
158
+ const want = numel(rule.shape);
159
+
160
+ if (rule.op.kind === "fill") {
161
+ return new Float32Array(want).fill(rule.op.value);
162
+ }
163
+ if (!source) {
164
+ // Unreachable via executePortPlan (which checks first) — kept so a direct
165
+ // caller gets a real message rather than a null dereference.
166
+ throw new Error(`import/foreign: rule for "${rule.target}" has no source tensor`);
167
+ }
168
+
169
+ const where = `"${source.name}" → "${rule.target}"`;
170
+
171
+ switch (rule.op.kind) {
172
+ case "copy":
173
+ case "negExpToSoftplusInv": {
174
+ if (!sameShape(squeeze(source.shape), squeeze(rule.shape))) {
175
+ throw new Error(
176
+ `import/foreign: ${where} shape mismatch — checkpoint has [${source.shape.join(", ")}], ` +
177
+ `this model expects [${rule.shape.join(", ")}]`,
178
+ );
179
+ }
180
+ if (source.data.length !== want) {
181
+ throw new Error(
182
+ `import/foreign: ${where} has ${source.data.length} elements, expected ${want}`,
183
+ );
184
+ }
185
+ if (rule.op.kind === "copy") return Float32Array.from(source.data);
186
+ const out = new Float32Array(want);
187
+ for (let i = 0; i < want; i++) out[i] = softplusInverse(Math.exp(source.data[i]!));
188
+ return out;
189
+ }
190
+
191
+ case "convTaps": {
192
+ const [channels, kernel] = squeeze(rule.shape) as [number, number];
193
+ const src = squeeze(source.shape);
194
+ if (!sameShape(src, [channels, kernel])) {
195
+ throw new Error(
196
+ `import/foreign: ${where} depthwise conv shape mismatch — checkpoint has ` +
197
+ `[${source.shape.join(", ")}], this model expects [${rule.shape.join(", ")}] ` +
198
+ `(a [channels, 1, kernel] PyTorch kernel squeezes to [channels, kernel])`,
199
+ );
200
+ }
201
+ const out = new Float32Array(want);
202
+ for (let c = 0; c < channels; c++) {
203
+ for (let k = 0; k < kernel; k++) {
204
+ // Reverse the tap axis — see PortOp.convTaps.
205
+ out[c * kernel + k] = source.data[c * kernel + (kernel - 1 - k)]!;
206
+ }
207
+ }
208
+ return out;
209
+ }
210
+
211
+ case "rowSlice": {
212
+ if (source.shape.length !== 2) {
213
+ throw new Error(
214
+ `import/foreign: ${where} rowSlice needs a rank-2 source, got ` +
215
+ `[${source.shape.join(", ")}]`,
216
+ );
217
+ }
218
+ const [rows, cols] = source.shape as [number, number];
219
+ const { start, count } = rule.op;
220
+ if (start < 0 || start + count > rows) {
221
+ throw new Error(
222
+ `import/foreign: ${where} rowSlice [${start}, ${start + count}) is outside the ` +
223
+ `checkpoint tensor's ${rows} rows — the source layout is not what this adapter expects`,
224
+ );
225
+ }
226
+ if (!sameShape(squeeze(rule.shape), squeeze([count, cols]))) {
227
+ throw new Error(
228
+ `import/foreign: ${where} rowSlice yields [${count}, ${cols}] but this model expects ` +
229
+ `[${rule.shape.join(", ")}]`,
230
+ );
231
+ }
232
+ return Float32Array.from(source.data.subarray(start * cols, (start + count) * cols));
233
+ }
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Execute a plan against a checkpoint's tensors. Every rule must resolve; the
239
+ * first failure throws naming the offending tensor. Sources no rule touched are
240
+ * returned rather than ignored.
241
+ */
242
+ export function executePortPlan(plan: PortPlan, tensors: NamedTensor[]): PortedTensors {
243
+ const byName = new Map<string, NamedTensor>();
244
+ for (const t of tensors) {
245
+ const name = normaliseSourceName(t.name);
246
+ byName.set(name, { ...t, name });
247
+ }
248
+
249
+ const weights = new Map<string, Float32Array>();
250
+ const synthesisedTargets: SynthesisedTarget[] = [];
251
+ const consumed = new Set<string>();
252
+
253
+ for (const rule of plan.rules) {
254
+ if (weights.has(rule.target)) {
255
+ throw new Error(`import/foreign: plan "${plan.adapter}" defines "${rule.target}" twice`);
256
+ }
257
+
258
+ if (rule.from.length === 0) {
259
+ if (rule.op.kind !== "fill") {
260
+ throw new Error(
261
+ `import/foreign: rule for "${rule.target}" names no source but is not a fill rule`,
262
+ );
263
+ }
264
+ weights.set(rule.target, applyRule(rule, null));
265
+ synthesisedTargets.push({
266
+ target: rule.target,
267
+ value: rule.op.value,
268
+ reason: rule.note ?? "this architecture has no corresponding tensor",
269
+ });
270
+ continue;
271
+ }
272
+
273
+ const source = rule.from.map((n) => byName.get(n)).find((t): t is NamedTensor => t != null);
274
+ if (!source) {
275
+ throw new Error(
276
+ `import/foreign: checkpoint is missing "${rule.from[0]}" (needed for "${rule.target}", ` +
277
+ `expected shape [${rule.shape.join(", ")}])` +
278
+ (rule.from.length > 1 ? `; also tried ${rule.from.slice(1).map((n) => `"${n}"`).join(", ")}` : ""),
279
+ );
280
+ }
281
+ consumed.add(source.name);
282
+ weights.set(rule.target, applyRule(rule, source));
283
+ }
284
+
285
+ const unmappedSources = [...byName.keys()].filter((n) => !consumed.has(n)).sort();
286
+
287
+ return {
288
+ adapter: plan.adapter,
289
+ weights,
290
+ unmappedSources,
291
+ synthesisedTargets,
292
+ discardedSources: plan.discards,
293
+ };
294
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * import/foreign/registry.ts — config-driven adapter selection and the public
3
+ * port entry points.
4
+ *
5
+ * Given a checkpoint's `config.json`, pick the adapter by `model_type` /
6
+ * `architectures` and run its plan. An architecture nobody claims fails loudly
7
+ * with the list of what IS supported — and, for a transformer checkpoint, says
8
+ * why no map could ever exist for it.
9
+ */
10
+
11
+ import type { NamedTensor } from "../../export/tensors.js";
12
+ import type { HybridMambaModelConfig } from "../../model/mamba_model.js";
13
+ import { safetensorsToTensors } from "../safetensors.js";
14
+ import { executePortPlan, type PortDiscard, type PortedTensors, type SynthesisedTarget } from "./plan.js";
15
+ import { falconMambaAdapter } from "./falcon_mamba.js";
16
+ import { codestralMambaAdapter } from "./codestral_mamba.js";
17
+ import type { ForeignConfig, ForeignMambaAdapter } from "./adapter.js";
18
+
19
+ /** Every foreign SSM architecture this engine can warm-start from. */
20
+ export const FOREIGN_MAMBA_ADAPTERS: readonly ForeignMambaAdapter[] = [
21
+ falconMambaAdapter,
22
+ codestralMambaAdapter,
23
+ ];
24
+
25
+ /** A completed port: the model to build, its weights, and what didn't line up. */
26
+ export interface PortedCheckpoint extends PortedTensors {
27
+ /** Construct a `HybridMambaModel` with this to receive {@link PortedTensors.weights}. */
28
+ modelConfig: HybridMambaModelConfig;
29
+ }
30
+
31
+ export type { PortDiscard, SynthesisedTarget };
32
+
33
+ // ── Detection ────────────────────────────────────────────────────────────────
34
+
35
+ function architecturesOf(config: ForeignConfig): string[] {
36
+ const raw = config["architectures"];
37
+ return Array.isArray(raw) ? raw.filter((a): a is string => typeof a === "string") : [];
38
+ }
39
+
40
+ function modelTypeOf(config: ForeignConfig): string | undefined {
41
+ const raw = config["model_type"];
42
+ return typeof raw === "string" ? raw : undefined;
43
+ }
44
+
45
+ /**
46
+ * A checkpoint is a transformer when any declared architecture carries an
47
+ * attention-stack marker. Recognised only to give a better error: attention
48
+ * weights (Q/K/V/O, positional embeddings, per-token KV cache) have no
49
+ * counterpart in a state-space mixer, so no rename or reshape can port them.
50
+ */
51
+ function looksLikeTransformer(architectures: string[], modelType: string | undefined): boolean {
52
+ const haystack = [...architectures, modelType ?? ""].join(" ").toLowerCase();
53
+ if (haystack.includes("mamba") || haystack.includes("ssm")) return false;
54
+ return /llama|mistral|qwen|gpt|phi|gemma|falcon(?!_?mamba)|starcoder|deepseek|olmo|attention|forcausallm/.test(
55
+ haystack,
56
+ );
57
+ }
58
+
59
+ function supportedList(): string {
60
+ return FOREIGN_MAMBA_ADAPTERS.map(
61
+ (a) => `${a.label} — architectures ${a.architectures.join(" / ")}, model_type ${a.modelTypes.join(" / ")}`,
62
+ ).join("; ");
63
+ }
64
+
65
+ /**
66
+ * The adapter that claims this checkpoint. Throws — never guesses — when no
67
+ * adapter matches, because a wrong map produces a model that loads and then
68
+ * generates noise.
69
+ */
70
+ export function foreignMambaAdapterFor(config: ForeignConfig): ForeignMambaAdapter {
71
+ const architectures = architecturesOf(config);
72
+ const modelType = modelTypeOf(config);
73
+
74
+ const byArch = FOREIGN_MAMBA_ADAPTERS.find((a) =>
75
+ architectures.some((name) => a.architectures.includes(name)),
76
+ );
77
+ if (byArch) return byArch;
78
+
79
+ const byType = modelType
80
+ ? FOREIGN_MAMBA_ADAPTERS.find((a) => a.modelTypes.includes(modelType))
81
+ : undefined;
82
+ if (byType) return byType;
83
+
84
+ const seen =
85
+ architectures.length > 0
86
+ ? `architectures [${architectures.join(", ")}]`
87
+ : modelType
88
+ ? `model_type "${modelType}"`
89
+ : "a config declaring neither `architectures` nor `model_type`";
90
+
91
+ if (looksLikeTransformer(architectures, modelType)) {
92
+ throw new Error(
93
+ `import/foreign: ${seen} is a TRANSFORMER checkpoint — its attention weights ` +
94
+ `(q/k/v/o projections, positional embeddings) have no counterpart in a state-space ` +
95
+ `mixer, so no weight port can exist for it at any tensor naming. Transformer coders ` +
96
+ `(IQuest, NousCoder, Maincoder and the like) can only be brought across by ` +
97
+ `DISTILLATION — train this engine's SSM against the transformer's outputs. ` +
98
+ `Weight porting is supported only for: ${supportedList()}.`,
99
+ );
100
+ }
101
+
102
+ throw new Error(
103
+ `import/foreign: unsupported checkpoint — ${seen}. Supported: ${supportedList()}. ` +
104
+ `A transformer checkpoint cannot be weight-ported at all; distillation is the only route.`,
105
+ );
106
+ }
107
+
108
+ // ── Porting ──────────────────────────────────────────────────────────────────
109
+
110
+ /**
111
+ * Port a foreign checkpoint's tensors onto this engine's parameters. The result
112
+ * is device-free — {@link applyPortedWeights} uploads it into a live model.
113
+ */
114
+ export function portForeignMamba(config: ForeignConfig, tensors: NamedTensor[]): PortedCheckpoint {
115
+ const adapter = foreignMambaAdapterFor(config);
116
+ const { modelConfig, plan } = adapter.describe(config);
117
+ return { ...executePortPlan(plan, tensors), modelConfig };
118
+ }
119
+
120
+ /** Convenience: port straight from a `.safetensors` buffer. */
121
+ export function portForeignMambaSafetensors(
122
+ config: ForeignConfig,
123
+ bytes: Uint8Array,
124
+ ): PortedCheckpoint {
125
+ return portForeignMamba(config, safetensorsToTensors(bytes));
126
+ }
@@ -4,8 +4,38 @@
4
4
  * The inverse of {@link ../export}: read a `.safetensors` checkpoint back into a
5
5
  * live {@link ../lm/evermind_lm.EvermindLM}. Round-trips Evermind's own exports,
6
6
  * and warm-starts a foreign SSM checkpoint via a `rename` map.
7
+ *
8
+ * `./foreign` is the same seam for checkpoints that were never ours: published
9
+ * Mamba-1/Mamba-2 models, whose tensor names, layouts and `A_log`/conv
10
+ * conventions are reconciled onto `HybridMambaModel`'s parameters by a
11
+ * config-selected adapter.
7
12
  */
8
13
 
9
14
  export { safetensorsToTensors } from "./safetensors.js";
10
15
  export { importEvermind, importEvermindTensors, inferArchFromTensors } from "./evermind.js";
11
16
  export type { ImportOptions } from "./evermind.js";
17
+
18
+ // ── Foreign checkpoints (Falcon-Mamba / Codestral-Mamba → HybridMambaModel) ────
19
+ export {
20
+ FOREIGN_MAMBA_ADAPTERS,
21
+ foreignMambaAdapterFor,
22
+ portForeignMamba,
23
+ portForeignMambaSafetensors,
24
+ applyPortedWeights,
25
+ executePortPlan,
26
+ normaliseSourceName,
27
+ falconMambaAdapter,
28
+ codestralMambaAdapter,
29
+ } from "./foreign/index.js";
30
+ export type {
31
+ ForeignConfig,
32
+ ForeignMambaAdapter,
33
+ PortTarget,
34
+ PortDiscard,
35
+ PortOp,
36
+ PortPlan,
37
+ PortRule,
38
+ PortedCheckpoint,
39
+ PortedTensors,
40
+ SynthesisedTarget,
41
+ } from "./foreign/index.js";
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * MambaCode.js – Entry Point (v2.0.0)
2
+ * @seanhogg/builderforce-memory-engine – entry point
3
3
  */
4
4
 
5
5
  // ── Model classes ─────────────────────────────────────────────────────────────
@@ -22,7 +22,6 @@ export {
22
22
  DEFAULT_MOE_CONFIG,
23
23
  DEFAULT_MOE_SEED,
24
24
  MoETrainer,
25
- EvermindModelPackage,
26
25
  } from './moe/index.js';
27
26
  export type {
28
27
  MoEConfig,
@@ -31,23 +30,82 @@ export type {
31
30
  MoESample,
32
31
  MoETrainOptions,
33
32
  MoEEpochResult,
33
+ } from './moe/index.js';
34
+
35
+ // ── Evermind (the .evermind artifact + the shared learning pipeline) ───────────
36
+ export {
37
+ EvermindModelPackage,
38
+ adaptAndDiff,
39
+ EVERMIND_ADAPT_MAX_CHARS,
40
+ EVERMIND_ADAPT_WINDOW_TOKENS,
41
+ parseDeltaLearnPayload,
42
+ buildDeltaLearnPayload,
43
+ decodeDeltaPayload,
44
+ deltaUnusableReason,
45
+ MAX_DELTA_B64_CHARS,
46
+ DELTA_LABEL_MAX_CHARS,
47
+ mergeCheckpointDiffs,
48
+ meanEvalLoss,
49
+ sequenceLoss,
50
+ EVAL_MAX_TOKENS,
51
+ } from './evermind/index.js';
52
+ export type {
34
53
  EvermindModelManifest,
35
54
  EvermindModelCard,
36
55
  EvermindModelType,
37
56
  EvermindModality,
38
57
  PackageMeta,
39
58
  ValidationResult,
40
- } from './moe/index.js';
59
+ AdaptResult,
60
+ AdaptOptions,
61
+ AdaptTokenizer,
62
+ DeltaLearnPayload,
63
+ DeltaParseResult,
64
+ MergeResult,
65
+ EvalExample,
66
+ EvalTokenizer,
67
+ } from './evermind/index.js';
41
68
 
42
69
  // ── EvermindLM (the generative model) + AdamW ──────────────────────────────────
43
- export { EvermindLM, EvermindLMTrainer, DEFAULT_LM_CONFIG, DEFAULT_LM_SEED } from './lm/index.js';
44
- export type { EvermindLMConfig, LMGenerateOptions, TextCodec } from './lm/index.js';
45
- export { AdamW } from './optim/adamw.js';
46
- export type { AdamWOptions, OptimTarget, OptimParam } from './optim/adamw.js';
70
+ export { EvermindLM, EvermindLMTrainer, DEFAULT_LM_CONFIG, DEFAULT_LM_SEED, logProbOfToken } from './lm/index.js';
71
+ // CPU text→vector embedder — SSM-embedding recall with no GPU.
72
+ export { EvermindTextEmbedder } from './lm/index.js';
73
+ export type { EvermindLMConfig, LMGenerateOptions, TextCodec, EvermindLMTrainOptions, EvermindLMDecodeState } from './lm/index.js';
74
+ export type { TextEmbedder, EmbedderCodec } from './lm/index.js';
75
+ export { AdamW, adamwUpdateInPlace } from './optim/adamw.js';
76
+ export type { AdamWOptions, OptimTarget, OptimParam, ShardSpec, AdamWKernelStep } from './optim/adamw.js';
77
+
78
+ // PEFT / efficient-training toolkit (LoRA, QLoRA, mixed precision, checkpointing).
79
+ export { LoRAAdapter, EvermindLMLoRA, quantizeBase } from './training/lora.js';
80
+ export type { LoRAConfig, LoRAFitOptions, BaseQuant } from './training/lora.js';
81
+ export { DynamicLossScaler, roundFp16, fp16View } from './training/mixed_precision.js';
82
+ export type { LossScalerOptions } from './training/mixed_precision.js';
47
83
 
48
84
  // ── Training ──────────────────────────────────────────────────────────────────
49
85
 
50
- export { MambaTrainer } from './training/trainer.js';
86
+ export { MambaTrainer, cpuDimsFor, buildChunks, buildBatches, WSLA_MAX_DELTA } from './training/trainer.js';
87
+ export type { TrainOptions } from './training/trainer.js';
88
+ // The exact CPU forward + backward the trainer differentiates with. Exported so a
89
+ // host can compute gradients (or gradient-check the model) without a GPU.
90
+ export {
91
+ cpuModelForward, cpuModelLoss, cpuModelBackward,
92
+ zeroCpuModelGrads, toNamedGrads, namedWeights, cpuWeightsFromNamed,
93
+ paramMatrixShape, DEFAULT_LORA_TARGETS, CPU_GRADIENT_LAYER_TYPES,
94
+ } from './training/model_cpu.js';
95
+ export type {
96
+ CpuModelDims, CpuModelWeights, CpuModelGrads, CpuBackwardResult, CpuBackwardOptions, CpuGradientLayerType,
97
+ } from './training/model_cpu.js';
98
+ export {
99
+ mamba1CpuForward, mamba1CpuBackward, zeroMamba1Grads,
100
+ MAMBA1_PARAM_NAMES, A_LOG_CLAMP_LO, A_LOG_CLAMP_HI,
101
+ } from './training/mamba1_cpu.js';
102
+ export type {
103
+ Mamba1CpuWeights, Mamba1CpuDims, Mamba1CpuCache, Mamba1CpuGrads, Mamba1CpuForwardResult,
104
+ } from './training/mamba1_cpu.js';
105
+ export {
106
+ linearForward, linearBackward, rmsNormForward, rmsNormBackward,
107
+ silu, siluGrad, softplus, sigmoid, RMSNORM_EPS,
108
+ } from './training/cpu_ops.js';
51
109
  export {
52
110
  Tensor,
53
111
  backward,
@@ -62,6 +120,7 @@ export {
62
120
  // ── Tokenizer ─────────────────────────────────────────────────────────────────
63
121
 
64
122
  export { BPETokenizer } from './tokenizer/bpe.js';
123
+ export type { BPETokenizerSpec } from './tokenizer/bpe.js';
65
124
  export type { BPEEncodeOptions, PadSide, HuggingFaceTokenizerSpec, SpecialTokenOverrides } from './tokenizer/bpe.js';
66
125
 
67
126
  // ── Modality codecs (media ⇄ tokens; lets EvermindLM generate video) ────────────
@@ -123,6 +182,34 @@ export {
123
182
  } from './import/index.js';
124
183
  export type { ImportOptions } from './import/index.js';
125
184
 
185
+ // ── Foreign weight port (Falcon-Mamba / Codestral-Mamba → HybridMambaModel) ────
186
+ // Transformer checkpoints are NOT portable — distillation is the only route, and
187
+ // `foreignMambaAdapterFor` rejects them saying so.
188
+
189
+ export {
190
+ FOREIGN_MAMBA_ADAPTERS,
191
+ foreignMambaAdapterFor,
192
+ portForeignMamba,
193
+ portForeignMambaSafetensors,
194
+ applyPortedWeights,
195
+ executePortPlan,
196
+ normaliseSourceName,
197
+ falconMambaAdapter,
198
+ codestralMambaAdapter,
199
+ } from './import/index.js';
200
+ export type {
201
+ ForeignConfig,
202
+ ForeignMambaAdapter,
203
+ PortTarget,
204
+ PortDiscard,
205
+ PortOp,
206
+ PortPlan,
207
+ PortRule,
208
+ PortedCheckpoint,
209
+ PortedTensors,
210
+ SynthesisedTarget,
211
+ } from './import/index.js';
212
+
126
213
  // ── Benchmarking (held-out perplexity / accuracy / throughput + A/B) ──────────
127
214
 
128
215
  export {
@@ -133,6 +220,10 @@ export {
133
220
  compareReports,
134
221
  corpusToSequences,
135
222
  trainAndBenchmark,
223
+ benchmarkAdaptationCost,
224
+ formatAdaptationCostReport,
225
+ tokenWindows,
226
+ adaptationsAfterSkip,
136
227
  argmax as benchArgmax,
137
228
  topKIndices,
138
229
  perplexity,
@@ -147,6 +238,9 @@ export type {
147
238
  ComparisonReport,
148
239
  TrainAndBenchmarkOptions,
149
240
  TrainAndBenchmarkResult,
241
+ AdaptationCostOptions,
242
+ AdaptationCostPoint,
243
+ AdaptationCostReport,
150
244
  } from './bench/index.js';
151
245
 
152
246
  // ── Checkpoint integrity (CRC-32 + trailer) ───────────────────────────────────
@@ -162,6 +256,7 @@ export {
162
256
  applyCheckpointDiff,
163
257
  } from './utils/delta.js';
164
258
  export type { RowDelta } from './utils/delta.js';
259
+ export { bytesToBase64, base64ToBytes } from './utils/base64.js';
165
260
 
166
261
  // ── Seeded RNG (reproducible weight init) ─────────────────────────────────────
167
262
 
@@ -222,6 +317,7 @@ export { CONV1D_FORWARD_WGSL, CONV1D_BACKWARD_WGSL }
222
317
  from './kernels/conv1d.js';
223
318
  export { LINEAR_FORWARD_WGSL, LINEAR_BACKWARD_WGSL }
224
319
  from './kernels/linear_projection.js';
320
+ export { COL_SLICE_WGSL, COL_SLICE_ENTRY, dispatchColumnSlice } from './kernels/slice.js';
225
321
  export { WEIGHT_UPDATE_WGSL, GRAD_CLIP_WGSL }
226
322
  from './kernels/weight_update.js';
227
323
  export { ACTIVATIONS_WGSL, ACTIVATIONS_BACKWARD_WGSL, SOFTMAX_FORWARD_WGSL, SOFTMAX_BACKWARD_WGSL }
@@ -271,8 +367,3 @@ export type {
271
367
  } from './limbic/index.js';
272
368
 
273
369
  export { LIMBIC_AFFECT_WGSL } from './kernels/limbic_affect.js';
274
-
275
- // ── Version ───────────────────────────────────────────────────────────────────
276
-
277
- export const VERSION = '2.0.0';
278
- export const DESCRIPTION = 'MambaCode.js: WebGPU-accelerated Mamba-1/2/3 and Hybrid SSM for browser code models';
@@ -132,21 +132,27 @@ fn forward_scan(
132
132
  // ---- Kogge-Stone inclusive prefix scan within tile ----
133
133
  // Associative operator: (a1, b1) ∘ (a2, b2) = (a1*a2, a1*b2 + b1)
134
134
  // This computes cumulative state recurrence in log2(TILE) steps.
135
+ // NOTE the barrier placement. Both barriers MUST sit in uniform control
136
+ // flow — every invocation in the workgroup has to reach them. The earlier
137
+ // version put the first workgroupBarrier() INSIDE the if (lid >= stride)
138
+ // branch, which is a WGSL uniformity violation: shader creation fails on a
139
+ // conformant implementation. Computing into locals first, then writing
140
+ // back after a uniform barrier, is the correct read-then-write split.
135
141
  var stride: u32 = 1u;
136
142
  loop {
137
143
  if (stride >= TILE) { break; }
144
+ var new_a = wg_a[lid];
145
+ var new_bu = wg_bu[lid];
138
146
  if (lid >= stride) {
139
- let prev_a = wg_a[lid - stride];
140
- let prev_bu = wg_bu[lid - stride];
141
- // Combine: new_state = prev_a * cur_a (product of A_bars)
142
- // new_bu = prev_a * cur_bu + prev_bu
143
- let new_a = prev_a * wg_a[lid];
144
- let new_bu = prev_a * wg_bu[lid] + prev_bu;
145
- workgroupBarrier();
146
- wg_a[lid] = new_a;
147
- wg_bu[lid] = new_bu;
147
+ // Combine: new_a = prev_a * cur_a (product of A_bars)
148
+ // new_bu = prev_a * cur_bu + prev_bu
149
+ new_a = wg_a[lid - stride] * new_a;
150
+ new_bu = wg_a[lid - stride] * new_bu + wg_bu[lid - stride];
148
151
  }
149
152
  workgroupBarrier();
153
+ wg_a[lid] = new_a;
154
+ wg_bu[lid] = new_bu;
155
+ workgroupBarrier();
150
156
  stride = stride << 1u;
151
157
  }
152
158