@seanhogg/builderforce-memory 2026.6.18
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 +21 -0
- package/README.md +582 -0
- package/dist/agent/SSMAgent.d.ts +146 -0
- package/dist/agent/SSMAgent.d.ts.map +1 -0
- package/dist/agent/SSMAgent.js +231 -0
- package/dist/agent/SSMAgent.js.map +1 -0
- package/dist/agent/index.d.ts +3 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +2 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/bridges/AnthropicBridge.d.ts +47 -0
- package/dist/bridges/AnthropicBridge.d.ts.map +1 -0
- package/dist/bridges/AnthropicBridge.js +120 -0
- package/dist/bridges/AnthropicBridge.js.map +1 -0
- package/dist/bridges/CachingBridge.d.ts +44 -0
- package/dist/bridges/CachingBridge.d.ts.map +1 -0
- package/dist/bridges/CachingBridge.js +62 -0
- package/dist/bridges/CachingBridge.js.map +1 -0
- package/dist/bridges/FetchBridge.d.ts +30 -0
- package/dist/bridges/FetchBridge.d.ts.map +1 -0
- package/dist/bridges/FetchBridge.js +24 -0
- package/dist/bridges/FetchBridge.js.map +1 -0
- package/dist/bridges/OpenAIBridge.d.ts +33 -0
- package/dist/bridges/OpenAIBridge.d.ts.map +1 -0
- package/dist/bridges/OpenAIBridge.js +110 -0
- package/dist/bridges/OpenAIBridge.js.map +1 -0
- package/dist/bridges/ResponseCache.d.ts +65 -0
- package/dist/bridges/ResponseCache.d.ts.map +1 -0
- package/dist/bridges/ResponseCache.js +97 -0
- package/dist/bridges/ResponseCache.js.map +1 -0
- package/dist/bridges/SemanticCachingBridge.d.ts +31 -0
- package/dist/bridges/SemanticCachingBridge.d.ts.map +1 -0
- package/dist/bridges/SemanticCachingBridge.js +44 -0
- package/dist/bridges/SemanticCachingBridge.js.map +1 -0
- package/dist/bridges/TransformerBridge.d.ts +35 -0
- package/dist/bridges/TransformerBridge.d.ts.map +1 -0
- package/dist/bridges/TransformerBridge.js +10 -0
- package/dist/bridges/TransformerBridge.js.map +1 -0
- package/dist/bridges/index.d.ts +14 -0
- package/dist/bridges/index.d.ts.map +1 -0
- package/dist/bridges/index.js +7 -0
- package/dist/bridges/index.js.map +1 -0
- package/dist/cache/FetchSemanticCacheBackend.d.ts +40 -0
- package/dist/cache/FetchSemanticCacheBackend.d.ts.map +1 -0
- package/dist/cache/FetchSemanticCacheBackend.js +61 -0
- package/dist/cache/FetchSemanticCacheBackend.js.map +1 -0
- package/dist/cache/SemanticCache.d.ts +105 -0
- package/dist/cache/SemanticCache.d.ts.map +1 -0
- package/dist/cache/SemanticCache.js +130 -0
- package/dist/cache/SemanticCache.js.map +1 -0
- package/dist/cache/index.d.ts +5 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +3 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/distillation/DistillationEngine.d.ts +107 -0
- package/dist/distillation/DistillationEngine.d.ts.map +1 -0
- package/dist/distillation/DistillationEngine.js +152 -0
- package/dist/distillation/DistillationEngine.js.map +1 -0
- package/dist/distillation/index.d.ts +3 -0
- package/dist/distillation/index.d.ts.map +1 -0
- package/dist/distillation/index.js +2 -0
- package/dist/distillation/index.js.map +1 -0
- package/dist/errors/SSMError.d.ts +14 -0
- package/dist/errors/SSMError.d.ts.map +1 -0
- package/dist/errors/SSMError.js +18 -0
- package/dist/errors/SSMError.js.map +1 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/MemoryStore.d.ts +152 -0
- package/dist/memory/MemoryStore.d.ts.map +1 -0
- package/dist/memory/MemoryStore.js +290 -0
- package/dist/memory/MemoryStore.js.map +1 -0
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/router/InferenceRouter.d.ts +92 -0
- package/dist/router/InferenceRouter.d.ts.map +1 -0
- package/dist/router/InferenceRouter.js +113 -0
- package/dist/router/InferenceRouter.js.map +1 -0
- package/dist/router/index.d.ts +3 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +2 -0
- package/dist/router/index.js.map +1 -0
- package/dist/runtime/SSMRuntime.d.ts +167 -0
- package/dist/runtime/SSMRuntime.d.ts.map +1 -0
- package/dist/runtime/SSMRuntime.js +199 -0
- package/dist/runtime/SSMRuntime.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/session/errors.d.ts +10 -0
- package/dist/session/errors.d.ts.map +1 -0
- package/dist/session/errors.js +14 -0
- package/dist/session/errors.js.map +1 -0
- package/dist/session/index.d.ts +11 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +7 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/persistence.d.ts +14 -0
- package/dist/session/persistence.d.ts.map +1 -0
- package/dist/session/persistence.js +100 -0
- package/dist/session/persistence.js.map +1 -0
- package/dist/session/presets.d.ts +31 -0
- package/dist/session/presets.d.ts.map +1 -0
- package/dist/session/presets.js +91 -0
- package/dist/session/presets.js.map +1 -0
- package/dist/session/session.d.ts +186 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +358 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/streaming.d.ts +13 -0
- package/dist/session/streaming.d.ts.map +1 -0
- package/dist/session/streaming.js +74 -0
- package/dist/session/streaming.js.map +1 -0
- package/dist/session/tokenizer.d.ts +18 -0
- package/dist/session/tokenizer.d.ts.map +1 -0
- package/dist/session/tokenizer.js +11 -0
- package/dist/session/tokenizer.js.map +1 -0
- package/dist/similarity/index.d.ts +19 -0
- package/dist/similarity/index.d.ts.map +1 -0
- package/dist/similarity/index.js +42 -0
- package/dist/similarity/index.js.map +1 -0
- package/package.json +120 -0
- package/src/agent/SSMAgent.ts +327 -0
- package/src/agent/index.ts +2 -0
- package/src/bridges/AnthropicBridge.ts +166 -0
- package/src/bridges/CachingBridge.ts +79 -0
- package/src/bridges/FetchBridge.ts +41 -0
- package/src/bridges/OpenAIBridge.ts +143 -0
- package/src/bridges/ResponseCache.ts +131 -0
- package/src/bridges/SemanticCachingBridge.ts +60 -0
- package/src/bridges/TransformerBridge.ts +38 -0
- package/src/bridges/index.ts +13 -0
- package/src/cache/FetchSemanticCacheBackend.ts +79 -0
- package/src/cache/SemanticCache.ts +196 -0
- package/src/cache/index.ts +9 -0
- package/src/distillation/DistillationEngine.ts +248 -0
- package/src/distillation/index.ts +2 -0
- package/src/errors/SSMError.ts +26 -0
- package/src/errors/index.ts +2 -0
- package/src/index.ts +128 -0
- package/src/memory/MemoryStore.ts +408 -0
- package/src/memory/index.ts +2 -0
- package/src/router/InferenceRouter.ts +201 -0
- package/src/router/index.ts +2 -0
- package/src/runtime/SSMRuntime.ts +309 -0
- package/src/runtime/index.ts +2 -0
- package/src/session/errors.ts +24 -0
- package/src/session/index.ts +25 -0
- package/src/session/persistence.ts +142 -0
- package/src/session/presets.ts +122 -0
- package/src/session/session.ts +657 -0
- package/src/session/streaming.ts +97 -0
- package/src/session/tokenizer.ts +18 -0
- package/src/similarity/index.ts +42 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FetchSemanticCacheBackend – the shared (L2) tier of the SemanticCache, backed
|
|
3
|
+
* by the BuilderForce.ai gateway's vector store over HTTP.
|
|
4
|
+
*
|
|
5
|
+
* One client used by both consumers (browser + agent) so a semantic hit on one
|
|
6
|
+
* surface is reusable by the other. Pure `fetch` — no environment-specific deps;
|
|
7
|
+
* inject `fetchImpl` in tests.
|
|
8
|
+
*
|
|
9
|
+
* Wire protocol (gateway `/v1/semantic-cache`):
|
|
10
|
+
* POST /lookup { embedding: number[], threshold, namespace? } → { hit?: { response, score } }
|
|
11
|
+
* POST /store { embedding: number[], response, namespace?, meta? } → 2xx
|
|
12
|
+
*/
|
|
13
|
+
import type { SemanticCacheBackend } from './SemanticCache.js';
|
|
14
|
+
export interface FetchSemanticCacheBackendOptions {
|
|
15
|
+
/** Gateway base URL, e.g. 'https://api.builderforce.ai'. Trailing slash trimmed. */
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
/** Tenant API key (sent as a bearer token). */
|
|
18
|
+
apiKey: string;
|
|
19
|
+
/**
|
|
20
|
+
* Optional cache partition. Scope hits to a tenant/model/agent so unrelated
|
|
21
|
+
* traffic can't cross-hit. Defaults to the gateway's per-tenant default.
|
|
22
|
+
*/
|
|
23
|
+
namespace?: string;
|
|
24
|
+
/** Injectable fetch (defaults to global fetch). */
|
|
25
|
+
fetchImpl?: typeof fetch;
|
|
26
|
+
}
|
|
27
|
+
export declare class FetchSemanticCacheBackend implements SemanticCacheBackend {
|
|
28
|
+
private readonly _base;
|
|
29
|
+
private readonly _apiKey;
|
|
30
|
+
private readonly _namespace;
|
|
31
|
+
private readonly _fetch;
|
|
32
|
+
constructor(opts: FetchSemanticCacheBackendOptions);
|
|
33
|
+
lookup(embedding: Float32Array, threshold: number): Promise<{
|
|
34
|
+
response: string;
|
|
35
|
+
score: number;
|
|
36
|
+
} | undefined>;
|
|
37
|
+
store(embedding: Float32Array, response: string, meta?: Record<string, unknown>): Promise<void>;
|
|
38
|
+
private _headers;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=FetchSemanticCacheBackend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FetchSemanticCacheBackend.d.ts","sourceRoot":"","sources":["../../src/cache/FetchSemanticCacheBackend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,MAAM,WAAW,gCAAgC;IAC7C,oFAAoF;IACpF,OAAO,EAAI,MAAM,CAAC;IAClB,+CAA+C;IAC/C,MAAM,EAAK,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAG,MAAM,CAAC;IACpB,mDAAmD;IACnD,SAAS,CAAC,EAAG,OAAO,KAAK,CAAC;CAC7B;AAED,qBAAa,yBAA0B,YAAW,oBAAoB;IAClE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;gBAE/B,IAAI,EAAE,gCAAgC;IAO5C,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAiB5G,KAAK,CAAC,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAarG,OAAO,CAAC,QAAQ;CAMnB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FetchSemanticCacheBackend – the shared (L2) tier of the SemanticCache, backed
|
|
3
|
+
* by the BuilderForce.ai gateway's vector store over HTTP.
|
|
4
|
+
*
|
|
5
|
+
* One client used by both consumers (browser + agent) so a semantic hit on one
|
|
6
|
+
* surface is reusable by the other. Pure `fetch` — no environment-specific deps;
|
|
7
|
+
* inject `fetchImpl` in tests.
|
|
8
|
+
*
|
|
9
|
+
* Wire protocol (gateway `/v1/semantic-cache`):
|
|
10
|
+
* POST /lookup { embedding: number[], threshold, namespace? } → { hit?: { response, score } }
|
|
11
|
+
* POST /store { embedding: number[], response, namespace?, meta? } → 2xx
|
|
12
|
+
*/
|
|
13
|
+
export class FetchSemanticCacheBackend {
|
|
14
|
+
_base;
|
|
15
|
+
_apiKey;
|
|
16
|
+
_namespace;
|
|
17
|
+
_fetch;
|
|
18
|
+
constructor(opts) {
|
|
19
|
+
this._base = opts.baseUrl.replace(/\/$/, '');
|
|
20
|
+
this._apiKey = opts.apiKey;
|
|
21
|
+
this._namespace = opts.namespace;
|
|
22
|
+
this._fetch = opts.fetchImpl ?? fetch;
|
|
23
|
+
}
|
|
24
|
+
async lookup(embedding, threshold) {
|
|
25
|
+
const res = await this._fetch(`${this._base}/v1/semantic-cache/lookup`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: this._headers(),
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
embedding: Array.from(embedding),
|
|
30
|
+
threshold,
|
|
31
|
+
...(this._namespace ? { namespace: this._namespace } : {}),
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
if (!res.ok)
|
|
35
|
+
return undefined;
|
|
36
|
+
const json = await res.json().catch(() => null);
|
|
37
|
+
const hit = json?.hit;
|
|
38
|
+
if (!hit || typeof hit.response !== 'string' || typeof hit.score !== 'number')
|
|
39
|
+
return undefined;
|
|
40
|
+
return { response: hit.response, score: hit.score };
|
|
41
|
+
}
|
|
42
|
+
async store(embedding, response, meta) {
|
|
43
|
+
await this._fetch(`${this._base}/v1/semantic-cache/store`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: this._headers(),
|
|
46
|
+
body: JSON.stringify({
|
|
47
|
+
embedding: Array.from(embedding),
|
|
48
|
+
response,
|
|
49
|
+
...(this._namespace ? { namespace: this._namespace } : {}),
|
|
50
|
+
...(meta ? { meta } : {}),
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
_headers() {
|
|
55
|
+
return {
|
|
56
|
+
'Content-Type': 'application/json',
|
|
57
|
+
Authorization: `Bearer ${this._apiKey}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=FetchSemanticCacheBackend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FetchSemanticCacheBackend.js","sourceRoot":"","sources":["../../src/cache/FetchSemanticCacheBackend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH,MAAM,OAAO,yBAAyB;IACjB,KAAK,CAAe;IACpB,OAAO,CAAa;IACpB,UAAU,CAAsB;IAChC,MAAM,CAAoB;IAE3C,YAAY,IAAsC;QAC9C,IAAI,CAAC,KAAK,GAAQ,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,OAAO,GAAM,IAAI,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,MAAM,GAAO,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAuB,EAAE,SAAiB;QACnD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,2BAA2B,EAAE;YACpE,MAAM,EAAG,MAAM;YACf,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE;YACxB,IAAI,EAAK,IAAI,CAAC,SAAS,CAAC;gBACpB,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;gBAChC,SAAS;gBACT,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7D,CAAC;SACL,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,SAAS,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAA6D,CAAC;QAC5G,MAAM,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC;QACtB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAChG,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAuB,EAAE,QAAgB,EAAE,IAA8B;QACjF,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,0BAA0B,EAAE;YACvD,MAAM,EAAG,MAAM;YACf,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE;YACxB,IAAI,EAAK,IAAI,CAAC,SAAS,CAAC;gBACpB,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;gBAChC,QAAQ;gBACR,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1D,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC5B,CAAC;SACL,CAAC,CAAC;IACP,CAAC;IAEO,QAAQ;QACZ,OAAO;YACH,cAAc,EAAG,kBAAkB;YACnC,aAAa,EAAI,UAAU,IAAI,CAAC,OAAO,EAAE;SAC5C,CAAC;IACN,CAAC;CACJ"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SemanticCache – an embedding-keyed read-through cache for LLM completions.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the exact-match ResponseCache (which keys on the byte-identical prompt),
|
|
5
|
+
* this keys on the *meaning* of the query: it embeds the query and serves a
|
|
6
|
+
* cached answer when a stored entry is within `threshold` cosine similarity.
|
|
7
|
+
* That catches paraphrases — "fix the auth bug" ≈ "login is broken" — which is
|
|
8
|
+
* where real frontier-call avoidance (and token savings) comes from.
|
|
9
|
+
*
|
|
10
|
+
* Two tiers, mirroring the project's L1-in-process / L2-shared read-through
|
|
11
|
+
* pattern:
|
|
12
|
+
* - L1: an in-process vector list, scanned locally (fast, offline-capable).
|
|
13
|
+
* - L2: an optional shared backend (e.g. the BuilderForce.ai gateway vector
|
|
14
|
+
* store) so a hit on one surface — web or agent — benefits the other.
|
|
15
|
+
*
|
|
16
|
+
* Fully portable: the embedder and the L2 backend are injected, so the same
|
|
17
|
+
* class runs in the browser (WebGPU SSM + native fetch) and in Node (the agent's
|
|
18
|
+
* `@webgpu/node` SSM + fetch) with no environment-specific forks.
|
|
19
|
+
*/
|
|
20
|
+
/** Produces an embedding vector for a piece of text (the on-device SSM, typically). */
|
|
21
|
+
export type Embedder = (text: string) => Promise<Float32Array>;
|
|
22
|
+
/**
|
|
23
|
+
* The shared (L2) cache tier. Implemented by `FetchSemanticCacheBackend` against
|
|
24
|
+
* the gateway, but any store satisfying this shape can be injected.
|
|
25
|
+
*/
|
|
26
|
+
export interface SemanticCacheBackend {
|
|
27
|
+
/** Returns the best stored entry at/above `threshold` cosine similarity, or undefined. */
|
|
28
|
+
lookup(embedding: Float32Array, threshold: number): Promise<{
|
|
29
|
+
response: string;
|
|
30
|
+
score: number;
|
|
31
|
+
} | undefined>;
|
|
32
|
+
/** Persists an embedding → response association. */
|
|
33
|
+
store(embedding: Float32Array, response: string, meta?: Record<string, unknown>): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export interface SemanticCacheHit {
|
|
36
|
+
response: string;
|
|
37
|
+
/** Cosine similarity of the matched entry to the query. */
|
|
38
|
+
score: number;
|
|
39
|
+
/** Which tier served the hit. */
|
|
40
|
+
tier: 'l1' | 'l2';
|
|
41
|
+
}
|
|
42
|
+
export interface SemanticCacheOptions {
|
|
43
|
+
/** Embeds queries. Required — this is what makes the cache semantic. */
|
|
44
|
+
embed: Embedder;
|
|
45
|
+
/**
|
|
46
|
+
* Cosine similarity at/above which a stored entry counts as a hit.
|
|
47
|
+
* Higher = stricter (fewer false hits, lower hit rate). Default: 0.92.
|
|
48
|
+
*/
|
|
49
|
+
threshold?: number;
|
|
50
|
+
/** Max L1 entries retained (oldest evicted first). Default: 500. */
|
|
51
|
+
maxEntries?: number;
|
|
52
|
+
/** Optional TTL (ms) for L1 entries. Omit for no expiry. */
|
|
53
|
+
ttlMs?: number;
|
|
54
|
+
/** Optional shared L2 backend (e.g. the gateway). */
|
|
55
|
+
l2?: SemanticCacheBackend;
|
|
56
|
+
/**
|
|
57
|
+
* When true (default), an answer served by L2 is also written into L1 so the
|
|
58
|
+
* next local lookup is a fast hit — read-through cache warming.
|
|
59
|
+
*/
|
|
60
|
+
warmL1FromL2?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export declare class SemanticCache {
|
|
63
|
+
private readonly _embed;
|
|
64
|
+
private readonly _threshold;
|
|
65
|
+
private readonly _maxEntries;
|
|
66
|
+
private readonly _ttlMs;
|
|
67
|
+
private readonly _l2;
|
|
68
|
+
private readonly _warmL1;
|
|
69
|
+
private readonly _l1;
|
|
70
|
+
private _l1Hits;
|
|
71
|
+
private _l2Hits;
|
|
72
|
+
private _misses;
|
|
73
|
+
constructor(opts: SemanticCacheOptions);
|
|
74
|
+
/**
|
|
75
|
+
* Read-through entry point: returns a cached answer for a semantically-similar
|
|
76
|
+
* prior query, otherwise runs `generate()`, stores the result in both tiers,
|
|
77
|
+
* and returns it. Embeds the query exactly once (lookup + store share it).
|
|
78
|
+
*/
|
|
79
|
+
getOrGenerate(query: string, generate: () => Promise<string>, meta?: Record<string, unknown>): Promise<{
|
|
80
|
+
response: string;
|
|
81
|
+
cached: boolean;
|
|
82
|
+
tier?: 'l1' | 'l2';
|
|
83
|
+
score?: number;
|
|
84
|
+
}>;
|
|
85
|
+
/** Looks up a semantically-similar cached answer without generating on a miss. */
|
|
86
|
+
lookup(query: string): Promise<SemanticCacheHit | undefined>;
|
|
87
|
+
/** Stores a query → response association in both tiers. */
|
|
88
|
+
store(query: string, response: string, meta?: Record<string, unknown>): Promise<void>;
|
|
89
|
+
/** Drops all L1 entries. Does not touch the shared L2 backend. */
|
|
90
|
+
clear(): void;
|
|
91
|
+
/** Current L1 entry count. */
|
|
92
|
+
get size(): number;
|
|
93
|
+
/** Cumulative hit/miss counters across both tiers — for measuring savings. */
|
|
94
|
+
get stats(): {
|
|
95
|
+
l1Hits: number;
|
|
96
|
+
l2Hits: number;
|
|
97
|
+
misses: number;
|
|
98
|
+
};
|
|
99
|
+
private _lookupVec;
|
|
100
|
+
private _storeVec;
|
|
101
|
+
/** Linear cosine scan over L1, dropping expired entries en route. */
|
|
102
|
+
private _searchL1;
|
|
103
|
+
private _addL1;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=SemanticCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SemanticCache.d.ts","sourceRoot":"","sources":["../../src/cache/SemanticCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SemanticCache – an embedding-keyed read-through cache for LLM completions.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the exact-match ResponseCache (which keys on the byte-identical prompt),
|
|
5
|
+
* this keys on the *meaning* of the query: it embeds the query and serves a
|
|
6
|
+
* cached answer when a stored entry is within `threshold` cosine similarity.
|
|
7
|
+
* That catches paraphrases — "fix the auth bug" ≈ "login is broken" — which is
|
|
8
|
+
* where real frontier-call avoidance (and token savings) comes from.
|
|
9
|
+
*
|
|
10
|
+
* Two tiers, mirroring the project's L1-in-process / L2-shared read-through
|
|
11
|
+
* pattern:
|
|
12
|
+
* - L1: an in-process vector list, scanned locally (fast, offline-capable).
|
|
13
|
+
* - L2: an optional shared backend (e.g. the BuilderForce.ai gateway vector
|
|
14
|
+
* store) so a hit on one surface — web or agent — benefits the other.
|
|
15
|
+
*
|
|
16
|
+
* Fully portable: the embedder and the L2 backend are injected, so the same
|
|
17
|
+
* class runs in the browser (WebGPU SSM + native fetch) and in Node (the agent's
|
|
18
|
+
* `@webgpu/node` SSM + fetch) with no environment-specific forks.
|
|
19
|
+
*/
|
|
20
|
+
import { cosineSimilarity } from '../similarity/index.js';
|
|
21
|
+
const DEFAULT_THRESHOLD = 0.92;
|
|
22
|
+
const DEFAULT_MAX_ENTRIES = 500;
|
|
23
|
+
export class SemanticCache {
|
|
24
|
+
_embed;
|
|
25
|
+
_threshold;
|
|
26
|
+
_maxEntries;
|
|
27
|
+
_ttlMs;
|
|
28
|
+
_l2;
|
|
29
|
+
_warmL1;
|
|
30
|
+
_l1 = [];
|
|
31
|
+
_l1Hits = 0;
|
|
32
|
+
_l2Hits = 0;
|
|
33
|
+
_misses = 0;
|
|
34
|
+
constructor(opts) {
|
|
35
|
+
this._embed = opts.embed;
|
|
36
|
+
this._threshold = opts.threshold ?? DEFAULT_THRESHOLD;
|
|
37
|
+
this._maxEntries = opts.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
38
|
+
this._ttlMs = opts.ttlMs;
|
|
39
|
+
this._l2 = opts.l2;
|
|
40
|
+
this._warmL1 = opts.warmL1FromL2 ?? true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read-through entry point: returns a cached answer for a semantically-similar
|
|
44
|
+
* prior query, otherwise runs `generate()`, stores the result in both tiers,
|
|
45
|
+
* and returns it. Embeds the query exactly once (lookup + store share it).
|
|
46
|
+
*/
|
|
47
|
+
async getOrGenerate(query, generate, meta) {
|
|
48
|
+
const qv = await this._embed(query);
|
|
49
|
+
const hit = await this._lookupVec(qv);
|
|
50
|
+
if (hit)
|
|
51
|
+
return { response: hit.response, cached: true, tier: hit.tier, score: hit.score };
|
|
52
|
+
const response = await generate();
|
|
53
|
+
await this._storeVec(qv, response, meta);
|
|
54
|
+
return { response, cached: false };
|
|
55
|
+
}
|
|
56
|
+
/** Looks up a semantically-similar cached answer without generating on a miss. */
|
|
57
|
+
async lookup(query) {
|
|
58
|
+
return this._lookupVec(await this._embed(query));
|
|
59
|
+
}
|
|
60
|
+
/** Stores a query → response association in both tiers. */
|
|
61
|
+
async store(query, response, meta) {
|
|
62
|
+
await this._storeVec(await this._embed(query), response, meta);
|
|
63
|
+
}
|
|
64
|
+
/** Drops all L1 entries. Does not touch the shared L2 backend. */
|
|
65
|
+
clear() {
|
|
66
|
+
this._l1.length = 0;
|
|
67
|
+
}
|
|
68
|
+
/** Current L1 entry count. */
|
|
69
|
+
get size() {
|
|
70
|
+
return this._l1.length;
|
|
71
|
+
}
|
|
72
|
+
/** Cumulative hit/miss counters across both tiers — for measuring savings. */
|
|
73
|
+
get stats() {
|
|
74
|
+
return { l1Hits: this._l1Hits, l2Hits: this._l2Hits, misses: this._misses };
|
|
75
|
+
}
|
|
76
|
+
// ── Internals (operate on a precomputed embedding) ────────────────────────
|
|
77
|
+
async _lookupVec(qv) {
|
|
78
|
+
const local = this._searchL1(qv);
|
|
79
|
+
if (local) {
|
|
80
|
+
this._l1Hits++;
|
|
81
|
+
return { response: local.response, score: local.score, tier: 'l1' };
|
|
82
|
+
}
|
|
83
|
+
if (this._l2) {
|
|
84
|
+
// L2 is best-effort: a gateway error degrades to local-only, never throws.
|
|
85
|
+
const remote = await this._l2.lookup(qv, this._threshold).catch(() => undefined);
|
|
86
|
+
if (remote && remote.score >= this._threshold) {
|
|
87
|
+
if (this._warmL1)
|
|
88
|
+
this._addL1(qv, remote.response);
|
|
89
|
+
this._l2Hits++;
|
|
90
|
+
return { response: remote.response, score: remote.score, tier: 'l2' };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
this._misses++;
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
async _storeVec(qv, response, meta) {
|
|
97
|
+
this._addL1(qv, response);
|
|
98
|
+
if (this._l2) {
|
|
99
|
+
// Best-effort: failing to share to L2 must not fail the caller's request.
|
|
100
|
+
await this._l2.store(qv, response, meta).catch(() => { });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Linear cosine scan over L1, dropping expired entries en route. */
|
|
104
|
+
_searchL1(qv) {
|
|
105
|
+
const now = Date.now();
|
|
106
|
+
let best;
|
|
107
|
+
let bestScore = -Infinity;
|
|
108
|
+
for (let i = this._l1.length - 1; i >= 0; i--) {
|
|
109
|
+
const entry = this._l1[i];
|
|
110
|
+
if (this._ttlMs != null && now > entry.timestamp + this._ttlMs) {
|
|
111
|
+
this._l1.splice(i, 1);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const score = cosineSimilarity(qv, entry.embedding);
|
|
115
|
+
if (score > bestScore) {
|
|
116
|
+
bestScore = score;
|
|
117
|
+
best = entry;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return best && bestScore >= this._threshold
|
|
121
|
+
? { response: best.response, score: bestScore }
|
|
122
|
+
: undefined;
|
|
123
|
+
}
|
|
124
|
+
_addL1(qv, response) {
|
|
125
|
+
this._l1.push({ embedding: qv, response, timestamp: Date.now() });
|
|
126
|
+
while (this._l1.length > this._maxEntries)
|
|
127
|
+
this._l1.shift();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=SemanticCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SemanticCache.js","sourceRoot":"","sources":["../../src/cache/SemanticCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SemanticCache } from './SemanticCache.js';
|
|
2
|
+
export type { Embedder, SemanticCacheBackend, SemanticCacheHit, SemanticCacheOptions, } from './SemanticCache.js';
|
|
3
|
+
export { FetchSemanticCacheBackend } from './FetchSemanticCacheBackend.js';
|
|
4
|
+
export type { FetchSemanticCacheBackendOptions } from './FetchSemanticCacheBackend.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EACR,QAAQ,EACR,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,YAAY,EAAE,gCAAgC,EAAE,MAAM,gCAAgC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DistillationEngine – JS-only online knowledge distillation.
|
|
3
|
+
*
|
|
4
|
+
* The core insight: use a transformer as a *teacher* to generate high-quality
|
|
5
|
+
* responses, then adapt the SSM *student* on those responses using WSLA.
|
|
6
|
+
* This runs entirely in the browser with no Python or full-retraining required.
|
|
7
|
+
*
|
|
8
|
+
* Distillation flow:
|
|
9
|
+
* 1. bridge.generate(input) → teacher output
|
|
10
|
+
* 2. runtime.adapt(teacherOutput, opts.adapt) → SSM trains on it
|
|
11
|
+
* 3. Return both results for inspection
|
|
12
|
+
*/
|
|
13
|
+
import type { AdaptOptions, AdaptResult } from '../session/index.js';
|
|
14
|
+
import type { SSMRuntime } from '../runtime/SSMRuntime.js';
|
|
15
|
+
import type { TransformerBridge, BridgeGenerateOptions } from '../bridges/TransformerBridge.js';
|
|
16
|
+
export interface QualityGate {
|
|
17
|
+
/**
|
|
18
|
+
* Minimum character length of the teacher output.
|
|
19
|
+
* Outputs shorter than this are considered low quality and are skipped.
|
|
20
|
+
*/
|
|
21
|
+
minLength?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Maximum SSM perplexity threshold.
|
|
24
|
+
* When the SSM already achieves perplexity below this value on the teacher
|
|
25
|
+
* output, the content is considered already learned and adaptation is skipped.
|
|
26
|
+
*/
|
|
27
|
+
maxPerplexity?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface DistillOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Options forwarded to `runtime.adapt()`.
|
|
32
|
+
* Default: { wsla: true, epochs: 3 }
|
|
33
|
+
* WSLA is preferred because it is fast and targets the selective
|
|
34
|
+
* projection rows — exactly the parameters that encode token routing.
|
|
35
|
+
*/
|
|
36
|
+
adapt?: AdaptOptions;
|
|
37
|
+
/**
|
|
38
|
+
* Options forwarded to `bridge.generate()`.
|
|
39
|
+
*/
|
|
40
|
+
generate?: BridgeGenerateOptions;
|
|
41
|
+
/**
|
|
42
|
+
* Quality gate filters that can skip adaptation for low-quality or
|
|
43
|
+
* already-learned inputs.
|
|
44
|
+
*/
|
|
45
|
+
qualityGate?: QualityGate;
|
|
46
|
+
}
|
|
47
|
+
export interface DistillResult {
|
|
48
|
+
/** The input prompt that was distilled. */
|
|
49
|
+
input: string;
|
|
50
|
+
/** The teacher's (transformer bridge) response to the input. */
|
|
51
|
+
teacherOutput: string;
|
|
52
|
+
/** The adapt() result from training the SSM on the teacher output. */
|
|
53
|
+
adaptResult: AdaptResult;
|
|
54
|
+
/** Whether adaptation was skipped by the quality gate. */
|
|
55
|
+
skipped?: boolean;
|
|
56
|
+
/** Reason adaptation was skipped, if applicable. */
|
|
57
|
+
skipReason?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface DistillBatchResult {
|
|
60
|
+
results: DistillResult[];
|
|
61
|
+
/** Total number of adapt epochs run across all inputs. */
|
|
62
|
+
totalEpochs: number;
|
|
63
|
+
/** Wall-clock time for the entire batch in milliseconds. */
|
|
64
|
+
totalMs: number;
|
|
65
|
+
}
|
|
66
|
+
export interface DistillationLog {
|
|
67
|
+
timestamp: number;
|
|
68
|
+
input: string;
|
|
69
|
+
teacherOutputLength: number;
|
|
70
|
+
skipped: boolean;
|
|
71
|
+
skipReason?: string;
|
|
72
|
+
finalLoss?: number;
|
|
73
|
+
epochs: number;
|
|
74
|
+
}
|
|
75
|
+
export declare class DistillationEngine {
|
|
76
|
+
private readonly _runtime;
|
|
77
|
+
private readonly _bridge;
|
|
78
|
+
private readonly _log;
|
|
79
|
+
/**
|
|
80
|
+
* @param runtime The SSMRuntime whose SSM will be trained as the student.
|
|
81
|
+
* @param bridge The transformer bridge acting as teacher.
|
|
82
|
+
* A bridge must be provided — distillation requires one.
|
|
83
|
+
*/
|
|
84
|
+
constructor(runtime: SSMRuntime, bridge: TransformerBridge);
|
|
85
|
+
/**
|
|
86
|
+
* Runs a single distillation pass:
|
|
87
|
+
* 1. Teacher generates a response for `input`
|
|
88
|
+
* 2. Quality gate is evaluated (if configured)
|
|
89
|
+
* 3. SSM is adapted on the teacher's output (WSLA by default)
|
|
90
|
+
*
|
|
91
|
+
* The training signal is the teacher's full response — this teaches the
|
|
92
|
+
* SSM what a good response to that prompt looks like, without requiring
|
|
93
|
+
* labelled data or a loss function beyond the standard LM objective.
|
|
94
|
+
*/
|
|
95
|
+
distill(input: string, opts?: DistillOptions): Promise<DistillResult>;
|
|
96
|
+
/**
|
|
97
|
+
* Runs distillation for each input in sequence.
|
|
98
|
+
* Aggregate statistics are returned alongside individual results.
|
|
99
|
+
*/
|
|
100
|
+
distillBatch(inputs: string[], opts?: DistillOptions): Promise<DistillBatchResult>;
|
|
101
|
+
/**
|
|
102
|
+
* Returns a copy of the in-memory distillation log (last 200 entries).
|
|
103
|
+
*/
|
|
104
|
+
getLog(): DistillationLog[];
|
|
105
|
+
private _appendLog;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=DistillationEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DistillationEngine.d.ts","sourceRoot":"","sources":["../../src/distillation/DistillationEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAKhG,MAAM,WAAW,WAAW;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAO,MAAM,CAAC;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAG,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,EAAS,YAAY,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,EAAM,qBAAqB,CAAC;IAErC;;;OAGG;IACH,WAAW,CAAC,EAAG,WAAW,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC1B,2CAA2C;IAC3C,KAAK,EAAU,MAAM,CAAC;IACtB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,WAAW,EAAI,WAAW,CAAC;IAC3B,0DAA0D;IAC1D,OAAO,CAAC,EAAO,OAAO,CAAC;IACvB,oDAAoD;IACpD,UAAU,CAAC,EAAI,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAM,aAAa,EAAE,CAAC;IAC7B,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,OAAO,EAAM,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC5B,SAAS,EAAY,MAAM,CAAC;IAC5B,KAAK,EAAgB,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAc,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAU,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAW,MAAM,CAAC;IAC5B,MAAM,EAAe,MAAM,CAAC;CAC/B;AAOD,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IAEpD;;;;OAIG;gBACS,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB;IAK1D;;;;;;;;;OASG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;IA8F/E;;;OAGG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAkB5F;;OAEG;IACH,MAAM,IAAI,eAAe,EAAE;IAM3B,OAAO,CAAC,UAAU;CAMrB"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DistillationEngine – JS-only online knowledge distillation.
|
|
3
|
+
*
|
|
4
|
+
* The core insight: use a transformer as a *teacher* to generate high-quality
|
|
5
|
+
* responses, then adapt the SSM *student* on those responses using WSLA.
|
|
6
|
+
* This runs entirely in the browser with no Python or full-retraining required.
|
|
7
|
+
*
|
|
8
|
+
* Distillation flow:
|
|
9
|
+
* 1. bridge.generate(input) → teacher output
|
|
10
|
+
* 2. runtime.adapt(teacherOutput, opts.adapt) → SSM trains on it
|
|
11
|
+
* 3. Return both results for inspection
|
|
12
|
+
*/
|
|
13
|
+
import { SSMError } from '../errors/SSMError.js';
|
|
14
|
+
/** Maximum number of distillation log entries to retain in memory. */
|
|
15
|
+
const MAX_LOG_ENTRIES = 200;
|
|
16
|
+
// ── DistillationEngine ────────────────────────────────────────────────────────
|
|
17
|
+
export class DistillationEngine {
|
|
18
|
+
_runtime;
|
|
19
|
+
_bridge;
|
|
20
|
+
_log = [];
|
|
21
|
+
/**
|
|
22
|
+
* @param runtime The SSMRuntime whose SSM will be trained as the student.
|
|
23
|
+
* @param bridge The transformer bridge acting as teacher.
|
|
24
|
+
* A bridge must be provided — distillation requires one.
|
|
25
|
+
*/
|
|
26
|
+
constructor(runtime, bridge) {
|
|
27
|
+
this._runtime = runtime;
|
|
28
|
+
this._bridge = bridge;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Runs a single distillation pass:
|
|
32
|
+
* 1. Teacher generates a response for `input`
|
|
33
|
+
* 2. Quality gate is evaluated (if configured)
|
|
34
|
+
* 3. SSM is adapted on the teacher's output (WSLA by default)
|
|
35
|
+
*
|
|
36
|
+
* The training signal is the teacher's full response — this teaches the
|
|
37
|
+
* SSM what a good response to that prompt looks like, without requiring
|
|
38
|
+
* labelled data or a loss function beyond the standard LM objective.
|
|
39
|
+
*/
|
|
40
|
+
async distill(input, opts = {}) {
|
|
41
|
+
const adaptOpts = {
|
|
42
|
+
wsla: true,
|
|
43
|
+
epochs: 3,
|
|
44
|
+
...opts.adapt,
|
|
45
|
+
};
|
|
46
|
+
let teacherOutput;
|
|
47
|
+
try {
|
|
48
|
+
teacherOutput = await this._bridge.generate(input, opts.generate);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
throw new SSMError('DISTILL_FAILED', `Teacher bridge failed to generate for distillation: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
52
|
+
}
|
|
53
|
+
// ── Quality gate ──────────────────────────────────────────────────────
|
|
54
|
+
if (opts.qualityGate) {
|
|
55
|
+
const gate = opts.qualityGate;
|
|
56
|
+
if (gate.minLength != null && teacherOutput.length < gate.minLength) {
|
|
57
|
+
const result = {
|
|
58
|
+
input,
|
|
59
|
+
teacherOutput,
|
|
60
|
+
adaptResult: { losses: [], epochCount: 0, durationMs: 0 },
|
|
61
|
+
skipped: true,
|
|
62
|
+
skipReason: 'low_quality',
|
|
63
|
+
};
|
|
64
|
+
this._appendLog({
|
|
65
|
+
input,
|
|
66
|
+
teacherOutputLength: teacherOutput.length,
|
|
67
|
+
skipped: true,
|
|
68
|
+
skipReason: 'low_quality',
|
|
69
|
+
epochs: 0,
|
|
70
|
+
});
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
if (gate.maxPerplexity != null) {
|
|
74
|
+
let perplexity;
|
|
75
|
+
try {
|
|
76
|
+
perplexity = await this._runtime.evaluate(teacherOutput);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Evaluation failure is non-fatal — proceed with adaptation
|
|
80
|
+
}
|
|
81
|
+
if (perplexity != null && perplexity < gate.maxPerplexity) {
|
|
82
|
+
const result = {
|
|
83
|
+
input,
|
|
84
|
+
teacherOutput,
|
|
85
|
+
adaptResult: { losses: [], epochCount: 0, durationMs: 0 },
|
|
86
|
+
skipped: true,
|
|
87
|
+
skipReason: 'already_learned',
|
|
88
|
+
};
|
|
89
|
+
this._appendLog({
|
|
90
|
+
input,
|
|
91
|
+
teacherOutputLength: teacherOutput.length,
|
|
92
|
+
skipped: true,
|
|
93
|
+
skipReason: 'already_learned',
|
|
94
|
+
epochs: 0,
|
|
95
|
+
});
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Train the SSM on the teacher's output.
|
|
101
|
+
// Prepend the input so the model learns the (prompt → response) mapping.
|
|
102
|
+
const trainingText = `${input}\n${teacherOutput}`;
|
|
103
|
+
let adaptResult;
|
|
104
|
+
try {
|
|
105
|
+
adaptResult = await this._runtime.adapt(trainingText, adaptOpts);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
throw new SSMError('DISTILL_FAILED', `SSM adaptation failed during distillation: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
109
|
+
}
|
|
110
|
+
this._appendLog({
|
|
111
|
+
input,
|
|
112
|
+
teacherOutputLength: teacherOutput.length,
|
|
113
|
+
skipped: false,
|
|
114
|
+
finalLoss: adaptResult.losses.at(-1),
|
|
115
|
+
epochs: adaptResult.epochCount,
|
|
116
|
+
});
|
|
117
|
+
return { input, teacherOutput, adaptResult, skipped: false };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Runs distillation for each input in sequence.
|
|
121
|
+
* Aggregate statistics are returned alongside individual results.
|
|
122
|
+
*/
|
|
123
|
+
async distillBatch(inputs, opts = {}) {
|
|
124
|
+
const startMs = Date.now();
|
|
125
|
+
const results = [];
|
|
126
|
+
let totalEpochs = 0;
|
|
127
|
+
for (const input of inputs) {
|
|
128
|
+
const result = await this.distill(input, opts);
|
|
129
|
+
results.push(result);
|
|
130
|
+
totalEpochs += result.adaptResult.epochCount;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
results,
|
|
134
|
+
totalEpochs,
|
|
135
|
+
totalMs: Date.now() - startMs,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Returns a copy of the in-memory distillation log (last 200 entries).
|
|
140
|
+
*/
|
|
141
|
+
getLog() {
|
|
142
|
+
return this._log.slice();
|
|
143
|
+
}
|
|
144
|
+
// ── Private helpers ───────────────────────────────────────────────────────
|
|
145
|
+
_appendLog(entry) {
|
|
146
|
+
this._log.push({ timestamp: Date.now(), ...entry });
|
|
147
|
+
if (this._log.length > MAX_LOG_ENTRIES) {
|
|
148
|
+
this._log.shift();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=DistillationEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DistillationEngine.js","sourceRoot":"","sources":["../../src/distillation/DistillationEngine.ts"],"names":[],"mappings":"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"}
|