@openfluke/welvet 0.1.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.
- package/LICENSE +201 -0
- package/README.md +656 -0
- package/dist/env.d.ts +3 -0
- package/dist/env.js +3 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +85 -0
- package/dist/loader.d.ts +3 -0
- package/dist/loader.js +90 -0
- package/dist/loom.wasm +0 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.js +12 -0
- package/dist/wasm_exec.js +665 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ensureGoRuntime, resolvePackagedWasmURL, instantiateGoWasm, } from "./loader";
|
|
2
|
+
// tiny helper that waits until WASM has placed symbols on globalThis
|
|
3
|
+
async function waitForExports(keys, timeoutMs = 5000) {
|
|
4
|
+
const t0 = performance.now();
|
|
5
|
+
for (;;) {
|
|
6
|
+
const ok = keys.every((k) => globalThis[k]);
|
|
7
|
+
if (ok)
|
|
8
|
+
return;
|
|
9
|
+
if (performance.now() - t0 > timeoutMs) {
|
|
10
|
+
throw new Error(`loom: timed out waiting for exports: ${keys.join(", ")}`);
|
|
11
|
+
}
|
|
12
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the LOOM WASM module and return the API
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { initLoom, ActivationType } from '@openfluke/loom';
|
|
21
|
+
*
|
|
22
|
+
* const loom = await initLoom();
|
|
23
|
+
*
|
|
24
|
+
* // Create a network: 784 → 392 → 10
|
|
25
|
+
* const network = loom.NewNetwork(784, 1, 1, 2);
|
|
26
|
+
*
|
|
27
|
+
* // Configure layers
|
|
28
|
+
* const layer0 = loom.InitDenseLayer(784, 392, ActivationType.ReLU);
|
|
29
|
+
* const layer1 = loom.InitDenseLayer(392, 10, ActivationType.Sigmoid);
|
|
30
|
+
*
|
|
31
|
+
* network.SetLayer(JSON.stringify([0, 0, 0, JSON.parse(layer0)]));
|
|
32
|
+
* network.SetLayer(JSON.stringify([0, 0, 1, JSON.parse(layer1)]));
|
|
33
|
+
*
|
|
34
|
+
* // Forward pass
|
|
35
|
+
* const input = new Array(784).fill(0).map(() => Math.random());
|
|
36
|
+
* const resultJSON = network.ForwardCPU(JSON.stringify([input]));
|
|
37
|
+
* const [output, duration] = JSON.parse(resultJSON);
|
|
38
|
+
*
|
|
39
|
+
* console.log('Output:', output);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export async function initLoom(opts = {}) {
|
|
43
|
+
await ensureGoRuntime(opts.injectGoRuntime !== false);
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const go = new globalThis.Go();
|
|
46
|
+
const wasmUrl = await resolvePackagedWasmURL(opts.wasmUrl);
|
|
47
|
+
const instance = await instantiateGoWasm(go, wasmUrl);
|
|
48
|
+
// IMPORTANT: don't await this — it resolves when the Go program exits.
|
|
49
|
+
// Let it run; then poll for the exported symbols.
|
|
50
|
+
// no await:
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
52
|
+
Promise.resolve().then(() => go.run(instance));
|
|
53
|
+
// wait until your Go code has installed the globals
|
|
54
|
+
await waitForExports(["NewNetwork", "LoadModelFromString", "CallLayerInit"]);
|
|
55
|
+
const g = globalThis;
|
|
56
|
+
// Helper function to call layer init functions from the registry
|
|
57
|
+
const callLayerInit = (funcName, ...params) => {
|
|
58
|
+
return g.CallLayerInit(funcName, JSON.stringify(params));
|
|
59
|
+
};
|
|
60
|
+
const api = {
|
|
61
|
+
NewNetwork: g.NewNetwork,
|
|
62
|
+
LoadModelFromString: g.LoadModelFromString,
|
|
63
|
+
// Layer initialization functions using CallLayerInit (registry-based)
|
|
64
|
+
InitDenseLayer: (inputSize, outputSize, activation) => {
|
|
65
|
+
return callLayerInit("InitDenseLayer", inputSize, outputSize, activation);
|
|
66
|
+
},
|
|
67
|
+
InitConv2DLayer: (inChannels, outChannels, kernelSize, stride, padding, inputH, inputW, activation) => {
|
|
68
|
+
return callLayerInit("InitConv2DLayer", inChannels, outChannels, kernelSize, stride, padding, inputH, inputW, activation);
|
|
69
|
+
},
|
|
70
|
+
InitMultiHeadAttentionLayer: (dModel, numHeads, seqLength, activation) => {
|
|
71
|
+
return callLayerInit("InitMultiHeadAttentionLayer", dModel, numHeads, seqLength, activation);
|
|
72
|
+
},
|
|
73
|
+
InitRNNLayer: (inputSize, hiddenSize, seqLength, outputSize) => {
|
|
74
|
+
return callLayerInit("InitRNNLayer", inputSize, hiddenSize, seqLength, outputSize);
|
|
75
|
+
},
|
|
76
|
+
InitLSTMLayer: (inputSize, hiddenSize, seqLength, outputSize) => {
|
|
77
|
+
return callLayerInit("InitLSTMLayer", inputSize, hiddenSize, seqLength, outputSize);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
if (!api.NewNetwork) {
|
|
81
|
+
throw new Error("loom: NewNetwork not found after WASM init");
|
|
82
|
+
}
|
|
83
|
+
return api;
|
|
84
|
+
}
|
|
85
|
+
export { ActivationType } from "./types";
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function ensureGoRuntime(inject?: boolean): Promise<void>;
|
|
2
|
+
export declare function resolvePackagedWasmURL(override?: string | URL): Promise<string | URL>;
|
|
3
|
+
export declare function instantiateGoWasm(go: any, wasmUrl: string | URL): Promise<WebAssembly.Instance>;
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isBrowser } from "./env";
|
|
2
|
+
let goRuntimeInjected = false;
|
|
3
|
+
let wasmExecTextBundled;
|
|
4
|
+
let wasmUrlBundled;
|
|
5
|
+
async function tryLoadBundlerAssets() {
|
|
6
|
+
try {
|
|
7
|
+
const raw = await import("./wasm_exec.js?raw");
|
|
8
|
+
wasmExecTextBundled = raw.default;
|
|
9
|
+
}
|
|
10
|
+
catch { }
|
|
11
|
+
try {
|
|
12
|
+
const url = await import("./loom.wasm?url");
|
|
13
|
+
wasmUrlBundled = url.default;
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
}
|
|
17
|
+
export async function ensureGoRuntime(inject = true) {
|
|
18
|
+
if (!inject || goRuntimeInjected)
|
|
19
|
+
return;
|
|
20
|
+
if (isBrowser) {
|
|
21
|
+
if (!wasmExecTextBundled)
|
|
22
|
+
await tryLoadBundlerAssets();
|
|
23
|
+
if (wasmExecTextBundled) {
|
|
24
|
+
new Function(wasmExecTextBundled)(); // defines global Go
|
|
25
|
+
goRuntimeInjected = true;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const url = new URL("./wasm_exec.js", import.meta.url);
|
|
29
|
+
const res = await fetch(url);
|
|
30
|
+
if (!res.ok)
|
|
31
|
+
throw new Error(`Failed to fetch wasm_exec.js (${res.status} ${res.statusText}) from ${url}`);
|
|
32
|
+
const jsText = await res.text();
|
|
33
|
+
new Function(jsText)();
|
|
34
|
+
goRuntimeInjected = true;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const { readFile } = await import("node:fs/promises");
|
|
38
|
+
const { fileURLToPath } = await import("node:url");
|
|
39
|
+
const wasmExecUrl = new URL("./wasm_exec.js", import.meta.url);
|
|
40
|
+
const filePath = fileURLToPath(wasmExecUrl);
|
|
41
|
+
const jsText = await readFile(filePath, "utf8");
|
|
42
|
+
new Function(jsText)();
|
|
43
|
+
goRuntimeInjected = true;
|
|
44
|
+
}
|
|
45
|
+
export async function resolvePackagedWasmURL(override) {
|
|
46
|
+
if (override)
|
|
47
|
+
return override;
|
|
48
|
+
if (isBrowser) {
|
|
49
|
+
if (!wasmUrlBundled)
|
|
50
|
+
await tryLoadBundlerAssets();
|
|
51
|
+
if (wasmUrlBundled)
|
|
52
|
+
return wasmUrlBundled;
|
|
53
|
+
return new URL("./loom.wasm", import.meta.url);
|
|
54
|
+
}
|
|
55
|
+
return new URL("./loom.wasm", import.meta.url);
|
|
56
|
+
}
|
|
57
|
+
export async function instantiateGoWasm(go, wasmUrl) {
|
|
58
|
+
const asString = typeof wasmUrl === "string" ? wasmUrl : wasmUrl.toString();
|
|
59
|
+
if (isBrowser) {
|
|
60
|
+
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
61
|
+
const res = await fetch(asString);
|
|
62
|
+
try {
|
|
63
|
+
const { instance } = await WebAssembly.instantiateStreaming(res, go.importObject);
|
|
64
|
+
return instance;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
const buf = await res.arrayBuffer();
|
|
68
|
+
const result = (await WebAssembly.instantiate(buf, go.importObject));
|
|
69
|
+
const instance = result.instance ?? result;
|
|
70
|
+
return instance;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const res = await fetch(asString);
|
|
75
|
+
const buf = await res.arrayBuffer();
|
|
76
|
+
const result = (await WebAssembly.instantiate(buf, go.importObject));
|
|
77
|
+
const instance = result.instance ?? result;
|
|
78
|
+
return instance;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Node/Bun
|
|
82
|
+
const { readFile } = await import("node:fs/promises");
|
|
83
|
+
const { fileURLToPath } = await import("node:url");
|
|
84
|
+
const url = typeof wasmUrl === "string" ? new URL(wasmUrl, import.meta.url) : wasmUrl;
|
|
85
|
+
const filePath = fileURLToPath(url);
|
|
86
|
+
const buf = await readFile(filePath);
|
|
87
|
+
const result = (await WebAssembly.instantiate(buf, go.importObject));
|
|
88
|
+
const instance = result.instance ?? result;
|
|
89
|
+
return instance;
|
|
90
|
+
}
|
package/dist/loom.wasm
ADDED
|
Binary file
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LOOM Network instance returned by NewNetwork()
|
|
3
|
+
* All Network methods are dynamically exposed via reflection
|
|
4
|
+
*/
|
|
5
|
+
export interface LoomNetwork {
|
|
6
|
+
ForwardCPU(inputJSON: string): string;
|
|
7
|
+
ForwardGPU?(inputJSON: string): string;
|
|
8
|
+
BackwardCPU(gradOutputJSON: string): string;
|
|
9
|
+
BackwardGPU?(gradOutputJSON: string): string;
|
|
10
|
+
UpdateWeights(learningRateJSON: string): string;
|
|
11
|
+
Train(batchesAndConfigJSON: string): string;
|
|
12
|
+
SetLayer(configJSON: string): string;
|
|
13
|
+
InitGPU?(argsJSON: string): string;
|
|
14
|
+
ReleaseGPU?(): string;
|
|
15
|
+
SaveModelToString(modelIDJSON: string): string;
|
|
16
|
+
SaveModel?(pathAndIDJSON: string): string;
|
|
17
|
+
GetMethods(): string;
|
|
18
|
+
ListMethods(): string;
|
|
19
|
+
GetMethodSignature(methodNameJSON: string): string;
|
|
20
|
+
HasMethod(methodName: string): boolean;
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Global WASM functions exposed by LOOM
|
|
25
|
+
*/
|
|
26
|
+
export interface LoomAPI {
|
|
27
|
+
/**
|
|
28
|
+
* Create a new neural network
|
|
29
|
+
* @param inputSize - Size of input layer
|
|
30
|
+
* @param gridRows - Number of grid rows
|
|
31
|
+
* @param gridCols - Number of grid columns
|
|
32
|
+
* @param layersPerCell - Number of layers per grid cell
|
|
33
|
+
* @returns LoomNetwork instance with all methods
|
|
34
|
+
*/
|
|
35
|
+
NewNetwork: (inputSize: number, gridRows: number, gridCols: number, layersPerCell: number) => LoomNetwork;
|
|
36
|
+
/**
|
|
37
|
+
* Load a model from JSON string
|
|
38
|
+
* @param modelJSON - JSON string of the model
|
|
39
|
+
* @param modelID - Model identifier
|
|
40
|
+
* @returns LoomNetwork instance
|
|
41
|
+
*/
|
|
42
|
+
LoadModelFromString: (modelJSON: string, modelID: string) => LoomNetwork;
|
|
43
|
+
/**
|
|
44
|
+
* Initialize a dense (fully-connected) layer configuration
|
|
45
|
+
* @param inputSize - Input dimension
|
|
46
|
+
* @param outputSize - Output dimension
|
|
47
|
+
* @param activation - Activation type (0=ReLU, 1=Sigmoid, 2=Tanh, 3=Linear)
|
|
48
|
+
* @returns JSON string of layer configuration
|
|
49
|
+
*/
|
|
50
|
+
InitDenseLayer: (inputSize: number, outputSize: number, activation: number) => string;
|
|
51
|
+
/**
|
|
52
|
+
* Initialize a multi-head attention layer configuration
|
|
53
|
+
* @param dModel - Model dimension
|
|
54
|
+
* @param numHeads - Number of attention heads
|
|
55
|
+
* @param seqLength - Sequence length
|
|
56
|
+
* @param activation - Activation type
|
|
57
|
+
* @returns JSON string of layer configuration
|
|
58
|
+
*/
|
|
59
|
+
InitMultiHeadAttentionLayer: (dModel: number, numHeads: number, seqLength: number, activation: number) => string;
|
|
60
|
+
/**
|
|
61
|
+
* Initialize a 2D convolutional layer configuration
|
|
62
|
+
* @param inputHeight - Input height
|
|
63
|
+
* @param inputWidth - Input width
|
|
64
|
+
* @param inputChannels - Number of input channels
|
|
65
|
+
* @param kernelSize - Kernel size
|
|
66
|
+
* @param stride - Stride
|
|
67
|
+
* @param padding - Padding
|
|
68
|
+
* @param filters - Number of filters
|
|
69
|
+
* @param activation - Activation type
|
|
70
|
+
* @returns JSON string of layer configuration
|
|
71
|
+
*/
|
|
72
|
+
InitConv2DLayer: (inputHeight: number, inputWidth: number, inputChannels: number, kernelSize: number, stride: number, padding: number, filters: number, activation: number) => string;
|
|
73
|
+
/**
|
|
74
|
+
* Initialize an RNN layer configuration
|
|
75
|
+
* @param inputSize - Input size
|
|
76
|
+
* @param hiddenSize - Hidden size
|
|
77
|
+
* @param batchSize - Batch size
|
|
78
|
+
* @param seqLength - Sequence length
|
|
79
|
+
* @returns JSON string of layer configuration
|
|
80
|
+
*/
|
|
81
|
+
InitRNNLayer: (inputSize: number, hiddenSize: number, batchSize: number, seqLength: number) => string;
|
|
82
|
+
/**
|
|
83
|
+
* Initialize an LSTM layer configuration
|
|
84
|
+
* @param inputSize - Input size
|
|
85
|
+
* @param hiddenSize - Hidden size
|
|
86
|
+
* @param batchSize - Batch size
|
|
87
|
+
* @param seqLength - Sequence length
|
|
88
|
+
* @returns JSON string of layer configuration
|
|
89
|
+
*/
|
|
90
|
+
InitLSTMLayer: (inputSize: number, hiddenSize: number, batchSize: number, seqLength: number) => string;
|
|
91
|
+
}
|
|
92
|
+
export interface InitOptions {
|
|
93
|
+
/** Override where the WASM is read from. Useful in exotic deploys (Capacitor, CDN). */
|
|
94
|
+
wasmUrl?: string | URL;
|
|
95
|
+
/** Set false to skip injecting wasm_exec.js (e.g., if you already included it). Default: true */
|
|
96
|
+
injectGoRuntime?: boolean;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Activation function types
|
|
100
|
+
*/
|
|
101
|
+
export declare enum ActivationType {
|
|
102
|
+
ReLU = 0,
|
|
103
|
+
Sigmoid = 1,
|
|
104
|
+
Tanh = 2,
|
|
105
|
+
Softplus = 3,
|
|
106
|
+
LeakyReLU = 4,
|
|
107
|
+
Linear = 5
|
|
108
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activation function types
|
|
3
|
+
*/
|
|
4
|
+
export var ActivationType;
|
|
5
|
+
(function (ActivationType) {
|
|
6
|
+
ActivationType[ActivationType["ReLU"] = 0] = "ReLU";
|
|
7
|
+
ActivationType[ActivationType["Sigmoid"] = 1] = "Sigmoid";
|
|
8
|
+
ActivationType[ActivationType["Tanh"] = 2] = "Tanh";
|
|
9
|
+
ActivationType[ActivationType["Softplus"] = 3] = "Softplus";
|
|
10
|
+
ActivationType[ActivationType["LeakyReLU"] = 4] = "LeakyReLU";
|
|
11
|
+
ActivationType[ActivationType["Linear"] = 5] = "Linear";
|
|
12
|
+
})(ActivationType || (ActivationType = {}));
|