@seanhogg/builderforce-memory-engine 2026.9.34 → 2026.10.1
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.
- package/dist/evermind/delta_wire.d.ts +10 -1
- package/dist/evermind/delta_wire.d.ts.map +1 -1
- package/dist/evermind/delta_wire.js +25 -3
- package/dist/evermind/delta_wire.js.map +1 -1
- package/dist/evermind/index.d.ts +4 -4
- package/dist/evermind/index.d.ts.map +1 -1
- package/dist/evermind/index.js +2 -2
- package/dist/evermind/index.js.map +1 -1
- package/dist/evermind/merge.d.ts +25 -0
- package/dist/evermind/merge.d.ts.map +1 -1
- package/dist/evermind/merge.js +30 -0
- package/dist/evermind/merge.js.map +1 -1
- package/dist/evermind/package.d.ts +9 -0
- package/dist/evermind/package.d.ts.map +1 -1
- package/dist/evermind/package.js +20 -0
- package/dist/evermind/package.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/limbic/index.d.ts +3 -0
- package/dist/limbic/index.d.ts.map +1 -1
- package/dist/limbic/index.js +3 -0
- package/dist/limbic/index.js.map +1 -1
- package/dist/limbic/limbic_task.d.ts +22 -0
- package/dist/limbic/limbic_task.d.ts.map +1 -0
- package/dist/limbic/limbic_task.js +45 -0
- package/dist/limbic/limbic_task.js.map +1 -0
- package/dist/limbic/limbic_trainer.d.ts +17 -30
- package/dist/limbic/limbic_trainer.d.ts.map +1 -1
- package/dist/limbic/limbic_trainer.js +38 -144
- package/dist/limbic/limbic_trainer.js.map +1 -1
- package/dist/ml/classical/ewma.d.ts +32 -0
- package/dist/ml/classical/ewma.d.ts.map +1 -0
- package/dist/ml/classical/ewma.js +46 -0
- package/dist/ml/classical/ewma.js.map +1 -0
- package/dist/ml/classical/gbm.d.ts +42 -0
- package/dist/ml/classical/gbm.d.ts.map +1 -0
- package/dist/ml/classical/gbm.js +116 -0
- package/dist/ml/classical/gbm.js.map +1 -0
- package/dist/ml/classical/index.d.ts +48 -0
- package/dist/ml/classical/index.d.ts.map +1 -0
- package/dist/ml/classical/index.js +45 -0
- package/dist/ml/classical/index.js.map +1 -0
- package/dist/ml/classical/isolation_forest.d.ts +37 -0
- package/dist/ml/classical/isolation_forest.d.ts.map +1 -0
- package/dist/ml/classical/isolation_forest.js +72 -0
- package/dist/ml/classical/isolation_forest.js.map +1 -0
- package/dist/ml/classical/kmeans.d.ts +31 -0
- package/dist/ml/classical/kmeans.d.ts.map +1 -0
- package/dist/ml/classical/kmeans.js +93 -0
- package/dist/ml/classical/kmeans.js.map +1 -0
- package/dist/ml/classical/knn.d.ts +48 -0
- package/dist/ml/classical/knn.d.ts.map +1 -0
- package/dist/ml/classical/knn.js +68 -0
- package/dist/ml/classical/knn.js.map +1 -0
- package/dist/ml/classical/linear.d.ts +29 -0
- package/dist/ml/classical/linear.d.ts.map +1 -0
- package/dist/ml/classical/linear.js +114 -0
- package/dist/ml/classical/linear.js.map +1 -0
- package/dist/ml/classical/logistic.d.ts +39 -0
- package/dist/ml/classical/logistic.d.ts.map +1 -0
- package/dist/ml/classical/logistic.js +103 -0
- package/dist/ml/classical/logistic.js.map +1 -0
- package/dist/ml/classical/math.d.ts +26 -0
- package/dist/ml/classical/math.d.ts.map +1 -0
- package/dist/ml/classical/math.js +90 -0
- package/dist/ml/classical/math.js.map +1 -0
- package/dist/ml/classical/naive_bayes.d.ts +24 -0
- package/dist/ml/classical/naive_bayes.d.ts.map +1 -0
- package/dist/ml/classical/naive_bayes.js +67 -0
- package/dist/ml/classical/naive_bayes.js.map +1 -0
- package/dist/ml/classical/robust_z.d.ts +30 -0
- package/dist/ml/classical/robust_z.d.ts.map +1 -0
- package/dist/ml/classical/robust_z.js +46 -0
- package/dist/ml/classical/robust_z.js.map +1 -0
- package/dist/ml/dense_head.d.ts +33 -0
- package/dist/ml/dense_head.d.ts.map +1 -0
- package/dist/ml/dense_head.js +140 -0
- package/dist/ml/dense_head.js.map +1 -0
- package/dist/ml/features.d.ts +25 -0
- package/dist/ml/features.d.ts.map +1 -0
- package/dist/ml/features.js +46 -0
- package/dist/ml/features.js.map +1 -0
- package/dist/ml/federate.d.ts +70 -0
- package/dist/ml/federate.d.ts.map +1 -0
- package/dist/ml/federate.js +122 -0
- package/dist/ml/federate.js.map +1 -0
- package/dist/ml/head_trainer.d.ts +76 -0
- package/dist/ml/head_trainer.d.ts.map +1 -0
- package/dist/ml/head_trainer.js +182 -0
- package/dist/ml/head_trainer.js.map +1 -0
- package/dist/ml/index.d.ts +23 -0
- package/dist/ml/index.d.ts.map +1 -0
- package/dist/ml/index.js +11 -0
- package/dist/ml/index.js.map +1 -0
- package/dist/ml/registry.d.ts +20 -0
- package/dist/ml/registry.d.ts.map +1 -0
- package/dist/ml/registry.js +43 -0
- package/dist/ml/registry.js.map +1 -0
- package/dist/ml/run.d.ts +94 -0
- package/dist/ml/run.d.ts.map +1 -0
- package/dist/ml/run.js +130 -0
- package/dist/ml/run.js.map +1 -0
- package/dist/ml/tasks/experience.d.ts +90 -0
- package/dist/ml/tasks/experience.d.ts.map +1 -0
- package/dist/ml/tasks/experience.js +156 -0
- package/dist/ml/tasks/experience.js.map +1 -0
- package/dist/ml/tasks/index.d.ts +10 -0
- package/dist/ml/tasks/index.d.ts.map +1 -0
- package/dist/ml/tasks/index.js +28 -0
- package/dist/ml/tasks/index.js.map +1 -0
- package/dist/ml/tasks/local_success.d.ts +55 -0
- package/dist/ml/tasks/local_success.d.ts.map +1 -0
- package/dist/ml/tasks/local_success.js +77 -0
- package/dist/ml/tasks/local_success.js.map +1 -0
- package/dist/ml/tasks/run_anomaly.d.ts +51 -0
- package/dist/ml/tasks/run_anomaly.d.ts.map +1 -0
- package/dist/ml/tasks/run_anomaly.js +60 -0
- package/dist/ml/tasks/run_anomaly.js.map +1 -0
- package/dist/ml/train.d.ts +64 -0
- package/dist/ml/train.d.ts.map +1 -0
- package/dist/ml/train.js +101 -0
- package/dist/ml/train.js.map +1 -0
- package/dist/ml/types.d.ts +112 -0
- package/dist/ml/types.d.ts.map +1 -0
- package/dist/ml/types.js +23 -0
- package/dist/ml/types.js.map +1 -0
- package/dist/moe/moe_model.d.ts.map +1 -1
- package/dist/moe/moe_model.js +1 -16
- package/dist/moe/moe_model.js.map +1 -1
- package/dist/training/cpu_ops.d.ts +2 -0
- package/dist/training/cpu_ops.d.ts.map +1 -1
- package/dist/training/cpu_ops.js +16 -0
- package/dist/training/cpu_ops.js.map +1 -1
- package/package.json +1 -1
- package/src/evermind/delta_wire.ts +148 -114
- package/src/evermind/index.ts +35 -32
- package/src/evermind/merge.ts +53 -0
- package/src/evermind/package.ts +488 -464
- package/src/index.ts +15 -1
- package/src/limbic/index.ts +5 -0
- package/src/limbic/limbic_task.ts +62 -0
- package/src/limbic/limbic_trainer.ts +91 -258
- package/src/ml/classical/ewma.ts +68 -0
- package/src/ml/classical/gbm.ts +143 -0
- package/src/ml/classical/index.ts +76 -0
- package/src/ml/classical/isolation_forest.ts +88 -0
- package/src/ml/classical/kmeans.ts +102 -0
- package/src/ml/classical/knn.ts +98 -0
- package/src/ml/classical/linear.ts +119 -0
- package/src/ml/classical/logistic.ts +125 -0
- package/src/ml/classical/math.ts +93 -0
- package/src/ml/classical/naive_bayes.ts +77 -0
- package/src/ml/classical/robust_z.ts +65 -0
- package/src/ml/dense_head.ts +157 -0
- package/src/ml/features.ts +61 -0
- package/src/ml/federate.ts +160 -0
- package/src/ml/head_trainer.ts +268 -0
- package/src/ml/index.ts +57 -0
- package/src/ml/registry.ts +44 -0
- package/src/ml/run.ts +178 -0
- package/src/ml/tasks/experience.ts +214 -0
- package/src/ml/tasks/index.ts +30 -0
- package/src/ml/tasks/local_success.ts +118 -0
- package/src/ml/tasks/run_anomaly.ts +90 -0
- package/src/ml/train.ts +136 -0
- package/src/ml/types.ts +132 -0
- package/src/moe/moe_model.ts +1 -13
- package/src/training/cpu_ops.ts +141 -127
|
@@ -1,114 +1,148 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* evermind/delta_wire.ts — the wire contract of a pre-diffed weight-delta contribution.
|
|
3
|
-
*
|
|
4
|
-
* A learner with CPU to spare adapts a private copy of a model version, diffs it
|
|
5
|
-
* against that version (`diffCheckpoints`) and ships only the sparse diff; the single
|
|
6
|
-
* writer then merges it (`mergeCheckpointDiffs`) instead of running the fit.
|
|
7
|
-
* Everything the PRODUCER and the WRITER must agree on lives here, once:
|
|
8
|
-
* - the payload shape (`diff` base64 + integer `baseVersion`, optional weight/label
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
export const
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
/** A
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
1
|
+
/**
|
|
2
|
+
* evermind/delta_wire.ts — the wire contract of a pre-diffed weight-delta contribution.
|
|
3
|
+
*
|
|
4
|
+
* A learner with CPU to spare adapts a private copy of a model version, diffs it
|
|
5
|
+
* against that version (`diffCheckpoints`) and ships only the sparse diff; the single
|
|
6
|
+
* writer then merges it (`mergeCheckpointDiffs`) instead of running the fit.
|
|
7
|
+
* Everything the PRODUCER and the WRITER must agree on lives here, once:
|
|
8
|
+
* - the payload shape (`diff` base64 + integer `baseVersion`, optional weight/label,
|
|
9
|
+
* optional `target` naming WHICH checkpoint the diff is against — the model's own
|
|
10
|
+
* weights, or one trained ML head carried in the manifest's `mlHeads`);
|
|
11
|
+
* - the size cap, so a producer can fall back before it is refused;
|
|
12
|
+
* - the structural check a delta must pass before it may join a merge batch.
|
|
13
|
+
*
|
|
14
|
+
* That last one is load-bearing: `mergeCheckpointDiffs` throws on a malformed diff,
|
|
15
|
+
* and one throw drops the whole batch. A single bad push must cost only itself.
|
|
16
|
+
*/
|
|
17
|
+
import { deserializeRowDelta } from "../utils/delta.js";
|
|
18
|
+
import { verifyCrcTrailer } from "../utils/crc32.js";
|
|
19
|
+
import { base64ToBytes, bytesToBase64 } from "../utils/base64.js";
|
|
20
|
+
import { ML_TASK_ID_RE } from "../ml/types.js";
|
|
21
|
+
|
|
22
|
+
/** Max accepted base64 delta, in characters (~8 MiB). */
|
|
23
|
+
export const MAX_DELTA_B64_CHARS = 8 * 1024 * 1024;
|
|
24
|
+
|
|
25
|
+
/** Chars of provenance label kept — a diff carries no text, so the label is its only description. */
|
|
26
|
+
export const DELTA_LABEL_MAX_CHARS = 800;
|
|
27
|
+
|
|
28
|
+
/** A delta's target: absent = the model's own checkpoint; `ml:<task id>` = that trained ML head. */
|
|
29
|
+
export const DELTA_TARGET_RE = new RegExp(`^ml:${ML_TASK_ID_RE.source.slice(1)}`);
|
|
30
|
+
|
|
31
|
+
/** The delta target of the trained head for catalog task `taskId`. */
|
|
32
|
+
export function mlHeadTarget(taskId: string): string {
|
|
33
|
+
return `ml:${taskId}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Standard base64 alphabet with optional padding. */
|
|
37
|
+
const BASE64_RE = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
38
|
+
|
|
39
|
+
/** A validated delta contribution — the exact body a producer sends. */
|
|
40
|
+
export interface DeltaLearnPayload {
|
|
41
|
+
/** base64 serialized RowDelta (`diffCheckpoints(base, adapted)`). */
|
|
42
|
+
diff: string;
|
|
43
|
+
/** The model version the delta was diffed against. */
|
|
44
|
+
baseVersion: number;
|
|
45
|
+
/** FedAvg sample weight (> 0). Absent means the writer's default (1). */
|
|
46
|
+
weight?: number;
|
|
47
|
+
/** Provenance for the merged row (e.g. the run's task). */
|
|
48
|
+
label?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Which checkpoint the diff is against. Absent = the model's own weights (every
|
|
51
|
+
* delta written before heads federated); `ml:<task id>` = that trained ML head.
|
|
52
|
+
*/
|
|
53
|
+
target?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Why a body was refused: `invalid` (malformed) or `too-large` (over {@link MAX_DELTA_B64_CHARS}). */
|
|
57
|
+
export type DeltaParseResult =
|
|
58
|
+
| { ok: true; payload: DeltaLearnPayload }
|
|
59
|
+
| { ok: false; reason: "invalid" | "too-large"; error: string };
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse + validate a delta body. Pure — a front door calls it to refuse a bad body
|
|
63
|
+
* early, and the single writer calls it again because it trusts no caller.
|
|
64
|
+
*/
|
|
65
|
+
export function parseDeltaLearnPayload(raw: unknown): DeltaParseResult {
|
|
66
|
+
const body = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : {};
|
|
67
|
+
const diff = typeof body.diff === "string" ? body.diff : "";
|
|
68
|
+
const baseVersion = typeof body.baseVersion === "number" ? body.baseVersion : Number.NaN;
|
|
69
|
+
if (!diff || !Number.isInteger(baseVersion)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
reason: "invalid",
|
|
73
|
+
error: "diff (base64) and baseVersion (integer head version the delta was taken against) are required",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (diff.length > MAX_DELTA_B64_CHARS) {
|
|
77
|
+
return { ok: false, reason: "too-large", error: `delta too large (max ${MAX_DELTA_B64_CHARS} base64 characters)` };
|
|
78
|
+
}
|
|
79
|
+
if (diff.length % 4 !== 0 || !BASE64_RE.test(diff)) {
|
|
80
|
+
return { ok: false, reason: "invalid", error: "diff must be standard base64" };
|
|
81
|
+
}
|
|
82
|
+
const weight = typeof body.weight === "number" && Number.isFinite(body.weight) && body.weight > 0 ? body.weight : undefined;
|
|
83
|
+
const label = typeof body.label === "string" && body.label.trim() ? body.label.trim().slice(0, DELTA_LABEL_MAX_CHARS) : undefined;
|
|
84
|
+
if (body.target !== undefined && (typeof body.target !== "string" || !DELTA_TARGET_RE.test(body.target))) {
|
|
85
|
+
return { ok: false, reason: "invalid", error: "target, when present, must be ml:<task id>" };
|
|
86
|
+
}
|
|
87
|
+
const target = body.target as string | undefined;
|
|
88
|
+
return {
|
|
89
|
+
ok: true,
|
|
90
|
+
payload: {
|
|
91
|
+
diff,
|
|
92
|
+
baseVersion,
|
|
93
|
+
...(weight !== undefined ? { weight } : {}),
|
|
94
|
+
...(label ? { label } : {}),
|
|
95
|
+
...(target ? { target } : {}),
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Build the payload for one computed diff. The caller owns the size guard (compare
|
|
102
|
+
* `payload.diff.length` with {@link MAX_DELTA_B64_CHARS}) so it can choose a fallback.
|
|
103
|
+
*/
|
|
104
|
+
export function buildDeltaLearnPayload(
|
|
105
|
+
diff: ArrayBuffer,
|
|
106
|
+
baseVersion: number,
|
|
107
|
+
weight?: number,
|
|
108
|
+
label?: string,
|
|
109
|
+
target?: string,
|
|
110
|
+
): DeltaLearnPayload {
|
|
111
|
+
const trimmed = (label ?? "").trim();
|
|
112
|
+
if (target !== undefined && !DELTA_TARGET_RE.test(target)) throw new Error(`buildDeltaLearnPayload: bad target '${target}'`);
|
|
113
|
+
return {
|
|
114
|
+
diff: bytesToBase64(diff),
|
|
115
|
+
baseVersion,
|
|
116
|
+
...(typeof weight === "number" && Number.isFinite(weight) && weight > 0 ? { weight } : {}),
|
|
117
|
+
...(trimmed ? { label: trimmed.slice(0, DELTA_LABEL_MAX_CHARS) } : {}),
|
|
118
|
+
...(target ? { target } : {}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Decode a payload's base64 `diff` to the serialized RowDelta bytes. Throws on malformed base64. */
|
|
123
|
+
export function decodeDeltaPayload(payload: Pick<DeltaLearnPayload, "diff">): ArrayBuffer {
|
|
124
|
+
return base64ToBytes(payload.diff);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Structural check of ONE delta against the base it claims to diff: it must
|
|
129
|
+
* deserialize, be element-granular, index only elements the base has, and carry
|
|
130
|
+
* finite values. Returns why it is unusable, or null when it can join a merge.
|
|
131
|
+
*/
|
|
132
|
+
export function deltaUnusableReason(delta: ArrayBuffer, baseCheckpoint: ArrayBuffer): string | null {
|
|
133
|
+
try {
|
|
134
|
+
const rd = deserializeRowDelta(delta);
|
|
135
|
+
if (rd.rowSize !== 1) return `expected an element-granular delta (rowSize 1), got ${rd.rowSize}`;
|
|
136
|
+
const elements = verifyCrcTrailer(baseCheckpoint).body.byteLength / 4;
|
|
137
|
+
for (let i = 0; i < rd.rows.length; i++) {
|
|
138
|
+
const idx = rd.rows[i]!;
|
|
139
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= elements) {
|
|
140
|
+
return `row index ${idx} is outside the base checkpoint (${elements} elements)`;
|
|
141
|
+
}
|
|
142
|
+
if (!Number.isFinite(rd.data[i]!)) return `non-finite value at row ${idx}`;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return `not a serialized RowDelta: ${error instanceof Error ? error.message : String(error)}`;
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/evermind/index.ts
CHANGED
|
@@ -1,32 +1,35 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Evermind — the engine half of the Evermind module: the portable `.evermind`
|
|
3
|
-
* artifact plus the learning pipeline every learner shares (adapt, diff wire
|
|
4
|
-
* contract, FedAvg merge, pre/post-merge eval).
|
|
5
|
-
*/
|
|
6
|
-
export { EvermindModelPackage, PACKAGE_HEADER_BYTES } from "./package.js";
|
|
7
|
-
export type {
|
|
8
|
-
EvermindModelManifest,
|
|
9
|
-
EvermindModelCard,
|
|
10
|
-
EvermindModelType,
|
|
11
|
-
EvermindModality,
|
|
12
|
-
PackageMeta,
|
|
13
|
-
ValidationResult,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export type {
|
|
30
|
-
|
|
31
|
-
export {
|
|
32
|
-
export type {
|
|
1
|
+
/**
|
|
2
|
+
* Evermind — the engine half of the Evermind module: the portable `.evermind`
|
|
3
|
+
* artifact plus the learning pipeline every learner shares (adapt, diff wire
|
|
4
|
+
* contract, FedAvg merge, pre/post-merge eval).
|
|
5
|
+
*/
|
|
6
|
+
export { EvermindModelPackage, PACKAGE_HEADER_BYTES } from "./package.js";
|
|
7
|
+
export type {
|
|
8
|
+
EvermindModelManifest,
|
|
9
|
+
EvermindModelCard,
|
|
10
|
+
EvermindModelType,
|
|
11
|
+
EvermindModality,
|
|
12
|
+
PackageMeta,
|
|
13
|
+
ValidationResult,
|
|
14
|
+
EvermindMlHead,
|
|
15
|
+
} from "./package.js";
|
|
16
|
+
|
|
17
|
+
export { adaptAndDiff, adaptPackage, EVERMIND_ADAPT_MAX_CHARS } from "./adapt.js";
|
|
18
|
+
export type { AdaptResult, AdaptedPackage, AdaptOptions, AdaptTokenizer } from "./adapt.js";
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
parseDeltaLearnPayload,
|
|
22
|
+
buildDeltaLearnPayload,
|
|
23
|
+
decodeDeltaPayload,
|
|
24
|
+
deltaUnusableReason,
|
|
25
|
+
MAX_DELTA_B64_CHARS,
|
|
26
|
+
DELTA_TARGET_RE,
|
|
27
|
+
mlHeadTarget,
|
|
28
|
+
} from "./delta_wire.js";
|
|
29
|
+
export type { DeltaLearnPayload, DeltaParseResult } from "./delta_wire.js";
|
|
30
|
+
|
|
31
|
+
export { mergeCheckpointDiffs, mergeCheckpointDiffsGated } from "./merge.js";
|
|
32
|
+
export type { MergeResult, GatedMergeResult, MergeGateOptions } from "./merge.js";
|
|
33
|
+
|
|
34
|
+
export { meanEvalLoss } from "./eval.js";
|
|
35
|
+
export type { EvalExample, EvalTokenizer } from "./eval.js";
|
package/src/evermind/merge.ts
CHANGED
|
@@ -104,3 +104,56 @@ export function mergeCheckpointDiffs(
|
|
|
104
104
|
const checkpoint = applyCheckpointDiff(baseCheckpoint, serializeRowDelta(merged));
|
|
105
105
|
return { checkpoint, mergedRows: rows.length, contributors: parsed.length, deltaNorm: Math.sqrt(sumSq) };
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
/** A merge that passed (or failed) its held-out regression gate. */
|
|
109
|
+
export interface GatedMergeResult extends MergeResult {
|
|
110
|
+
/** True when the merged checkpoint was kept. When false, `checkpoint` is the BASE. */
|
|
111
|
+
accepted: boolean;
|
|
112
|
+
/** Held-out loss of the base / the merged candidate (null when unscorable). */
|
|
113
|
+
baseLoss: number | null;
|
|
114
|
+
newLoss: number | null;
|
|
115
|
+
/** Why the merge was refused. */
|
|
116
|
+
refusedReason?: "nothing-merged" | "regressed" | "unscored";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface MergeGateOptions {
|
|
120
|
+
/** Loss increase still accepted (absolute). Default 0 — any regression refuses. */
|
|
121
|
+
tolerance?: number;
|
|
122
|
+
/** Refuse when either side cannot be scored. Default false (an unscored merge is kept). */
|
|
123
|
+
requireEval?: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* {@link mergeCheckpointDiffs}, then the pre/post held-out check that LM merges
|
|
128
|
+
* record (`meanEvalLoss` of the previous vs the merged version) — enforced: a merge
|
|
129
|
+
* whose held-out loss rises by more than `tolerance` is refused and the base is
|
|
130
|
+
* returned unchanged. `score` maps a checkpoint to its held-out loss (lower is
|
|
131
|
+
* better); for an LM that is `meanEvalLoss` over the reloaded model, for a trained
|
|
132
|
+
* ML head its task's eval loss. Still the ONE merge — this only gates it.
|
|
133
|
+
*/
|
|
134
|
+
export function mergeCheckpointDiffsGated(
|
|
135
|
+
baseCheckpoint: ArrayBuffer,
|
|
136
|
+
diffs: ArrayBuffer[],
|
|
137
|
+
weights: number[] | undefined,
|
|
138
|
+
score: (checkpoint: ArrayBuffer) => number | null,
|
|
139
|
+
opts: MergeGateOptions = {},
|
|
140
|
+
): GatedMergeResult {
|
|
141
|
+
const merged = mergeCheckpointDiffs(baseCheckpoint, diffs, weights);
|
|
142
|
+
const refuse = (refusedReason: GatedMergeResult["refusedReason"], baseLoss: number | null, newLoss: number | null): GatedMergeResult => ({
|
|
143
|
+
...merged,
|
|
144
|
+
checkpoint: baseCheckpoint,
|
|
145
|
+
deltaNorm: 0,
|
|
146
|
+
accepted: false,
|
|
147
|
+
baseLoss,
|
|
148
|
+
newLoss,
|
|
149
|
+
refusedReason,
|
|
150
|
+
});
|
|
151
|
+
if (merged.mergedRows === 0) return refuse("nothing-merged", null, null);
|
|
152
|
+
const baseLoss = score(baseCheckpoint);
|
|
153
|
+
const newLoss = score(merged.checkpoint);
|
|
154
|
+
if (baseLoss == null || newLoss == null) {
|
|
155
|
+
return opts.requireEval ? refuse("unscored", baseLoss, newLoss) : { ...merged, accepted: true, baseLoss, newLoss };
|
|
156
|
+
}
|
|
157
|
+
if (newLoss > baseLoss + (opts.tolerance ?? 0)) return refuse("regressed", baseLoss, newLoss);
|
|
158
|
+
return { ...merged, accepted: true, baseLoss, newLoss };
|
|
159
|
+
}
|