@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/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";
@@ -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
@@ -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 = {}));