@atlaskit/editor-plugin-autocomplete 3.1.0 → 3.3.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/CHANGELOG.md +15 -0
- package/dist/cjs/pm-plugins/local-slow-lane-client.js +704 -148
- package/dist/es2019/pm-plugins/local-slow-lane-client.js +519 -85
- package/dist/esm/pm-plugins/local-slow-lane-client.js +696 -144
- package/dist/types/pm-plugins/local-slow-lane-client.d.ts +102 -15
- package/dist/types-ts4.5/pm-plugins/local-slow-lane-client.d.ts +102 -15
- package/package.json +1 -1
- package/scripts/gen_first_token_to_words.py +170 -0
- package/src/pm-plugins/data/first_token_to_words.json +1 -0
- package/src/pm-plugins/local-slow-lane-client.ts +627 -94
|
@@ -1,38 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
var
|
|
4
|
+
var _typeof3 = require("@babel/runtime/helpers/typeof");
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.createLocalSlowLaneClient = exports.
|
|
8
|
+
exports.wrapForArctic = exports.createLocalSlowLaneClient = exports.computeBePayload = exports.LOCAL_MLC_EMBEDDING_MODEL_ID = exports.LOCAL_MLC_CAUSAL_MODEL_ID = exports.BE_PARITY = void 0;
|
|
9
9
|
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
10
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
11
10
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
12
|
-
var
|
|
11
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
13
12
|
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
13
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
14
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
15
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
16
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
14
17
|
var _debugMode = require("./debug-mode");
|
|
15
18
|
var _slowLaneClient = require("./slow-lane-client");
|
|
16
|
-
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
17
|
-
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
18
|
-
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
19
19
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
20
20
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
21
|
-
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" !=
|
|
21
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
|
|
22
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
23
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
24
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /**
|
|
22
25
|
* Local Slow Lane Client: On-device inference via @mlc-ai/web-llm.
|
|
23
26
|
*
|
|
24
|
-
* Drop-in replacement for the network-based slow-lane-client.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
+
* Drop-in replacement for the network-based slow-lane-client. Instead of calling
|
|
28
|
+
* a backend API, this client runs two models in the browser via WebGPU, in a
|
|
29
|
+
* single MLCEngine, to reproduce the BE encoder's outputs on-device:
|
|
30
|
+
*
|
|
31
|
+
* - Causal LM (SmolLM2-135M-Instruct): one decode step per word boundary. A
|
|
32
|
+
* registered LogitProcessor captures the raw next-token logits, which
|
|
33
|
+
* `computeBePayload` turns into a whole-word `lm_logits` payload — a faithful
|
|
34
|
+
* port of the BE `CausalLMEncoder._get_top_k_probs` (masked softmax over the
|
|
35
|
+
* vocab's first-tokens, prefix expansion, L2 reservation, log-space pooling).
|
|
36
|
+
* - Semantic embedder (Snowflake Arctic Embed S): produces the real 384-d
|
|
37
|
+
* `semantic_vector`. Inputs are wrapped as passages (see `wrapForArctic`) so
|
|
38
|
+
* the runtime vector lands in the same space as the precomputed word bin.
|
|
27
39
|
*
|
|
28
40
|
* ── Why main thread (no Web Worker)? ─────────────────────────────────────
|
|
29
|
-
*
|
|
30
|
-
*
|
|
41
|
+
* The models are small enough (~640 MB combined VRAM) that WebGPU inference on
|
|
42
|
+
* the main thread is viable:
|
|
31
43
|
*
|
|
32
44
|
* - WebGPU GPU compute is inherently async (doesn't block the main thread)
|
|
33
|
-
* - CPU overhead (
|
|
34
|
-
* -
|
|
35
|
-
*
|
|
45
|
+
* - CPU overhead (BE-parity post-processing) is a few ms
|
|
46
|
+
* - Per-inference latency is well within autocomplete expectations
|
|
47
|
+
* (~250 ms between word boundaries)
|
|
36
48
|
*
|
|
37
49
|
* This avoids all the complexity of Web Workers:
|
|
38
50
|
* - No CSP workarounds (blob URLs, inline scripts)
|
|
@@ -52,17 +64,526 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
52
64
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
53
65
|
|
|
54
66
|
var DEFAULT_DEBOUNCE_MS = 300;
|
|
55
|
-
var
|
|
67
|
+
var LOCAL_MLC_CAUSAL_MODEL_ID = exports.LOCAL_MLC_CAUSAL_MODEL_ID = 'SmolLM2-135M-Instruct-q0f16-MLC';
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* MLC ID for the semantic embedder (Snowflake Arctic Embed S, batch=4 variant).
|
|
71
|
+
*
|
|
72
|
+
* The `-b4` suffix selects the prebuilt variant compiled for a max batch size of
|
|
73
|
+
* 4 (≈239 MB VRAM) rather than `-b32` (≈1023 MB VRAM). Autocomplete embeds one
|
|
74
|
+
* context at a time, so `-b4` is the right fit. This model IS in
|
|
75
|
+
* `prebuiltAppConfig.model_list` of web-llm 0.2.82 — no `customModelConfig` needed.
|
|
76
|
+
*/
|
|
77
|
+
var LOCAL_MLC_EMBEDDING_MODEL_ID = exports.LOCAL_MLC_EMBEDDING_MODEL_ID = 'snowflake-arctic-embed-s-q0f32-MLC-b4';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Wrap raw context text with BERT special tokens before embedding.
|
|
81
|
+
*
|
|
82
|
+
* web-llm's `EmbeddingPipeline` does NOT auto-prepend `[CLS]` / append `[SEP]`
|
|
83
|
+
* (the official MLC embeddings example wraps manually). The Python
|
|
84
|
+
* `sentence_transformers` side that generated the word-vector bin adds these
|
|
85
|
+
* inside `model.encode()`, so we must mirror it here for the runtime context
|
|
86
|
+
* vector to land in the same region of Arctic's embedding space as the bin.
|
|
87
|
+
*
|
|
88
|
+
* No query prefix is applied: the semantic step is sentence-to-sentence (`s2s`)
|
|
89
|
+
* similarity ("which words are conceptually similar to this context?"), not
|
|
90
|
+
* sentence-to-passage (`s2p`) retrieval. Arctic's query prefix would misframe
|
|
91
|
+
* the relationship. Encode both sides as passages. See implementation.md §4.3.
|
|
92
|
+
*/
|
|
93
|
+
var wrapForArctic = exports.wrapForArctic = function wrapForArctic(text) {
|
|
94
|
+
return "[CLS] ".concat(text, " [SEP]");
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* BE-parity constants — must match `CausalLMEncoder` defaults in the Python
|
|
99
|
+
* sidecar (`cc-smarts/python-sidecar/src/causal_lm_encoder.py`) and
|
|
100
|
+
* `SlowLaneEngine` (`typeahead_context_encoding.py`) so local payloads behave
|
|
101
|
+
* identically to the server-client setup.
|
|
102
|
+
*/
|
|
103
|
+
var BE_PARITY = exports.BE_PARITY = {
|
|
104
|
+
/** Final payload size cap (BE: `top_k_words`). */
|
|
105
|
+
TOP_K_WORDS: 2000,
|
|
106
|
+
/** L2 (domain) words admitted unconditionally before pooling (BE: `reserved_l2_slots`). */
|
|
107
|
+
RESERVED_L2_SLOTS: 500,
|
|
108
|
+
/** Log-space additive bias favouring L2 over L3 in the pool (BE: `l2_bias`). */
|
|
109
|
+
L2_BIAS: 1.0,
|
|
110
|
+
/** Drop words below this probability from the final payload (BE: `> 0.00001`). */
|
|
111
|
+
MIN_PROB: 0.00001,
|
|
112
|
+
/**
|
|
113
|
+
* Word-level approximation of the BE causal LM token limit.
|
|
114
|
+
*
|
|
115
|
+
* BE: `CausalLMEncoder.max_context_tokens = 100` (BPE tokens, left-truncated).
|
|
116
|
+
* FE: no tokenizer available, so we approximate with word count. English text
|
|
117
|
+
* averages ~1.3–1.5 BPE tokens/word, meaning 100 words ≈ 130–150 tokens.
|
|
118
|
+
* Using 100 words keeps the approximation simple and errs on the side of
|
|
119
|
+
* sending slightly more context than the BE sees — acceptable for a PoC.
|
|
120
|
+
*/
|
|
121
|
+
MAX_CONTEXT_TOKENS: 100,
|
|
122
|
+
/**
|
|
123
|
+
* Word-level rolling window for the semantic embedder.
|
|
124
|
+
*
|
|
125
|
+
* BE: `SlowLaneEngine.max_context_words = 100` (applied in
|
|
126
|
+
* `typeahead_context_encoding.py` before calling `SemanticEncoder.encode`).
|
|
127
|
+
* Truncated identically here so the runtime Arctic vector lands in the same
|
|
128
|
+
* region of the embedding space as the precomputed word-vector bin.
|
|
129
|
+
*/
|
|
130
|
+
MAX_CONTEXT_WORDS: 100
|
|
131
|
+
};
|
|
132
|
+
var splitOnWhitespace = function splitOnWhitespace(text) {
|
|
133
|
+
var trimmed = text.trim();
|
|
134
|
+
if (trimmed === '') {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
var words = [];
|
|
138
|
+
var wordStart = -1;
|
|
139
|
+
for (var i = 0; i < trimmed.length; i++) {
|
|
140
|
+
if (trimmed[i].trim() === '') {
|
|
141
|
+
if (wordStart !== -1) {
|
|
142
|
+
words.push(trimmed.slice(wordStart, i));
|
|
143
|
+
wordStart = -1;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (wordStart === -1) {
|
|
148
|
+
wordStart = i;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (wordStart !== -1) {
|
|
152
|
+
words.push(trimmed.slice(wordStart));
|
|
153
|
+
}
|
|
154
|
+
return words;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Return the last `n` whitespace-separated words of `text`, joined by spaces.
|
|
159
|
+
* Mirrors the BE rolling-window truncation applied before both encoders.
|
|
160
|
+
*/
|
|
161
|
+
var truncateToLastNWords = function truncateToLastNWords(text, n) {
|
|
162
|
+
var words = splitOnWhitespace(text);
|
|
163
|
+
return words.length <= n ? text : words.slice(-n).join(' ');
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// ─── Logit capture ─────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A LogitProcessor that captures the raw next-token logits and passes them
|
|
170
|
+
* through unmodified.
|
|
171
|
+
*
|
|
172
|
+
* web-llm invokes `processLogits` on the CPU after the model's forward pass and
|
|
173
|
+
* before sampling, handing us the full `Float32Array(vocab_size)` at the current
|
|
174
|
+
* decode position. We copy it off web-llm's shared buffer (which it may reuse
|
|
175
|
+
* across calls) and return the original untouched so sampling is unaffected.
|
|
176
|
+
*
|
|
177
|
+
* This is the raw-logit access the BE-parity algorithm needs (masked softmax +
|
|
178
|
+
* prefix expansion, consumed in a later step). Registered for the causal LM
|
|
179
|
+
* only — the embedder never decodes tokens, so it produces no logits.
|
|
180
|
+
*/
|
|
181
|
+
var CapturingLogitProcessor = /*#__PURE__*/(0, _createClass2.default)(function CapturingLogitProcessor() {
|
|
182
|
+
var _this = this;
|
|
183
|
+
(0, _classCallCheck2.default)(this, CapturingLogitProcessor);
|
|
184
|
+
(0, _defineProperty2.default)(this, "captured", null);
|
|
185
|
+
(0, _defineProperty2.default)(this, "processLogits", function (logits) {
|
|
186
|
+
// Copy off web-llm's shared buffer — it may reuse `logits` across calls.
|
|
187
|
+
_this.captured = new Float32Array(logits);
|
|
188
|
+
return logits;
|
|
189
|
+
});
|
|
190
|
+
(0, _defineProperty2.default)(this, "processSampledToken", function () {
|
|
191
|
+
// No-op — we don't track sampled tokens.
|
|
192
|
+
});
|
|
193
|
+
(0, _defineProperty2.default)(this, "resetState", function () {
|
|
194
|
+
_this.captured = null;
|
|
195
|
+
});
|
|
196
|
+
}); // ─── BE-parity data + algorithm ──────────────────────────────────────────────
|
|
197
|
+
/**
|
|
198
|
+
* Prefix-expansion map: first-token id → words whose space-prefixed SmolLM2
|
|
199
|
+
* encoding starts with that token. Generated offline by
|
|
200
|
+
* `scripts/gen_first_token_to_words.py`, which mirrors the BE's in-memory map
|
|
201
|
+
* (`CausalLMEncoder._ensure_loaded`).
|
|
202
|
+
*
|
|
203
|
+
* Populated lazily by `loadBePayloadData()` from a dynamically-imported JSON so
|
|
204
|
+
* the (large) payload is only fetched when the local client is actually
|
|
205
|
+
* initialised — keeping it out of the editor's main chunk for the vast majority
|
|
206
|
+
* of users (who run with `useLocalModel` off).
|
|
207
|
+
*/
|
|
208
|
+
var firstTokenToWords = new Map();
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* L2 (Atlassian-domain) word set, derived from the keys of `vocabulary_10k.json`.
|
|
212
|
+
* Used by `computeBePayload` for tier-aware ranking: any word in the prefix map
|
|
213
|
+
* that is not in this set is treated as L3 (general English), matching the BE.
|
|
214
|
+
* Populated lazily alongside `firstTokenToWords` — see `loadBePayloadData()`.
|
|
215
|
+
*/
|
|
216
|
+
var l2Words = new Set();
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Array of token IDs that appear as a first token for at least one vocabulary
|
|
220
|
+
* word. Derived from `firstTokenToWords` when the data loads so `computeBePayload`
|
|
221
|
+
* does not re-allocate this array on every word-boundary call.
|
|
222
|
+
*/
|
|
223
|
+
var prefixMapTokenIds = [];
|
|
224
|
+
|
|
225
|
+
/** De-dupes concurrent loads and lets repeated calls await the same payload. */
|
|
226
|
+
var bePayloadDataPromise;
|
|
56
227
|
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Unwrap a dynamically imported JSON module to the parsed JSON value, working
|
|
230
|
+
* across the two interop modes AFM's bundler chain emits:
|
|
231
|
+
*
|
|
232
|
+
* 1. **`.default`-wrapped namespace** — classic webpack (and Jest) hang the
|
|
233
|
+
* JSON value under the `default` export.
|
|
234
|
+
* 2. **Named-exports namespace** — webpack 5 / atlaspack with JSON
|
|
235
|
+
* named-exports (or native ESM JSON modules) expose each top-level key as
|
|
236
|
+
* a named export and shadow `default`, so `mod.default` can be `undefined`
|
|
237
|
+
* (or some unrelated value) even though `mod` itself holds the data.
|
|
238
|
+
*
|
|
239
|
+
* The caller MUST declare the underlying JSON shape via `shape` because, in
|
|
240
|
+
* named-exports mode, a dense array `["a","b"]` and a sparse numeric-keyed
|
|
241
|
+
* object `{"5":"a","12":"b"}` are emitted identically (`{"0":..}` / `{"5":..}`);
|
|
242
|
+
* no runtime heuristic can tell them apart, so only the caller knows which:
|
|
243
|
+
*
|
|
244
|
+
* - `'object'` — the JSON is a `{...}` (including sparse maps keyed by integer
|
|
245
|
+
* IDs). The named exports are rebuilt into a plain object so `Object.entries`
|
|
246
|
+
* yields the real keys, not synthetic array indices.
|
|
247
|
+
* - `'array'` — the JSON is a `[...]`, reconstructed from the `0..n-1` indices.
|
|
248
|
+
*
|
|
249
|
+
* :param mod: The raw module object returned by `await import('./*.json')`.
|
|
250
|
+
* :param shape: `'object'` if the source JSON is `{...}`, `'array'` if `[...]`.
|
|
251
|
+
* :returns: The parsed JSON value, or `null` if neither interop mode applies.
|
|
252
|
+
*/
|
|
253
|
+
var unwrapJsonModule = function unwrapJsonModule(mod, shape) {
|
|
254
|
+
if (mod == null || (0, _typeof2.default)(mod) !== 'object') {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
var namespace = mod;
|
|
258
|
+
|
|
259
|
+
// Compute the named-export own-keys (strip synthetic markers).
|
|
260
|
+
var ownKeys = Object.keys(namespace).filter(function (k) {
|
|
261
|
+
return k !== 'default' && k !== '__esModule';
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// PREFER named exports when present — they always reflect the JSON's real
|
|
265
|
+
// top-level keys / indices, regardless of what `default` happens to be.
|
|
266
|
+
// Under JSON named-exports mode `default` is not necessarily the parsed
|
|
267
|
+
// value (e.g. for `{"service": 0, ...}` it can be the number `0`, with the
|
|
268
|
+
// real data in the named exports), so taking `default` first would corrupt it.
|
|
269
|
+
if (ownKeys.length > 0) {
|
|
270
|
+
if (shape === 'array') {
|
|
271
|
+
// JSON arrays are dense; reconstruct from `0..length-1` indices.
|
|
272
|
+
var len = ownKeys.length;
|
|
273
|
+
var arr = new Array(len);
|
|
274
|
+
for (var i = 0; i < len; i++) {
|
|
275
|
+
arr[i] = namespace[String(i)];
|
|
276
|
+
}
|
|
277
|
+
return arr;
|
|
278
|
+
}
|
|
279
|
+
// shape === 'object'. Rebuild a plain object from the (stripped) own
|
|
280
|
+
// keys so callers can `Object.entries()` it without iterating over
|
|
281
|
+
// `default` / `__esModule`, and to detach from the module-namespace
|
|
282
|
+
// object (which is sealed/non-extensible on some bundler outputs).
|
|
283
|
+
var obj = {};
|
|
284
|
+
var _iterator = _createForOfIteratorHelper(ownKeys),
|
|
285
|
+
_step;
|
|
286
|
+
try {
|
|
287
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
288
|
+
var k = _step.value;
|
|
289
|
+
obj[k] = namespace[k];
|
|
290
|
+
}
|
|
291
|
+
} catch (err) {
|
|
292
|
+
_iterator.e(err);
|
|
293
|
+
} finally {
|
|
294
|
+
_iterator.f();
|
|
295
|
+
}
|
|
296
|
+
return obj;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Fallback: no named exports — classic webpack JSON-module interop where
|
|
300
|
+
// the whole parsed JSON value is hung under `default`. Trust it.
|
|
301
|
+
if ('default' in namespace && namespace.default != null) {
|
|
302
|
+
return namespace.default;
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Lazily load and build the BE-parity lookup tables from their JSON payloads.
|
|
309
|
+
* The dynamic imports are split into their own async chunks so neither file is
|
|
310
|
+
* bundled into the editor's main chunk unless local inference is initialised.
|
|
311
|
+
*
|
|
312
|
+
* :returns:
|
|
313
|
+
* A promise that resolves once `firstTokenToWords`, `l2Words` and
|
|
314
|
+
* `prefixMapTokenIds` are populated.
|
|
315
|
+
*/
|
|
316
|
+
var loadBePayloadData = function loadBePayloadData() {
|
|
317
|
+
if (!bePayloadDataPromise) {
|
|
318
|
+
bePayloadDataPromise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
319
|
+
var _yield$Promise$all, _yield$Promise$all2, firstTokenToWordsModule, vocabularyModule, firstTokenToWordsData, vocabularyData;
|
|
320
|
+
return _regenerator.default.wrap(function (_context) {
|
|
321
|
+
while (1) switch (_context.prev = _context.next) {
|
|
322
|
+
case 0:
|
|
323
|
+
_context.next = 1;
|
|
324
|
+
return Promise.all([Promise.resolve().then(function () {
|
|
325
|
+
return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-first-token-to-words" */'./data/first_token_to_words.json'));
|
|
326
|
+
}), Promise.resolve().then(function () {
|
|
327
|
+
return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-vocabulary-10k" */'./data/vocabulary_10k.json'));
|
|
328
|
+
})]);
|
|
329
|
+
case 1:
|
|
330
|
+
_yield$Promise$all = _context.sent;
|
|
331
|
+
_yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 2);
|
|
332
|
+
firstTokenToWordsModule = _yield$Promise$all2[0];
|
|
333
|
+
vocabularyModule = _yield$Promise$all2[1];
|
|
334
|
+
firstTokenToWordsData = unwrapJsonModule(firstTokenToWordsModule, 'object');
|
|
335
|
+
vocabularyData = unwrapJsonModule(vocabularyModule, 'object');
|
|
336
|
+
if (!(firstTokenToWordsData == null || (vocabularyData === null || vocabularyData === void 0 ? void 0 : vocabularyData.words) == null)) {
|
|
337
|
+
_context.next = 2;
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
throw new Error("[LocalSlowLane] JSON module could not be unwrapped \u2014 " + "firstTokenToWordsData=".concat(firstTokenToWordsData == null ? 'null/undefined' : 'defined', ", ") + "vocabularyData=".concat(vocabularyData == null ? 'null/undefined' : vocabularyData.words == null ? 'defined but missing .words' : 'defined'));
|
|
341
|
+
case 2:
|
|
342
|
+
firstTokenToWords = new Map(Object.entries(firstTokenToWordsData).map(function (_ref2) {
|
|
343
|
+
var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
|
|
344
|
+
tokenId = _ref3[0],
|
|
345
|
+
words = _ref3[1];
|
|
346
|
+
return [Number(tokenId), words];
|
|
347
|
+
}));
|
|
348
|
+
l2Words = new Set(Object.keys(vocabularyData.words));
|
|
349
|
+
prefixMapTokenIds = Array.from(firstTokenToWords.keys());
|
|
350
|
+
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
351
|
+
// eslint-disable-next-line no-console
|
|
352
|
+
console.log('%c[LocalSlowLane] %c✅ BE-parity payload data loaded:', 'color: #9c27b0; font-weight: bold;', 'color: #4caf50; font-weight: bold;', {
|
|
353
|
+
firstTokenToWordsEntries: firstTokenToWords.size,
|
|
354
|
+
l2WordsCount: l2Words.size,
|
|
355
|
+
prefixMapTokenIdsLength: prefixMapTokenIds.length
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
case 3:
|
|
359
|
+
case "end":
|
|
360
|
+
return _context.stop();
|
|
361
|
+
}
|
|
362
|
+
}, _callee);
|
|
363
|
+
}))().catch(function (e) {
|
|
364
|
+
// Don't cache a rejected promise — a transient import failure would
|
|
365
|
+
// otherwise prevent the local model from ever initialising again this
|
|
366
|
+
// session. Reset so the next init attempt retries.
|
|
367
|
+
bePayloadDataPromise = undefined;
|
|
368
|
+
throw e;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
return bePayloadDataPromise;
|
|
372
|
+
};
|
|
60
373
|
|
|
61
374
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
375
|
+
* Convert a raw next-token logit vector into a whole-word probability payload,
|
|
376
|
+
* faithfully porting the BE `CausalLMEncoder._get_top_k_probs`
|
|
377
|
+
* (`cc-smarts/python-sidecar/src/causal_lm_encoder.py`).
|
|
378
|
+
*
|
|
379
|
+
* Steps: (1) numerically-stable masked softmax over only the token ids present
|
|
380
|
+
* in the prefix-expansion map; (2) spread each token's probability to every
|
|
381
|
+
* whole word sharing that first token, taking the max; (3) reserve the top L2
|
|
382
|
+
* words unconditionally; (4) rank the remainder in a log-space pool with an
|
|
383
|
+
* additive L2 bias; (5) emit raw probabilities for the survivors, lowercased
|
|
384
|
+
* and trimmed at `MIN_PROB`.
|
|
385
|
+
*
|
|
386
|
+
* :params:
|
|
387
|
+
* rawLogits: Full-vocabulary logits from the LM's single decode step
|
|
388
|
+
* prefixMap: Map of first-token id to the words starting with that token
|
|
389
|
+
* domainWords: Set of L2 (domain) words, for tier-aware ranking
|
|
390
|
+
* :returns:
|
|
391
|
+
* A record of lowercase word to probability — the BE `lm_logits` payload
|
|
64
392
|
*/
|
|
65
|
-
var
|
|
393
|
+
var computeBePayload = exports.computeBePayload = function computeBePayload(rawLogits, prefixMap, domainWords) {
|
|
394
|
+
var validTokenIds = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : prefixMapTokenIds;
|
|
395
|
+
// 1. Numerically-stable masked softmax over validTokenIds only.
|
|
396
|
+
var maxLogit = -Infinity;
|
|
397
|
+
var _iterator2 = _createForOfIteratorHelper(validTokenIds),
|
|
398
|
+
_step2;
|
|
399
|
+
try {
|
|
400
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
401
|
+
var id = _step2.value;
|
|
402
|
+
var v = rawLogits[id];
|
|
403
|
+
if (v > maxLogit) {
|
|
404
|
+
maxLogit = v;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
} catch (err) {
|
|
408
|
+
_iterator2.e(err);
|
|
409
|
+
} finally {
|
|
410
|
+
_iterator2.f();
|
|
411
|
+
}
|
|
412
|
+
var sumExp = 0;
|
|
413
|
+
var expByToken = new Map();
|
|
414
|
+
var _iterator3 = _createForOfIteratorHelper(validTokenIds),
|
|
415
|
+
_step3;
|
|
416
|
+
try {
|
|
417
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
418
|
+
var _id = _step3.value;
|
|
419
|
+
var e = Math.exp(rawLogits[_id] - maxLogit);
|
|
420
|
+
expByToken.set(_id, e);
|
|
421
|
+
sumExp += e;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// 2. Prefix expansion with max aggregation (probabilities sum to 1 over the
|
|
425
|
+
// masked subset, so divide each token's exp by sumExp on the fly).
|
|
426
|
+
} catch (err) {
|
|
427
|
+
_iterator3.e(err);
|
|
428
|
+
} finally {
|
|
429
|
+
_iterator3.f();
|
|
430
|
+
}
|
|
431
|
+
var wordProbs = new Map();
|
|
432
|
+
var _iterator4 = _createForOfIteratorHelper(prefixMap),
|
|
433
|
+
_step4;
|
|
434
|
+
try {
|
|
435
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
436
|
+
var _expByToken$get;
|
|
437
|
+
var _step4$value = (0, _slicedToArray2.default)(_step4.value, 2),
|
|
438
|
+
_id2 = _step4$value[0],
|
|
439
|
+
words = _step4$value[1];
|
|
440
|
+
var _p = sumExp > 0 ? ((_expByToken$get = expByToken.get(_id2)) !== null && _expByToken$get !== void 0 ? _expByToken$get : 0) / sumExp : 0;
|
|
441
|
+
var _iterator9 = _createForOfIteratorHelper(words),
|
|
442
|
+
_step9;
|
|
443
|
+
try {
|
|
444
|
+
for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
|
|
445
|
+
var _wordProbs$get2;
|
|
446
|
+
var _w = _step9.value;
|
|
447
|
+
var prev = (_wordProbs$get2 = wordProbs.get(_w)) !== null && _wordProbs$get2 !== void 0 ? _wordProbs$get2 : 0;
|
|
448
|
+
if (_p > prev) {
|
|
449
|
+
wordProbs.set(_w, _p);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
} catch (err) {
|
|
453
|
+
_iterator9.e(err);
|
|
454
|
+
} finally {
|
|
455
|
+
_iterator9.f();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// 3. Split into L2 / L3 and reserve the top L2 slots unconditionally.
|
|
460
|
+
} catch (err) {
|
|
461
|
+
_iterator4.e(err);
|
|
462
|
+
} finally {
|
|
463
|
+
_iterator4.f();
|
|
464
|
+
}
|
|
465
|
+
var l2Matches = [];
|
|
466
|
+
var l3Matches = [];
|
|
467
|
+
var _iterator5 = _createForOfIteratorHelper(wordProbs),
|
|
468
|
+
_step5;
|
|
469
|
+
try {
|
|
470
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
471
|
+
var _step5$value = (0, _slicedToArray2.default)(_step5.value, 2),
|
|
472
|
+
_w2 = _step5$value[0],
|
|
473
|
+
_p2 = _step5$value[1];
|
|
474
|
+
if (domainWords.has(_w2)) {
|
|
475
|
+
l2Matches.push([_w2, _p2]);
|
|
476
|
+
} else {
|
|
477
|
+
l3Matches.push([_w2, _p2]);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} catch (err) {
|
|
481
|
+
_iterator5.e(err);
|
|
482
|
+
} finally {
|
|
483
|
+
_iterator5.f();
|
|
484
|
+
}
|
|
485
|
+
l2Matches.sort(function (a, b) {
|
|
486
|
+
return b[1] - a[1];
|
|
487
|
+
});
|
|
488
|
+
var reserved = l2Matches.slice(0, BE_PARITY.RESERVED_L2_SLOTS);
|
|
489
|
+
|
|
490
|
+
// 4. Pool the leftovers in log space; the L2 bias only affects ranking here.
|
|
491
|
+
// Words in l2Matches are unique and the array is sorted descending, so the
|
|
492
|
+
// non-reserved entries are exactly the tail after the reserved prefix — slice
|
|
493
|
+
// it directly rather than allocating a Set and scanning every entry on this
|
|
494
|
+
// hot path (runs ~every word boundary while typing).
|
|
495
|
+
var pool = [];
|
|
496
|
+
var _iterator6 = _createForOfIteratorHelper(l2Matches.slice(BE_PARITY.RESERVED_L2_SLOTS)),
|
|
497
|
+
_step6;
|
|
498
|
+
try {
|
|
499
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
500
|
+
var _step6$value = (0, _slicedToArray2.default)(_step6.value, 2),
|
|
501
|
+
_w3 = _step6$value[0],
|
|
502
|
+
_p3 = _step6$value[1];
|
|
503
|
+
pool.push([_w3, Math.log(Math.max(_p3, 1e-10)) + BE_PARITY.L2_BIAS]);
|
|
504
|
+
}
|
|
505
|
+
} catch (err) {
|
|
506
|
+
_iterator6.e(err);
|
|
507
|
+
} finally {
|
|
508
|
+
_iterator6.f();
|
|
509
|
+
}
|
|
510
|
+
for (var _i = 0, _l3Matches = l3Matches; _i < _l3Matches.length; _i++) {
|
|
511
|
+
var _l3Matches$_i = (0, _slicedToArray2.default)(_l3Matches[_i], 2),
|
|
512
|
+
w = _l3Matches$_i[0],
|
|
513
|
+
p = _l3Matches$_i[1];
|
|
514
|
+
pool.push([w, Math.log(Math.max(p, 1e-10))]);
|
|
515
|
+
}
|
|
516
|
+
pool.sort(function (a, b) {
|
|
517
|
+
return b[1] - a[1];
|
|
518
|
+
});
|
|
519
|
+
var remainingSlots = Math.max(0, BE_PARITY.TOP_K_WORDS - reserved.length);
|
|
520
|
+
var poolWinners = pool.slice(0, remainingSlots);
|
|
521
|
+
|
|
522
|
+
// 5. Assemble payload: store RAW probabilities (the bias was ranking-only),
|
|
523
|
+
// lowercase keys, trimmed at MIN_PROB. Reserved first, then pool winners.
|
|
524
|
+
// Reserved entries are written first; pool-winner writes must NOT clobber a
|
|
525
|
+
// reserved entry whose normalised key collides (two source words can
|
|
526
|
+
// `.trim().toLowerCase()` to the same key — e.g. "Function" vs "function ").
|
|
527
|
+
// Without the existence guard, a low-probability pool winner would silently
|
|
528
|
+
// overwrite the (higher-probability) reserved entry, degrading top-K
|
|
529
|
+
// quality in a way that's invisible from the debug summary.
|
|
530
|
+
var result = {};
|
|
531
|
+
var addEntry = function addEntry(word, prob, allowOverwrite) {
|
|
532
|
+
if (prob <= BE_PARITY.MIN_PROB) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
var key = word.trim().toLowerCase();
|
|
536
|
+
if (!allowOverwrite && key in result) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
result[key] = prob;
|
|
540
|
+
};
|
|
541
|
+
var _iterator7 = _createForOfIteratorHelper(reserved),
|
|
542
|
+
_step7;
|
|
543
|
+
try {
|
|
544
|
+
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
|
|
545
|
+
var _step7$value = (0, _slicedToArray2.default)(_step7.value, 2),
|
|
546
|
+
_w4 = _step7$value[0],
|
|
547
|
+
_p4 = _step7$value[1];
|
|
548
|
+
addEntry(_w4, _p4, true);
|
|
549
|
+
}
|
|
550
|
+
} catch (err) {
|
|
551
|
+
_iterator7.e(err);
|
|
552
|
+
} finally {
|
|
553
|
+
_iterator7.f();
|
|
554
|
+
}
|
|
555
|
+
var _iterator8 = _createForOfIteratorHelper(poolWinners),
|
|
556
|
+
_step8;
|
|
557
|
+
try {
|
|
558
|
+
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
|
|
559
|
+
var _wordProbs$get3;
|
|
560
|
+
var _step8$value = (0, _slicedToArray2.default)(_step8.value, 1),
|
|
561
|
+
_w5 = _step8$value[0];
|
|
562
|
+
addEntry(_w5, (_wordProbs$get3 = wordProbs.get(_w5)) !== null && _wordProbs$get3 !== void 0 ? _wordProbs$get3 : 0, false);
|
|
563
|
+
}
|
|
564
|
+
} catch (err) {
|
|
565
|
+
_iterator8.e(err);
|
|
566
|
+
} finally {
|
|
567
|
+
_iterator8.f();
|
|
568
|
+
}
|
|
569
|
+
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
570
|
+
var topReserved = reserved.slice(0, 5).map(function (_ref4) {
|
|
571
|
+
var _ref5 = (0, _slicedToArray2.default)(_ref4, 2),
|
|
572
|
+
w = _ref5[0],
|
|
573
|
+
p = _ref5[1];
|
|
574
|
+
return "".concat(w, ":").concat((p * 100).toFixed(2), "%");
|
|
575
|
+
}).join(', ');
|
|
576
|
+
var topPool = poolWinners.slice(0, 5).map(function (_ref6) {
|
|
577
|
+
var _wordProbs$get;
|
|
578
|
+
var _ref7 = (0, _slicedToArray2.default)(_ref6, 1),
|
|
579
|
+
w = _ref7[0];
|
|
580
|
+
return "".concat(w, ":").concat((((_wordProbs$get = wordProbs.get(w)) !== null && _wordProbs$get !== void 0 ? _wordProbs$get : 0) * 100).toFixed(2), "%");
|
|
581
|
+
}).join(', ');
|
|
582
|
+
// eslint-disable-next-line no-console
|
|
583
|
+
console.log('%c[computeBePayload] %c%d valid tokens → %d words expanded | L2: %d / L3: %d | reserved: %d | pool winners: %d | final: %d words\n maxLogit(masked): %s | sumExp: %s\n top reserved L2: %s\n top pool: %s', 'color: #9c27b0; font-weight: bold;', 'color: inherit;', validTokenIds.length, wordProbs.size, l2Matches.length, l3Matches.length, reserved.length, poolWinners.length, Object.keys(result).length, maxLogit.toFixed(3), sumExp.toFixed(1), topReserved || '(none)', topPool || '(none)');
|
|
584
|
+
}
|
|
585
|
+
return result;
|
|
586
|
+
};
|
|
66
587
|
|
|
67
588
|
// ─── Factory ─────────────────────────────────────────────────────────────────
|
|
68
589
|
|
|
@@ -91,7 +612,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
91
612
|
onUpdate = config.onUpdate,
|
|
92
613
|
onStatus = config.onStatus,
|
|
93
614
|
_config$modelId = config.modelId,
|
|
94
|
-
modelId = _config$modelId === void 0 ?
|
|
615
|
+
modelId = _config$modelId === void 0 ? LOCAL_MLC_CAUSAL_MODEL_ID : _config$modelId,
|
|
95
616
|
customModelConfig = config.customModelConfig;
|
|
96
617
|
|
|
97
618
|
// ── State ──────────────────────────────────────────────────────────────
|
|
@@ -106,6 +627,9 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
106
627
|
var initFailed = false;
|
|
107
628
|
var engine = null;
|
|
108
629
|
var engineInitPromise = null;
|
|
630
|
+
// Captures raw next-token logits from the LM's single decode step. Registered
|
|
631
|
+
// with the engine below; `lmLogitsCapture.captured` is consumed in a later step.
|
|
632
|
+
var lmLogitsCapture = new CapturingLogitProcessor();
|
|
109
633
|
var unloadEngine = function unloadEngine(engineToUnload) {
|
|
110
634
|
engineToUnload.unload().catch(function (error) {
|
|
111
635
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
@@ -126,31 +650,33 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
126
650
|
onStatus === null || onStatus === void 0 || onStatus(message);
|
|
127
651
|
};
|
|
128
652
|
var initEngine = /*#__PURE__*/function () {
|
|
129
|
-
var
|
|
130
|
-
var _yield$
|
|
131
|
-
return _regenerator.default.wrap(function (
|
|
132
|
-
while (1) switch (
|
|
653
|
+
var _ref8 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
|
|
654
|
+
var _yield$Promise$all3, _yield$Promise$all4, _yield$Promise$all4$, MLCEngineCtor, prebuiltAppConfig, customModelRecord, appConfig, newEngine, errorMsg, _t;
|
|
655
|
+
return _regenerator.default.wrap(function (_context2) {
|
|
656
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
133
657
|
case 0:
|
|
134
|
-
|
|
658
|
+
_context2.prev = 0;
|
|
135
659
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
136
660
|
// eslint-disable-next-line no-console
|
|
137
|
-
console.log("%c[LocalSlowLane] %c\uD83D\uDE80 Initialising MLC engine with
|
|
661
|
+
console.log("%c[LocalSlowLane] %c\uD83D\uDE80 Initialising MLC engine with models: ".concat(modelId, " (LM) + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, " (embedder)"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
|
|
138
662
|
}
|
|
139
|
-
onStatus === null || onStatus === void 0 || onStatus("Initialising
|
|
663
|
+
onStatus === null || onStatus === void 0 || onStatus("Initialising models: ".concat(modelId, " + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, "\u2026"));
|
|
140
664
|
if ('gpu' in navigator) {
|
|
141
|
-
|
|
665
|
+
_context2.next = 1;
|
|
142
666
|
break;
|
|
143
667
|
}
|
|
144
668
|
throw new Error('WebGPU not supported');
|
|
145
669
|
case 1:
|
|
146
|
-
|
|
147
|
-
return Promise.resolve().then(function () {
|
|
670
|
+
_context2.next = 2;
|
|
671
|
+
return Promise.all([Promise.resolve().then(function () {
|
|
148
672
|
return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-mlc-web-llm" */'@mlc-ai/web-llm'));
|
|
149
|
-
});
|
|
673
|
+
}), loadBePayloadData()]);
|
|
150
674
|
case 2:
|
|
151
|
-
_yield$
|
|
152
|
-
|
|
153
|
-
|
|
675
|
+
_yield$Promise$all3 = _context2.sent;
|
|
676
|
+
_yield$Promise$all4 = (0, _slicedToArray2.default)(_yield$Promise$all3, 1);
|
|
677
|
+
_yield$Promise$all4$ = _yield$Promise$all4[0];
|
|
678
|
+
MLCEngineCtor = _yield$Promise$all4$.MLCEngine;
|
|
679
|
+
prebuiltAppConfig = _yield$Promise$all4$.prebuiltAppConfig;
|
|
154
680
|
customModelRecord = customModelConfig ? _objectSpread(_objectSpread({
|
|
155
681
|
model: customModelConfig.model,
|
|
156
682
|
model_id: modelId,
|
|
@@ -166,34 +692,43 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
166
692
|
} : {}) : undefined;
|
|
167
693
|
appConfig = {
|
|
168
694
|
model_list: [].concat((0, _toConsumableArray2.default)(prebuiltAppConfig.model_list), (0, _toConsumableArray2.default)(customModelRecord ? [customModelRecord] : []))
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
|
|
695
|
+
}; // Construct the engine with the logit-capture processor registered for
|
|
696
|
+
// the causal LM only (the embedder never decodes tokens), then load
|
|
697
|
+
// both the LM and the embedder into the same engine (multi-model).
|
|
698
|
+
newEngine = new MLCEngineCtor({
|
|
172
699
|
appConfig: appConfig,
|
|
173
|
-
initProgressCallback: initProgressCallback
|
|
700
|
+
initProgressCallback: initProgressCallback,
|
|
701
|
+
logitProcessorRegistry: new Map([[modelId, lmLogitsCapture]])
|
|
174
702
|
});
|
|
703
|
+
_context2.next = 3;
|
|
704
|
+
return newEngine.reload([modelId, LOCAL_MLC_EMBEDDING_MODEL_ID]);
|
|
175
705
|
case 3:
|
|
176
|
-
engine = _context.sent;
|
|
177
706
|
if (!destroyed) {
|
|
178
|
-
|
|
707
|
+
_context2.next = 4;
|
|
179
708
|
break;
|
|
180
709
|
}
|
|
181
710
|
// destroy() was called while we were loading — clean up
|
|
182
|
-
unloadEngine(
|
|
183
|
-
|
|
184
|
-
return _context.abrupt("return");
|
|
711
|
+
unloadEngine(newEngine);
|
|
712
|
+
return _context2.abrupt("return");
|
|
185
713
|
case 4:
|
|
714
|
+
engine = newEngine;
|
|
186
715
|
ready = true;
|
|
187
716
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
188
717
|
// eslint-disable-next-line no-console
|
|
189
|
-
console.log('%c[LocalSlowLane] %c✅
|
|
718
|
+
console.log('%c[LocalSlowLane] %c✅ Both models loaded and ready', 'color: #9c27b0; font-weight: bold;', 'color: #4caf50;');
|
|
719
|
+
// One-time identity summary so you can confirm which models are active
|
|
720
|
+
// without digging through the init-progress scroll.
|
|
721
|
+
// eslint-disable-next-line no-console
|
|
722
|
+
console.log('%c[LocalSlowLane] %c🧠 Causal LM →', 'color: #9c27b0; font-weight: bold;', 'color: #2196f3; font-weight: bold;', modelId);
|
|
723
|
+
// eslint-disable-next-line no-console
|
|
724
|
+
console.log('%c[LocalSlowLane] %c🔢 Embedder →', 'color: #9c27b0; font-weight: bold;', 'color: #009688; font-weight: bold;', LOCAL_MLC_EMBEDDING_MODEL_ID);
|
|
190
725
|
}
|
|
191
726
|
onStatus === null || onStatus === void 0 || onStatus('Model loaded and ready.');
|
|
192
|
-
|
|
727
|
+
_context2.next = 6;
|
|
193
728
|
break;
|
|
194
729
|
case 5:
|
|
195
|
-
|
|
196
|
-
_t =
|
|
730
|
+
_context2.prev = 5;
|
|
731
|
+
_t = _context2["catch"](0);
|
|
197
732
|
errorMsg = _t instanceof Error ? _t.message : String(_t);
|
|
198
733
|
ready = false;
|
|
199
734
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
@@ -205,12 +740,12 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
205
740
|
initFailed = true;
|
|
206
741
|
case 6:
|
|
207
742
|
case "end":
|
|
208
|
-
return
|
|
743
|
+
return _context2.stop();
|
|
209
744
|
}
|
|
210
|
-
},
|
|
745
|
+
}, _callee2, null, [[0, 5]]);
|
|
211
746
|
}));
|
|
212
747
|
return function initEngine() {
|
|
213
|
-
return
|
|
748
|
+
return _ref8.apply(this, arguments);
|
|
214
749
|
};
|
|
215
750
|
}();
|
|
216
751
|
var ensureEngineInitialized = function ensureEngineInitialized() {
|
|
@@ -226,110 +761,131 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
226
761
|
// ── Inference ──────────────────────────────────────────────────────────
|
|
227
762
|
|
|
228
763
|
/**
|
|
229
|
-
* Run a single forward pass to
|
|
764
|
+
* Run a single forward pass to produce the BE-parity slow-lane outputs.
|
|
230
765
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
766
|
+
* Two calls run in parallel on the shared engine:
|
|
767
|
+
* - `completions.create({ max_tokens: 1 })` runs the causal LM for exactly
|
|
768
|
+
* one decode step. We ignore the generated text; the LogitProcessor
|
|
769
|
+
* captures the raw next-token logits during that step, which we turn into
|
|
770
|
+
* a whole-word payload via `computeBePayload`.
|
|
771
|
+
* - `embeddings.create(...)` runs the Arctic embedder to produce the real
|
|
772
|
+
* 384-d semantic vector (passage-encoded; see `wrapForArctic`).
|
|
234
773
|
*/
|
|
235
774
|
var runInference = /*#__PURE__*/function () {
|
|
236
|
-
var
|
|
237
|
-
var
|
|
238
|
-
return _regenerator.default.wrap(function (
|
|
239
|
-
while (1) switch (
|
|
775
|
+
var _ref9 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(text, requestId) {
|
|
776
|
+
var lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t2;
|
|
777
|
+
return _regenerator.default.wrap(function (_context3) {
|
|
778
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
240
779
|
case 0:
|
|
241
780
|
if (!(!engine || destroyed)) {
|
|
242
|
-
|
|
781
|
+
_context3.next = 1;
|
|
243
782
|
break;
|
|
244
783
|
}
|
|
245
|
-
return
|
|
784
|
+
return _context3.abrupt("return");
|
|
246
785
|
case 1:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
786
|
+
// Clear the capture buffer so we read only this pass's logits. The engine
|
|
787
|
+
// serialises per-model requests and updateContext is debounced, so the
|
|
788
|
+
// latest request's decode step is the last to populate `captured` before
|
|
789
|
+
// we read it below; stale requests bail on the latestRequestId guard.
|
|
790
|
+
lmLogitsCapture.resetState();
|
|
791
|
+
|
|
792
|
+
// Apply BE-parity rolling-window truncation before both encoders.
|
|
793
|
+
// BE semantic: last max_context_words words (typeahead_context_encoding.py:36)
|
|
794
|
+
// BE causal LM: last max_context_tokens BPE tokens (causal_lm_encoder.py:194–198),
|
|
795
|
+
// approximated here with word count (no tokenizer available on FE).
|
|
796
|
+
lmText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_TOKENS);
|
|
797
|
+
semanticText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_WORDS);
|
|
798
|
+
arcticInput = wrapForArctic(semanticText);
|
|
799
|
+
captureCompletionTime = function captureCompletionTime(promise, onResolved) {
|
|
800
|
+
return promise.then(function (value) {
|
|
801
|
+
onResolved(performance.now());
|
|
802
|
+
return value;
|
|
803
|
+
});
|
|
804
|
+
};
|
|
805
|
+
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
806
|
+
// eslint-disable-next-line no-console
|
|
807
|
+
console.log("%c[LocalSlowLane] %c\uD83D\uDD22 Arctic input (".concat(arcticInput.length, " chars, ").concat(splitOnWhitespace(semanticText).length, " words): \"").concat(arcticInput.length > 100 ? "".concat(arcticInput.slice(0, 100), "\u2026") : arcticInput, "\""), 'color: #9c27b0; font-weight: bold;', 'color: #009688;');
|
|
808
|
+
// eslint-disable-next-line no-console
|
|
809
|
+
console.log("%c[LocalSlowLane] %c\uD83E\uDDE0 LM input (".concat(lmText.length, " chars, ").concat(splitOnWhitespace(lmText).length, " words): \"").concat(lmText.length > 100 ? "".concat(lmText.slice(0, 100), "\u2026") : lmText, "\""), 'color: #9c27b0; font-weight: bold;', 'color: #2196f3;');
|
|
264
810
|
}
|
|
265
|
-
|
|
811
|
+
_context3.prev = 2;
|
|
812
|
+
tStart = performance.now();
|
|
813
|
+
tLmDone = 0;
|
|
814
|
+
tEmbDone = 0;
|
|
815
|
+
_context3.next = 3;
|
|
816
|
+
return Promise.all([captureCompletionTime(engine.completions.create({
|
|
817
|
+
model: modelId,
|
|
818
|
+
prompt: lmText,
|
|
819
|
+
max_tokens: 1,
|
|
820
|
+
temperature: 0,
|
|
821
|
+
logprobs: false
|
|
822
|
+
}), function (resolvedAt) {
|
|
823
|
+
tLmDone = resolvedAt;
|
|
824
|
+
}), captureCompletionTime(engine.embeddings.create({
|
|
825
|
+
model: LOCAL_MLC_EMBEDDING_MODEL_ID,
|
|
826
|
+
input: arcticInput
|
|
827
|
+
}), function (resolvedAt) {
|
|
828
|
+
tEmbDone = resolvedAt;
|
|
829
|
+
})]);
|
|
266
830
|
case 3:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (token.length > 0 && /^[a-z\u017F\u212A]/i.test(token)) {
|
|
275
|
-
lmLogits[token] = Math.exp(tokenLogprobs.logprob);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
831
|
+
_yield$Promise$all5 = _context3.sent;
|
|
832
|
+
_yield$Promise$all6 = (0, _slicedToArray2.default)(_yield$Promise$all5, 2);
|
|
833
|
+
embeddingResponse = _yield$Promise$all6[1];
|
|
834
|
+
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
835
|
+
// eslint-disable-next-line no-console
|
|
836
|
+
console.log("%c[LocalSlowLane] %c\u23F1 LM: ".concat((tLmDone - tStart).toFixed(0), "ms | Embedder: ").concat((tEmbDone - tStart).toFixed(0), "ms | Total: ").concat((Math.max(tLmDone, tEmbDone) - tStart).toFixed(0), "ms"), 'color: #9c27b0; font-weight: bold;', 'color: #ff9800;');
|
|
837
|
+
}
|
|
278
838
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
284
|
-
alt = _step.value;
|
|
285
|
-
_token = alt.token.trim().toLowerCase(); // @ts-ignore TS1501: Unicode regex flag requires a newer TS target than the declaration build uses.
|
|
286
|
-
if (_token.length > 0 && /^[a-z\u017F\u212A]/i.test(_token)) {
|
|
287
|
-
lmLogits[_token] = Math.exp(alt.logprob);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
} catch (err) {
|
|
291
|
-
_iterator.e(err);
|
|
292
|
-
} finally {
|
|
293
|
-
_iterator.f();
|
|
294
|
-
}
|
|
295
|
-
}
|
|
839
|
+
// Discard stale results
|
|
840
|
+
if (!(requestId < latestRequestId || destroyed)) {
|
|
841
|
+
_context3.next = 4;
|
|
842
|
+
break;
|
|
296
843
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// ──
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
//
|
|
305
|
-
// For a production implementation, you would use a dedicated
|
|
306
|
-
// embedding model (e.g. via web-llm's embeddings API with an
|
|
307
|
-
// embedding-specific model).
|
|
308
|
-
if (storedLmLogits) {
|
|
309
|
-
logitValues = Object.values(storedLmLogits);
|
|
310
|
-
storedContextVector = new Float32Array(logitValues);
|
|
844
|
+
return _context3.abrupt("return");
|
|
845
|
+
case 4:
|
|
846
|
+
// ── LM logits: whole-word BE-parity payload ──────────────────
|
|
847
|
+
rawLogits = lmLogitsCapture.captured;
|
|
848
|
+
if (rawLogits) {
|
|
849
|
+
payload = computeBePayload(rawLogits, firstTokenToWords, l2Words);
|
|
850
|
+
storedLmLogits = Object.keys(payload).length > 0 ? payload : null;
|
|
311
851
|
} else {
|
|
312
|
-
|
|
852
|
+
storedLmLogits = null;
|
|
313
853
|
}
|
|
854
|
+
|
|
855
|
+
// ── Semantic vector: real 384-d Arctic embedding ─────────────
|
|
856
|
+
// Guard against base64-encoded responses (encoding_format: 'base64' would
|
|
857
|
+
// yield a string, and new Float32Array(string) silently produces an empty
|
|
858
|
+
// array, corrupting downstream cosine-similarity scoring).
|
|
859
|
+
embedding = (_data = embeddingResponse.data) === null || _data === void 0 || (_data = _data[0]) === null || _data === void 0 ? void 0 : _data.embedding;
|
|
860
|
+
storedContextVector = Array.isArray(embedding) && embedding.length > 0 ? new Float32Array(embedding) : null;
|
|
314
861
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
315
862
|
// eslint-disable-next-line no-console
|
|
316
863
|
console.groupCollapsed("%c[LocalSlowLane] %c\uD83D\uDCE5 Inference result (request #".concat(requestId, ")"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
|
|
864
|
+
if (storedContextVector) {
|
|
865
|
+
sumSq = 0;
|
|
866
|
+
for (i = 0; i < storedContextVector.length; i++) {
|
|
867
|
+
sumSq += storedContextVector[i] * storedContextVector[i];
|
|
868
|
+
}
|
|
869
|
+
// eslint-disable-next-line no-console
|
|
870
|
+
console.log("\u2705 semantic vector: ".concat(storedContextVector.length, " dims (L2 norm ").concat(Math.sqrt(sumSq).toFixed(3), ")"));
|
|
871
|
+
} else {
|
|
872
|
+
// eslint-disable-next-line no-console
|
|
873
|
+
console.log('❌ No vector');
|
|
874
|
+
}
|
|
317
875
|
// eslint-disable-next-line no-console
|
|
318
|
-
console.log(
|
|
319
|
-
// eslint-disable-next-line no-console
|
|
320
|
-
console.log(storedLmLogits ? "\u2705 lm_logits: ".concat(Object.keys(storedLmLogits).length, " tokens") : '❌ No lm_logits');
|
|
876
|
+
console.log(storedLmLogits ? "\u2705 lm_logits: ".concat(Object.keys(storedLmLogits).length, " words") : '❌ No lm_logits');
|
|
321
877
|
if (storedLmLogits) {
|
|
322
|
-
topTokens = Object.entries(storedLmLogits).sort(function (
|
|
323
|
-
var
|
|
324
|
-
a =
|
|
325
|
-
var
|
|
326
|
-
b =
|
|
878
|
+
topTokens = Object.entries(storedLmLogits).sort(function (_ref0, _ref1) {
|
|
879
|
+
var _ref10 = (0, _slicedToArray2.default)(_ref0, 2),
|
|
880
|
+
a = _ref10[1];
|
|
881
|
+
var _ref11 = (0, _slicedToArray2.default)(_ref1, 2),
|
|
882
|
+
b = _ref11[1];
|
|
327
883
|
return b - a;
|
|
328
884
|
}).slice(0, 10); // eslint-disable-next-line no-console
|
|
329
|
-
console.log('Top 10 predictions:', topTokens.map(function (
|
|
330
|
-
var
|
|
331
|
-
t =
|
|
332
|
-
p =
|
|
885
|
+
console.log('Top 10 predictions:', topTokens.map(function (_ref12) {
|
|
886
|
+
var _ref13 = (0, _slicedToArray2.default)(_ref12, 2),
|
|
887
|
+
t = _ref13[0],
|
|
888
|
+
p = _ref13[1];
|
|
333
889
|
return "".concat(t, ": ").concat((p * 100).toFixed(1), "%");
|
|
334
890
|
}).join(', '));
|
|
335
891
|
}
|
|
@@ -341,17 +897,17 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
341
897
|
hasVector: storedContextVector !== null,
|
|
342
898
|
hasLmLogits: storedLmLogits !== null
|
|
343
899
|
});
|
|
344
|
-
|
|
900
|
+
_context3.next = 7;
|
|
345
901
|
break;
|
|
346
|
-
case
|
|
347
|
-
|
|
348
|
-
_t2 =
|
|
902
|
+
case 5:
|
|
903
|
+
_context3.prev = 5;
|
|
904
|
+
_t2 = _context3["catch"](2);
|
|
349
905
|
if (!(requestId < latestRequestId)) {
|
|
350
|
-
|
|
906
|
+
_context3.next = 6;
|
|
351
907
|
break;
|
|
352
908
|
}
|
|
353
|
-
return
|
|
354
|
-
case
|
|
909
|
+
return _context3.abrupt("return");
|
|
910
|
+
case 6:
|
|
355
911
|
storedContextVector = null;
|
|
356
912
|
storedLmLogits = null;
|
|
357
913
|
onUpdate === null || onUpdate === void 0 || onUpdate({
|
|
@@ -364,14 +920,14 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
364
920
|
// eslint-disable-next-line no-console
|
|
365
921
|
console.log("%c[LocalSlowLane] %c\u274C Inference error (request #".concat(requestId, "): ").concat(errorMsg), 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
|
|
366
922
|
}
|
|
367
|
-
case
|
|
923
|
+
case 7:
|
|
368
924
|
case "end":
|
|
369
|
-
return
|
|
925
|
+
return _context3.stop();
|
|
370
926
|
}
|
|
371
|
-
},
|
|
927
|
+
}, _callee3, null, [[2, 5]]);
|
|
372
928
|
}));
|
|
373
929
|
return function runInference(_x, _x2) {
|
|
374
|
-
return
|
|
930
|
+
return _ref9.apply(this, arguments);
|
|
375
931
|
};
|
|
376
932
|
}();
|
|
377
933
|
|