semantic-state-estimator 1.0.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/README.md +265 -0
- package/dist/SemanticStateEngine-BP5URJOJ.d.cts +108 -0
- package/dist/SemanticStateEngine-BP5URJOJ.d.ts +108 -0
- package/dist/embedding.worker.cjs +92 -0
- package/dist/embedding.worker.cjs.map +1 -0
- package/dist/embedding.worker.d.cts +38 -0
- package/dist/embedding.worker.d.ts +38 -0
- package/dist/embedding.worker.js +64 -0
- package/dist/embedding.worker.js.map +1 -0
- package/dist/index.cjs +236 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +78 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +202 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +39 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +13 -0
- package/dist/react.d.ts +13 -0
- package/dist/react.js +12 -0
- package/dist/react.js.map +1 -0
- package/dist/zustand.cjs +43 -0
- package/dist/zustand.cjs.map +1 -0
- package/dist/zustand.d.cts +21 -0
- package/dist/zustand.d.ts +21 -0
- package/dist/zustand.js +18 -0
- package/dist/zustand.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// src/worker/embedding.worker.ts
|
|
2
|
+
import { pipeline, env } from "@huggingface/transformers";
|
|
3
|
+
env.allowLocalModels = false;
|
|
4
|
+
var PipelineSingleton = class {
|
|
5
|
+
static async getInstance(modelName, progressCallback) {
|
|
6
|
+
if (this.instance === null || this.modelName !== modelName) {
|
|
7
|
+
this.modelName = modelName;
|
|
8
|
+
this.instance = pipeline("feature-extraction", modelName, {
|
|
9
|
+
dtype: "q8",
|
|
10
|
+
progress_callback: progressCallback
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
return this.instance;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
PipelineSingleton.instance = null;
|
|
17
|
+
PipelineSingleton.modelName = null;
|
|
18
|
+
var currentModelName = "Xenova/all-MiniLM-L6-v2";
|
|
19
|
+
function getModelName() {
|
|
20
|
+
return currentModelName;
|
|
21
|
+
}
|
|
22
|
+
async function handleInitMessage(event) {
|
|
23
|
+
currentModelName = event.data.modelName;
|
|
24
|
+
self.postMessage({ type: "STATUS", status: "loading" });
|
|
25
|
+
try {
|
|
26
|
+
await PipelineSingleton.getInstance(currentModelName, (data) => {
|
|
27
|
+
if (data.status === "progress" && data.file && data.progress !== void 0) {
|
|
28
|
+
self.postMessage({ type: "PROGRESS", file: data.file, progress: data.progress });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
self.postMessage({ type: "STATUS", status: "ready" });
|
|
32
|
+
} catch (err) {
|
|
33
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
34
|
+
self.postMessage({ type: "STATUS", status: "failed", error });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function handleMessage(event) {
|
|
38
|
+
const { id, text } = event.data;
|
|
39
|
+
try {
|
|
40
|
+
const extractor = await PipelineSingleton.getInstance(currentModelName);
|
|
41
|
+
const output = await extractor(text, { pooling: "mean", normalize: true });
|
|
42
|
+
const response = { type: "EMBED_RES", id, vector: output.data };
|
|
43
|
+
self.postMessage(response);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
46
|
+
const response = { type: "EMBED_RES", id, vector: null, error };
|
|
47
|
+
self.postMessage(response);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
self.addEventListener("message", (event) => {
|
|
51
|
+
const msg = event.data;
|
|
52
|
+
if (msg.type === "INIT") {
|
|
53
|
+
handleInitMessage(event);
|
|
54
|
+
} else {
|
|
55
|
+
handleMessage(event);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
export {
|
|
59
|
+
PipelineSingleton,
|
|
60
|
+
getModelName,
|
|
61
|
+
handleInitMessage,
|
|
62
|
+
handleMessage
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=embedding.worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/worker/embedding.worker.ts"],"sourcesContent":["import { pipeline, env } from \"@huggingface/transformers\";\nimport type { EmbeddingRequest, EmbeddingResponse, WorkerIncomingMessage, WorkerInitMessage } from \"./types.js\";\n\n// Disable local models; always load from the HuggingFace Hub.\nenv.allowLocalModels = false;\n\nexport class PipelineSingleton {\n static instance: Promise<any> | null = null;\n static modelName: string | null = null;\n\n static async getInstance(modelName: string, progressCallback?: (data: any) => void) {\n if (this.instance === null || this.modelName !== modelName) {\n this.modelName = modelName;\n this.instance = pipeline('feature-extraction', modelName, {\n dtype: 'q8',\n progress_callback: progressCallback,\n });\n }\n return this.instance;\n }\n}\n\nlet currentModelName: string = \"Xenova/all-MiniLM-L6-v2\";\n\n/** Returns the model name currently configured in the worker. */\nexport function getModelName(): string {\n return currentModelName;\n}\n\n/**\n * Handles an INIT message: saves the model name and starts loading the pipeline,\n * broadcasting STATUS events so the main thread can track the model lifecycle.\n */\nexport async function handleInitMessage(event: MessageEvent<WorkerInitMessage>): Promise<void> {\n currentModelName = event.data.modelName;\n self.postMessage({ type: 'STATUS', status: 'loading' });\n try {\n await PipelineSingleton.getInstance(currentModelName, (data: any) => {\n if (data.status === 'progress' && data.file && data.progress !== undefined) {\n self.postMessage({ type: 'PROGRESS', file: data.file, progress: data.progress });\n }\n });\n self.postMessage({ type: 'STATUS', status: 'ready' });\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n self.postMessage({ type: 'STATUS', status: 'failed', error });\n }\n}\n\n/**\n * Handles an EMBED message: runs the text through the pipeline and posts back\n * the resulting normalized 1D Float32Array.\n */\nexport async function handleMessage(event: MessageEvent<EmbeddingRequest>): Promise<void> {\n const { id, text } = event.data;\n try {\n const extractor = await PipelineSingleton.getInstance(currentModelName);\n const output = await extractor(text, { pooling: 'mean', normalize: true });\n const response: EmbeddingResponse = { type: \"EMBED_RES\", id, vector: output.data as Float32Array };\n self.postMessage(response);\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n const response: EmbeddingResponse = { type: \"EMBED_RES\", id, vector: null, error };\n self.postMessage(response);\n }\n}\n\nself.addEventListener(\"message\", (event: Event) => {\n const msg = (event as MessageEvent<WorkerIncomingMessage>).data;\n if (msg.type === \"INIT\") {\n handleInitMessage(event as MessageEvent<WorkerInitMessage>);\n } else {\n handleMessage(event as MessageEvent<EmbeddingRequest>);\n }\n});\n"],"mappings":";AAAA,SAAS,UAAU,WAAW;AAI9B,IAAI,mBAAmB;AAEhB,IAAM,oBAAN,MAAwB;AAAA,EAI7B,aAAa,YAAY,WAAmB,kBAAwC;AAClF,QAAI,KAAK,aAAa,QAAQ,KAAK,cAAc,WAAW;AAC1D,WAAK,YAAY;AACjB,WAAK,WAAW,SAAS,sBAAsB,WAAW;AAAA,QACxD,OAAO;AAAA,QACP,mBAAmB;AAAA,MACrB,CAAC;AAAA,IACH;AACA,WAAO,KAAK;AAAA,EACd;AACF;AAda,kBACJ,WAAgC;AAD5B,kBAEJ,YAA2B;AAcpC,IAAI,mBAA2B;AAGxB,SAAS,eAAuB;AACrC,SAAO;AACT;AAMA,eAAsB,kBAAkB,OAAuD;AAC7F,qBAAmB,MAAM,KAAK;AAC9B,OAAK,YAAY,EAAE,MAAM,UAAU,QAAQ,UAAU,CAAC;AACtD,MAAI;AACF,UAAM,kBAAkB,YAAY,kBAAkB,CAAC,SAAc;AACnE,UAAI,KAAK,WAAW,cAAc,KAAK,QAAQ,KAAK,aAAa,QAAW;AAC1E,aAAK,YAAY,EAAE,MAAM,YAAY,MAAM,KAAK,MAAM,UAAU,KAAK,SAAS,CAAC;AAAA,MACjF;AAAA,IACF,CAAC;AACD,SAAK,YAAY,EAAE,MAAM,UAAU,QAAQ,QAAQ,CAAC;AAAA,EACtD,SAAS,KAAK;AACZ,UAAM,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC7D,SAAK,YAAY,EAAE,MAAM,UAAU,QAAQ,UAAU,MAAM,CAAC;AAAA,EAC9D;AACF;AAMA,eAAsB,cAAc,OAAsD;AACxF,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM;AAC3B,MAAI;AACF,UAAM,YAAY,MAAM,kBAAkB,YAAY,gBAAgB;AACtE,UAAM,SAAS,MAAM,UAAU,MAAM,EAAE,SAAS,QAAQ,WAAW,KAAK,CAAC;AACzE,UAAM,WAA8B,EAAE,MAAM,aAAa,IAAI,QAAQ,OAAO,KAAqB;AACjG,SAAK,YAAY,QAAQ;AAAA,EAC3B,SAAS,KAAK;AACZ,UAAM,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC7D,UAAM,WAA8B,EAAE,MAAM,aAAa,IAAI,QAAQ,MAAM,MAAM;AACjF,SAAK,YAAY,QAAQ;AAAA,EAC3B;AACF;AAEA,KAAK,iBAAiB,WAAW,CAAC,UAAiB;AACjD,QAAM,MAAO,MAA8C;AAC3D,MAAI,IAAI,SAAS,QAAQ;AACvB,sBAAkB,KAAwC;AAAA,EAC5D,OAAO;AACL,kBAAc,KAAuC;AAAA,EACvD;AACF,CAAC;","names":[]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
SemanticStateEngine: () => SemanticStateEngine,
|
|
24
|
+
WorkerManager: () => WorkerManager,
|
|
25
|
+
add: () => add,
|
|
26
|
+
cosineSimilarity: () => cosineSimilarity,
|
|
27
|
+
emaFusion: () => emaFusion,
|
|
28
|
+
normalize: () => normalize,
|
|
29
|
+
scale: () => scale
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(src_exports);
|
|
32
|
+
|
|
33
|
+
// src/math/vector.ts
|
|
34
|
+
function assertSameDimension(a, b) {
|
|
35
|
+
if (a.length !== b.length) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Vector dimension mismatch: a=${a.length}, b=${b.length}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function add(a, b) {
|
|
42
|
+
assertSameDimension(a, b);
|
|
43
|
+
return a.map((val, i) => val + b[i]);
|
|
44
|
+
}
|
|
45
|
+
function scale(v, scalar) {
|
|
46
|
+
return v.map((val) => val * scalar);
|
|
47
|
+
}
|
|
48
|
+
function normalize(v) {
|
|
49
|
+
const mag = Math.sqrt(v.reduce((sum, val) => sum + val * val, 0));
|
|
50
|
+
if (mag === 0) {
|
|
51
|
+
return v.map(() => 0);
|
|
52
|
+
}
|
|
53
|
+
return v.map((val) => val / mag);
|
|
54
|
+
}
|
|
55
|
+
function cosineSimilarity(a, b) {
|
|
56
|
+
assertSameDimension(a, b);
|
|
57
|
+
const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
|
|
58
|
+
const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
|
|
59
|
+
const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
|
|
60
|
+
if (magA === 0 || magB === 0) {
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
return dot / (magA * magB);
|
|
64
|
+
}
|
|
65
|
+
function emaFusion(current, previous, alpha) {
|
|
66
|
+
assertSameDimension(current, previous);
|
|
67
|
+
if (alpha <= 0 || alpha > 1) {
|
|
68
|
+
throw new Error(`Alpha must be in the range (0, 1], got ${alpha}`);
|
|
69
|
+
}
|
|
70
|
+
return current.map((val, i) => alpha * val + (1 - alpha) * previous[i]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/engine/SemanticStateEngine.ts
|
|
74
|
+
var AGE_DECAY_RATE = 1e-4;
|
|
75
|
+
var DRIFT_WEIGHT = 0.5;
|
|
76
|
+
var SemanticStateEngine = class {
|
|
77
|
+
constructor(config) {
|
|
78
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
79
|
+
this.alpha = config.alpha;
|
|
80
|
+
this.driftThreshold = config.driftThreshold;
|
|
81
|
+
this.onDriftDetected = config.onDriftDetected;
|
|
82
|
+
this.provider = config.provider;
|
|
83
|
+
this.modelName = config.modelName ?? "Xenova/all-MiniLM-L6-v2";
|
|
84
|
+
this.stateVector = [];
|
|
85
|
+
this.lastUpdatedAt = Date.now();
|
|
86
|
+
this.lastDrift = 0;
|
|
87
|
+
this.updateCount = 0;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Obtains an embedding for `text` from the WorkerManager and fuses it into
|
|
91
|
+
* the rolling semantic state using EMA.
|
|
92
|
+
*
|
|
93
|
+
* On the first call the embedding establishes the baseline.
|
|
94
|
+
* On subsequent calls, if the cosine similarity between the current state
|
|
95
|
+
* and the new embedding falls below {@link SemanticStateEngineConfig.driftThreshold},
|
|
96
|
+
* the {@link SemanticStateEngineConfig.onDriftDetected} callback is fired
|
|
97
|
+
* *before* the EMA fusion is applied.
|
|
98
|
+
*
|
|
99
|
+
* @param text Raw text whose embedding will be fused into the state.
|
|
100
|
+
*/
|
|
101
|
+
async update(text) {
|
|
102
|
+
const raw = await this.provider.getEmbedding(text);
|
|
103
|
+
if (raw === null) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const embedding = Array.from(raw);
|
|
107
|
+
if (this.updateCount === 0) {
|
|
108
|
+
const zero = new Array(embedding.length).fill(0);
|
|
109
|
+
this.stateVector = emaFusion(embedding, zero, this.alpha);
|
|
110
|
+
this.lastDrift = 0;
|
|
111
|
+
} else {
|
|
112
|
+
if (embedding.length !== this.stateVector.length) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Embedding dimension mismatch: expected ${this.stateVector.length}, got ${embedding.length}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const similarity = cosineSimilarity(this.stateVector, embedding);
|
|
118
|
+
const drift = 1 - similarity;
|
|
119
|
+
if (similarity < this.driftThreshold) {
|
|
120
|
+
this.onDriftDetected?.(embedding, drift);
|
|
121
|
+
}
|
|
122
|
+
this.stateVector = emaFusion(embedding, this.stateVector, this.alpha);
|
|
123
|
+
this.lastDrift = drift;
|
|
124
|
+
}
|
|
125
|
+
this.lastUpdatedAt = Date.now();
|
|
126
|
+
this.updateCount++;
|
|
127
|
+
this.listeners.forEach((l) => l());
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Subscribes to state changes. Returns an unsubscribe function.
|
|
131
|
+
* The listener is called after every successful `update`.
|
|
132
|
+
*/
|
|
133
|
+
subscribe(listener) {
|
|
134
|
+
this.listeners.add(listener);
|
|
135
|
+
return () => this.listeners.delete(listener);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Returns a point-in-time snapshot of the current semantic state.
|
|
139
|
+
*/
|
|
140
|
+
getSnapshot() {
|
|
141
|
+
const healthScore = this.calculateHealth();
|
|
142
|
+
return {
|
|
143
|
+
vector: [...this.stateVector],
|
|
144
|
+
healthScore,
|
|
145
|
+
timestamp: this.lastUpdatedAt,
|
|
146
|
+
semanticSummary: this.buildSummary(healthScore)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Computes the current healthScore.
|
|
151
|
+
*
|
|
152
|
+
* Starts at 1.0 and subtracts:
|
|
153
|
+
* - An age penalty proportional to milliseconds elapsed since the last update.
|
|
154
|
+
* - A drift penalty proportional to the most recent drift magnitude.
|
|
155
|
+
*
|
|
156
|
+
* The result is clamped to [0, 1].
|
|
157
|
+
*/
|
|
158
|
+
calculateHealth() {
|
|
159
|
+
const timeSinceUpdate = Date.now() - this.lastUpdatedAt;
|
|
160
|
+
const agePenalty = timeSinceUpdate * AGE_DECAY_RATE;
|
|
161
|
+
const driftPenalty = this.lastDrift * DRIFT_WEIGHT;
|
|
162
|
+
return Math.max(0, Math.min(1, 1 - agePenalty - driftPenalty));
|
|
163
|
+
}
|
|
164
|
+
buildSummary(healthScore) {
|
|
165
|
+
if (healthScore > 0.8) return "stable";
|
|
166
|
+
if (healthScore > 0.5) return "drifting";
|
|
167
|
+
return "volatile";
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/worker/WorkerManager.ts
|
|
172
|
+
var import_meta = {};
|
|
173
|
+
var WorkerManager = class {
|
|
174
|
+
constructor(workerUrl = new URL("./embedding.worker.js", import_meta.url), modelName = "Xenova/all-MiniLM-L6-v2") {
|
|
175
|
+
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
176
|
+
this.isReady = false;
|
|
177
|
+
this.worker = new Worker(workerUrl, { type: "module" });
|
|
178
|
+
this.worker.onmessage = (event) => {
|
|
179
|
+
const data = event.data;
|
|
180
|
+
switch (data.type) {
|
|
181
|
+
case "STATUS":
|
|
182
|
+
this.isReady = data.status === "ready";
|
|
183
|
+
return;
|
|
184
|
+
case "PROGRESS":
|
|
185
|
+
return;
|
|
186
|
+
case "EMBED_RES": {
|
|
187
|
+
const { id, vector, error } = data;
|
|
188
|
+
const pending = this.pendingRequests.get(id);
|
|
189
|
+
if (!pending) return;
|
|
190
|
+
this.pendingRequests.delete(id);
|
|
191
|
+
if (error !== void 0) {
|
|
192
|
+
pending.reject(new Error(error));
|
|
193
|
+
} else if (vector !== null) {
|
|
194
|
+
pending.resolve(vector);
|
|
195
|
+
} else {
|
|
196
|
+
pending.reject(new Error("Worker returned null vector without an error message"));
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const initMessage = { type: "INIT", modelName };
|
|
203
|
+
this.worker.postMessage(initMessage);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Sends `text` to the worker and returns a Promise that resolves with the
|
|
207
|
+
* resulting embedding vector, or rejects if the worker reports an error.
|
|
208
|
+
*
|
|
209
|
+
* If the worker is not yet ready (model still loading), the request is
|
|
210
|
+
* silently dropped and the Promise resolves with `null` to avoid
|
|
211
|
+
* unhandled promise rejections during the initial page load.
|
|
212
|
+
*/
|
|
213
|
+
getEmbedding(text) {
|
|
214
|
+
if (!this.isReady) {
|
|
215
|
+
console.warn("SemanticStateEngine: Worker still loading, dropping early event.");
|
|
216
|
+
return Promise.resolve(null);
|
|
217
|
+
}
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
const id = crypto.randomUUID();
|
|
220
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
221
|
+
const request = { type: "EMBED", id, text };
|
|
222
|
+
this.worker.postMessage(request);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
227
|
+
0 && (module.exports = {
|
|
228
|
+
SemanticStateEngine,
|
|
229
|
+
WorkerManager,
|
|
230
|
+
add,
|
|
231
|
+
cosineSimilarity,
|
|
232
|
+
emaFusion,
|
|
233
|
+
normalize,
|
|
234
|
+
scale
|
|
235
|
+
});
|
|
236
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/math/vector.ts","../src/engine/SemanticStateEngine.ts","../src/worker/WorkerManager.ts"],"sourcesContent":["/**\n * semantic-state-estimator\n *\n * A TypeScript library that acts as an event-stream middleware to track\n * the implicit semantic intent, emotional state, or \"vibe\" of a user/system.\n */\n\nexport { emaFusion, cosineSimilarity, normalize, add, scale } from \"./math/vector.js\";\nexport {\n SemanticStateEngine,\n type EmbeddingProvider,\n type SemanticStateEngineConfig,\n type Snapshot,\n} from \"./engine/SemanticStateEngine.js\";\nexport { WorkerManager } from \"./worker/WorkerManager.js\";\n","/**\n * Pure vector math utilities for semantic state estimation.\n *\n * Provides vector addition, scalar multiplication, normalization,\n * cosine similarity, and EMA (Exponential Moving Average) fusion.\n */\n\n/** Asserts that two vectors have the same length, throwing otherwise. */\nfunction assertSameDimension(a: number[], b: number[]): void {\n if (a.length !== b.length) {\n throw new Error(\n `Vector dimension mismatch: a=${a.length}, b=${b.length}`,\n );\n }\n}\n\n/**\n * Adds two vectors element-wise.\n *\n * @param a First vector\n * @param b Second vector\n * @returns Element-wise sum\n */\nexport function add(a: number[], b: number[]): number[] {\n assertSameDimension(a, b);\n return a.map((val, i) => val + b[i]!);\n}\n\n/**\n * Multiplies every element of a vector by a scalar.\n *\n * @param v Input vector\n * @param scalar Scalar multiplier\n * @returns Scaled vector\n */\nexport function scale(v: number[], scalar: number): number[] {\n return v.map((val) => val * scalar);\n}\n\n/**\n * Normalizes a vector to unit length (L2 normalization).\n *\n * @param v Input vector\n * @returns Unit vector, or zero vector if input magnitude is 0\n */\nexport function normalize(v: number[]): number[] {\n const mag = Math.sqrt(v.reduce((sum, val) => sum + val * val, 0));\n if (mag === 0) {\n return v.map(() => 0);\n }\n return v.map((val) => val / mag);\n}\n\n/**\n * Computes the cosine similarity between two vectors.\n *\n * @param a First vector\n * @param b Second vector\n * @returns Cosine similarity in [-1, 1], or 0 if either vector has zero magnitude\n */\nexport function cosineSimilarity(a: number[], b: number[]): number {\n assertSameDimension(a, b);\n const dot = a.reduce((sum, val, i) => sum + val * b[i]!, 0);\n const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));\n const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));\n if (magA === 0 || magB === 0) {\n return 0;\n }\n return dot / (magA * magB);\n}\n\n/**\n * Computes the Exponential Moving Average (EMA) fusion of two vectors.\n *\n * Formula: S_t = α · E_t + (1 − α) · S_{t-1}\n *\n * @param current New embedding vector E_t\n * @param previous Previous state vector S_{t-1}\n * @param alpha Decay factor α ∈ (0, 1]. Higher values weight recent events more.\n * @returns Updated state vector S_t\n */\nexport function emaFusion(\n current: number[],\n previous: number[],\n alpha: number,\n): number[] {\n assertSameDimension(current, previous);\n if (alpha <= 0 || alpha > 1) {\n throw new Error(`Alpha must be in the range (0, 1], got ${alpha}`);\n }\n return current.map((val, i) => alpha * val + (1 - alpha) * previous[i]!);\n}\n","import { emaFusion, cosineSimilarity } from \"../math/vector.js\";\n\n/**\n * A generic embedding provider contract.\n * Any object that can return an embedding vector for a given text satisfies this interface.\n * This includes `WorkerManager` (on-device WebWorker) as well as custom OpenAI, Ollama,\n * or other remote-inference wrappers.\n */\nexport interface EmbeddingProvider {\n getEmbedding(text: string): Promise<Float32Array | number[]>;\n}\n\n/**\n * Age-based health decay rate: health lost per millisecond of inactivity.\n * At this rate, age alone reduces health to 0 after ~10 seconds of inactivity.\n */\nconst AGE_DECAY_RATE = 0.0001;\n\n/**\n * Weight applied to the most-recent drift value when computing healthScore.\n * A drift of 1.0 (orthogonal vectors) reduces health by 0.5.\n */\nconst DRIFT_WEIGHT = 0.5;\n\n/**\n * Configuration for the SemanticStateEngine.\n */\nexport interface SemanticStateEngineConfig {\n /** EMA decay factor α ∈ (0, 1]. Higher values weight recent embeddings more. */\n alpha: number;\n\n /** Minimum cosine similarity below which drift is detected and the callback fires. */\n driftThreshold: number;\n\n /**\n * Optional callback invoked when the incoming embedding drifts beyond the threshold.\n * Fired *before* the EMA fusion is applied.\n *\n * @param vector The incoming embedding that triggered the drift.\n * @param driftScore Drift magnitude: 1 − cosine_similarity ∈ [0, 2].\n */\n onDriftDetected?: (vector: number[], driftScore: number) => void;\n /**\n * The embedding provider used to obtain embedding vectors asynchronously.\n * Any object implementing `getEmbedding(text: string): Promise<Float32Array | number[]>`\n * satisfies this interface — including `WorkerManager`, or a custom OpenAI / Ollama wrapper.\n */\n provider: EmbeddingProvider;\n\n /**\n * The name of the embedding model to use.\n * Must match the modelName passed to the WorkerManager so the worker\n * loads the correct model.\n * @default \"Xenova/all-MiniLM-L6-v2\"\n */\n modelName?: string;\n}\n\n/**\n * A point-in-time snapshot of the current semantic state.\n */\nexport interface Snapshot {\n /** The current EMA state vector. */\n vector: number[];\n\n /** Reliability indicator in [0, 1]. Degrades with age and high drift. */\n healthScore: number;\n\n /** Unix timestamp (ms) of the last state update. */\n timestamp: number;\n\n /** Human-readable description of the current state quality. */\n semanticSummary: string;\n}\n\n/**\n * SemanticStateEngine tracks the implicit semantic intent of an event stream\n * using Exponential Moving Average (EMA) vector fusion.\n *\n * It fires an optional drift callback when incoming embeddings diverge\n * significantly from the current state, and exposes a healthScore that\n * degrades with both age and volatility.\n */\nexport class SemanticStateEngine {\n private readonly alpha: number;\n private readonly driftThreshold: number;\n private readonly onDriftDetected?: (\n vector: number[],\n driftScore: number,\n ) => void;\n private readonly provider: EmbeddingProvider;\n readonly modelName: string;\n\n private stateVector: number[];\n private lastUpdatedAt: number;\n private lastDrift: number;\n private updateCount: number;\n private readonly listeners = new Set<() => void>();\n\n constructor(config: SemanticStateEngineConfig) {\n this.alpha = config.alpha;\n this.driftThreshold = config.driftThreshold;\n this.onDriftDetected = config.onDriftDetected;\n this.provider = config.provider;\n this.modelName = config.modelName ?? \"Xenova/all-MiniLM-L6-v2\";\n\n this.stateVector = [];\n this.lastUpdatedAt = Date.now();\n this.lastDrift = 0;\n this.updateCount = 0;\n }\n\n /**\n * Obtains an embedding for `text` from the WorkerManager and fuses it into\n * the rolling semantic state using EMA.\n *\n * On the first call the embedding establishes the baseline.\n * On subsequent calls, if the cosine similarity between the current state\n * and the new embedding falls below {@link SemanticStateEngineConfig.driftThreshold},\n * the {@link SemanticStateEngineConfig.onDriftDetected} callback is fired\n * *before* the EMA fusion is applied.\n *\n * @param text Raw text whose embedding will be fused into the state.\n */\n async update(text: string): Promise<void> {\n const raw = await this.provider.getEmbedding(text);\n if (raw === null) {\n return;\n }\n const embedding = Array.from(raw);\n\n if (this.updateCount === 0) {\n // First call: establish baseline from a zero-vector origin.\n const zero = new Array(embedding.length).fill(0) as number[];\n this.stateVector = emaFusion(embedding, zero, this.alpha);\n this.lastDrift = 0;\n } else {\n if (embedding.length !== this.stateVector.length) {\n throw new Error(\n `Embedding dimension mismatch: expected ${this.stateVector.length}, got ${embedding.length}`,\n );\n }\n\n const similarity = cosineSimilarity(this.stateVector, embedding);\n const drift = 1 - similarity;\n\n if (similarity < this.driftThreshold) {\n this.onDriftDetected?.(embedding, drift);\n }\n\n this.stateVector = emaFusion(embedding, this.stateVector, this.alpha);\n this.lastDrift = drift;\n }\n\n this.lastUpdatedAt = Date.now();\n this.updateCount++;\n this.listeners.forEach((l) => l());\n }\n\n /**\n * Subscribes to state changes. Returns an unsubscribe function.\n * The listener is called after every successful `update`.\n */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Returns a point-in-time snapshot of the current semantic state.\n */\n getSnapshot(): Snapshot {\n const healthScore = this.calculateHealth();\n return {\n vector: [...this.stateVector],\n healthScore,\n timestamp: this.lastUpdatedAt,\n semanticSummary: this.buildSummary(healthScore),\n };\n }\n\n /**\n * Computes the current healthScore.\n *\n * Starts at 1.0 and subtracts:\n * - An age penalty proportional to milliseconds elapsed since the last update.\n * - A drift penalty proportional to the most recent drift magnitude.\n *\n * The result is clamped to [0, 1].\n */\n private calculateHealth(): number {\n const timeSinceUpdate = Date.now() - this.lastUpdatedAt;\n const agePenalty = timeSinceUpdate * AGE_DECAY_RATE;\n const driftPenalty = this.lastDrift * DRIFT_WEIGHT;\n return Math.max(0, Math.min(1, 1.0 - agePenalty - driftPenalty));\n }\n\n private buildSummary(healthScore: number): string {\n if (healthScore > 0.8) return \"stable\";\n if (healthScore > 0.5) return \"drifting\";\n return \"volatile\";\n }\n}\n","import type { EmbeddingRequest, EmbeddingResponse, WorkerInitMessage, WorkerStatusEvent } from \"./types.js\";\n\ntype PendingRequest = {\n resolve: (value: Float32Array) => void;\n reject: (reason: Error) => void;\n};\n\n/**\n * WorkerManager wraps a browser Worker in a clean async/await API.\n *\n * It maintains a Map of pending Promises keyed by request UUID so that\n * out-of-order worker responses are still dispatched to the correct caller.\n *\n * Calls to `getEmbedding()` while the worker is not yet ready are silently\n * dropped (returning null) to avoid unhandled promise rejections during the\n * model loading phase, which is acceptable for probabilistic semantic state.\n */\nexport class WorkerManager {\n private readonly worker: Worker;\n private readonly pendingRequests = new Map<string, PendingRequest>();\n private isReady: boolean = false;\n\n constructor(\n workerUrl: string | URL = new URL(\"./embedding.worker.js\", import.meta.url),\n modelName: string = \"Xenova/all-MiniLM-L6-v2\",\n ) {\n this.worker = new Worker(workerUrl, { type: \"module\" });\n this.worker.onmessage = (event: MessageEvent<EmbeddingResponse | WorkerStatusEvent>) => {\n const data = event.data;\n switch (data.type) {\n case 'STATUS':\n this.isReady = data.status === 'ready';\n return;\n case 'PROGRESS':\n return;\n case 'EMBED_RES': {\n const { id, vector, error } = data;\n const pending = this.pendingRequests.get(id);\n if (!pending) return;\n this.pendingRequests.delete(id);\n if (error !== undefined) {\n pending.reject(new Error(error));\n } else if (vector !== null) {\n pending.resolve(vector);\n } else {\n pending.reject(new Error(\"Worker returned null vector without an error message\"));\n }\n return;\n }\n }\n };\n\n const initMessage: WorkerInitMessage = { type: \"INIT\", modelName };\n this.worker.postMessage(initMessage);\n }\n\n /**\n * Sends `text` to the worker and returns a Promise that resolves with the\n * resulting embedding vector, or rejects if the worker reports an error.\n *\n * If the worker is not yet ready (model still loading), the request is\n * silently dropped and the Promise resolves with `null` to avoid\n * unhandled promise rejections during the initial page load.\n */\n getEmbedding(text: string): Promise<Float32Array | null> {\n if (!this.isReady) {\n console.warn(\"SemanticStateEngine: Worker still loading, dropping early event.\");\n return Promise.resolve(null);\n }\n return new Promise<Float32Array>((resolve, reject) => {\n const id = crypto.randomUUID();\n this.pendingRequests.set(id, { resolve, reject });\n const request: EmbeddingRequest = { type: \"EMBED\", id, text };\n this.worker.postMessage(request);\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQA,SAAS,oBAAoB,GAAa,GAAmB;AAC3D,MAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,UAAM,IAAI;AAAA,MACR,gCAAgC,EAAE,MAAM,OAAO,EAAE,MAAM;AAAA,IACzD;AAAA,EACF;AACF;AASO,SAAS,IAAI,GAAa,GAAuB;AACtD,sBAAoB,GAAG,CAAC;AACxB,SAAO,EAAE,IAAI,CAAC,KAAK,MAAM,MAAM,EAAE,CAAC,CAAE;AACtC;AASO,SAAS,MAAM,GAAa,QAA0B;AAC3D,SAAO,EAAE,IAAI,CAAC,QAAQ,MAAM,MAAM;AACpC;AAQO,SAAS,UAAU,GAAuB;AAC/C,QAAM,MAAM,KAAK,KAAK,EAAE,OAAO,CAAC,KAAK,QAAQ,MAAM,MAAM,KAAK,CAAC,CAAC;AAChE,MAAI,QAAQ,GAAG;AACb,WAAO,EAAE,IAAI,MAAM,CAAC;AAAA,EACtB;AACA,SAAO,EAAE,IAAI,CAAC,QAAQ,MAAM,GAAG;AACjC;AASO,SAAS,iBAAiB,GAAa,GAAqB;AACjE,sBAAoB,GAAG,CAAC;AACxB,QAAM,MAAM,EAAE,OAAO,CAAC,KAAK,KAAK,MAAM,MAAM,MAAM,EAAE,CAAC,GAAI,CAAC;AAC1D,QAAM,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,KAAK,QAAQ,MAAM,MAAM,KAAK,CAAC,CAAC;AACjE,QAAM,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,KAAK,QAAQ,MAAM,MAAM,KAAK,CAAC,CAAC;AACjE,MAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,SAAO,OAAO,OAAO;AACvB;AAYO,SAAS,UACd,SACA,UACA,OACU;AACV,sBAAoB,SAAS,QAAQ;AACrC,MAAI,SAAS,KAAK,QAAQ,GAAG;AAC3B,UAAM,IAAI,MAAM,0CAA0C,KAAK,EAAE;AAAA,EACnE;AACA,SAAO,QAAQ,IAAI,CAAC,KAAK,MAAM,QAAQ,OAAO,IAAI,SAAS,SAAS,CAAC,CAAE;AACzE;;;AC3EA,IAAM,iBAAiB;AAMvB,IAAM,eAAe;AA6Dd,IAAM,sBAAN,MAA0B;AAAA,EAgB/B,YAAY,QAAmC;AAF/C,SAAiB,YAAY,oBAAI,IAAgB;AAG/C,SAAK,QAAQ,OAAO;AACpB,SAAK,iBAAiB,OAAO;AAC7B,SAAK,kBAAkB,OAAO;AAC9B,SAAK,WAAW,OAAO;AACvB,SAAK,YAAY,OAAO,aAAa;AAErC,SAAK,cAAc,CAAC;AACpB,SAAK,gBAAgB,KAAK,IAAI;AAC9B,SAAK,YAAY;AACjB,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,OAAO,MAA6B;AACxC,UAAM,MAAM,MAAM,KAAK,SAAS,aAAa,IAAI;AACjD,QAAI,QAAQ,MAAM;AAChB;AAAA,IACF;AACA,UAAM,YAAY,MAAM,KAAK,GAAG;AAEhC,QAAI,KAAK,gBAAgB,GAAG;AAE1B,YAAM,OAAO,IAAI,MAAM,UAAU,MAAM,EAAE,KAAK,CAAC;AAC/C,WAAK,cAAc,UAAU,WAAW,MAAM,KAAK,KAAK;AACxD,WAAK,YAAY;AAAA,IACnB,OAAO;AACL,UAAI,UAAU,WAAW,KAAK,YAAY,QAAQ;AAChD,cAAM,IAAI;AAAA,UACR,0CAA0C,KAAK,YAAY,MAAM,SAAS,UAAU,MAAM;AAAA,QAC5F;AAAA,MACF;AAEA,YAAM,aAAa,iBAAiB,KAAK,aAAa,SAAS;AAC/D,YAAM,QAAQ,IAAI;AAElB,UAAI,aAAa,KAAK,gBAAgB;AACpC,aAAK,kBAAkB,WAAW,KAAK;AAAA,MACzC;AAEA,WAAK,cAAc,UAAU,WAAW,KAAK,aAAa,KAAK,KAAK;AACpE,WAAK,YAAY;AAAA,IACnB;AAEA,SAAK,gBAAgB,KAAK,IAAI;AAC9B,SAAK;AACL,SAAK,UAAU,QAAQ,CAAC,MAAM,EAAE,CAAC;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,UAAkC;AAC1C,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,cAAwB;AACtB,UAAM,cAAc,KAAK,gBAAgB;AACzC,WAAO;AAAA,MACL,QAAQ,CAAC,GAAG,KAAK,WAAW;AAAA,MAC5B;AAAA,MACA,WAAW,KAAK;AAAA,MAChB,iBAAiB,KAAK,aAAa,WAAW;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,kBAA0B;AAChC,UAAM,kBAAkB,KAAK,IAAI,IAAI,KAAK;AAC1C,UAAM,aAAa,kBAAkB;AACrC,UAAM,eAAe,KAAK,YAAY;AACtC,WAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,IAAM,aAAa,YAAY,CAAC;AAAA,EACjE;AAAA,EAEQ,aAAa,aAA6B;AAChD,QAAI,cAAc,IAAK,QAAO;AAC9B,QAAI,cAAc,IAAK,QAAO;AAC9B,WAAO;AAAA,EACT;AACF;;;AC1MA;AAiBO,IAAM,gBAAN,MAAoB;AAAA,EAKzB,YACE,YAA0B,IAAI,IAAI,yBAAyB,YAAY,GAAG,GAC1E,YAAoB,2BACpB;AANF,SAAiB,kBAAkB,oBAAI,IAA4B;AACnE,SAAQ,UAAmB;AAMzB,SAAK,SAAS,IAAI,OAAO,WAAW,EAAE,MAAM,SAAS,CAAC;AACtD,SAAK,OAAO,YAAY,CAAC,UAA+D;AACtF,YAAM,OAAO,MAAM;AACnB,cAAQ,KAAK,MAAM;AAAA,QACjB,KAAK;AACH,eAAK,UAAU,KAAK,WAAW;AAC/B;AAAA,QACF,KAAK;AACH;AAAA,QACF,KAAK,aAAa;AAChB,gBAAM,EAAE,IAAI,QAAQ,MAAM,IAAI;AAC9B,gBAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,cAAI,CAAC,QAAS;AACd,eAAK,gBAAgB,OAAO,EAAE;AAC9B,cAAI,UAAU,QAAW;AACvB,oBAAQ,OAAO,IAAI,MAAM,KAAK,CAAC;AAAA,UACjC,WAAW,WAAW,MAAM;AAC1B,oBAAQ,QAAQ,MAAM;AAAA,UACxB,OAAO;AACL,oBAAQ,OAAO,IAAI,MAAM,sDAAsD,CAAC;AAAA,UAClF;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,cAAiC,EAAE,MAAM,QAAQ,UAAU;AACjE,SAAK,OAAO,YAAY,WAAW;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,aAAa,MAA4C;AACvD,QAAI,CAAC,KAAK,SAAS;AACjB,cAAQ,KAAK,kEAAkE;AAC/E,aAAO,QAAQ,QAAQ,IAAI;AAAA,IAC7B;AACA,WAAO,IAAI,QAAsB,CAAC,SAAS,WAAW;AACpD,YAAM,KAAK,OAAO,WAAW;AAC7B,WAAK,gBAAgB,IAAI,IAAI,EAAE,SAAS,OAAO,CAAC;AAChD,YAAM,UAA4B,EAAE,MAAM,SAAS,IAAI,KAAK;AAC5D,WAAK,OAAO,YAAY,OAAO;AAAA,IACjC,CAAC;AAAA,EACH;AACF;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export { E as EmbeddingProvider, S as SemanticStateEngine, a as SemanticStateEngineConfig, b as Snapshot } from './SemanticStateEngine-BP5URJOJ.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure vector math utilities for semantic state estimation.
|
|
5
|
+
*
|
|
6
|
+
* Provides vector addition, scalar multiplication, normalization,
|
|
7
|
+
* cosine similarity, and EMA (Exponential Moving Average) fusion.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Adds two vectors element-wise.
|
|
11
|
+
*
|
|
12
|
+
* @param a First vector
|
|
13
|
+
* @param b Second vector
|
|
14
|
+
* @returns Element-wise sum
|
|
15
|
+
*/
|
|
16
|
+
declare function add(a: number[], b: number[]): number[];
|
|
17
|
+
/**
|
|
18
|
+
* Multiplies every element of a vector by a scalar.
|
|
19
|
+
*
|
|
20
|
+
* @param v Input vector
|
|
21
|
+
* @param scalar Scalar multiplier
|
|
22
|
+
* @returns Scaled vector
|
|
23
|
+
*/
|
|
24
|
+
declare function scale(v: number[], scalar: number): number[];
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes a vector to unit length (L2 normalization).
|
|
27
|
+
*
|
|
28
|
+
* @param v Input vector
|
|
29
|
+
* @returns Unit vector, or zero vector if input magnitude is 0
|
|
30
|
+
*/
|
|
31
|
+
declare function normalize(v: number[]): number[];
|
|
32
|
+
/**
|
|
33
|
+
* Computes the cosine similarity between two vectors.
|
|
34
|
+
*
|
|
35
|
+
* @param a First vector
|
|
36
|
+
* @param b Second vector
|
|
37
|
+
* @returns Cosine similarity in [-1, 1], or 0 if either vector has zero magnitude
|
|
38
|
+
*/
|
|
39
|
+
declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
40
|
+
/**
|
|
41
|
+
* Computes the Exponential Moving Average (EMA) fusion of two vectors.
|
|
42
|
+
*
|
|
43
|
+
* Formula: S_t = α · E_t + (1 − α) · S_{t-1}
|
|
44
|
+
*
|
|
45
|
+
* @param current New embedding vector E_t
|
|
46
|
+
* @param previous Previous state vector S_{t-1}
|
|
47
|
+
* @param alpha Decay factor α ∈ (0, 1]. Higher values weight recent events more.
|
|
48
|
+
* @returns Updated state vector S_t
|
|
49
|
+
*/
|
|
50
|
+
declare function emaFusion(current: number[], previous: number[], alpha: number): number[];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* WorkerManager wraps a browser Worker in a clean async/await API.
|
|
54
|
+
*
|
|
55
|
+
* It maintains a Map of pending Promises keyed by request UUID so that
|
|
56
|
+
* out-of-order worker responses are still dispatched to the correct caller.
|
|
57
|
+
*
|
|
58
|
+
* Calls to `getEmbedding()` while the worker is not yet ready are silently
|
|
59
|
+
* dropped (returning null) to avoid unhandled promise rejections during the
|
|
60
|
+
* model loading phase, which is acceptable for probabilistic semantic state.
|
|
61
|
+
*/
|
|
62
|
+
declare class WorkerManager {
|
|
63
|
+
private readonly worker;
|
|
64
|
+
private readonly pendingRequests;
|
|
65
|
+
private isReady;
|
|
66
|
+
constructor(workerUrl?: string | URL, modelName?: string);
|
|
67
|
+
/**
|
|
68
|
+
* Sends `text` to the worker and returns a Promise that resolves with the
|
|
69
|
+
* resulting embedding vector, or rejects if the worker reports an error.
|
|
70
|
+
*
|
|
71
|
+
* If the worker is not yet ready (model still loading), the request is
|
|
72
|
+
* silently dropped and the Promise resolves with `null` to avoid
|
|
73
|
+
* unhandled promise rejections during the initial page load.
|
|
74
|
+
*/
|
|
75
|
+
getEmbedding(text: string): Promise<Float32Array | null>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { WorkerManager, add, cosineSimilarity, emaFusion, normalize, scale };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export { E as EmbeddingProvider, S as SemanticStateEngine, a as SemanticStateEngineConfig, b as Snapshot } from './SemanticStateEngine-BP5URJOJ.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure vector math utilities for semantic state estimation.
|
|
5
|
+
*
|
|
6
|
+
* Provides vector addition, scalar multiplication, normalization,
|
|
7
|
+
* cosine similarity, and EMA (Exponential Moving Average) fusion.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Adds two vectors element-wise.
|
|
11
|
+
*
|
|
12
|
+
* @param a First vector
|
|
13
|
+
* @param b Second vector
|
|
14
|
+
* @returns Element-wise sum
|
|
15
|
+
*/
|
|
16
|
+
declare function add(a: number[], b: number[]): number[];
|
|
17
|
+
/**
|
|
18
|
+
* Multiplies every element of a vector by a scalar.
|
|
19
|
+
*
|
|
20
|
+
* @param v Input vector
|
|
21
|
+
* @param scalar Scalar multiplier
|
|
22
|
+
* @returns Scaled vector
|
|
23
|
+
*/
|
|
24
|
+
declare function scale(v: number[], scalar: number): number[];
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes a vector to unit length (L2 normalization).
|
|
27
|
+
*
|
|
28
|
+
* @param v Input vector
|
|
29
|
+
* @returns Unit vector, or zero vector if input magnitude is 0
|
|
30
|
+
*/
|
|
31
|
+
declare function normalize(v: number[]): number[];
|
|
32
|
+
/**
|
|
33
|
+
* Computes the cosine similarity between two vectors.
|
|
34
|
+
*
|
|
35
|
+
* @param a First vector
|
|
36
|
+
* @param b Second vector
|
|
37
|
+
* @returns Cosine similarity in [-1, 1], or 0 if either vector has zero magnitude
|
|
38
|
+
*/
|
|
39
|
+
declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
40
|
+
/**
|
|
41
|
+
* Computes the Exponential Moving Average (EMA) fusion of two vectors.
|
|
42
|
+
*
|
|
43
|
+
* Formula: S_t = α · E_t + (1 − α) · S_{t-1}
|
|
44
|
+
*
|
|
45
|
+
* @param current New embedding vector E_t
|
|
46
|
+
* @param previous Previous state vector S_{t-1}
|
|
47
|
+
* @param alpha Decay factor α ∈ (0, 1]. Higher values weight recent events more.
|
|
48
|
+
* @returns Updated state vector S_t
|
|
49
|
+
*/
|
|
50
|
+
declare function emaFusion(current: number[], previous: number[], alpha: number): number[];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* WorkerManager wraps a browser Worker in a clean async/await API.
|
|
54
|
+
*
|
|
55
|
+
* It maintains a Map of pending Promises keyed by request UUID so that
|
|
56
|
+
* out-of-order worker responses are still dispatched to the correct caller.
|
|
57
|
+
*
|
|
58
|
+
* Calls to `getEmbedding()` while the worker is not yet ready are silently
|
|
59
|
+
* dropped (returning null) to avoid unhandled promise rejections during the
|
|
60
|
+
* model loading phase, which is acceptable for probabilistic semantic state.
|
|
61
|
+
*/
|
|
62
|
+
declare class WorkerManager {
|
|
63
|
+
private readonly worker;
|
|
64
|
+
private readonly pendingRequests;
|
|
65
|
+
private isReady;
|
|
66
|
+
constructor(workerUrl?: string | URL, modelName?: string);
|
|
67
|
+
/**
|
|
68
|
+
* Sends `text` to the worker and returns a Promise that resolves with the
|
|
69
|
+
* resulting embedding vector, or rejects if the worker reports an error.
|
|
70
|
+
*
|
|
71
|
+
* If the worker is not yet ready (model still loading), the request is
|
|
72
|
+
* silently dropped and the Promise resolves with `null` to avoid
|
|
73
|
+
* unhandled promise rejections during the initial page load.
|
|
74
|
+
*/
|
|
75
|
+
getEmbedding(text: string): Promise<Float32Array | null>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { WorkerManager, add, cosineSimilarity, emaFusion, normalize, scale };
|