@atlaskit/editor-plugin-autocomplete 3.2.0 → 3.4.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 +14 -0
- package/dist/cjs/pm-plugins/autocomplete-plugin.js +162 -22
- package/dist/cjs/pm-plugins/local-slow-lane-client.js +108 -17
- package/dist/es2019/pm-plugins/autocomplete-plugin.js +148 -22
- package/dist/es2019/pm-plugins/local-slow-lane-client.js +105 -16
- package/dist/esm/pm-plugins/autocomplete-plugin.js +162 -22
- package/dist/esm/pm-plugins/local-slow-lane-client.js +108 -17
- package/package.json +2 -2
- package/src/pm-plugins/autocomplete-plugin.ts +170 -25
- package/src/pm-plugins/local-slow-lane-client.ts +129 -21
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
|
+
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; } } }; }
|
|
4
|
+
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; } }
|
|
5
|
+
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; }
|
|
2
6
|
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; }
|
|
3
7
|
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) { _defineProperty(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; }
|
|
4
8
|
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
@@ -7,12 +11,26 @@ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
|
7
11
|
import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
|
|
8
12
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
9
13
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
14
|
+
import { isAutocompleteDebugEnabled } from './debug-mode';
|
|
10
15
|
import { createGhostTextDecorationSet } from './ghost-text-decoration';
|
|
11
16
|
import { createLocalSlowLaneClient } from './local-slow-lane-client';
|
|
12
17
|
import { createSlowLaneClient, setDefaultSlowLaneClient, isWordBoundary } from './slow-lane-client';
|
|
13
18
|
import { predict, loadDefaultVocabulary, loadVectorsAsync, incrementSessionFreq, ingestDocumentPage } from './text-predictor';
|
|
14
19
|
export var autocompletePluginKey = new PluginKey('autocomplete');
|
|
15
20
|
var DEBOUNCE_MS = 150;
|
|
21
|
+
var NETWORK_SLOW_LANE_DEBOUNCE_MS = 300;
|
|
22
|
+
var LOCAL_SLOW_LANE_DEBOUNCE_MS = 100;
|
|
23
|
+
var CONTEXT_REFRESH_THROTTLE_MS = 1000;
|
|
24
|
+
// Caps the dedup Set so long editing sessions don't retain every distinct
|
|
25
|
+
// version of the (potentially hundreds-of-KB) page content for the plugin's
|
|
26
|
+
// lifetime. Eviction is FIFO; a re-ingest of an evicted text is harmless.
|
|
27
|
+
var MAX_INGESTED_CONTEXT_TEXTS = 50;
|
|
28
|
+
// Caps how many times the word-boundary path will retry getContext() while the
|
|
29
|
+
// parent comment is still missing. Combined with the 1s throttle this gives a
|
|
30
|
+
// ~5s window to cover a still-loading comment thread, then stops permanently so
|
|
31
|
+
// non-comment editors (where parentCommentContent never arrives) don't refetch
|
|
32
|
+
// on every word boundary for the plugin's lifetime.
|
|
33
|
+
var MAX_CONTEXT_REFRESH_ATTEMPTS = 5;
|
|
16
34
|
var hasDestroy = function hasDestroy(client) {
|
|
17
35
|
return 'destroy' in client && typeof client.destroy === 'function';
|
|
18
36
|
};
|
|
@@ -177,6 +195,12 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
177
195
|
var debounceTimer = null;
|
|
178
196
|
var hasIngestedPage = false;
|
|
179
197
|
var resolvedContext;
|
|
198
|
+
/**
|
|
199
|
+
* Kept in sync with the live EditorView so the async getContext() promise
|
|
200
|
+
* can re-trigger a slow-lane update the moment context arrives, even if the
|
|
201
|
+
* user has already typed several words before the promise resolved.
|
|
202
|
+
*/
|
|
203
|
+
var currentView = null;
|
|
180
204
|
/**
|
|
181
205
|
* Set after accepting a suggestion so the next doc-change update
|
|
182
206
|
* skips scheduling a new prediction for the just-inserted text.
|
|
@@ -220,12 +244,125 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
220
244
|
});
|
|
221
245
|
};
|
|
222
246
|
var slowLaneClient = options !== null && options !== void 0 && options.useLocalModel ? createLocalSlowLaneClient({
|
|
223
|
-
debounceMs:
|
|
247
|
+
debounceMs: LOCAL_SLOW_LANE_DEBOUNCE_MS
|
|
224
248
|
}) : createSlowLaneClient({
|
|
225
249
|
baseUrl: '',
|
|
226
|
-
debounceMs:
|
|
250
|
+
debounceMs: NETWORK_SLOW_LANE_DEBOUNCE_MS
|
|
227
251
|
});
|
|
228
252
|
setDefaultSlowLaneClient(slowLaneClient);
|
|
253
|
+
var contextRequestInFlight = false;
|
|
254
|
+
var lastContextRefreshAt = 0;
|
|
255
|
+
// Bounds the word-boundary retry loop so it terminates even when the editor is
|
|
256
|
+
// not in a comment thread (parentCommentContent never resolves).
|
|
257
|
+
var wordBoundaryRefreshAttempts = 0;
|
|
258
|
+
// Set when the plugin is torn down so in-flight getContext() resolutions don't
|
|
259
|
+
// mutate the global text-predictor state after destruction.
|
|
260
|
+
var destroyed = false;
|
|
261
|
+
var ingestedContextTexts = new Set();
|
|
262
|
+
var logContextResolved = function logContextResolved(source, context) {
|
|
263
|
+
var _context$parentCommen, _context$siblingComme4, _context$siblingComme5;
|
|
264
|
+
if (!isAutocompleteDebugEnabled()) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// eslint-disable-next-line no-console
|
|
269
|
+
console.log('%c[Autocomplete] %cgetContext resolved', 'color: #00b8d9; font-weight: bold;', 'color: inherit;', {
|
|
270
|
+
source: source,
|
|
271
|
+
hasParentComment: !!(context !== null && context !== void 0 && context.parentCommentContent),
|
|
272
|
+
parentCommentPreview: context === null || context === void 0 || (_context$parentCommen = context.parentCommentContent) === null || _context$parentCommen === void 0 ? void 0 : _context$parentCommen.slice(0, 80),
|
|
273
|
+
siblingCount: (_context$siblingComme4 = context === null || context === void 0 || (_context$siblingComme5 = context.siblingCommentsContents) === null || _context$siblingComme5 === void 0 ? void 0 : _context$siblingComme5.length) !== null && _context$siblingComme4 !== void 0 ? _context$siblingComme4 : 0,
|
|
274
|
+
hasFullPage: !!(context !== null && context !== void 0 && context.fullPageContent)
|
|
275
|
+
});
|
|
276
|
+
};
|
|
277
|
+
var applyContext = function applyContext(context) {
|
|
278
|
+
var _context$siblingComme6;
|
|
279
|
+
// Merge rather than replace: the word-boundary retry may resolve only a
|
|
280
|
+
// late-arriving field (e.g. parentCommentContent) without re-sending
|
|
281
|
+
// fullPageContent, so replacing would drop previously resolved context.
|
|
282
|
+
// Strip undefined values first so a field explicitly set to undefined by
|
|
283
|
+
// getContext doesn't overwrite a previously resolved value.
|
|
284
|
+
var definedContext = Object.fromEntries(Object.entries(context).filter(function (_ref) {
|
|
285
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
286
|
+
value = _ref2[1];
|
|
287
|
+
return value !== undefined;
|
|
288
|
+
}));
|
|
289
|
+
resolvedContext = _objectSpread(_objectSpread({}, resolvedContext), definedContext);
|
|
290
|
+
var ingestContextText = function ingestContextText(text) {
|
|
291
|
+
if (!text || ingestedContextTexts.has(text)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
ingestedContextTexts.add(text);
|
|
295
|
+
// Evict oldest entries (Set preserves insertion order) to bound memory.
|
|
296
|
+
while (ingestedContextTexts.size > MAX_INGESTED_CONTEXT_TEXTS) {
|
|
297
|
+
var oldest = ingestedContextTexts.values().next().value;
|
|
298
|
+
if (oldest === undefined) {
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
ingestedContextTexts.delete(oldest);
|
|
302
|
+
}
|
|
303
|
+
ingestDocumentPage(text);
|
|
304
|
+
};
|
|
305
|
+
ingestContextText(context.fullPageContent);
|
|
306
|
+
ingestContextText(context.parentCommentContent);
|
|
307
|
+
var _iterator = _createForOfIteratorHelper((_context$siblingComme6 = context.siblingCommentsContents) !== null && _context$siblingComme6 !== void 0 ? _context$siblingComme6 : []),
|
|
308
|
+
_step;
|
|
309
|
+
try {
|
|
310
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
311
|
+
var siblingCommentContent = _step.value;
|
|
312
|
+
ingestContextText(siblingCommentContent);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Context arrived after word boundaries may already have fired. Re-send
|
|
316
|
+
// slow-lane context immediately so the next inference includes the thread.
|
|
317
|
+
} catch (err) {
|
|
318
|
+
_iterator.e(err);
|
|
319
|
+
} finally {
|
|
320
|
+
_iterator.f();
|
|
321
|
+
}
|
|
322
|
+
if (currentView) {
|
|
323
|
+
slowLaneClient.updateContext(buildSlowLaneText(currentView.state.doc.textContent, resolvedContext));
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Returns true when a fetch was actually started, false when it was skipped
|
|
329
|
+
* (no getContext, a request already in flight, or throttled). Callers that
|
|
330
|
+
* track a retry budget should only count attempts where this returns true.
|
|
331
|
+
*/
|
|
332
|
+
var refreshContext = function refreshContext(_ref3) {
|
|
333
|
+
var source = _ref3.source,
|
|
334
|
+
_ref3$allowThrottle = _ref3.allowThrottle,
|
|
335
|
+
allowThrottle = _ref3$allowThrottle === void 0 ? true : _ref3$allowThrottle;
|
|
336
|
+
if (!(options !== null && options !== void 0 && options.getContext) || contextRequestInFlight) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
var now = Date.now();
|
|
340
|
+
if (allowThrottle && now - lastContextRefreshAt < CONTEXT_REFRESH_THROTTLE_MS) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
contextRequestInFlight = true;
|
|
344
|
+
lastContextRefreshAt = now;
|
|
345
|
+
options.getContext().then(function (context) {
|
|
346
|
+
// Bail if the plugin was destroyed while the fetch was in flight —
|
|
347
|
+
// applyContext mutates global text-predictor state we must not touch
|
|
348
|
+
// after teardown.
|
|
349
|
+
if (destroyed) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
logContextResolved(source, context);
|
|
353
|
+
if (!context) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
applyContext(context);
|
|
357
|
+
}).catch(function (error) {
|
|
358
|
+
logException(error, {
|
|
359
|
+
location: 'editor-plugin-autocomplete/getContext'
|
|
360
|
+
});
|
|
361
|
+
}).finally(function () {
|
|
362
|
+
contextRequestInFlight = false;
|
|
363
|
+
});
|
|
364
|
+
return true;
|
|
365
|
+
};
|
|
229
366
|
|
|
230
367
|
/**
|
|
231
368
|
* Schedule a prediction after a short debounce.
|
|
@@ -397,26 +534,10 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
397
534
|
});
|
|
398
535
|
if (!hasIngestedPage) {
|
|
399
536
|
hasIngestedPage = true;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
resolvedContext = context;
|
|
407
|
-
if (context.fullPageContent) {
|
|
408
|
-
ingestDocumentPage(context.fullPageContent);
|
|
409
|
-
}
|
|
410
|
-
if (context.parentCommentContent) {
|
|
411
|
-
ingestDocumentPage(context.parentCommentContent);
|
|
412
|
-
}
|
|
413
|
-
(_context$siblingComme4 = context.siblingCommentsContents) === null || _context$siblingComme4 === void 0 || _context$siblingComme4.forEach(ingestDocumentPage);
|
|
414
|
-
}).catch(function (error) {
|
|
415
|
-
logException(error, {
|
|
416
|
-
location: 'editor-plugin-autocomplete/getContext'
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
}
|
|
537
|
+
refreshContext({
|
|
538
|
+
source: 'focus',
|
|
539
|
+
allowThrottle: false
|
|
540
|
+
});
|
|
420
541
|
}
|
|
421
542
|
return false;
|
|
422
543
|
}
|
|
@@ -425,6 +546,7 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
425
546
|
view: function view() {
|
|
426
547
|
return {
|
|
427
548
|
update: function update(view, prevState) {
|
|
549
|
+
currentView = view;
|
|
428
550
|
if (!prevState.doc.eq(view.state.doc)) {
|
|
429
551
|
if (justAccepted) {
|
|
430
552
|
justAccepted = false;
|
|
@@ -440,18 +562,36 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
440
562
|
maybeUpdateSessionFrequency(view, prevState);
|
|
441
563
|
var textBefore = getTextBeforeCursor(view.state);
|
|
442
564
|
if (isWordBoundary(textBefore)) {
|
|
565
|
+
var _resolvedContext;
|
|
443
566
|
slowLaneClient.updateContext(buildSlowLaneText(view.state.doc.textContent, resolvedContext));
|
|
567
|
+
|
|
568
|
+
// Context may not have resolved on first focus (e.g. comment
|
|
569
|
+
// thread still loading). Retry on word boundaries until we have
|
|
570
|
+
// the parent comment, throttled so we don't refetch constantly
|
|
571
|
+
// and capped so non-comment editors stop retrying entirely.
|
|
572
|
+
if (!((_resolvedContext = resolvedContext) !== null && _resolvedContext !== void 0 && _resolvedContext.parentCommentContent) && wordBoundaryRefreshAttempts < MAX_CONTEXT_REFRESH_ATTEMPTS) {
|
|
573
|
+
// Only count the attempt when a fetch actually started, so an
|
|
574
|
+
// in-flight or throttled no-op doesn't burn the retry budget.
|
|
575
|
+
if (refreshContext({
|
|
576
|
+
source: 'word-boundary'
|
|
577
|
+
})) {
|
|
578
|
+
wordBoundaryRefreshAttempts++;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
444
581
|
}
|
|
445
582
|
schedulePrediction(view);
|
|
446
583
|
}
|
|
447
584
|
},
|
|
448
585
|
destroy: function destroy() {
|
|
586
|
+
destroyed = true;
|
|
587
|
+
currentView = null;
|
|
449
588
|
if (debounceTimer) {
|
|
450
589
|
clearTimeout(debounceTimer);
|
|
451
590
|
}
|
|
452
591
|
if (hasDestroy(slowLaneClient)) {
|
|
453
592
|
slowLaneClient.destroy();
|
|
454
593
|
}
|
|
594
|
+
ingestedContextTexts.clear();
|
|
455
595
|
setDefaultSlowLaneClient(null);
|
|
456
596
|
}
|
|
457
597
|
};
|
|
@@ -123,13 +123,37 @@ export var BE_PARITY = {
|
|
|
123
123
|
*/
|
|
124
124
|
MAX_CONTEXT_WORDS: 100
|
|
125
125
|
};
|
|
126
|
+
var splitOnWhitespace = function splitOnWhitespace(text) {
|
|
127
|
+
var trimmed = text.trim();
|
|
128
|
+
if (trimmed === '') {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
var words = [];
|
|
132
|
+
var wordStart = -1;
|
|
133
|
+
for (var i = 0; i < trimmed.length; i++) {
|
|
134
|
+
if (trimmed[i].trim() === '') {
|
|
135
|
+
if (wordStart !== -1) {
|
|
136
|
+
words.push(trimmed.slice(wordStart, i));
|
|
137
|
+
wordStart = -1;
|
|
138
|
+
}
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (wordStart === -1) {
|
|
142
|
+
wordStart = i;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (wordStart !== -1) {
|
|
146
|
+
words.push(trimmed.slice(wordStart));
|
|
147
|
+
}
|
|
148
|
+
return words;
|
|
149
|
+
};
|
|
126
150
|
|
|
127
151
|
/**
|
|
128
152
|
* Return the last `n` whitespace-separated words of `text`, joined by spaces.
|
|
129
153
|
* Mirrors the BE rolling-window truncation applied before both encoders.
|
|
130
154
|
*/
|
|
131
155
|
var truncateToLastNWords = function truncateToLastNWords(text, n) {
|
|
132
|
-
var words = text
|
|
156
|
+
var words = splitOnWhitespace(text);
|
|
133
157
|
return words.length <= n ? text : words.slice(-n).join(' ');
|
|
134
158
|
};
|
|
135
159
|
|
|
@@ -588,6 +612,14 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
588
612
|
var lastRequestedText = '';
|
|
589
613
|
var requestCounter = 0;
|
|
590
614
|
var latestRequestId = -1;
|
|
615
|
+
var inferenceInFlight = false;
|
|
616
|
+
var activeInferenceText = null;
|
|
617
|
+
// The requestId of the inference currently in flight. Tracked so the
|
|
618
|
+
// in-flight dedup path can restore `latestRequestId` to it — otherwise an
|
|
619
|
+
// intermediate keystroke that bumped `latestRequestId` would cause the
|
|
620
|
+
// in-flight (still-current) result to be discarded as stale.
|
|
621
|
+
var activeInferenceRequestId = -1;
|
|
622
|
+
var pendingInference = null;
|
|
591
623
|
var ready = false;
|
|
592
624
|
var destroyed = false;
|
|
593
625
|
var initFailed = false;
|
|
@@ -737,7 +769,7 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
737
769
|
*/
|
|
738
770
|
var runInference = /*#__PURE__*/function () {
|
|
739
771
|
var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(text, requestId) {
|
|
740
|
-
var lmText, semanticText, arcticInput,
|
|
772
|
+
var lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t2;
|
|
741
773
|
return _regeneratorRuntime.wrap(function (_context3) {
|
|
742
774
|
while (1) switch (_context3.prev = _context3.next) {
|
|
743
775
|
case 0:
|
|
@@ -760,32 +792,36 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
760
792
|
lmText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_TOKENS);
|
|
761
793
|
semanticText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_WORDS);
|
|
762
794
|
arcticInput = wrapForArctic(semanticText);
|
|
795
|
+
captureCompletionTime = function captureCompletionTime(promise, onResolved) {
|
|
796
|
+
return promise.then(function (value) {
|
|
797
|
+
onResolved(performance.now());
|
|
798
|
+
return value;
|
|
799
|
+
});
|
|
800
|
+
};
|
|
763
801
|
if (isAutocompleteDebugEnabled()) {
|
|
764
802
|
// eslint-disable-next-line no-console
|
|
765
|
-
console.log("%c[LocalSlowLane] %c\uD83D\uDD22 Arctic input (".concat(arcticInput.length, " chars, ").concat(semanticText
|
|
803
|
+
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;');
|
|
766
804
|
// eslint-disable-next-line no-console
|
|
767
|
-
console.log("%c[LocalSlowLane] %c\uD83E\uDDE0 LM input (".concat(lmText.length, " chars, ").concat(lmText
|
|
805
|
+
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;');
|
|
768
806
|
}
|
|
769
807
|
_context3.prev = 2;
|
|
770
808
|
tStart = performance.now();
|
|
771
809
|
tLmDone = 0;
|
|
772
810
|
tEmbDone = 0;
|
|
773
811
|
_context3.next = 3;
|
|
774
|
-
return Promise.all([engine.completions.create({
|
|
812
|
+
return Promise.all([captureCompletionTime(engine.completions.create({
|
|
775
813
|
model: modelId,
|
|
776
814
|
prompt: lmText,
|
|
777
815
|
max_tokens: 1,
|
|
778
816
|
temperature: 0,
|
|
779
817
|
logprobs: false
|
|
780
|
-
})
|
|
781
|
-
tLmDone =
|
|
782
|
-
|
|
783
|
-
}), engine.embeddings.create({
|
|
818
|
+
}), function (resolvedAt) {
|
|
819
|
+
tLmDone = resolvedAt;
|
|
820
|
+
}), captureCompletionTime(engine.embeddings.create({
|
|
784
821
|
model: LOCAL_MLC_EMBEDDING_MODEL_ID,
|
|
785
822
|
input: arcticInput
|
|
786
|
-
})
|
|
787
|
-
tEmbDone =
|
|
788
|
-
return r;
|
|
823
|
+
}), function (resolvedAt) {
|
|
824
|
+
tEmbDone = resolvedAt;
|
|
789
825
|
})]);
|
|
790
826
|
case 3:
|
|
791
827
|
_yield$Promise$all5 = _context3.sent;
|
|
@@ -816,7 +852,7 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
816
852
|
// Guard against base64-encoded responses (encoding_format: 'base64' would
|
|
817
853
|
// yield a string, and new Float32Array(string) silently produces an empty
|
|
818
854
|
// array, corrupting downstream cosine-similarity scoring).
|
|
819
|
-
embedding = (
|
|
855
|
+
embedding = (_data = embeddingResponse.data) === null || _data === void 0 || (_data = _data[0]) === null || _data === void 0 ? void 0 : _data.embedding;
|
|
820
856
|
storedContextVector = Array.isArray(embedding) && embedding.length > 0 ? new Float32Array(embedding) : null;
|
|
821
857
|
if (isAutocompleteDebugEnabled()) {
|
|
822
858
|
// eslint-disable-next-line no-console
|
|
@@ -862,7 +898,7 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
862
898
|
case 5:
|
|
863
899
|
_context3.prev = 5;
|
|
864
900
|
_t2 = _context3["catch"](2);
|
|
865
|
-
if (!(requestId < latestRequestId)) {
|
|
901
|
+
if (!(requestId < latestRequestId || destroyed)) {
|
|
866
902
|
_context3.next = 6;
|
|
867
903
|
break;
|
|
868
904
|
}
|
|
@@ -893,10 +929,48 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
893
929
|
|
|
894
930
|
// ── Context update (debounced) ─────────────────────────────────────────
|
|
895
931
|
|
|
932
|
+
var _startInference = function startInference(text, requestId) {
|
|
933
|
+
// Self-contained guard: never start a new inference cycle after teardown,
|
|
934
|
+
// regardless of caller discipline.
|
|
935
|
+
if (destroyed) {
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
inferenceInFlight = true;
|
|
939
|
+
activeInferenceText = text;
|
|
940
|
+
activeInferenceRequestId = requestId;
|
|
941
|
+
void ensureEngineInitialized().then(function () {
|
|
942
|
+
return runInference(text, requestId);
|
|
943
|
+
}).catch(function () {}).finally(function () {
|
|
944
|
+
inferenceInFlight = false;
|
|
945
|
+
activeInferenceText = null;
|
|
946
|
+
activeInferenceRequestId = -1;
|
|
947
|
+
var next = pendingInference;
|
|
948
|
+
pendingInference = null;
|
|
949
|
+
if (next && !destroyed) {
|
|
950
|
+
_startInference(next.text, next.requestId);
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
};
|
|
896
954
|
var doUpdateContext = function doUpdateContext(text) {
|
|
955
|
+
var _pendingInference;
|
|
897
956
|
if (destroyed || !text || text.trim().length === 0) {
|
|
898
957
|
return;
|
|
899
958
|
}
|
|
959
|
+
if (inferenceInFlight && text === activeInferenceText) {
|
|
960
|
+
// The latest desired text already matches the in-flight inference, so
|
|
961
|
+
// re-running it would be wasted work. But an intermediate keystroke may
|
|
962
|
+
// have bumped `latestRequestId` past the in-flight request (and then been
|
|
963
|
+
// coalesced away), which would cause runInference to discard the
|
|
964
|
+
// still-current result as stale. Pin `latestRequestId` back to the active
|
|
965
|
+
// request so its result is accepted, and drop any now-superseded pending
|
|
966
|
+
// request.
|
|
967
|
+
latestRequestId = activeInferenceRequestId;
|
|
968
|
+
pendingInference = null;
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
if (inferenceInFlight && ((_pendingInference = pendingInference) === null || _pendingInference === void 0 ? void 0 : _pendingInference.text) === text) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
900
974
|
var requestId = ++requestCounter;
|
|
901
975
|
latestRequestId = requestId;
|
|
902
976
|
if (isAutocompleteDebugEnabled()) {
|
|
@@ -910,14 +984,27 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
910
984
|
// eslint-disable-next-line no-console
|
|
911
985
|
console.groupEnd();
|
|
912
986
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
987
|
+
if (inferenceInFlight) {
|
|
988
|
+
pendingInference = {
|
|
989
|
+
text: text,
|
|
990
|
+
requestId: requestId
|
|
991
|
+
};
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
_startInference(text, requestId);
|
|
916
995
|
};
|
|
917
996
|
var updateContextDebounced = function updateContextDebounced(text) {
|
|
918
997
|
if (debounceTimer) {
|
|
919
998
|
clearTimeout(debounceTimer);
|
|
920
999
|
}
|
|
1000
|
+
if (inferenceInFlight) {
|
|
1001
|
+
pendingInference = null;
|
|
1002
|
+
if (text === activeInferenceText) {
|
|
1003
|
+
latestRequestId = activeInferenceRequestId;
|
|
1004
|
+
lastRequestedText = text;
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
921
1008
|
lastRequestedText = text;
|
|
922
1009
|
debounceTimer = setTimeout(function () {
|
|
923
1010
|
debounceTimer = null;
|
|
@@ -956,6 +1043,10 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
956
1043
|
unloadEngine(engineToUnload);
|
|
957
1044
|
}
|
|
958
1045
|
engineInitPromise = null;
|
|
1046
|
+
inferenceInFlight = false;
|
|
1047
|
+
activeInferenceText = null;
|
|
1048
|
+
activeInferenceRequestId = -1;
|
|
1049
|
+
pendingInference = null;
|
|
959
1050
|
storedContextVector = null;
|
|
960
1051
|
storedLmLogits = null;
|
|
961
1052
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-autocomplete",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"wink-nlp": "^2.4.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@atlaskit/editor-common": "^115.
|
|
38
|
+
"@atlaskit/editor-common": "^115.7.0",
|
|
39
39
|
"@atlaskit/editor-plugin-analytics": "^11.0.0",
|
|
40
40
|
"react": "^18.2.0"
|
|
41
41
|
},
|