@seanhogg/builderforce-memory 2026.6.20 → 2026.6.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.
- package/dist/cognition/EvermindCognition.d.ts +61 -0
- package/dist/cognition/EvermindCognition.d.ts.map +1 -0
- package/dist/cognition/EvermindCognition.js +109 -0
- package/dist/cognition/EvermindCognition.js.map +1 -0
- package/dist/cognition/gatherers.d.ts +22 -0
- package/dist/cognition/gatherers.d.ts.map +1 -0
- package/dist/cognition/gatherers.js +29 -0
- package/dist/cognition/gatherers.js.map +1 -0
- package/dist/cognition/index.d.ts +12 -0
- package/dist/cognition/index.d.ts.map +1 -0
- package/dist/cognition/index.js +9 -0
- package/dist/cognition/index.js.map +1 -0
- package/dist/cognition/types.d.ts +84 -0
- package/dist/cognition/types.d.ts.map +1 -0
- package/dist/cognition/types.js +12 -0
- package/dist/cognition/types.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/limbic/LimbicSession.d.ts +63 -0
- package/dist/limbic/LimbicSession.d.ts.map +1 -0
- package/dist/limbic/LimbicSession.js +188 -0
- package/dist/limbic/LimbicSession.js.map +1 -0
- package/package.json +3 -3
- package/src/cognition/EvermindCognition.ts +156 -0
- package/src/cognition/gatherers.ts +40 -0
- package/src/cognition/index.ts +20 -0
- package/src/cognition/types.ts +88 -0
- package/src/index.ts +42 -0
- package/src/limbic/LimbicSession.ts +253 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LimbicSession.ts – high-level facade over the limbic affect model.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors {@link MambaSession}: collapses GPU acquisition, model construction,
|
|
5
|
+
* checkpoint load, and the trainer into a single `LimbicSession.create()` call,
|
|
6
|
+
* with the same WebGPU-or-CPU-fallback contract. The agent runtime consumes
|
|
7
|
+
* this through `@seanhogg/builderforce-memory` to run the limbic system on a
|
|
8
|
+
* self-hosted node (GPU via @webgpu/node when present, CPU otherwise).
|
|
9
|
+
*
|
|
10
|
+
* const limbic = await LimbicSession.create({ gpuAdapter, checkpointBuffer });
|
|
11
|
+
* const { delta, reward } = await limbic.step(experienceEmbedding, state);
|
|
12
|
+
* await limbic.train(samples, { epochs: 30 });
|
|
13
|
+
* const bin = limbic.exportWeights({ fp16: true });
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
LimbicModel,
|
|
18
|
+
LimbicTrainer,
|
|
19
|
+
LIMBIC_AFFECT_WGSL,
|
|
20
|
+
createStorageBuffer,
|
|
21
|
+
createEmptyStorageBuffer,
|
|
22
|
+
createUniformBuffer,
|
|
23
|
+
createComputePipeline,
|
|
24
|
+
createBindGroup,
|
|
25
|
+
dispatchKernel,
|
|
26
|
+
readBuffer,
|
|
27
|
+
type LimbicModelConfig,
|
|
28
|
+
type LimbicForward,
|
|
29
|
+
type LimbicSample,
|
|
30
|
+
type LimbicTrainOptions,
|
|
31
|
+
} from "@seanhogg/builderforce-memory-engine";
|
|
32
|
+
|
|
33
|
+
import { saveToIndexedDB, loadFromIndexedDB } from "../session/persistence.js";
|
|
34
|
+
|
|
35
|
+
export type LimbicGpuMode = "webgpu" | "cpu-fallback" | "cpu";
|
|
36
|
+
|
|
37
|
+
export interface LimbicSessionOptions {
|
|
38
|
+
/** Pre-created GPUAdapter (e.g. from @webgpu/node). When set, navigator.gpu is not used. */
|
|
39
|
+
gpuAdapter?: GPUAdapter;
|
|
40
|
+
/** Attempt a software (CPU) WebGPU adapter when no GPU is available. Default false. */
|
|
41
|
+
allowCpuFallback?: boolean;
|
|
42
|
+
/** Pre-read checkpoint bytes (Node: read the .bin with fs and pass the ArrayBuffer). */
|
|
43
|
+
checkpointBuffer?: ArrayBuffer;
|
|
44
|
+
/** IndexedDB key for save()/load(). Default 'limbic-default'. */
|
|
45
|
+
name?: string;
|
|
46
|
+
/** Injected IDBFactory (e.g. fake-indexeddb in Node). */
|
|
47
|
+
idbFactory?: IDBFactory;
|
|
48
|
+
/** Model configuration overrides. */
|
|
49
|
+
modelConfig?: Partial<LimbicModelConfig>;
|
|
50
|
+
/** Deterministic init seed. */
|
|
51
|
+
seed?: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface StepBuffers {
|
|
55
|
+
win: GPUBuffer;
|
|
56
|
+
ws: GPUBuffer;
|
|
57
|
+
aLogit: GPUBuffer;
|
|
58
|
+
woutState: GPUBuffer;
|
|
59
|
+
boutState: GPUBuffer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class LimbicSession {
|
|
63
|
+
readonly model: LimbicModel;
|
|
64
|
+
readonly trainer: LimbicTrainer;
|
|
65
|
+
readonly device: GPUDevice | null;
|
|
66
|
+
readonly gpuMode: LimbicGpuMode;
|
|
67
|
+
private readonly _name: string;
|
|
68
|
+
private readonly _idbFactory: IDBFactory | undefined;
|
|
69
|
+
|
|
70
|
+
// GPU step pipeline + buffers, allocated lazily on first GPU step.
|
|
71
|
+
private _pipeline: GPUComputePipeline | null = null;
|
|
72
|
+
private _dimsBuf: GPUBuffer | null = null;
|
|
73
|
+
private _paramBufs: StepBuffers | null = null;
|
|
74
|
+
private _paramsDirty = true;
|
|
75
|
+
|
|
76
|
+
private constructor(
|
|
77
|
+
model: LimbicModel,
|
|
78
|
+
trainer: LimbicTrainer,
|
|
79
|
+
device: GPUDevice | null,
|
|
80
|
+
gpuMode: LimbicGpuMode,
|
|
81
|
+
name: string,
|
|
82
|
+
idbFactory: IDBFactory | undefined,
|
|
83
|
+
) {
|
|
84
|
+
this.model = model;
|
|
85
|
+
this.trainer = trainer;
|
|
86
|
+
this.device = device;
|
|
87
|
+
this.gpuMode = gpuMode;
|
|
88
|
+
this._name = name;
|
|
89
|
+
this._idbFactory = idbFactory;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static async create(options: LimbicSessionOptions = {}): Promise<LimbicSession> {
|
|
93
|
+
let device: GPUDevice | null = null;
|
|
94
|
+
let gpuMode: LimbicGpuMode = "cpu";
|
|
95
|
+
|
|
96
|
+
if (options.gpuAdapter != null) {
|
|
97
|
+
try {
|
|
98
|
+
device = await options.gpuAdapter.requestDevice();
|
|
99
|
+
gpuMode = "webgpu";
|
|
100
|
+
} catch {
|
|
101
|
+
device = null;
|
|
102
|
+
gpuMode = "cpu";
|
|
103
|
+
}
|
|
104
|
+
} else if (typeof navigator !== "undefined" && navigator.gpu) {
|
|
105
|
+
try {
|
|
106
|
+
const adapter = await navigator.gpu.requestAdapter({ powerPreference: "high-performance" });
|
|
107
|
+
if (adapter) {
|
|
108
|
+
device = await adapter.requestDevice();
|
|
109
|
+
gpuMode = "webgpu";
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
device = null;
|
|
113
|
+
}
|
|
114
|
+
if (!device && options.allowCpuFallback && typeof navigator !== "undefined" && navigator.gpu) {
|
|
115
|
+
try {
|
|
116
|
+
const fb = await navigator.gpu.requestAdapter({ forceFallbackAdapter: true });
|
|
117
|
+
if (fb) {
|
|
118
|
+
device = await fb.requestDevice();
|
|
119
|
+
gpuMode = "cpu-fallback";
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
device = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const model = new LimbicModel({ ...options.modelConfig, seed: options.seed });
|
|
128
|
+
if (options.checkpointBuffer) {
|
|
129
|
+
model.loadWeights(options.checkpointBuffer);
|
|
130
|
+
}
|
|
131
|
+
const trainer = new LimbicTrainer(model, device);
|
|
132
|
+
return new LimbicSession(model, trainer, device, gpuMode, options.name ?? "limbic-default", options.idbFactory);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** One affect step. Uses the GPU kernel when a device is available, else CPU. */
|
|
136
|
+
async step(input: ArrayLike<number>, state: ArrayLike<number>, hidden?: ArrayLike<number>): Promise<LimbicForward> {
|
|
137
|
+
const h = hidden ?? this.model.initHidden();
|
|
138
|
+
if (!this.device) return this.model.forward(input, h, state);
|
|
139
|
+
return this._stepGpu(input, h, state);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private _ensureGpuStep(): { pipeline: GPUComputePipeline; params: StepBuffers; dims: GPUBuffer } {
|
|
143
|
+
const device = this.device!;
|
|
144
|
+
if (!this._pipeline) {
|
|
145
|
+
this._pipeline = createComputePipeline(device, LIMBIC_AFFECT_WGSL, "affect_step");
|
|
146
|
+
const { inputDim, hiddenDim, stateDim } = this.model.config;
|
|
147
|
+
const dims = new ArrayBuffer(16);
|
|
148
|
+
new Uint32Array(dims).set([inputDim, hiddenDim, stateDim, 0]);
|
|
149
|
+
this._dimsBuf = createUniformBuffer(device, dims);
|
|
150
|
+
}
|
|
151
|
+
if (this._paramsDirty || !this._paramBufs) {
|
|
152
|
+
this._destroyParamBufs();
|
|
153
|
+
this._paramBufs = {
|
|
154
|
+
win: createStorageBuffer(device, this.model.win, false),
|
|
155
|
+
ws: createStorageBuffer(device, this.model.ws, false),
|
|
156
|
+
aLogit: createStorageBuffer(device, this.model.aLogit, false),
|
|
157
|
+
woutState: createStorageBuffer(device, this.model.woutState, false),
|
|
158
|
+
boutState: createStorageBuffer(device, this.model.boutState, false),
|
|
159
|
+
};
|
|
160
|
+
this._paramsDirty = false;
|
|
161
|
+
}
|
|
162
|
+
return { pipeline: this._pipeline, params: this._paramBufs, dims: this._dimsBuf! };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private async _stepGpu(input: ArrayLike<number>, hPrev: ArrayLike<number>, sPrev: ArrayLike<number>): Promise<LimbicForward> {
|
|
166
|
+
const device = this.device!;
|
|
167
|
+
const { inputDim, hiddenDim, stateDim } = this.model.config;
|
|
168
|
+
const { pipeline, params, dims } = this._ensureGpuStep();
|
|
169
|
+
|
|
170
|
+
const xBuf = createStorageBuffer(device, Float32Array.from({ length: inputDim }, (_, i) => input[i] ?? 0), false);
|
|
171
|
+
const hBuf = createStorageBuffer(device, Float32Array.from({ length: hiddenDim }, (_, j) => hPrev[j] ?? 0), false);
|
|
172
|
+
const sBuf = createStorageBuffer(device, Float32Array.from({ length: stateDim }, (_, k) => sPrev[k] ?? 0), false);
|
|
173
|
+
const hOut = createEmptyStorageBuffer(device, hiddenDim * 4, true);
|
|
174
|
+
const dOut = createEmptyStorageBuffer(device, stateDim * 4, true);
|
|
175
|
+
|
|
176
|
+
const bg = createBindGroup(device, pipeline, [
|
|
177
|
+
dims,
|
|
178
|
+
params.win,
|
|
179
|
+
params.ws,
|
|
180
|
+
params.aLogit,
|
|
181
|
+
params.woutState,
|
|
182
|
+
params.boutState,
|
|
183
|
+
xBuf,
|
|
184
|
+
hBuf,
|
|
185
|
+
sBuf,
|
|
186
|
+
hOut,
|
|
187
|
+
dOut,
|
|
188
|
+
]);
|
|
189
|
+
dispatchKernel(device, pipeline, bg, [1, 1, 1]);
|
|
190
|
+
|
|
191
|
+
const hidden = (await readBuffer(device, hOut, hiddenDim * 4)).subarray(0, hiddenDim);
|
|
192
|
+
const delta = (await readBuffer(device, dOut, stateDim * 4)).subarray(0, stateDim);
|
|
193
|
+
|
|
194
|
+
// Reward head is small — compute on CPU from the GPU-produced hidden state.
|
|
195
|
+
let reward = this.model.boutReward[0]!;
|
|
196
|
+
for (let j = 0; j < hiddenDim; j++) reward += this.model.woutReward[j]! * hidden[j]!;
|
|
197
|
+
|
|
198
|
+
xBuf.destroy();
|
|
199
|
+
hBuf.destroy();
|
|
200
|
+
sBuf.destroy();
|
|
201
|
+
hOut.destroy();
|
|
202
|
+
dOut.destroy();
|
|
203
|
+
|
|
204
|
+
return { hidden: Float32Array.from(hidden), delta: Float32Array.from(delta), reward };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Train the affect model on observed experiences. Marks GPU step buffers dirty. */
|
|
208
|
+
async train(samples: LimbicSample[], opts?: LimbicTrainOptions): Promise<number[]> {
|
|
209
|
+
const losses = await this.trainer.train(samples, opts);
|
|
210
|
+
this._paramsDirty = true; // weights changed → GPU step buffers must be re-uploaded
|
|
211
|
+
return losses;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
evaluate(samples: LimbicSample[]): number {
|
|
215
|
+
return this.trainer.evaluate(samples);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
exportWeights(opts?: { fp16?: boolean }): ArrayBuffer {
|
|
219
|
+
return this.model.exportWeights(opts);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Persist weights to IndexedDB under the session name. */
|
|
223
|
+
async save(): Promise<void> {
|
|
224
|
+
await saveToIndexedDB(`limbic_${this._name}`, this.model.exportWeights({ fp16: true }), this._idbFactory);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Load weights from IndexedDB; returns true if a checkpoint was found. */
|
|
228
|
+
async load(): Promise<boolean> {
|
|
229
|
+
const buf = await loadFromIndexedDB(`limbic_${this._name}`, this._idbFactory);
|
|
230
|
+
if (!buf) return false;
|
|
231
|
+
this.model.loadWeights(buf);
|
|
232
|
+
this._paramsDirty = true;
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private _destroyParamBufs(): void {
|
|
237
|
+
if (this._paramBufs) {
|
|
238
|
+
this._paramBufs.win.destroy();
|
|
239
|
+
this._paramBufs.ws.destroy();
|
|
240
|
+
this._paramBufs.aLogit.destroy();
|
|
241
|
+
this._paramBufs.woutState.destroy();
|
|
242
|
+
this._paramBufs.boutState.destroy();
|
|
243
|
+
this._paramBufs = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
destroy(): void {
|
|
248
|
+
this._destroyParamBufs();
|
|
249
|
+
this._dimsBuf?.destroy();
|
|
250
|
+
this._dimsBuf = null;
|
|
251
|
+
this._pipeline = null;
|
|
252
|
+
}
|
|
253
|
+
}
|