@atlaskit/editor-plugin-autocomplete 4.0.2 → 4.0.4
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 +26 -0
- package/dist/cjs/analytics/ufo.js +13 -7
- package/dist/cjs/pm-plugins/autocomplete-plugin.js +22 -2
- package/dist/cjs/pm-plugins/local-slow-lane-client.js +37 -10
- package/dist/cjs/pm-plugins/slow-lane-client.js +11 -5
- package/dist/cjs/pm-plugins/text-predictor.js +2 -2
- package/dist/es2019/analytics/ufo.js +13 -4
- package/dist/es2019/pm-plugins/autocomplete-plugin.js +68 -46
- package/dist/es2019/pm-plugins/local-slow-lane-client.js +36 -6
- package/dist/es2019/pm-plugins/slow-lane-client.js +11 -5
- package/dist/es2019/pm-plugins/text-predictor.js +2 -2
- package/dist/esm/analytics/ufo.js +13 -5
- package/dist/esm/pm-plugins/autocomplete-plugin.js +22 -2
- package/dist/esm/pm-plugins/local-slow-lane-client.js +37 -10
- package/dist/esm/pm-plugins/slow-lane-client.js +11 -5
- package/dist/esm/pm-plugins/text-predictor.js +2 -2
- package/dist/types/analytics/ufo.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/pm-plugins/autocomplete-plugin.d.ts +6 -0
- package/package.json +2 -2
- package/src/analytics/ufo.ts +13 -2
- package/src/index.ts +4 -0
- package/src/pm-plugins/autocomplete-plugin.ts +82 -54
- package/src/pm-plugins/local-slow-lane-client.ts +44 -5
- package/src/pm-plugins/slow-lane-client.ts +13 -3
- package/src/pm-plugins/text-predictor.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-autocomplete
|
|
2
2
|
|
|
3
|
+
## 4.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`ac12ed0b41ef4`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ac12ed0b41ef4) -
|
|
8
|
+
Refactor contextual autocomplete wiring so the conversation-store-aware ChatInput owns the
|
|
9
|
+
autocomplete context bridge (getContext + subscribeToContextUpdates) via a new
|
|
10
|
+
useAutocompleteEditorContext hook, and the shared RovoChatPromptInput simply forwards a single
|
|
11
|
+
`autocomplete` prop to the editor preset. Removes the duplicated keying / listener-set plumbing
|
|
12
|
+
from the reusable input.
|
|
13
|
+
|
|
14
|
+
Adds an optional `subscribeToContextUpdates` option to the autocomplete plugin so host surfaces
|
|
15
|
+
that stream context after mount (e.g. Rovo chat messages) can push refreshes, complementing the
|
|
16
|
+
existing word-boundary retry that only covers a one-time, still-loading comment thread.
|
|
17
|
+
|
|
18
|
+
## 4.0.3
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- [`9869d944172b6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9869d944172b6) -
|
|
23
|
+
Instrument the local (LocalLLM) slow-lane client with the existing `slow-lane-fetch` UFO
|
|
24
|
+
experience, tagged with an `isLocalLLM` flag (matching `load-vectors`/`load-vocabulary`) so
|
|
25
|
+
on-device inference latency and success-rate feed the same FE Reliability SLO as the network
|
|
26
|
+
slow-lane fetch. The `isLocalLLM` flag is now also emitted on the abort path of both the local and
|
|
27
|
+
network clients.
|
|
28
|
+
|
|
3
29
|
## 4.0.2
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
|
@@ -7,7 +7,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.succeedExp = exports.startExp = exports.failExp = exports.abortExp = exports.EXPERIENCE_NAME = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _ufo = require("@atlaskit/ufo");
|
|
10
|
-
|
|
10
|
+
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; }
|
|
11
|
+
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; } /**
|
|
11
12
|
* UFO experience tracking helpers for `@atlaskit/editor-plugin-autocomplete`.
|
|
12
13
|
*
|
|
13
14
|
* Pattern follows `packages/linking-platform/smart-card/src/state/analytics/ufoExperiences.ts`
|
|
@@ -19,7 +20,6 @@ var _ufo = require("@atlaskit/ufo");
|
|
|
19
20
|
* they can power FE Reliability SLOs:
|
|
20
21
|
* https://hello.atlassian.net/wiki/spaces/AA6/pages/3961393753
|
|
21
22
|
*/
|
|
22
|
-
|
|
23
23
|
/**
|
|
24
24
|
* Experience name strings are surfaced downstream by the UFO pipeline as
|
|
25
25
|
* `platform.fe.<type>.<platform.component>.<name>` (e.g.
|
|
@@ -95,15 +95,21 @@ var failExp = exports.failExp = function failExp(name, id, metadata) {
|
|
|
95
95
|
// UFO errors must never break the plugin
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
|
-
var abortExp = exports.abortExp = function abortExp(name, id, reason) {
|
|
98
|
+
var abortExp = exports.abortExp = function abortExp(name, id, reason, metadata) {
|
|
99
99
|
if (!isUfoEnabled()) {
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
102
|
try {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
// `reason` is the dedicated, canonical channel for the abort reason and
|
|
104
|
+
// intentionally takes precedence over a `reason` key in `metadata`. Callers
|
|
105
|
+
// must pass the reason via the param, not inside `metadata` (the `reason`
|
|
106
|
+
// key there is reserved and would be overwritten).
|
|
107
|
+
var merged = _objectSpread(_objectSpread({}, metadata), reason ? {
|
|
108
|
+
reason: reason
|
|
109
|
+
} : {});
|
|
110
|
+
var hasMetadata = Object.keys(merged).length > 0;
|
|
111
|
+
experiences[name].getInstance(id).abort(hasMetadata ? {
|
|
112
|
+
metadata: merged
|
|
107
113
|
} : undefined);
|
|
108
114
|
} catch (_unused4) {
|
|
109
115
|
// UFO errors must never break the plugin
|
|
@@ -249,6 +249,7 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
|
|
|
249
249
|
* user has already typed several words before the promise resolved.
|
|
250
250
|
*/
|
|
251
251
|
var currentView = null;
|
|
252
|
+
var unsubscribeFromContextUpdates;
|
|
252
253
|
/**
|
|
253
254
|
* Set after accepting a suggestion so the next doc-change update
|
|
254
255
|
* skips scheduling a new prediction for the just-inserted text.
|
|
@@ -670,7 +671,21 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
|
|
|
670
671
|
}
|
|
671
672
|
}
|
|
672
673
|
},
|
|
673
|
-
view: function view() {
|
|
674
|
+
view: function view(editorView) {
|
|
675
|
+
// Capture up front so a subscription notification before the first PM
|
|
676
|
+
// transaction can still drive slowLaneClient.updateContext (gated on currentView).
|
|
677
|
+
currentView = editorView;
|
|
678
|
+
|
|
679
|
+
// Push channel for hosts that keep producing context after mount (e.g. Rovo chat).
|
|
680
|
+
if (isAutocompleteEnabled && options !== null && options !== void 0 && options.subscribeToContextUpdates) {
|
|
681
|
+
unsubscribeFromContextUpdates = options.subscribeToContextUpdates(function () {
|
|
682
|
+
// Bypass throttle for freshness; the in-flight guard prevents overlap.
|
|
683
|
+
refreshContext({
|
|
684
|
+
source: 'subscription',
|
|
685
|
+
allowThrottle: false
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
}
|
|
674
689
|
return {
|
|
675
690
|
update: function update(view, prevState) {
|
|
676
691
|
if (!isAutocompleteEnabled) {
|
|
@@ -699,7 +714,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
|
|
|
699
714
|
// thread still loading). Retry on word boundaries until we have
|
|
700
715
|
// the parent comment, throttled so we don't refetch constantly
|
|
701
716
|
// and capped so non-comment editors stop retrying entirely.
|
|
702
|
-
|
|
717
|
+
// Skipped for push-channel hosts (subscribeToContextUpdates): they
|
|
718
|
+
// never grow parentCommentContent, so polling only burns the budget.
|
|
719
|
+
if (!(options !== null && options !== void 0 && options.subscribeToContextUpdates) && !((_resolvedContext = resolvedContext) !== null && _resolvedContext !== void 0 && _resolvedContext.parentCommentContent) && wordBoundaryRefreshAttempts < MAX_CONTEXT_REFRESH_ATTEMPTS) {
|
|
703
720
|
// Only count the attempt when a fetch actually started, so an
|
|
704
721
|
// in-flight or throttled no-op doesn't burn the retry budget.
|
|
705
722
|
if (refreshContext({
|
|
@@ -713,8 +730,11 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
|
|
|
713
730
|
}
|
|
714
731
|
},
|
|
715
732
|
destroy: function destroy() {
|
|
733
|
+
var _unsubscribeFromConte;
|
|
716
734
|
destroyed = true;
|
|
717
735
|
currentView = null;
|
|
736
|
+
(_unsubscribeFromConte = unsubscribeFromContextUpdates) === null || _unsubscribeFromConte === void 0 || _unsubscribeFromConte();
|
|
737
|
+
unsubscribeFromContextUpdates = undefined;
|
|
718
738
|
if (debounceTimer) {
|
|
719
739
|
clearTimeout(debounceTimer);
|
|
720
740
|
}
|
|
@@ -14,6 +14,7 @@ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
|
14
14
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
15
15
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
16
16
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
17
|
+
var _ufo = require("../analytics/ufo");
|
|
17
18
|
var _debugMode = require("./debug-mode");
|
|
18
19
|
var _slowLaneClient = require("./slow-lane-client");
|
|
19
20
|
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; }
|
|
@@ -263,7 +264,7 @@ var bePayloadDataPromise;
|
|
|
263
264
|
* :param shape: `'object'` if the source JSON is `{...}`, `'array'` if `[...]`.
|
|
264
265
|
* :returns: The parsed JSON value, or `null` if neither interop mode applies.
|
|
265
266
|
*/
|
|
266
|
-
|
|
267
|
+
function unwrapJsonModule(mod, shape) {
|
|
267
268
|
if (mod == null || (0, _typeof2.default)(mod) !== 'object') {
|
|
268
269
|
return null;
|
|
269
270
|
}
|
|
@@ -315,7 +316,7 @@ var unwrapJsonModule = function unwrapJsonModule(mod, shape) {
|
|
|
315
316
|
return namespace.default;
|
|
316
317
|
}
|
|
317
318
|
return null;
|
|
318
|
-
}
|
|
319
|
+
}
|
|
319
320
|
|
|
320
321
|
/**
|
|
321
322
|
* Lazily load and build the BE-parity lookup tables from their JSON payloads.
|
|
@@ -959,17 +960,23 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
959
960
|
*/
|
|
960
961
|
var runInference = /*#__PURE__*/function () {
|
|
961
962
|
var _ref0 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(text, requestId) {
|
|
962
|
-
var lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t5;
|
|
963
|
+
var experienceId, lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t5;
|
|
963
964
|
return _regenerator.default.wrap(function (_context4) {
|
|
964
965
|
while (1) switch (_context4.prev = _context4.next) {
|
|
965
966
|
case 0:
|
|
967
|
+
captureCompletionTime = function _captureCompletionTim(promise, onResolved) {
|
|
968
|
+
return promise.then(function (value) {
|
|
969
|
+
onResolved(performance.now());
|
|
970
|
+
return value;
|
|
971
|
+
});
|
|
972
|
+
};
|
|
966
973
|
if (!(!engine || destroyed)) {
|
|
967
974
|
_context4.next = 1;
|
|
968
975
|
break;
|
|
969
976
|
}
|
|
970
977
|
return _context4.abrupt("return");
|
|
971
978
|
case 1:
|
|
972
|
-
// Clear the capture buffer so we read only this pass's logits. The engine
|
|
979
|
+
experienceId = String(requestId); // Clear the capture buffer so we read only this pass's logits. The engine
|
|
973
980
|
// serialises per-model requests and updateContext is debounced, so the
|
|
974
981
|
// latest request's decode step is the last to populate `captured` before
|
|
975
982
|
// we read it below; stale requests bail on the latestRequestId guard.
|
|
@@ -982,12 +989,6 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
982
989
|
lmText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_TOKENS);
|
|
983
990
|
semanticText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_WORDS);
|
|
984
991
|
arcticInput = wrapForArctic(semanticText);
|
|
985
|
-
captureCompletionTime = function captureCompletionTime(promise, onResolved) {
|
|
986
|
-
return promise.then(function (value) {
|
|
987
|
-
onResolved(performance.now());
|
|
988
|
-
return value;
|
|
989
|
-
});
|
|
990
|
-
};
|
|
991
992
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
992
993
|
// eslint-disable-next-line no-console
|
|
993
994
|
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;');
|
|
@@ -995,6 +996,16 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
995
996
|
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;');
|
|
996
997
|
}
|
|
997
998
|
_context4.prev = 2;
|
|
999
|
+
// Reuse the network slow-lane-fetch UFO experience (tagged isLocalLLM:true,
|
|
1000
|
+
// matching LOAD_VECTORS/LOAD_VOCABULARY) so on-device inference
|
|
1001
|
+
// latency/success-rate feeds the same FE Reliability SLO. Started inside
|
|
1002
|
+
// the try — post engine-init (parity with the network fetch, which
|
|
1003
|
+
// excludes the one-time model load) and so the catch below always
|
|
1004
|
+
// terminates the experience, even on a synchronous throw.
|
|
1005
|
+
(0, _ufo.startExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1006
|
+
textLength: text.length,
|
|
1007
|
+
isLocalLLM: true
|
|
1008
|
+
});
|
|
998
1009
|
tStart = performance.now();
|
|
999
1010
|
tLmDone = 0;
|
|
1000
1011
|
tEmbDone = 0;
|
|
@@ -1027,6 +1038,9 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
1027
1038
|
_context4.next = 4;
|
|
1028
1039
|
break;
|
|
1029
1040
|
}
|
|
1041
|
+
(0, _ufo.abortExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
1042
|
+
isLocalLLM: true
|
|
1043
|
+
});
|
|
1030
1044
|
return _context4.abrupt("return");
|
|
1031
1045
|
case 4:
|
|
1032
1046
|
// ── LM logits: whole-word BE-parity payload ──────────────────
|
|
@@ -1078,6 +1092,12 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
1078
1092
|
// eslint-disable-next-line no-console
|
|
1079
1093
|
console.groupEnd();
|
|
1080
1094
|
}
|
|
1095
|
+
(0, _ufo.succeedExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1096
|
+
textLength: text.length,
|
|
1097
|
+
hasVector: storedContextVector !== null,
|
|
1098
|
+
hasLmLogits: storedLmLogits !== null,
|
|
1099
|
+
isLocalLLM: true
|
|
1100
|
+
});
|
|
1081
1101
|
onUpdate === null || onUpdate === void 0 || onUpdate({
|
|
1082
1102
|
textLength: text.length,
|
|
1083
1103
|
hasVector: storedContextVector !== null,
|
|
@@ -1092,10 +1112,17 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
1092
1112
|
_context4.next = 6;
|
|
1093
1113
|
break;
|
|
1094
1114
|
}
|
|
1115
|
+
(0, _ufo.abortExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
1116
|
+
isLocalLLM: true
|
|
1117
|
+
});
|
|
1095
1118
|
return _context4.abrupt("return");
|
|
1096
1119
|
case 6:
|
|
1097
1120
|
storedContextVector = null;
|
|
1098
1121
|
storedLmLogits = null;
|
|
1122
|
+
(0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1123
|
+
errorType: 'inference',
|
|
1124
|
+
isLocalLLM: true
|
|
1125
|
+
});
|
|
1099
1126
|
onUpdate === null || onUpdate === void 0 || onUpdate({
|
|
1100
1127
|
textLength: text.length,
|
|
1101
1128
|
hasVector: false,
|
|
@@ -87,7 +87,8 @@ var createSlowLaneClient = exports.createSlowLaneClient = function createSlowLan
|
|
|
87
87
|
session_id: sessionId
|
|
88
88
|
};
|
|
89
89
|
(0, _ufo.startExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
90
|
-
textLength: text.length
|
|
90
|
+
textLength: text.length,
|
|
91
|
+
isLocalLLM: false
|
|
91
92
|
});
|
|
92
93
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
93
94
|
// eslint-disable-next-line no-console
|
|
@@ -116,7 +117,8 @@ var createSlowLaneClient = exports.createSlowLaneClient = function createSlowLan
|
|
|
116
117
|
storedLmLogits = null;
|
|
117
118
|
(0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
118
119
|
status: res.status,
|
|
119
|
-
errorType: 'http_error'
|
|
120
|
+
errorType: 'http_error',
|
|
121
|
+
isLocalLLM: false
|
|
120
122
|
});
|
|
121
123
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
122
124
|
// eslint-disable-next-line no-console
|
|
@@ -151,7 +153,8 @@ var createSlowLaneClient = exports.createSlowLaneClient = function createSlowLan
|
|
|
151
153
|
(0, _ufo.succeedExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
152
154
|
textLength: text.length,
|
|
153
155
|
hasVector: storedContextVector !== null,
|
|
154
|
-
hasLmLogits: storedLmLogits !== null
|
|
156
|
+
hasLmLogits: storedLmLogits !== null,
|
|
157
|
+
isLocalLLM: false
|
|
155
158
|
});
|
|
156
159
|
onUpdate === null || onUpdate === void 0 || onUpdate({
|
|
157
160
|
textLength: text.length,
|
|
@@ -167,7 +170,8 @@ var createSlowLaneClient = exports.createSlowLaneClient = function createSlowLan
|
|
|
167
170
|
storedContextVector = null;
|
|
168
171
|
storedLmLogits = null;
|
|
169
172
|
(0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
170
|
-
errorType: 'network'
|
|
173
|
+
errorType: 'network',
|
|
174
|
+
isLocalLLM: false
|
|
171
175
|
});
|
|
172
176
|
if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
|
|
173
177
|
// eslint-disable-next-line no-console
|
|
@@ -197,7 +201,9 @@ var createSlowLaneClient = exports.createSlowLaneClient = function createSlowLan
|
|
|
197
201
|
debounceTimer = setTimeout(function () {
|
|
198
202
|
debounceTimer = null;
|
|
199
203
|
if (inflightRequestId !== null) {
|
|
200
|
-
(0, _ufo.abortExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded'
|
|
204
|
+
(0, _ufo.abortExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded', {
|
|
205
|
+
isLocalLLM: false
|
|
206
|
+
});
|
|
201
207
|
}
|
|
202
208
|
var requestId = String(++requestSeq);
|
|
203
209
|
inflightRequestId = requestId;
|
|
@@ -694,7 +694,7 @@ var predict = exports.predict = function predict(textBefore) {
|
|
|
694
694
|
* array and a sparse numeric-keyed object are emitted identically as named
|
|
695
695
|
* exports. Kept in lock-step with the matching helper in local-slow-lane-client.ts.
|
|
696
696
|
*/
|
|
697
|
-
|
|
697
|
+
function unwrapJsonModule(mod, shape) {
|
|
698
698
|
if (mod == null || (0, _typeof2.default)(mod) !== 'object') {
|
|
699
699
|
return null;
|
|
700
700
|
}
|
|
@@ -730,7 +730,7 @@ var unwrapJsonModule = function unwrapJsonModule(mod, shape) {
|
|
|
730
730
|
return namespace.default;
|
|
731
731
|
}
|
|
732
732
|
return null;
|
|
733
|
-
}
|
|
733
|
+
}
|
|
734
734
|
var loadVectorsAsync = exports.loadVectorsAsync = /*#__PURE__*/function () {
|
|
735
735
|
var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(options) {
|
|
736
736
|
var _options$isLocalLLM;
|
|
@@ -94,15 +94,24 @@ export const failExp = (name, id, metadata) => {
|
|
|
94
94
|
// UFO errors must never break the plugin
|
|
95
95
|
}
|
|
96
96
|
};
|
|
97
|
-
export const abortExp = (name, id, reason) => {
|
|
97
|
+
export const abortExp = (name, id, reason, metadata) => {
|
|
98
98
|
if (!isUfoEnabled()) {
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
101
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
// `reason` is the dedicated, canonical channel for the abort reason and
|
|
103
|
+
// intentionally takes precedence over a `reason` key in `metadata`. Callers
|
|
104
|
+
// must pass the reason via the param, not inside `metadata` (the `reason`
|
|
105
|
+
// key there is reserved and would be overwritten).
|
|
106
|
+
const merged = {
|
|
107
|
+
...metadata,
|
|
108
|
+
...(reason ? {
|
|
104
109
|
reason
|
|
105
|
-
}
|
|
110
|
+
} : {})
|
|
111
|
+
};
|
|
112
|
+
const hasMetadata = Object.keys(merged).length > 0;
|
|
113
|
+
experiences[name].getInstance(id).abort(hasMetadata ? {
|
|
114
|
+
metadata: merged
|
|
106
115
|
} : undefined);
|
|
107
116
|
} catch {
|
|
108
117
|
// UFO errors must never break the plugin
|
|
@@ -237,6 +237,7 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
237
237
|
* user has already typed several words before the promise resolved.
|
|
238
238
|
*/
|
|
239
239
|
let currentView = null;
|
|
240
|
+
let unsubscribeFromContextUpdates;
|
|
240
241
|
/**
|
|
241
242
|
* Set after accepting a suggestion so the next doc-change update
|
|
242
243
|
* skips scheduling a new prediction for the just-inserted text.
|
|
@@ -657,59 +658,80 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
657
658
|
}
|
|
658
659
|
}
|
|
659
660
|
},
|
|
660
|
-
view:
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
currentView = view;
|
|
666
|
-
if (!prevState.doc.eq(view.state.doc)) {
|
|
667
|
-
if (justAccepted) {
|
|
668
|
-
justAccepted = false;
|
|
661
|
+
view: editorView => {
|
|
662
|
+
// Capture up front so a subscription notification before the first PM
|
|
663
|
+
// transaction can still drive slowLaneClient.updateContext (gated on currentView).
|
|
664
|
+
currentView = editorView;
|
|
669
665
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
666
|
+
// Push channel for hosts that keep producing context after mount (e.g. Rovo chat).
|
|
667
|
+
if (isAutocompleteEnabled && options !== null && options !== void 0 && options.subscribeToContextUpdates) {
|
|
668
|
+
unsubscribeFromContextUpdates = options.subscribeToContextUpdates(() => {
|
|
669
|
+
// Bypass throttle for freshness; the in-flight guard prevents overlap.
|
|
670
|
+
refreshContext({
|
|
671
|
+
source: 'subscription',
|
|
672
|
+
allowThrottle: false
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
return {
|
|
677
|
+
update: (view, prevState) => {
|
|
678
|
+
if (!isAutocompleteEnabled) {
|
|
676
679
|
return;
|
|
677
680
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
681
|
+
currentView = view;
|
|
682
|
+
if (!prevState.doc.eq(view.state.doc)) {
|
|
683
|
+
if (justAccepted) {
|
|
684
|
+
justAccepted = false;
|
|
685
|
+
|
|
686
|
+
// Snapshot the post-acceptance text so follow-up transactions hit
|
|
687
|
+
// the dismissedContext guard and abort until the user types again.
|
|
688
|
+
dismissedContext = getTextBeforeCursor(view.state);
|
|
689
|
+
if (debounceTimer) {
|
|
690
|
+
clearTimeout(debounceTimer);
|
|
691
|
+
}
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
maybeUpdateSessionFrequency(view, prevState);
|
|
695
|
+
const textBefore = getTextBeforeCursor(view.state);
|
|
696
|
+
if (isWordBoundary(textBefore)) {
|
|
697
|
+
var _resolvedContext;
|
|
698
|
+
slowLaneClient.updateContext(buildSlowLaneText(view.state.doc.textContent, resolvedContext));
|
|
683
699
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
//
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
700
|
+
// Context may not have resolved on first focus (e.g. comment
|
|
701
|
+
// thread still loading). Retry on word boundaries until we have
|
|
702
|
+
// the parent comment, throttled so we don't refetch constantly
|
|
703
|
+
// and capped so non-comment editors stop retrying entirely.
|
|
704
|
+
// Skipped for push-channel hosts (subscribeToContextUpdates): they
|
|
705
|
+
// never grow parentCommentContent, so polling only burns the budget.
|
|
706
|
+
if (!(options !== null && options !== void 0 && options.subscribeToContextUpdates) && !((_resolvedContext = resolvedContext) !== null && _resolvedContext !== void 0 && _resolvedContext.parentCommentContent) && wordBoundaryRefreshAttempts < MAX_CONTEXT_REFRESH_ATTEMPTS) {
|
|
707
|
+
// Only count the attempt when a fetch actually started, so an
|
|
708
|
+
// in-flight or throttled no-op doesn't burn the retry budget.
|
|
709
|
+
if (refreshContext({
|
|
710
|
+
source: 'word-boundary'
|
|
711
|
+
})) {
|
|
712
|
+
wordBoundaryRefreshAttempts++;
|
|
713
|
+
}
|
|
695
714
|
}
|
|
696
715
|
}
|
|
716
|
+
schedulePrediction(view);
|
|
697
717
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
slowLaneClient
|
|
718
|
+
},
|
|
719
|
+
destroy: () => {
|
|
720
|
+
var _unsubscribeFromConte;
|
|
721
|
+
destroyed = true;
|
|
722
|
+
currentView = null;
|
|
723
|
+
(_unsubscribeFromConte = unsubscribeFromContextUpdates) === null || _unsubscribeFromConte === void 0 ? void 0 : _unsubscribeFromConte();
|
|
724
|
+
unsubscribeFromContextUpdates = undefined;
|
|
725
|
+
if (debounceTimer) {
|
|
726
|
+
clearTimeout(debounceTimer);
|
|
727
|
+
}
|
|
728
|
+
if (hasDestroy(slowLaneClient)) {
|
|
729
|
+
slowLaneClient.destroy();
|
|
730
|
+
}
|
|
731
|
+
ingestedContextTexts.clear();
|
|
732
|
+
setDefaultSlowLaneClient(null);
|
|
709
733
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
})
|
|
734
|
+
};
|
|
735
|
+
}
|
|
714
736
|
});
|
|
715
737
|
};
|
|
@@ -36,6 +36,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
36
36
|
* asynchronously after each updateContext() call.
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
|
+
import { abortExp, EXPERIENCE_NAME, failExp, startExp, succeedExp } from '../analytics/ufo';
|
|
39
40
|
import { isAutocompleteDebugEnabled } from './debug-mode';
|
|
40
41
|
import { isWordBoundary } from './slow-lane-client';
|
|
41
42
|
|
|
@@ -246,7 +247,7 @@ let bePayloadDataPromise;
|
|
|
246
247
|
* :param shape: `'object'` if the source JSON is `{...}`, `'array'` if `[...]`.
|
|
247
248
|
* :returns: The parsed JSON value, or `null` if neither interop mode applies.
|
|
248
249
|
*/
|
|
249
|
-
|
|
250
|
+
function unwrapJsonModule(mod, shape) {
|
|
250
251
|
if (mod == null || typeof mod !== 'object') {
|
|
251
252
|
return null;
|
|
252
253
|
}
|
|
@@ -287,7 +288,7 @@ const unwrapJsonModule = (mod, shape) => {
|
|
|
287
288
|
return namespace.default;
|
|
288
289
|
}
|
|
289
290
|
return null;
|
|
290
|
-
}
|
|
291
|
+
}
|
|
291
292
|
|
|
292
293
|
/**
|
|
293
294
|
* Lazily load and build the BE-parity lookup tables from their JSON payloads.
|
|
@@ -748,6 +749,7 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
748
749
|
if (!engine || destroyed) {
|
|
749
750
|
return;
|
|
750
751
|
}
|
|
752
|
+
const experienceId = String(requestId);
|
|
751
753
|
|
|
752
754
|
// Clear the capture buffer so we read only this pass's logits. The engine
|
|
753
755
|
// serialises per-model requests and updateContext is debounced, so the
|
|
@@ -762,10 +764,12 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
762
764
|
const lmText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_TOKENS);
|
|
763
765
|
const semanticText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_WORDS);
|
|
764
766
|
const arcticInput = wrapForArctic(semanticText);
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
767
|
+
function captureCompletionTime(promise, onResolved) {
|
|
768
|
+
return promise.then(value => {
|
|
769
|
+
onResolved(performance.now());
|
|
770
|
+
return value;
|
|
771
|
+
});
|
|
772
|
+
}
|
|
769
773
|
if (isAutocompleteDebugEnabled()) {
|
|
770
774
|
// eslint-disable-next-line no-console
|
|
771
775
|
console.log(`%c[LocalSlowLane] %c🔢 Arctic input (${arcticInput.length} chars, ${splitOnWhitespace(semanticText).length} words): "${arcticInput.length > 100 ? `${arcticInput.slice(0, 100)}…` : arcticInput}"`, 'color: #9c27b0; font-weight: bold;', 'color: #009688;');
|
|
@@ -774,6 +778,16 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
774
778
|
}
|
|
775
779
|
try {
|
|
776
780
|
var _data, _data$;
|
|
781
|
+
// Reuse the network slow-lane-fetch UFO experience (tagged isLocalLLM:true,
|
|
782
|
+
// matching LOAD_VECTORS/LOAD_VOCABULARY) so on-device inference
|
|
783
|
+
// latency/success-rate feeds the same FE Reliability SLO. Started inside
|
|
784
|
+
// the try — post engine-init (parity with the network fetch, which
|
|
785
|
+
// excludes the one-time model load) and so the catch below always
|
|
786
|
+
// terminates the experience, even on a synchronous throw.
|
|
787
|
+
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
788
|
+
textLength: text.length,
|
|
789
|
+
isLocalLLM: true
|
|
790
|
+
});
|
|
777
791
|
const tStart = performance.now();
|
|
778
792
|
let tLmDone = 0;
|
|
779
793
|
let tEmbDone = 0;
|
|
@@ -798,6 +812,9 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
798
812
|
|
|
799
813
|
// Discard stale results
|
|
800
814
|
if (requestId < latestRequestId || destroyed) {
|
|
815
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
816
|
+
isLocalLLM: true
|
|
817
|
+
});
|
|
801
818
|
return;
|
|
802
819
|
}
|
|
803
820
|
|
|
@@ -840,6 +857,12 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
840
857
|
// eslint-disable-next-line no-console
|
|
841
858
|
console.groupEnd();
|
|
842
859
|
}
|
|
860
|
+
succeedExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
861
|
+
textLength: text.length,
|
|
862
|
+
hasVector: storedContextVector !== null,
|
|
863
|
+
hasLmLogits: storedLmLogits !== null,
|
|
864
|
+
isLocalLLM: true
|
|
865
|
+
});
|
|
843
866
|
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({
|
|
844
867
|
textLength: text.length,
|
|
845
868
|
hasVector: storedContextVector !== null,
|
|
@@ -848,10 +871,17 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
848
871
|
} catch (err) {
|
|
849
872
|
// Discard errors for stale requests or after teardown
|
|
850
873
|
if (requestId < latestRequestId || destroyed) {
|
|
874
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
875
|
+
isLocalLLM: true
|
|
876
|
+
});
|
|
851
877
|
return;
|
|
852
878
|
}
|
|
853
879
|
storedContextVector = null;
|
|
854
880
|
storedLmLogits = null;
|
|
881
|
+
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
882
|
+
errorType: 'inference',
|
|
883
|
+
isLocalLLM: true
|
|
884
|
+
});
|
|
855
885
|
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({
|
|
856
886
|
textLength: text.length,
|
|
857
887
|
hasVector: false,
|
|
@@ -69,7 +69,8 @@ export const createSlowLaneClient = config => {
|
|
|
69
69
|
session_id: sessionId
|
|
70
70
|
};
|
|
71
71
|
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
72
|
-
textLength: text.length
|
|
72
|
+
textLength: text.length,
|
|
73
|
+
isLocalLLM: false
|
|
73
74
|
});
|
|
74
75
|
if (isAutocompleteDebugEnabled()) {
|
|
75
76
|
// eslint-disable-next-line no-console
|
|
@@ -92,7 +93,8 @@ export const createSlowLaneClient = config => {
|
|
|
92
93
|
storedLmLogits = null;
|
|
93
94
|
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
94
95
|
status: res.status,
|
|
95
|
-
errorType: 'http_error'
|
|
96
|
+
errorType: 'http_error',
|
|
97
|
+
isLocalLLM: false
|
|
96
98
|
});
|
|
97
99
|
if (isAutocompleteDebugEnabled()) {
|
|
98
100
|
// eslint-disable-next-line no-console
|
|
@@ -124,7 +126,8 @@ export const createSlowLaneClient = config => {
|
|
|
124
126
|
succeedExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
125
127
|
textLength: text.length,
|
|
126
128
|
hasVector: storedContextVector !== null,
|
|
127
|
-
hasLmLogits: storedLmLogits !== null
|
|
129
|
+
hasLmLogits: storedLmLogits !== null,
|
|
130
|
+
isLocalLLM: false
|
|
128
131
|
});
|
|
129
132
|
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({
|
|
130
133
|
textLength: text.length,
|
|
@@ -136,7 +139,8 @@ export const createSlowLaneClient = config => {
|
|
|
136
139
|
storedContextVector = null;
|
|
137
140
|
storedLmLogits = null;
|
|
138
141
|
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
139
|
-
errorType: 'network'
|
|
142
|
+
errorType: 'network',
|
|
143
|
+
isLocalLLM: false
|
|
140
144
|
});
|
|
141
145
|
if (isAutocompleteDebugEnabled()) {
|
|
142
146
|
// eslint-disable-next-line no-console
|
|
@@ -156,7 +160,9 @@ export const createSlowLaneClient = config => {
|
|
|
156
160
|
debounceTimer = setTimeout(() => {
|
|
157
161
|
debounceTimer = null;
|
|
158
162
|
if (inflightRequestId !== null) {
|
|
159
|
-
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded'
|
|
163
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded', {
|
|
164
|
+
isLocalLLM: false
|
|
165
|
+
});
|
|
160
166
|
}
|
|
161
167
|
const requestId = String(++requestSeq);
|
|
162
168
|
inflightRequestId = requestId;
|