@atlaskit/editor-plugin-autocomplete 4.0.1 → 4.0.3
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 +19 -0
- package/dist/cjs/analytics/ufo.js +13 -7
- package/dist/cjs/pm-plugins/autocomplete-plugin.js +18 -0
- package/dist/cjs/pm-plugins/local-slow-lane-client.js +29 -2
- package/dist/cjs/pm-plugins/slow-lane-client.js +11 -5
- package/dist/es2019/analytics/ufo.js +13 -4
- package/dist/es2019/pm-plugins/autocomplete-plugin.js +18 -0
- package/dist/es2019/pm-plugins/local-slow-lane-client.js +28 -0
- package/dist/es2019/pm-plugins/slow-lane-client.js +11 -5
- package/dist/esm/analytics/ufo.js +13 -5
- package/dist/esm/pm-plugins/autocomplete-plugin.js +18 -0
- package/dist/esm/pm-plugins/local-slow-lane-client.js +29 -2
- package/dist/esm/pm-plugins/slow-lane-client.js +11 -5
- package/dist/types/analytics/ufo.d.ts +1 -1
- package/dist/types/pm-plugins/autocomplete-plugin.d.ts +5 -0
- package/package.json +2 -2
- package/src/analytics/ufo.ts +13 -2
- package/src/pm-plugins/autocomplete-plugin.ts +29 -0
- package/src/pm-plugins/local-slow-lane-client.ts +38 -0
- package/src/pm-plugins/slow-lane-client.ts +13 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-autocomplete
|
|
2
2
|
|
|
3
|
+
## 4.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`9869d944172b6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9869d944172b6) -
|
|
8
|
+
Instrument the local (LocalLLM) slow-lane client with the existing `slow-lane-fetch` UFO
|
|
9
|
+
experience, tagged with an `isLocalLLM` flag (matching `load-vectors`/`load-vocabulary`) so
|
|
10
|
+
on-device inference latency and success-rate feed the same FE Reliability SLO as the network
|
|
11
|
+
slow-lane fetch. The `isLocalLLM` flag is now also emitted on the abort path of both the local and
|
|
12
|
+
network clients.
|
|
13
|
+
|
|
14
|
+
## 4.0.2
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [`56263a9d5bb6d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/56263a9d5bb6d) -
|
|
19
|
+
Gate editor autocomplete to English locales so suggestions and model loading are disabled for
|
|
20
|
+
non-English users.
|
|
21
|
+
|
|
3
22
|
## 4.0.1
|
|
4
23
|
|
|
5
24
|
### 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
|
|
@@ -230,7 +230,16 @@ var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefor
|
|
|
230
230
|
var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
|
|
231
231
|
return trailingToken.length;
|
|
232
232
|
};
|
|
233
|
+
var isEnglishLocale = function isEnglishLocale(locale) {
|
|
234
|
+
if (!locale) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
return locale.toLowerCase().startsWith('en');
|
|
238
|
+
};
|
|
233
239
|
var createAutocompletePlugin = exports.createAutocompletePlugin = function createAutocompletePlugin(options, api) {
|
|
240
|
+
var _options$locale;
|
|
241
|
+
var locale = (_options$locale = options === null || options === void 0 ? void 0 : options.locale) !== null && _options$locale !== void 0 ? _options$locale : typeof navigator !== 'undefined' ? navigator.language : undefined;
|
|
242
|
+
var isAutocompleteEnabled = isEnglishLocale(locale);
|
|
234
243
|
var debounceTimer = null;
|
|
235
244
|
var hasIngestedPage = false;
|
|
236
245
|
var resolvedContext;
|
|
@@ -620,6 +629,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
|
|
|
620
629
|
}),
|
|
621
630
|
handleDOMEvents: {
|
|
622
631
|
blur: function blur(view) {
|
|
632
|
+
if (!isAutocompleteEnabled) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
623
635
|
var pluginState = autocompletePluginKey.getState(view.state);
|
|
624
636
|
if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
|
|
625
637
|
clearGhostText(view.state, view.dispatch);
|
|
@@ -629,6 +641,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
|
|
|
629
641
|
},
|
|
630
642
|
focus: function focus() {
|
|
631
643
|
var _options$useLocalMode, _options$useLocalMode2;
|
|
644
|
+
if (!isAutocompleteEnabled) {
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
632
647
|
(0, _textPredictor.loadDefaultVocabulary)({
|
|
633
648
|
isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false
|
|
634
649
|
}).catch(function (error) {
|
|
@@ -658,6 +673,9 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
|
|
|
658
673
|
view: function view() {
|
|
659
674
|
return {
|
|
660
675
|
update: function update(view, prevState) {
|
|
676
|
+
if (!isAutocompleteEnabled) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
661
679
|
currentView = view;
|
|
662
680
|
if (!prevState.doc.eq(view.state.doc)) {
|
|
663
681
|
if (justAccepted) {
|
|
@@ -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; }
|
|
@@ -959,7 +960,7 @@ 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:
|
|
@@ -969,7 +970,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
|
|
|
969
970
|
}
|
|
970
971
|
return _context4.abrupt("return");
|
|
971
972
|
case 1:
|
|
972
|
-
// Clear the capture buffer so we read only this pass's logits. The engine
|
|
973
|
+
experienceId = String(requestId); // Clear the capture buffer so we read only this pass's logits. The engine
|
|
973
974
|
// serialises per-model requests and updateContext is debounced, so the
|
|
974
975
|
// latest request's decode step is the last to populate `captured` before
|
|
975
976
|
// we read it below; stale requests bail on the latestRequestId guard.
|
|
@@ -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;
|
|
@@ -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
|
|
@@ -218,7 +218,16 @@ const getTypedLengthForPrediction = textBefore => {
|
|
|
218
218
|
const trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
|
|
219
219
|
return trailingToken.length;
|
|
220
220
|
};
|
|
221
|
+
const isEnglishLocale = locale => {
|
|
222
|
+
if (!locale) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
return locale.toLowerCase().startsWith('en');
|
|
226
|
+
};
|
|
221
227
|
export const createAutocompletePlugin = (options, api) => {
|
|
228
|
+
var _options$locale;
|
|
229
|
+
const locale = (_options$locale = options === null || options === void 0 ? void 0 : options.locale) !== null && _options$locale !== void 0 ? _options$locale : typeof navigator !== 'undefined' ? navigator.language : undefined;
|
|
230
|
+
const isAutocompleteEnabled = isEnglishLocale(locale);
|
|
222
231
|
let debounceTimer = null;
|
|
223
232
|
let hasIngestedPage = false;
|
|
224
233
|
let resolvedContext;
|
|
@@ -607,6 +616,9 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
607
616
|
}),
|
|
608
617
|
handleDOMEvents: {
|
|
609
618
|
blur: view => {
|
|
619
|
+
if (!isAutocompleteEnabled) {
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
610
622
|
const pluginState = autocompletePluginKey.getState(view.state);
|
|
611
623
|
if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
|
|
612
624
|
clearGhostText(view.state, view.dispatch);
|
|
@@ -616,6 +628,9 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
616
628
|
},
|
|
617
629
|
focus: () => {
|
|
618
630
|
var _options$useLocalMode, _options$useLocalMode2;
|
|
631
|
+
if (!isAutocompleteEnabled) {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
619
634
|
loadDefaultVocabulary({
|
|
620
635
|
isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false
|
|
621
636
|
}).catch(error => {
|
|
@@ -644,6 +659,9 @@ export const createAutocompletePlugin = (options, api) => {
|
|
|
644
659
|
},
|
|
645
660
|
view: () => ({
|
|
646
661
|
update: (view, prevState) => {
|
|
662
|
+
if (!isAutocompleteEnabled) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
647
665
|
currentView = view;
|
|
648
666
|
if (!prevState.doc.eq(view.state.doc)) {
|
|
649
667
|
if (justAccepted) {
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -774,6 +776,16 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
774
776
|
}
|
|
775
777
|
try {
|
|
776
778
|
var _data, _data$;
|
|
779
|
+
// Reuse the network slow-lane-fetch UFO experience (tagged isLocalLLM:true,
|
|
780
|
+
// matching LOAD_VECTORS/LOAD_VOCABULARY) so on-device inference
|
|
781
|
+
// latency/success-rate feeds the same FE Reliability SLO. Started inside
|
|
782
|
+
// the try — post engine-init (parity with the network fetch, which
|
|
783
|
+
// excludes the one-time model load) and so the catch below always
|
|
784
|
+
// terminates the experience, even on a synchronous throw.
|
|
785
|
+
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
786
|
+
textLength: text.length,
|
|
787
|
+
isLocalLLM: true
|
|
788
|
+
});
|
|
777
789
|
const tStart = performance.now();
|
|
778
790
|
let tLmDone = 0;
|
|
779
791
|
let tEmbDone = 0;
|
|
@@ -798,6 +810,9 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
798
810
|
|
|
799
811
|
// Discard stale results
|
|
800
812
|
if (requestId < latestRequestId || destroyed) {
|
|
813
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
814
|
+
isLocalLLM: true
|
|
815
|
+
});
|
|
801
816
|
return;
|
|
802
817
|
}
|
|
803
818
|
|
|
@@ -840,6 +855,12 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
840
855
|
// eslint-disable-next-line no-console
|
|
841
856
|
console.groupEnd();
|
|
842
857
|
}
|
|
858
|
+
succeedExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
859
|
+
textLength: text.length,
|
|
860
|
+
hasVector: storedContextVector !== null,
|
|
861
|
+
hasLmLogits: storedLmLogits !== null,
|
|
862
|
+
isLocalLLM: true
|
|
863
|
+
});
|
|
843
864
|
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({
|
|
844
865
|
textLength: text.length,
|
|
845
866
|
hasVector: storedContextVector !== null,
|
|
@@ -848,10 +869,17 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
848
869
|
} catch (err) {
|
|
849
870
|
// Discard errors for stale requests or after teardown
|
|
850
871
|
if (requestId < latestRequestId || destroyed) {
|
|
872
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
873
|
+
isLocalLLM: true
|
|
874
|
+
});
|
|
851
875
|
return;
|
|
852
876
|
}
|
|
853
877
|
storedContextVector = null;
|
|
854
878
|
storedLmLogits = null;
|
|
879
|
+
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
880
|
+
errorType: 'inference',
|
|
881
|
+
isLocalLLM: true
|
|
882
|
+
});
|
|
855
883
|
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({
|
|
856
884
|
textLength: text.length,
|
|
857
885
|
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;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
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
|
+
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; }
|
|
2
4
|
/**
|
|
3
5
|
* UFO experience tracking helpers for `@atlaskit/editor-plugin-autocomplete`.
|
|
4
6
|
*
|
|
@@ -89,15 +91,21 @@ export var failExp = function failExp(name, id, metadata) {
|
|
|
89
91
|
// UFO errors must never break the plugin
|
|
90
92
|
}
|
|
91
93
|
};
|
|
92
|
-
export var abortExp = function abortExp(name, id, reason) {
|
|
94
|
+
export var abortExp = function abortExp(name, id, reason, metadata) {
|
|
93
95
|
if (!isUfoEnabled()) {
|
|
94
96
|
return;
|
|
95
97
|
}
|
|
96
98
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
// `reason` is the dedicated, canonical channel for the abort reason and
|
|
100
|
+
// intentionally takes precedence over a `reason` key in `metadata`. Callers
|
|
101
|
+
// must pass the reason via the param, not inside `metadata` (the `reason`
|
|
102
|
+
// key there is reserved and would be overwritten).
|
|
103
|
+
var merged = _objectSpread(_objectSpread({}, metadata), reason ? {
|
|
104
|
+
reason: reason
|
|
105
|
+
} : {});
|
|
106
|
+
var hasMetadata = Object.keys(merged).length > 0;
|
|
107
|
+
experiences[name].getInstance(id).abort(hasMetadata ? {
|
|
108
|
+
metadata: merged
|
|
101
109
|
} : undefined);
|
|
102
110
|
} catch (_unused4) {
|
|
103
111
|
// UFO errors must never break the plugin
|
|
@@ -223,7 +223,16 @@ var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefor
|
|
|
223
223
|
var trailingToken = (_textBefore$match$ = (_textBefore$match = textBefore.match(/([^\s.,;:!?]*)$/)) === null || _textBefore$match === void 0 ? void 0 : _textBefore$match[1]) !== null && _textBefore$match$ !== void 0 ? _textBefore$match$ : '';
|
|
224
224
|
return trailingToken.length;
|
|
225
225
|
};
|
|
226
|
+
var isEnglishLocale = function isEnglishLocale(locale) {
|
|
227
|
+
if (!locale) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return locale.toLowerCase().startsWith('en');
|
|
231
|
+
};
|
|
226
232
|
export var createAutocompletePlugin = function createAutocompletePlugin(options, api) {
|
|
233
|
+
var _options$locale;
|
|
234
|
+
var locale = (_options$locale = options === null || options === void 0 ? void 0 : options.locale) !== null && _options$locale !== void 0 ? _options$locale : typeof navigator !== 'undefined' ? navigator.language : undefined;
|
|
235
|
+
var isAutocompleteEnabled = isEnglishLocale(locale);
|
|
227
236
|
var debounceTimer = null;
|
|
228
237
|
var hasIngestedPage = false;
|
|
229
238
|
var resolvedContext;
|
|
@@ -613,6 +622,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
613
622
|
}),
|
|
614
623
|
handleDOMEvents: {
|
|
615
624
|
blur: function blur(view) {
|
|
625
|
+
if (!isAutocompleteEnabled) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
616
628
|
var pluginState = autocompletePluginKey.getState(view.state);
|
|
617
629
|
if (pluginState !== null && pluginState !== void 0 && pluginState.ghostText) {
|
|
618
630
|
clearGhostText(view.state, view.dispatch);
|
|
@@ -622,6 +634,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
622
634
|
},
|
|
623
635
|
focus: function focus() {
|
|
624
636
|
var _options$useLocalMode, _options$useLocalMode2;
|
|
637
|
+
if (!isAutocompleteEnabled) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
625
640
|
loadDefaultVocabulary({
|
|
626
641
|
isLocalLLM: (_options$useLocalMode = options === null || options === void 0 ? void 0 : options.useLocalModel) !== null && _options$useLocalMode !== void 0 ? _options$useLocalMode : false
|
|
627
642
|
}).catch(function (error) {
|
|
@@ -651,6 +666,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
651
666
|
view: function view() {
|
|
652
667
|
return {
|
|
653
668
|
update: function update(view, prevState) {
|
|
669
|
+
if (!isAutocompleteEnabled) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
654
672
|
currentView = view;
|
|
655
673
|
if (!prevState.doc.eq(view.state.doc)) {
|
|
656
674
|
if (justAccepted) {
|
|
@@ -48,6 +48,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
48
48
|
* asynchronously after each updateContext() call.
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
+
import { abortExp, EXPERIENCE_NAME, failExp, startExp, succeedExp } from '../analytics/ufo';
|
|
51
52
|
import { isAutocompleteDebugEnabled } from './debug-mode';
|
|
52
53
|
import { isWordBoundary } from './slow-lane-client';
|
|
53
54
|
|
|
@@ -947,7 +948,7 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
947
948
|
*/
|
|
948
949
|
var runInference = /*#__PURE__*/function () {
|
|
949
950
|
var _ref0 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(text, requestId) {
|
|
950
|
-
var lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t5;
|
|
951
|
+
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;
|
|
951
952
|
return _regeneratorRuntime.wrap(function (_context4) {
|
|
952
953
|
while (1) switch (_context4.prev = _context4.next) {
|
|
953
954
|
case 0:
|
|
@@ -957,7 +958,7 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
957
958
|
}
|
|
958
959
|
return _context4.abrupt("return");
|
|
959
960
|
case 1:
|
|
960
|
-
// Clear the capture buffer so we read only this pass's logits. The engine
|
|
961
|
+
experienceId = String(requestId); // Clear the capture buffer so we read only this pass's logits. The engine
|
|
961
962
|
// serialises per-model requests and updateContext is debounced, so the
|
|
962
963
|
// latest request's decode step is the last to populate `captured` before
|
|
963
964
|
// we read it below; stale requests bail on the latestRequestId guard.
|
|
@@ -983,6 +984,16 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
983
984
|
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;');
|
|
984
985
|
}
|
|
985
986
|
_context4.prev = 2;
|
|
987
|
+
// Reuse the network slow-lane-fetch UFO experience (tagged isLocalLLM:true,
|
|
988
|
+
// matching LOAD_VECTORS/LOAD_VOCABULARY) so on-device inference
|
|
989
|
+
// latency/success-rate feeds the same FE Reliability SLO. Started inside
|
|
990
|
+
// the try — post engine-init (parity with the network fetch, which
|
|
991
|
+
// excludes the one-time model load) and so the catch below always
|
|
992
|
+
// terminates the experience, even on a synchronous throw.
|
|
993
|
+
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
994
|
+
textLength: text.length,
|
|
995
|
+
isLocalLLM: true
|
|
996
|
+
});
|
|
986
997
|
tStart = performance.now();
|
|
987
998
|
tLmDone = 0;
|
|
988
999
|
tEmbDone = 0;
|
|
@@ -1015,6 +1026,9 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
1015
1026
|
_context4.next = 4;
|
|
1016
1027
|
break;
|
|
1017
1028
|
}
|
|
1029
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
1030
|
+
isLocalLLM: true
|
|
1031
|
+
});
|
|
1018
1032
|
return _context4.abrupt("return");
|
|
1019
1033
|
case 4:
|
|
1020
1034
|
// ── LM logits: whole-word BE-parity payload ──────────────────
|
|
@@ -1066,6 +1080,12 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
1066
1080
|
// eslint-disable-next-line no-console
|
|
1067
1081
|
console.groupEnd();
|
|
1068
1082
|
}
|
|
1083
|
+
succeedExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1084
|
+
textLength: text.length,
|
|
1085
|
+
hasVector: storedContextVector !== null,
|
|
1086
|
+
hasLmLogits: storedLmLogits !== null,
|
|
1087
|
+
isLocalLLM: true
|
|
1088
|
+
});
|
|
1069
1089
|
onUpdate === null || onUpdate === void 0 || onUpdate({
|
|
1070
1090
|
textLength: text.length,
|
|
1071
1091
|
hasVector: storedContextVector !== null,
|
|
@@ -1080,10 +1100,17 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
|
|
|
1080
1100
|
_context4.next = 6;
|
|
1081
1101
|
break;
|
|
1082
1102
|
}
|
|
1103
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, destroyed ? 'destroyed' : 'superseded', {
|
|
1104
|
+
isLocalLLM: true
|
|
1105
|
+
});
|
|
1083
1106
|
return _context4.abrupt("return");
|
|
1084
1107
|
case 6:
|
|
1085
1108
|
storedContextVector = null;
|
|
1086
1109
|
storedLmLogits = null;
|
|
1110
|
+
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1111
|
+
errorType: 'inference',
|
|
1112
|
+
isLocalLLM: true
|
|
1113
|
+
});
|
|
1087
1114
|
onUpdate === null || onUpdate === void 0 || onUpdate({
|
|
1088
1115
|
textLength: text.length,
|
|
1089
1116
|
hasVector: false,
|
|
@@ -81,7 +81,8 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
|
|
|
81
81
|
session_id: sessionId
|
|
82
82
|
};
|
|
83
83
|
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
84
|
-
textLength: text.length
|
|
84
|
+
textLength: text.length,
|
|
85
|
+
isLocalLLM: false
|
|
85
86
|
});
|
|
86
87
|
if (isAutocompleteDebugEnabled()) {
|
|
87
88
|
// eslint-disable-next-line no-console
|
|
@@ -110,7 +111,8 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
|
|
|
110
111
|
storedLmLogits = null;
|
|
111
112
|
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
112
113
|
status: res.status,
|
|
113
|
-
errorType: 'http_error'
|
|
114
|
+
errorType: 'http_error',
|
|
115
|
+
isLocalLLM: false
|
|
114
116
|
});
|
|
115
117
|
if (isAutocompleteDebugEnabled()) {
|
|
116
118
|
// eslint-disable-next-line no-console
|
|
@@ -145,7 +147,8 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
|
|
|
145
147
|
succeedExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
146
148
|
textLength: text.length,
|
|
147
149
|
hasVector: storedContextVector !== null,
|
|
148
|
-
hasLmLogits: storedLmLogits !== null
|
|
150
|
+
hasLmLogits: storedLmLogits !== null,
|
|
151
|
+
isLocalLLM: false
|
|
149
152
|
});
|
|
150
153
|
onUpdate === null || onUpdate === void 0 || onUpdate({
|
|
151
154
|
textLength: text.length,
|
|
@@ -161,7 +164,8 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
|
|
|
161
164
|
storedContextVector = null;
|
|
162
165
|
storedLmLogits = null;
|
|
163
166
|
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
164
|
-
errorType: 'network'
|
|
167
|
+
errorType: 'network',
|
|
168
|
+
isLocalLLM: false
|
|
165
169
|
});
|
|
166
170
|
if (isAutocompleteDebugEnabled()) {
|
|
167
171
|
// eslint-disable-next-line no-console
|
|
@@ -191,7 +195,9 @@ export var createSlowLaneClient = function createSlowLaneClient(config) {
|
|
|
191
195
|
debounceTimer = setTimeout(function () {
|
|
192
196
|
debounceTimer = null;
|
|
193
197
|
if (inflightRequestId !== null) {
|
|
194
|
-
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded'
|
|
198
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded', {
|
|
199
|
+
isLocalLLM: false
|
|
200
|
+
});
|
|
195
201
|
}
|
|
196
202
|
var requestId = String(++requestSeq);
|
|
197
203
|
inflightRequestId = requestId;
|
|
@@ -35,4 +35,4 @@ export type AutocompleteExperienceName = (typeof EXPERIENCE_NAME)[keyof typeof E
|
|
|
35
35
|
export declare const startExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
|
|
36
36
|
export declare const succeedExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
|
|
37
37
|
export declare const failExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
|
|
38
|
-
export declare const abortExp: (name: AutocompleteExperienceName, id: string, reason?: string) => void;
|
|
38
|
+
export declare const abortExp: (name: AutocompleteExperienceName, id: string, reason?: string, metadata?: CustomData) => void;
|
|
@@ -40,6 +40,11 @@ export interface AutocompletePluginOptions {
|
|
|
40
40
|
* Use this to serve vectors from a CDN or media service in production.
|
|
41
41
|
*/
|
|
42
42
|
getVectorsBinaryUrl?: () => Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* User locale used to determine whether autocomplete should run.
|
|
45
|
+
* Defaults to browser locale when omitted.
|
|
46
|
+
*/
|
|
47
|
+
locale?: string;
|
|
43
48
|
/**
|
|
44
49
|
* When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
|
|
45
50
|
* the network-based slow-lane backend. Defaults to false (network client).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-autocomplete",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"wink-nlp": "^2.4.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@atlaskit/editor-common": "^116.
|
|
30
|
+
"@atlaskit/editor-common": "^116.6.0",
|
|
31
31
|
"@atlaskit/editor-plugin-analytics": "^12.0.0",
|
|
32
32
|
"react": "^18.2.0"
|
|
33
33
|
},
|
package/src/analytics/ufo.ts
CHANGED
|
@@ -120,12 +120,23 @@ export const failExp = (
|
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
-
export const abortExp = (
|
|
123
|
+
export const abortExp = (
|
|
124
|
+
name: AutocompleteExperienceName,
|
|
125
|
+
id: string,
|
|
126
|
+
reason?: string,
|
|
127
|
+
metadata?: CustomData,
|
|
128
|
+
): void => {
|
|
124
129
|
if (!isUfoEnabled()) {
|
|
125
130
|
return;
|
|
126
131
|
}
|
|
127
132
|
try {
|
|
128
|
-
|
|
133
|
+
// `reason` is the dedicated, canonical channel for the abort reason and
|
|
134
|
+
// intentionally takes precedence over a `reason` key in `metadata`. Callers
|
|
135
|
+
// must pass the reason via the param, not inside `metadata` (the `reason`
|
|
136
|
+
// key there is reserved and would be overwritten).
|
|
137
|
+
const merged: CustomData = { ...metadata, ...(reason ? { reason } : {}) };
|
|
138
|
+
const hasMetadata = Object.keys(merged).length > 0;
|
|
139
|
+
experiences[name].getInstance(id).abort(hasMetadata ? { metadata: merged } : undefined);
|
|
129
140
|
} catch {
|
|
130
141
|
// UFO errors must never break the plugin
|
|
131
142
|
}
|
|
@@ -214,6 +214,11 @@ export interface AutocompletePluginOptions {
|
|
|
214
214
|
* Use this to serve vectors from a CDN or media service in production.
|
|
215
215
|
*/
|
|
216
216
|
getVectorsBinaryUrl?: () => Promise<string>;
|
|
217
|
+
/**
|
|
218
|
+
* User locale used to determine whether autocomplete should run.
|
|
219
|
+
* Defaults to browser locale when omitted.
|
|
220
|
+
*/
|
|
221
|
+
locale?: string;
|
|
217
222
|
/**
|
|
218
223
|
* When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
|
|
219
224
|
* the network-based slow-lane backend. Defaults to false (network client).
|
|
@@ -299,10 +304,22 @@ const getTypedLengthForPrediction = (textBefore: string): number => {
|
|
|
299
304
|
return trailingToken.length;
|
|
300
305
|
};
|
|
301
306
|
|
|
307
|
+
const isEnglishLocale = (locale?: string): boolean => {
|
|
308
|
+
if (!locale) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return locale.toLowerCase().startsWith('en');
|
|
313
|
+
};
|
|
314
|
+
|
|
302
315
|
export const createAutocompletePlugin = (
|
|
303
316
|
options?: AutocompletePluginOptions,
|
|
304
317
|
api?: ExtractInjectionAPI<AutocompletePlugin>,
|
|
305
318
|
): SafePlugin<AutocompletePluginState> => {
|
|
319
|
+
const locale =
|
|
320
|
+
options?.locale ?? (typeof navigator !== 'undefined' ? navigator.language : undefined);
|
|
321
|
+
const isAutocompleteEnabled = isEnglishLocale(locale);
|
|
322
|
+
|
|
306
323
|
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
307
324
|
let hasIngestedPage = false;
|
|
308
325
|
let resolvedContext: AutocompleteContext | undefined;
|
|
@@ -730,6 +747,10 @@ export const createAutocompletePlugin = (
|
|
|
730
747
|
|
|
731
748
|
handleDOMEvents: {
|
|
732
749
|
blur: (view: EditorView) => {
|
|
750
|
+
if (!isAutocompleteEnabled) {
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
|
|
733
754
|
const pluginState = autocompletePluginKey.getState(view.state) as
|
|
734
755
|
| AutocompletePluginState
|
|
735
756
|
| undefined;
|
|
@@ -740,6 +761,10 @@ export const createAutocompletePlugin = (
|
|
|
740
761
|
return false;
|
|
741
762
|
},
|
|
742
763
|
focus: () => {
|
|
764
|
+
if (!isAutocompleteEnabled) {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
|
|
743
768
|
loadDefaultVocabulary({ isLocalLLM: options?.useLocalModel ?? false }).catch((error) => {
|
|
744
769
|
logException(error as Error, {
|
|
745
770
|
location: 'editor-plugin-autocomplete/loadDefaultVocabulary',
|
|
@@ -767,6 +792,10 @@ export const createAutocompletePlugin = (
|
|
|
767
792
|
|
|
768
793
|
view: () => ({
|
|
769
794
|
update: (view: EditorView, prevState: EditorState) => {
|
|
795
|
+
if (!isAutocompleteEnabled) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
|
|
770
799
|
currentView = view;
|
|
771
800
|
if (!prevState.doc.eq(view.state.doc)) {
|
|
772
801
|
if (justAccepted) {
|
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
|
|
38
38
|
import type { MLCEngine, InitProgressReport, AppConfig, LogitProcessor } from '@mlc-ai/web-llm';
|
|
39
39
|
|
|
40
|
+
import { abortExp, EXPERIENCE_NAME, failExp, startExp, succeedExp } from '../analytics/ufo';
|
|
41
|
+
|
|
40
42
|
import { isAutocompleteDebugEnabled } from './debug-mode';
|
|
41
43
|
import { isWordBoundary } from './slow-lane-client';
|
|
42
44
|
|
|
@@ -987,6 +989,8 @@ export const createLocalSlowLaneClient = (
|
|
|
987
989
|
return;
|
|
988
990
|
}
|
|
989
991
|
|
|
992
|
+
const experienceId = String(requestId);
|
|
993
|
+
|
|
990
994
|
// Clear the capture buffer so we read only this pass's logits. The engine
|
|
991
995
|
// serialises per-model requests and updateContext is debounced, so the
|
|
992
996
|
// latest request's decode step is the last to populate `captured` before
|
|
@@ -1025,6 +1029,17 @@ export const createLocalSlowLaneClient = (
|
|
|
1025
1029
|
}
|
|
1026
1030
|
|
|
1027
1031
|
try {
|
|
1032
|
+
// Reuse the network slow-lane-fetch UFO experience (tagged isLocalLLM:true,
|
|
1033
|
+
// matching LOAD_VECTORS/LOAD_VOCABULARY) so on-device inference
|
|
1034
|
+
// latency/success-rate feeds the same FE Reliability SLO. Started inside
|
|
1035
|
+
// the try — post engine-init (parity with the network fetch, which
|
|
1036
|
+
// excludes the one-time model load) and so the catch below always
|
|
1037
|
+
// terminates the experience, even on a synchronous throw.
|
|
1038
|
+
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1039
|
+
textLength: text.length,
|
|
1040
|
+
isLocalLLM: true,
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1028
1043
|
const tStart = performance.now();
|
|
1029
1044
|
let tLmDone = 0;
|
|
1030
1045
|
let tEmbDone = 0;
|
|
@@ -1064,6 +1079,12 @@ export const createLocalSlowLaneClient = (
|
|
|
1064
1079
|
|
|
1065
1080
|
// Discard stale results
|
|
1066
1081
|
if (requestId < latestRequestId || destroyed) {
|
|
1082
|
+
abortExp(
|
|
1083
|
+
EXPERIENCE_NAME.SLOW_LANE_FETCH,
|
|
1084
|
+
experienceId,
|
|
1085
|
+
destroyed ? 'destroyed' : 'superseded',
|
|
1086
|
+
{ isLocalLLM: true },
|
|
1087
|
+
);
|
|
1067
1088
|
return;
|
|
1068
1089
|
}
|
|
1069
1090
|
|
|
@@ -1126,6 +1147,13 @@ export const createLocalSlowLaneClient = (
|
|
|
1126
1147
|
console.groupEnd();
|
|
1127
1148
|
}
|
|
1128
1149
|
|
|
1150
|
+
succeedExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1151
|
+
textLength: text.length,
|
|
1152
|
+
hasVector: storedContextVector !== null,
|
|
1153
|
+
hasLmLogits: storedLmLogits !== null,
|
|
1154
|
+
isLocalLLM: true,
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1129
1157
|
onUpdate?.({
|
|
1130
1158
|
textLength: text.length,
|
|
1131
1159
|
hasVector: storedContextVector !== null,
|
|
@@ -1134,11 +1162,21 @@ export const createLocalSlowLaneClient = (
|
|
|
1134
1162
|
} catch (err) {
|
|
1135
1163
|
// Discard errors for stale requests or after teardown
|
|
1136
1164
|
if (requestId < latestRequestId || destroyed) {
|
|
1165
|
+
abortExp(
|
|
1166
|
+
EXPERIENCE_NAME.SLOW_LANE_FETCH,
|
|
1167
|
+
experienceId,
|
|
1168
|
+
destroyed ? 'destroyed' : 'superseded',
|
|
1169
|
+
{ isLocalLLM: true },
|
|
1170
|
+
);
|
|
1137
1171
|
return;
|
|
1138
1172
|
}
|
|
1139
1173
|
|
|
1140
1174
|
storedContextVector = null;
|
|
1141
1175
|
storedLmLogits = null;
|
|
1176
|
+
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, {
|
|
1177
|
+
errorType: 'inference',
|
|
1178
|
+
isLocalLLM: true,
|
|
1179
|
+
});
|
|
1142
1180
|
onUpdate?.({ textLength: text.length, hasVector: false, hasLmLogits: false });
|
|
1143
1181
|
|
|
1144
1182
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -101,7 +101,10 @@ export const createSlowLaneClient = (
|
|
|
101
101
|
session_id: sessionId,
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
-
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
104
|
+
startExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
105
|
+
textLength: text.length,
|
|
106
|
+
isLocalLLM: false,
|
|
107
|
+
});
|
|
105
108
|
|
|
106
109
|
if (isAutocompleteDebugEnabled()) {
|
|
107
110
|
// eslint-disable-next-line no-console
|
|
@@ -131,6 +134,7 @@ export const createSlowLaneClient = (
|
|
|
131
134
|
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
132
135
|
status: res.status,
|
|
133
136
|
errorType: 'http_error',
|
|
137
|
+
isLocalLLM: false,
|
|
134
138
|
});
|
|
135
139
|
if (isAutocompleteDebugEnabled()) {
|
|
136
140
|
// eslint-disable-next-line no-console
|
|
@@ -184,6 +188,7 @@ export const createSlowLaneClient = (
|
|
|
184
188
|
textLength: text.length,
|
|
185
189
|
hasVector: storedContextVector !== null,
|
|
186
190
|
hasLmLogits: storedLmLogits !== null,
|
|
191
|
+
isLocalLLM: false,
|
|
187
192
|
});
|
|
188
193
|
|
|
189
194
|
onUpdate?.({
|
|
@@ -195,7 +200,10 @@ export const createSlowLaneClient = (
|
|
|
195
200
|
} catch (e) {
|
|
196
201
|
storedContextVector = null;
|
|
197
202
|
storedLmLogits = null;
|
|
198
|
-
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
203
|
+
failExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, requestId, {
|
|
204
|
+
errorType: 'network',
|
|
205
|
+
isLocalLLM: false,
|
|
206
|
+
});
|
|
199
207
|
if (isAutocompleteDebugEnabled()) {
|
|
200
208
|
// eslint-disable-next-line no-console
|
|
201
209
|
console.log(
|
|
@@ -219,7 +227,9 @@ export const createSlowLaneClient = (
|
|
|
219
227
|
debounceTimer = setTimeout(() => {
|
|
220
228
|
debounceTimer = null;
|
|
221
229
|
if (inflightRequestId !== null) {
|
|
222
|
-
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded'
|
|
230
|
+
abortExp(EXPERIENCE_NAME.SLOW_LANE_FETCH, inflightRequestId, 'superseded', {
|
|
231
|
+
isLocalLLM: false,
|
|
232
|
+
});
|
|
223
233
|
}
|
|
224
234
|
const requestId = String(++requestSeq);
|
|
225
235
|
inflightRequestId = requestId;
|