@atlaskit/editor-plugin-autocomplete 3.5.0 → 3.6.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 +44 -5
- package/dist/cjs/pm-plugins/local-slow-lane-client.js +239 -63
- package/dist/es2019/pm-plugins/autocomplete-plugin.js +46 -5
- package/dist/es2019/pm-plugins/local-slow-lane-client.js +139 -11
- package/dist/esm/pm-plugins/autocomplete-plugin.js +44 -5
- package/dist/esm/pm-plugins/local-slow-lane-client.js +238 -62
- package/dist/types/pm-plugins/local-slow-lane-client.d.ts +52 -0
- package/dist/types-ts4.5/pm-plugins/local-slow-lane-client.d.ts +52 -0
- package/package.json +2 -2
- package/src/pm-plugins/autocomplete-plugin.ts +45 -1
- package/src/pm-plugins/local-slow-lane-client.ts +223 -12
|
@@ -43,6 +43,19 @@ import { isWordBoundary } from './slow-lane-client';
|
|
|
43
43
|
|
|
44
44
|
// Same return type as createSlowLaneClient for drop-in compatibility
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Why the local engine failed to load/start.
|
|
48
|
+
*
|
|
49
|
+
* The first three are user-machine limitations (WebGPU missing, no compatible
|
|
50
|
+
* GPU adapter, GPU lacks the `shader-f16` feature the model needs);
|
|
51
|
+
* `insufficient_memory` is hit when weights don't fit in VRAM. The rest cover
|
|
52
|
+
* delivery/runtime failures unrelated to hardware.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/** Snapshot of the machine's WebGPU support, used to explain hardware limits. */
|
|
56
|
+
|
|
57
|
+
// Minimal WebGPU shape: lib.dom types aren't guaranteed in this build target.
|
|
58
|
+
|
|
46
59
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
47
60
|
|
|
48
61
|
const DEFAULT_DEBOUNCE_MS = 300;
|
|
@@ -470,6 +483,8 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
470
483
|
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
471
484
|
onUpdate,
|
|
472
485
|
onStatus,
|
|
486
|
+
onLoadError,
|
|
487
|
+
onLoadSuccess,
|
|
473
488
|
modelId = LOCAL_MLC_CAUSAL_MODEL_ID,
|
|
474
489
|
customModelConfig
|
|
475
490
|
} = config;
|
|
@@ -516,16 +531,129 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
516
531
|
}
|
|
517
532
|
onStatus === null || onStatus === void 0 ? void 0 : onStatus(message);
|
|
518
533
|
};
|
|
534
|
+
const bytesToMB = bytes => typeof bytes === 'number' ? Math.round(bytes / (1024 * 1024)) : undefined;
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Inspect the machine's WebGPU support so a load failure can be attributed
|
|
538
|
+
* to a concrete hardware/browser limitation rather than a generic error.
|
|
539
|
+
*/
|
|
540
|
+
const probeWebGpuCapabilities = async () => {
|
|
541
|
+
const gpu = navigator.gpu;
|
|
542
|
+
if (!gpu) {
|
|
543
|
+
return {
|
|
544
|
+
available: false
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
try {
|
|
548
|
+
var _adapter$limits, _adapter$limits2;
|
|
549
|
+
const adapter = await gpu.requestAdapter();
|
|
550
|
+
if (!adapter) {
|
|
551
|
+
return {
|
|
552
|
+
available: true,
|
|
553
|
+
adapterAvailable: false
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
let vendor;
|
|
557
|
+
let architecture;
|
|
558
|
+
try {
|
|
559
|
+
var _adapter$info, _adapter$requestAdapt;
|
|
560
|
+
const info = (_adapter$info = adapter.info) !== null && _adapter$info !== void 0 ? _adapter$info : await ((_adapter$requestAdapt = adapter.requestAdapterInfo) === null || _adapter$requestAdapt === void 0 ? void 0 : _adapter$requestAdapt.call(adapter));
|
|
561
|
+
vendor = (info === null || info === void 0 ? void 0 : info.vendor) || undefined;
|
|
562
|
+
architecture = (info === null || info === void 0 ? void 0 : info.architecture) || undefined;
|
|
563
|
+
} catch {
|
|
564
|
+
// adapter info is best-effort
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
available: true,
|
|
568
|
+
adapterAvailable: true,
|
|
569
|
+
shaderF16Supported: adapter.features.has('shader-f16'),
|
|
570
|
+
maxBufferSizeMB: bytesToMB((_adapter$limits = adapter.limits) === null || _adapter$limits === void 0 ? void 0 : _adapter$limits.maxBufferSize),
|
|
571
|
+
maxStorageBufferBindingSizeMB: bytesToMB((_adapter$limits2 = adapter.limits) === null || _adapter$limits2 === void 0 ? void 0 : _adapter$limits2.maxStorageBufferBindingSize),
|
|
572
|
+
vendor,
|
|
573
|
+
architecture
|
|
574
|
+
};
|
|
575
|
+
} catch {
|
|
576
|
+
return {
|
|
577
|
+
available: true,
|
|
578
|
+
adapterAvailable: false
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
/** Map an MLC/WebLLM engine-creation error message to a coarse reason. */
|
|
584
|
+
const classifyEngineError = message => {
|
|
585
|
+
const lower = message.toLowerCase();
|
|
586
|
+
if (lower.includes('loading chunk') || lower.includes('dynamically imported module') || lower.includes('dynamic import')) {
|
|
587
|
+
return 'module_load_failed';
|
|
588
|
+
}
|
|
589
|
+
if (lower.includes('out of memory') || /\boom\b/u.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
|
|
590
|
+
return 'insufficient_memory';
|
|
591
|
+
}
|
|
592
|
+
// Pre-flight already returns missing_shader_f16 when the feature is absent,
|
|
593
|
+
// so only match the exact feature token here — not bare 'shader' (compile
|
|
594
|
+
// errors) or bare 'f16' (present in model ids like q0f16-MLC).
|
|
595
|
+
if (lower.includes('shader-f16') || lower.includes('shader_f16')) {
|
|
596
|
+
return 'missing_shader_f16';
|
|
597
|
+
}
|
|
598
|
+
if (lower.includes('fetch') || lower.includes('network') || lower.includes('download') || lower.includes('http') || lower.includes('cache')) {
|
|
599
|
+
return 'model_download_failed';
|
|
600
|
+
}
|
|
601
|
+
return 'init_failed';
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// Canonical, controlled failure descriptions. We never emit the raw engine
|
|
605
|
+
// error into analytics — it can embed customer-context URLs/paths (HOT-120175)
|
|
606
|
+
// — so the analytics `message` is always one of these fixed strings.
|
|
607
|
+
const LOAD_FAILURE_MESSAGE = {
|
|
608
|
+
webgpu_unavailable: 'WebGPU is not available in this browser',
|
|
609
|
+
webgpu_no_adapter: 'No compatible WebGPU adapter found',
|
|
610
|
+
missing_shader_f16: 'GPU does not support the shader-f16 feature',
|
|
611
|
+
insufficient_memory: 'Insufficient GPU memory to load the model',
|
|
612
|
+
model_download_failed: 'Failed to download model assets',
|
|
613
|
+
module_load_failed: 'Failed to load the web-llm runtime module',
|
|
614
|
+
init_failed: 'Model engine failed to initialise'
|
|
615
|
+
};
|
|
616
|
+
const handleLoadFailure = (reason, capabilities, debugDetail) => {
|
|
617
|
+
ready = false;
|
|
618
|
+
const message = LOAD_FAILURE_MESSAGE[reason];
|
|
619
|
+
if (isAutocompleteDebugEnabled()) {
|
|
620
|
+
// eslint-disable-next-line no-console
|
|
621
|
+
console.log(`[LocalSlowLane] Engine initialisation failed (${reason}): ${debugDetail !== null && debugDetail !== void 0 ? debugDetail : message}`);
|
|
622
|
+
}
|
|
623
|
+
onStatus === null || onStatus === void 0 ? void 0 : onStatus(`Engine initialisation failed: ${message}`);
|
|
624
|
+
onLoadError === null || onLoadError === void 0 ? void 0 : onLoadError({
|
|
625
|
+
reason,
|
|
626
|
+
message,
|
|
627
|
+
modelId,
|
|
628
|
+
embeddingModelId: LOCAL_MLC_EMBEDDING_MODEL_ID,
|
|
629
|
+
capabilities
|
|
630
|
+
});
|
|
631
|
+
engineInitPromise = null;
|
|
632
|
+
initFailed = true;
|
|
633
|
+
};
|
|
519
634
|
const initEngine = async () => {
|
|
635
|
+
const capabilities = await probeWebGpuCapabilities();
|
|
636
|
+
|
|
637
|
+
// ── Pre-flight: machine limitations short-circuit before the expensive load ──
|
|
638
|
+
if (!capabilities.available) {
|
|
639
|
+
handleLoadFailure('webgpu_unavailable', capabilities);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (capabilities.adapterAvailable === false) {
|
|
643
|
+
handleLoadFailure('webgpu_no_adapter', capabilities);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (capabilities.shaderF16Supported === false) {
|
|
647
|
+
handleLoadFailure('missing_shader_f16', capabilities);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const startTime = performance.now();
|
|
520
651
|
try {
|
|
521
652
|
if (isAutocompleteDebugEnabled()) {
|
|
522
653
|
// eslint-disable-next-line no-console
|
|
523
654
|
console.log(`%c[LocalSlowLane] %c🚀 Initialising MLC engine with models: ${modelId} (LM) + ${LOCAL_MLC_EMBEDDING_MODEL_ID} (embedder)`, 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
|
|
524
655
|
}
|
|
525
656
|
onStatus === null || onStatus === void 0 ? void 0 : onStatus(`Initialising models: ${modelId} + ${LOCAL_MLC_EMBEDDING_MODEL_ID}…`);
|
|
526
|
-
if (!('gpu' in navigator)) {
|
|
527
|
-
throw new Error('WebGPU not supported');
|
|
528
|
-
}
|
|
529
657
|
|
|
530
658
|
// Fetch the web-llm runtime and the BE-parity lookup tables in parallel;
|
|
531
659
|
// both are dynamically imported so they stay out of the main editor chunk.
|
|
@@ -568,6 +696,7 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
568
696
|
}
|
|
569
697
|
engine = newEngine;
|
|
570
698
|
ready = true;
|
|
699
|
+
const loadDurationMs = Math.round(performance.now() - startTime);
|
|
571
700
|
if (isAutocompleteDebugEnabled()) {
|
|
572
701
|
// eslint-disable-next-line no-console
|
|
573
702
|
console.log('%c[LocalSlowLane] %c✅ Both models loaded and ready', 'color: #9c27b0; font-weight: bold;', 'color: #4caf50;');
|
|
@@ -579,16 +708,15 @@ export const createLocalSlowLaneClient = (config = {}) => {
|
|
|
579
708
|
console.log('%c[LocalSlowLane] %c🔢 Embedder →', 'color: #9c27b0; font-weight: bold;', 'color: #009688; font-weight: bold;', LOCAL_MLC_EMBEDDING_MODEL_ID);
|
|
580
709
|
}
|
|
581
710
|
onStatus === null || onStatus === void 0 ? void 0 : onStatus('Model loaded and ready.');
|
|
711
|
+
onLoadSuccess === null || onLoadSuccess === void 0 ? void 0 : onLoadSuccess({
|
|
712
|
+
modelId,
|
|
713
|
+
embeddingModelId: LOCAL_MLC_EMBEDDING_MODEL_ID,
|
|
714
|
+
loadDurationMs,
|
|
715
|
+
capabilities
|
|
716
|
+
});
|
|
582
717
|
} catch (err) {
|
|
583
718
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
584
|
-
|
|
585
|
-
if (isAutocompleteDebugEnabled()) {
|
|
586
|
-
// eslint-disable-next-line no-console
|
|
587
|
-
console.log(`[LocalSlowLane] Engine initialisation failed: ${errorMsg}`);
|
|
588
|
-
}
|
|
589
|
-
onStatus === null || onStatus === void 0 ? void 0 : onStatus(`Engine initialisation failed: ${errorMsg}`);
|
|
590
|
-
engineInitPromise = null;
|
|
591
|
-
initFailed = true;
|
|
719
|
+
handleLoadFailure(classifyEngineError(errorMsg), capabilities, errorMsg);
|
|
592
720
|
}
|
|
593
721
|
};
|
|
594
722
|
const ensureEngineInitialized = () => {
|
|
@@ -240,12 +240,49 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
240
240
|
}
|
|
241
241
|
});
|
|
242
242
|
};
|
|
243
|
-
var
|
|
243
|
+
var fireLocalModelLoadedAnalytics = function fireLocalModelLoadedAnalytics(info) {
|
|
244
244
|
var _api$analytics2;
|
|
245
|
+
api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || _api$analytics2.actions.fireAnalyticsEvent({
|
|
246
|
+
action: ACTION.LOCAL_MODEL_LOADED,
|
|
247
|
+
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
248
|
+
eventType: EVENT_TYPE.TRACK,
|
|
249
|
+
attributes: {
|
|
250
|
+
modelId: info.modelId,
|
|
251
|
+
embeddingModelId: info.embeddingModelId,
|
|
252
|
+
loadDurationMs: info.loadDurationMs,
|
|
253
|
+
gpuVendor: info.capabilities.vendor,
|
|
254
|
+
gpuArchitecture: info.capabilities.architecture
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
var fireLocalModelLoadFailedAnalytics = function fireLocalModelLoadFailedAnalytics(error) {
|
|
259
|
+
var _api$analytics3;
|
|
260
|
+
var capabilities = error.capabilities;
|
|
261
|
+
api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || _api$analytics3.actions.fireAnalyticsEvent({
|
|
262
|
+
action: ACTION.LOCAL_MODEL_LOAD_FAILED,
|
|
263
|
+
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
264
|
+
eventType: EVENT_TYPE.TRACK,
|
|
265
|
+
attributes: {
|
|
266
|
+
reason: error.reason,
|
|
267
|
+
message: error.message,
|
|
268
|
+
modelId: error.modelId,
|
|
269
|
+
embeddingModelId: error.embeddingModelId,
|
|
270
|
+
webgpuAvailable: capabilities.available,
|
|
271
|
+
adapterAvailable: capabilities.adapterAvailable,
|
|
272
|
+
shaderF16Supported: capabilities.shaderF16Supported,
|
|
273
|
+
maxBufferSizeMB: capabilities.maxBufferSizeMB,
|
|
274
|
+
maxStorageBufferBindingSizeMB: capabilities.maxStorageBufferBindingSizeMB,
|
|
275
|
+
gpuVendor: capabilities.vendor,
|
|
276
|
+
gpuArchitecture: capabilities.architecture
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
var fireSuggestionInsertedAnalytics = function fireSuggestionInsertedAnalytics(ghostText) {
|
|
281
|
+
var _api$analytics4;
|
|
245
282
|
var typedLength = lastSuggestionTypedLength;
|
|
246
283
|
var suggestionLength = lastSuggestionLength || typedLength + ghostText.length;
|
|
247
284
|
var kssDelta = suggestionLength - typedLength;
|
|
248
|
-
api === null || api === void 0 || (_api$
|
|
285
|
+
api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 || _api$analytics4.actions.fireAnalyticsEvent({
|
|
249
286
|
action: ACTION.SUGGESTION_INSERTED,
|
|
250
287
|
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
251
288
|
eventType: EVENT_TYPE.TRACK,
|
|
@@ -258,7 +295,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
258
295
|
});
|
|
259
296
|
};
|
|
260
297
|
var slowLaneClient = options !== null && options !== void 0 && options.useLocalModel ? createLocalSlowLaneClient({
|
|
261
|
-
debounceMs: LOCAL_SLOW_LANE_DEBOUNCE_MS
|
|
298
|
+
debounceMs: LOCAL_SLOW_LANE_DEBOUNCE_MS,
|
|
299
|
+
onLoadSuccess: fireLocalModelLoadedAnalytics,
|
|
300
|
+
onLoadError: fireLocalModelLoadFailedAnalytics
|
|
262
301
|
}) : createSlowLaneClient({
|
|
263
302
|
baseUrl: '',
|
|
264
303
|
debounceMs: NETWORK_SLOW_LANE_DEBOUNCE_MS
|
|
@@ -412,9 +451,9 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
|
|
|
412
451
|
lastSuggestionLength = typedLength + prediction.length;
|
|
413
452
|
showGhostText(view, prediction, selection.from);
|
|
414
453
|
if (prediction !== lastShownGhostText) {
|
|
415
|
-
var _api$
|
|
454
|
+
var _api$analytics5;
|
|
416
455
|
lastShownGhostText = prediction;
|
|
417
|
-
api === null || api === void 0 || (_api$
|
|
456
|
+
api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 || _api$analytics5.actions.fireAnalyticsEvent({
|
|
418
457
|
action: ACTION.SUGGESTION_VIEWED,
|
|
419
458
|
actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
|
|
420
459
|
eventType: EVENT_TYPE.TRACK,
|