@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.
@@ -55,6 +55,19 @@ import { isWordBoundary } from './slow-lane-client';
55
55
 
56
56
  // Same return type as createSlowLaneClient for drop-in compatibility
57
57
 
58
+ /**
59
+ * Why the local engine failed to load/start.
60
+ *
61
+ * The first three are user-machine limitations (WebGPU missing, no compatible
62
+ * GPU adapter, GPU lacks the `shader-f16` feature the model needs);
63
+ * `insufficient_memory` is hit when weights don't fit in VRAM. The rest cover
64
+ * delivery/runtime failures unrelated to hardware.
65
+ */
66
+
67
+ /** Snapshot of the machine's WebGPU support, used to explain hardware limits. */
68
+
69
+ // Minimal WebGPU shape: lib.dom types aren't guaranteed in this build target.
70
+
58
71
  // ─── Constants ───────────────────────────────────────────────────────────────
59
72
 
60
73
  var DEFAULT_DEBOUNCE_MS = 300;
@@ -601,6 +614,8 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
601
614
  debounceMs = _config$debounceMs === void 0 ? DEFAULT_DEBOUNCE_MS : _config$debounceMs,
602
615
  onUpdate = config.onUpdate,
603
616
  onStatus = config.onStatus,
617
+ onLoadError = config.onLoadError,
618
+ onLoadSuccess = config.onLoadSuccess,
604
619
  _config$modelId = config.modelId,
605
620
  modelId = _config$modelId === void 0 ? LOCAL_MLC_CAUSAL_MODEL_ID : _config$modelId,
606
621
  customModelConfig = config.customModelConfig;
@@ -647,28 +662,189 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
647
662
  }
648
663
  onStatus === null || onStatus === void 0 || onStatus(message);
649
664
  };
650
- var initEngine = /*#__PURE__*/function () {
665
+ var bytesToMB = function bytesToMB(bytes) {
666
+ return typeof bytes === 'number' ? Math.round(bytes / (1024 * 1024)) : undefined;
667
+ };
668
+
669
+ /**
670
+ * Inspect the machine's WebGPU support so a load failure can be attributed
671
+ * to a concrete hardware/browser limitation rather than a generic error.
672
+ */
673
+ var probeWebGpuCapabilities = /*#__PURE__*/function () {
651
674
  var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
652
- var _yield$Promise$all3, _yield$Promise$all4, _yield$Promise$all4$, MLCEngineCtor, prebuiltAppConfig, customModelRecord, appConfig, newEngine, errorMsg, _t;
675
+ var gpu, _adapter$limits, _adapter$limits2, adapter, vendor, architecture, _adapter$info, _adapter$requestAdapt, info, _t, _t2, _t3;
653
676
  return _regeneratorRuntime.wrap(function (_context2) {
654
677
  while (1) switch (_context2.prev = _context2.next) {
655
678
  case 0:
656
- _context2.prev = 0;
657
- if (isAutocompleteDebugEnabled()) {
658
- // eslint-disable-next-line no-console
659
- console.log("%c[LocalSlowLane] %c\uD83D\uDE80 Initialising MLC engine with models: ".concat(modelId, " (LM) + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, " (embedder)"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
660
- }
661
- onStatus === null || onStatus === void 0 || onStatus("Initialising models: ".concat(modelId, " + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, "\u2026"));
662
- if ('gpu' in navigator) {
679
+ gpu = navigator.gpu;
680
+ if (gpu) {
663
681
  _context2.next = 1;
664
682
  break;
665
683
  }
666
- throw new Error('WebGPU not supported');
684
+ return _context2.abrupt("return", {
685
+ available: false
686
+ });
667
687
  case 1:
688
+ _context2.prev = 1;
668
689
  _context2.next = 2;
669
- return Promise.all([import( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-mlc-web-llm" */'@mlc-ai/web-llm'), loadBePayloadData()]);
690
+ return gpu.requestAdapter();
691
+ case 2:
692
+ adapter = _context2.sent;
693
+ if (adapter) {
694
+ _context2.next = 3;
695
+ break;
696
+ }
697
+ return _context2.abrupt("return", {
698
+ available: true,
699
+ adapterAvailable: false
700
+ });
701
+ case 3:
702
+ _context2.prev = 3;
703
+ if (!((_adapter$info = adapter.info) !== null && _adapter$info !== void 0)) {
704
+ _context2.next = 4;
705
+ break;
706
+ }
707
+ _t = _adapter$info;
708
+ _context2.next = 6;
709
+ break;
710
+ case 4:
711
+ _context2.next = 5;
712
+ return (_adapter$requestAdapt = adapter.requestAdapterInfo) === null || _adapter$requestAdapt === void 0 ? void 0 : _adapter$requestAdapt.call(adapter);
713
+ case 5:
714
+ _t = _context2.sent;
715
+ case 6:
716
+ info = _t;
717
+ vendor = (info === null || info === void 0 ? void 0 : info.vendor) || undefined;
718
+ architecture = (info === null || info === void 0 ? void 0 : info.architecture) || undefined;
719
+ _context2.next = 8;
720
+ break;
721
+ case 7:
722
+ _context2.prev = 7;
723
+ _t2 = _context2["catch"](3);
724
+ case 8:
725
+ return _context2.abrupt("return", {
726
+ available: true,
727
+ adapterAvailable: true,
728
+ shaderF16Supported: adapter.features.has('shader-f16'),
729
+ maxBufferSizeMB: bytesToMB((_adapter$limits = adapter.limits) === null || _adapter$limits === void 0 ? void 0 : _adapter$limits.maxBufferSize),
730
+ maxStorageBufferBindingSizeMB: bytesToMB((_adapter$limits2 = adapter.limits) === null || _adapter$limits2 === void 0 ? void 0 : _adapter$limits2.maxStorageBufferBindingSize),
731
+ vendor: vendor,
732
+ architecture: architecture
733
+ });
734
+ case 9:
735
+ _context2.prev = 9;
736
+ _t3 = _context2["catch"](1);
737
+ return _context2.abrupt("return", {
738
+ available: true,
739
+ adapterAvailable: false
740
+ });
741
+ case 10:
742
+ case "end":
743
+ return _context2.stop();
744
+ }
745
+ }, _callee2, null, [[1, 9], [3, 7]]);
746
+ }));
747
+ return function probeWebGpuCapabilities() {
748
+ return _ref8.apply(this, arguments);
749
+ };
750
+ }();
751
+
752
+ /** Map an MLC/WebLLM engine-creation error message to a coarse reason. */
753
+ var classifyEngineError = function classifyEngineError(message) {
754
+ var lower = message.toLowerCase();
755
+ if (lower.includes('loading chunk') || lower.includes('dynamically imported module') || lower.includes('dynamic import')) {
756
+ return 'module_load_failed';
757
+ }
758
+ if (lower.includes('out of memory') || /\boom\b/.test(lower) || lower.includes('allocation') || lower.includes('exceeds') || lower.includes('buffer size') || lower.includes('not enough memory')) {
759
+ return 'insufficient_memory';
760
+ }
761
+ // Pre-flight already returns missing_shader_f16 when the feature is absent,
762
+ // so only match the exact feature token here — not bare 'shader' (compile
763
+ // errors) or bare 'f16' (present in model ids like q0f16-MLC).
764
+ if (lower.includes('shader-f16') || lower.includes('shader_f16')) {
765
+ return 'missing_shader_f16';
766
+ }
767
+ if (lower.includes('fetch') || lower.includes('network') || lower.includes('download') || lower.includes('http') || lower.includes('cache')) {
768
+ return 'model_download_failed';
769
+ }
770
+ return 'init_failed';
771
+ };
772
+
773
+ // Canonical, controlled failure descriptions. We never emit the raw engine
774
+ // error into analytics — it can embed customer-context URLs/paths (HOT-120175)
775
+ // — so the analytics `message` is always one of these fixed strings.
776
+ var LOAD_FAILURE_MESSAGE = {
777
+ webgpu_unavailable: 'WebGPU is not available in this browser',
778
+ webgpu_no_adapter: 'No compatible WebGPU adapter found',
779
+ missing_shader_f16: 'GPU does not support the shader-f16 feature',
780
+ insufficient_memory: 'Insufficient GPU memory to load the model',
781
+ model_download_failed: 'Failed to download model assets',
782
+ module_load_failed: 'Failed to load the web-llm runtime module',
783
+ init_failed: 'Model engine failed to initialise'
784
+ };
785
+ var handleLoadFailure = function handleLoadFailure(reason, capabilities, debugDetail) {
786
+ ready = false;
787
+ var message = LOAD_FAILURE_MESSAGE[reason];
788
+ if (isAutocompleteDebugEnabled()) {
789
+ // eslint-disable-next-line no-console
790
+ console.log("[LocalSlowLane] Engine initialisation failed (".concat(reason, "): ").concat(debugDetail !== null && debugDetail !== void 0 ? debugDetail : message));
791
+ }
792
+ onStatus === null || onStatus === void 0 || onStatus("Engine initialisation failed: ".concat(message));
793
+ onLoadError === null || onLoadError === void 0 || onLoadError({
794
+ reason: reason,
795
+ message: message,
796
+ modelId: modelId,
797
+ embeddingModelId: LOCAL_MLC_EMBEDDING_MODEL_ID,
798
+ capabilities: capabilities
799
+ });
800
+ engineInitPromise = null;
801
+ initFailed = true;
802
+ };
803
+ var initEngine = /*#__PURE__*/function () {
804
+ var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
805
+ var capabilities, startTime, _yield$Promise$all3, _yield$Promise$all4, _yield$Promise$all4$, MLCEngineCtor, prebuiltAppConfig, customModelRecord, appConfig, newEngine, loadDurationMs, errorMsg, _t4;
806
+ return _regeneratorRuntime.wrap(function (_context3) {
807
+ while (1) switch (_context3.prev = _context3.next) {
808
+ case 0:
809
+ _context3.next = 1;
810
+ return probeWebGpuCapabilities();
811
+ case 1:
812
+ capabilities = _context3.sent;
813
+ if (capabilities.available) {
814
+ _context3.next = 2;
815
+ break;
816
+ }
817
+ handleLoadFailure('webgpu_unavailable', capabilities);
818
+ return _context3.abrupt("return");
670
819
  case 2:
671
- _yield$Promise$all3 = _context2.sent;
820
+ if (!(capabilities.adapterAvailable === false)) {
821
+ _context3.next = 3;
822
+ break;
823
+ }
824
+ handleLoadFailure('webgpu_no_adapter', capabilities);
825
+ return _context3.abrupt("return");
826
+ case 3:
827
+ if (!(capabilities.shaderF16Supported === false)) {
828
+ _context3.next = 4;
829
+ break;
830
+ }
831
+ handleLoadFailure('missing_shader_f16', capabilities);
832
+ return _context3.abrupt("return");
833
+ case 4:
834
+ startTime = performance.now();
835
+ _context3.prev = 5;
836
+ if (isAutocompleteDebugEnabled()) {
837
+ // eslint-disable-next-line no-console
838
+ console.log("%c[LocalSlowLane] %c\uD83D\uDE80 Initialising MLC engine with models: ".concat(modelId, " (LM) + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, " (embedder)"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
839
+ }
840
+ onStatus === null || onStatus === void 0 || onStatus("Initialising models: ".concat(modelId, " + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, "\u2026"));
841
+
842
+ // Fetch the web-llm runtime and the BE-parity lookup tables in parallel;
843
+ // both are dynamically imported so they stay out of the main editor chunk.
844
+ _context3.next = 6;
845
+ return Promise.all([import( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-mlc-web-llm" */'@mlc-ai/web-llm'), loadBePayloadData()]);
846
+ case 6:
847
+ _yield$Promise$all3 = _context3.sent;
672
848
  _yield$Promise$all4 = _slicedToArray(_yield$Promise$all3, 1);
673
849
  _yield$Promise$all4$ = _yield$Promise$all4[0];
674
850
  MLCEngineCtor = _yield$Promise$all4$.MLCEngine;
@@ -696,19 +872,20 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
696
872
  initProgressCallback: initProgressCallback,
697
873
  logitProcessorRegistry: new Map([[modelId, lmLogitsCapture]])
698
874
  });
699
- _context2.next = 3;
875
+ _context3.next = 7;
700
876
  return newEngine.reload([modelId, LOCAL_MLC_EMBEDDING_MODEL_ID]);
701
- case 3:
877
+ case 7:
702
878
  if (!destroyed) {
703
- _context2.next = 4;
879
+ _context3.next = 8;
704
880
  break;
705
881
  }
706
882
  // destroy() was called while we were loading — clean up
707
883
  unloadEngine(newEngine);
708
- return _context2.abrupt("return");
709
- case 4:
884
+ return _context3.abrupt("return");
885
+ case 8:
710
886
  engine = newEngine;
711
887
  ready = true;
888
+ loadDurationMs = Math.round(performance.now() - startTime);
712
889
  if (isAutocompleteDebugEnabled()) {
713
890
  // eslint-disable-next-line no-console
714
891
  console.log('%c[LocalSlowLane] %c✅ Both models loaded and ready', 'color: #9c27b0; font-weight: bold;', 'color: #4caf50;');
@@ -720,28 +897,27 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
720
897
  console.log('%c[LocalSlowLane] %c🔢 Embedder →', 'color: #9c27b0; font-weight: bold;', 'color: #009688; font-weight: bold;', LOCAL_MLC_EMBEDDING_MODEL_ID);
721
898
  }
722
899
  onStatus === null || onStatus === void 0 || onStatus('Model loaded and ready.');
723
- _context2.next = 6;
900
+ onLoadSuccess === null || onLoadSuccess === void 0 || onLoadSuccess({
901
+ modelId: modelId,
902
+ embeddingModelId: LOCAL_MLC_EMBEDDING_MODEL_ID,
903
+ loadDurationMs: loadDurationMs,
904
+ capabilities: capabilities
905
+ });
906
+ _context3.next = 10;
724
907
  break;
725
- case 5:
726
- _context2.prev = 5;
727
- _t = _context2["catch"](0);
728
- errorMsg = _t instanceof Error ? _t.message : String(_t);
729
- ready = false;
730
- if (isAutocompleteDebugEnabled()) {
731
- // eslint-disable-next-line no-console
732
- console.log("[LocalSlowLane] Engine initialisation failed: ".concat(errorMsg));
733
- }
734
- onStatus === null || onStatus === void 0 || onStatus("Engine initialisation failed: ".concat(errorMsg));
735
- engineInitPromise = null;
736
- initFailed = true;
737
- case 6:
908
+ case 9:
909
+ _context3.prev = 9;
910
+ _t4 = _context3["catch"](5);
911
+ errorMsg = _t4 instanceof Error ? _t4.message : String(_t4);
912
+ handleLoadFailure(classifyEngineError(errorMsg), capabilities, errorMsg);
913
+ case 10:
738
914
  case "end":
739
- return _context2.stop();
915
+ return _context3.stop();
740
916
  }
741
- }, _callee2, null, [[0, 5]]);
917
+ }, _callee3, null, [[5, 9]]);
742
918
  }));
743
919
  return function initEngine() {
744
- return _ref8.apply(this, arguments);
920
+ return _ref9.apply(this, arguments);
745
921
  };
746
922
  }();
747
923
  var ensureEngineInitialized = function ensureEngineInitialized() {
@@ -768,16 +944,16 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
768
944
  * 384-d semantic vector (passage-encoded; see `wrapForArctic`).
769
945
  */
770
946
  var runInference = /*#__PURE__*/function () {
771
- var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(text, requestId) {
772
- var lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t2;
773
- return _regeneratorRuntime.wrap(function (_context3) {
774
- while (1) switch (_context3.prev = _context3.next) {
947
+ var _ref0 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(text, requestId) {
948
+ var lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t5;
949
+ return _regeneratorRuntime.wrap(function (_context4) {
950
+ while (1) switch (_context4.prev = _context4.next) {
775
951
  case 0:
776
952
  if (!(!engine || destroyed)) {
777
- _context3.next = 1;
953
+ _context4.next = 1;
778
954
  break;
779
955
  }
780
- return _context3.abrupt("return");
956
+ return _context4.abrupt("return");
781
957
  case 1:
782
958
  // Clear the capture buffer so we read only this pass's logits. The engine
783
959
  // serialises per-model requests and updateContext is debounced, so the
@@ -804,11 +980,11 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
804
980
  // eslint-disable-next-line no-console
805
981
  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;');
806
982
  }
807
- _context3.prev = 2;
983
+ _context4.prev = 2;
808
984
  tStart = performance.now();
809
985
  tLmDone = 0;
810
986
  tEmbDone = 0;
811
- _context3.next = 3;
987
+ _context4.next = 3;
812
988
  return Promise.all([captureCompletionTime(engine.completions.create({
813
989
  model: modelId,
814
990
  prompt: lmText,
@@ -824,7 +1000,7 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
824
1000
  tEmbDone = resolvedAt;
825
1001
  })]);
826
1002
  case 3:
827
- _yield$Promise$all5 = _context3.sent;
1003
+ _yield$Promise$all5 = _context4.sent;
828
1004
  _yield$Promise$all6 = _slicedToArray(_yield$Promise$all5, 2);
829
1005
  embeddingResponse = _yield$Promise$all6[1];
830
1006
  if (isAutocompleteDebugEnabled()) {
@@ -834,10 +1010,10 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
834
1010
 
835
1011
  // Discard stale results
836
1012
  if (!(requestId < latestRequestId || destroyed)) {
837
- _context3.next = 4;
1013
+ _context4.next = 4;
838
1014
  break;
839
1015
  }
840
- return _context3.abrupt("return");
1016
+ return _context4.abrupt("return");
841
1017
  case 4:
842
1018
  // ── LM logits: whole-word BE-parity payload ──────────────────
843
1019
  rawLogits = lmLogitsCapture.captured;
@@ -871,17 +1047,17 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
871
1047
  // eslint-disable-next-line no-console
872
1048
  console.log(storedLmLogits ? "\u2705 lm_logits: ".concat(Object.keys(storedLmLogits).length, " words") : '❌ No lm_logits');
873
1049
  if (storedLmLogits) {
874
- topTokens = Object.entries(storedLmLogits).sort(function (_ref0, _ref1) {
875
- var _ref10 = _slicedToArray(_ref0, 2),
876
- a = _ref10[1];
1050
+ topTokens = Object.entries(storedLmLogits).sort(function (_ref1, _ref10) {
877
1051
  var _ref11 = _slicedToArray(_ref1, 2),
878
- b = _ref11[1];
1052
+ a = _ref11[1];
1053
+ var _ref12 = _slicedToArray(_ref10, 2),
1054
+ b = _ref12[1];
879
1055
  return b - a;
880
1056
  }).slice(0, 10); // eslint-disable-next-line no-console
881
- console.log('Top 10 predictions:', topTokens.map(function (_ref12) {
882
- var _ref13 = _slicedToArray(_ref12, 2),
883
- t = _ref13[0],
884
- p = _ref13[1];
1057
+ console.log('Top 10 predictions:', topTokens.map(function (_ref13) {
1058
+ var _ref14 = _slicedToArray(_ref13, 2),
1059
+ t = _ref14[0],
1060
+ p = _ref14[1];
885
1061
  return "".concat(t, ": ").concat((p * 100).toFixed(1), "%");
886
1062
  }).join(', '));
887
1063
  }
@@ -893,16 +1069,16 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
893
1069
  hasVector: storedContextVector !== null,
894
1070
  hasLmLogits: storedLmLogits !== null
895
1071
  });
896
- _context3.next = 7;
1072
+ _context4.next = 7;
897
1073
  break;
898
1074
  case 5:
899
- _context3.prev = 5;
900
- _t2 = _context3["catch"](2);
1075
+ _context4.prev = 5;
1076
+ _t5 = _context4["catch"](2);
901
1077
  if (!(requestId < latestRequestId || destroyed)) {
902
- _context3.next = 6;
1078
+ _context4.next = 6;
903
1079
  break;
904
1080
  }
905
- return _context3.abrupt("return");
1081
+ return _context4.abrupt("return");
906
1082
  case 6:
907
1083
  storedContextVector = null;
908
1084
  storedLmLogits = null;
@@ -911,19 +1087,19 @@ export var createLocalSlowLaneClient = function createLocalSlowLaneClient() {
911
1087
  hasVector: false,
912
1088
  hasLmLogits: false
913
1089
  });
914
- errorMsg = _t2 instanceof Error ? _t2.message : String(_t2);
1090
+ errorMsg = _t5 instanceof Error ? _t5.message : String(_t5);
915
1091
  if (isAutocompleteDebugEnabled()) {
916
1092
  // eslint-disable-next-line no-console
917
1093
  console.log("%c[LocalSlowLane] %c\u274C Inference error (request #".concat(requestId, "): ").concat(errorMsg), 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
918
1094
  }
919
1095
  case 7:
920
1096
  case "end":
921
- return _context3.stop();
1097
+ return _context4.stop();
922
1098
  }
923
- }, _callee3, null, [[2, 5]]);
1099
+ }, _callee4, null, [[2, 5]]);
924
1100
  }));
925
1101
  return function runInference(_x, _x2) {
926
- return _ref9.apply(this, arguments);
1102
+ return _ref0.apply(this, arguments);
927
1103
  };
928
1104
  }();
929
1105
 
@@ -60,6 +60,10 @@ export interface LocalSlowLaneClientConfig {
60
60
  * `customModelConfig` with the model URL and WASM library URL.
61
61
  */
62
62
  modelId?: string;
63
+ /** Callback fired when the engine fails to load/start. */
64
+ onLoadError?: (error: LocalSlowLaneLoadError) => void;
65
+ /** Callback fired when the engine successfully loads and is ready. */
66
+ onLoadSuccess?: (info: LocalSlowLaneLoadSuccess) => void;
63
67
  /** Callback fired with status messages (model loading progress, etc.). */
64
68
  onStatus?: (message: string) => void;
65
69
  /** Callback fired when inference returns new results. */
@@ -81,6 +85,54 @@ export interface LocalSlowLaneClient {
81
85
  setLmLogits: (logits: Record<string, number> | null) => void;
82
86
  updateContext: (text: string) => void;
83
87
  }
88
+ /**
89
+ * Why the local engine failed to load/start.
90
+ *
91
+ * The first three are user-machine limitations (WebGPU missing, no compatible
92
+ * GPU adapter, GPU lacks the `shader-f16` feature the model needs);
93
+ * `insufficient_memory` is hit when weights don't fit in VRAM. The rest cover
94
+ * delivery/runtime failures unrelated to hardware.
95
+ */
96
+ export type LocalSlowLaneLoadErrorReason = 'webgpu_unavailable' | 'webgpu_no_adapter' | 'missing_shader_f16' | 'insufficient_memory' | 'model_download_failed' | 'module_load_failed' | 'init_failed';
97
+ /** Snapshot of the machine's WebGPU support, used to explain hardware limits. */
98
+ export interface WebGpuCapabilities {
99
+ /** GPU architecture reported by the adapter (e.g. "metal-3", "rdna2"). */
100
+ architecture?: string;
101
+ /** Whether `navigator.gpu.requestAdapter()` returned a usable adapter. */
102
+ adapterAvailable?: boolean;
103
+ /** Whether `navigator.gpu` exists at all. */
104
+ available: boolean;
105
+ /** Largest single GPU buffer the adapter allows, in MB. */
106
+ maxBufferSizeMB?: number;
107
+ /** Largest storage-buffer binding the adapter allows, in MB. */
108
+ maxStorageBufferBindingSizeMB?: number;
109
+ /** Whether the adapter exposes the `shader-f16` feature the model requires. */
110
+ shaderF16Supported?: boolean;
111
+ /** GPU vendor reported by the adapter (e.g. "apple", "intel"). */
112
+ vendor?: string;
113
+ }
114
+ export interface LocalSlowLaneLoadError {
115
+ /** WebGPU support snapshot — explains hardware limitations behind the failure. */
116
+ capabilities: WebGpuCapabilities;
117
+ /** Semantic embedder loaded alongside the causal LM (loads atomically). */
118
+ embeddingModelId: string;
119
+ /** Canonical, controlled failure description (never raw error text). */
120
+ message: string;
121
+ /** Causal LM identifier that failed to load. */
122
+ modelId: string;
123
+ /** Coarse, privacy-safe failure category. */
124
+ reason: LocalSlowLaneLoadErrorReason;
125
+ }
126
+ export interface LocalSlowLaneLoadSuccess {
127
+ /** WebGPU support snapshot for the machine that loaded the model. */
128
+ capabilities: WebGpuCapabilities;
129
+ /** Semantic embedder loaded alongside the causal LM (loads atomically). */
130
+ embeddingModelId: string;
131
+ /** Model engine load time in ms (excludes the WebGPU capability probe). */
132
+ loadDurationMs: number;
133
+ /** Causal LM identifier that loaded. */
134
+ modelId: string;
135
+ }
84
136
  export declare const LOCAL_MLC_CAUSAL_MODEL_ID = "SmolLM2-135M-Instruct-q0f16-MLC";
85
137
  /**
86
138
  * MLC ID for the semantic embedder (Snowflake Arctic Embed S, batch=4 variant).
@@ -60,6 +60,10 @@ export interface LocalSlowLaneClientConfig {
60
60
  * `customModelConfig` with the model URL and WASM library URL.
61
61
  */
62
62
  modelId?: string;
63
+ /** Callback fired when the engine fails to load/start. */
64
+ onLoadError?: (error: LocalSlowLaneLoadError) => void;
65
+ /** Callback fired when the engine successfully loads and is ready. */
66
+ onLoadSuccess?: (info: LocalSlowLaneLoadSuccess) => void;
63
67
  /** Callback fired with status messages (model loading progress, etc.). */
64
68
  onStatus?: (message: string) => void;
65
69
  /** Callback fired when inference returns new results. */
@@ -81,6 +85,54 @@ export interface LocalSlowLaneClient {
81
85
  setLmLogits: (logits: Record<string, number> | null) => void;
82
86
  updateContext: (text: string) => void;
83
87
  }
88
+ /**
89
+ * Why the local engine failed to load/start.
90
+ *
91
+ * The first three are user-machine limitations (WebGPU missing, no compatible
92
+ * GPU adapter, GPU lacks the `shader-f16` feature the model needs);
93
+ * `insufficient_memory` is hit when weights don't fit in VRAM. The rest cover
94
+ * delivery/runtime failures unrelated to hardware.
95
+ */
96
+ export type LocalSlowLaneLoadErrorReason = 'webgpu_unavailable' | 'webgpu_no_adapter' | 'missing_shader_f16' | 'insufficient_memory' | 'model_download_failed' | 'module_load_failed' | 'init_failed';
97
+ /** Snapshot of the machine's WebGPU support, used to explain hardware limits. */
98
+ export interface WebGpuCapabilities {
99
+ /** GPU architecture reported by the adapter (e.g. "metal-3", "rdna2"). */
100
+ architecture?: string;
101
+ /** Whether `navigator.gpu.requestAdapter()` returned a usable adapter. */
102
+ adapterAvailable?: boolean;
103
+ /** Whether `navigator.gpu` exists at all. */
104
+ available: boolean;
105
+ /** Largest single GPU buffer the adapter allows, in MB. */
106
+ maxBufferSizeMB?: number;
107
+ /** Largest storage-buffer binding the adapter allows, in MB. */
108
+ maxStorageBufferBindingSizeMB?: number;
109
+ /** Whether the adapter exposes the `shader-f16` feature the model requires. */
110
+ shaderF16Supported?: boolean;
111
+ /** GPU vendor reported by the adapter (e.g. "apple", "intel"). */
112
+ vendor?: string;
113
+ }
114
+ export interface LocalSlowLaneLoadError {
115
+ /** WebGPU support snapshot — explains hardware limitations behind the failure. */
116
+ capabilities: WebGpuCapabilities;
117
+ /** Semantic embedder loaded alongside the causal LM (loads atomically). */
118
+ embeddingModelId: string;
119
+ /** Canonical, controlled failure description (never raw error text). */
120
+ message: string;
121
+ /** Causal LM identifier that failed to load. */
122
+ modelId: string;
123
+ /** Coarse, privacy-safe failure category. */
124
+ reason: LocalSlowLaneLoadErrorReason;
125
+ }
126
+ export interface LocalSlowLaneLoadSuccess {
127
+ /** WebGPU support snapshot for the machine that loaded the model. */
128
+ capabilities: WebGpuCapabilities;
129
+ /** Semantic embedder loaded alongside the causal LM (loads atomically). */
130
+ embeddingModelId: string;
131
+ /** Model engine load time in ms (excludes the WebGPU capability probe). */
132
+ loadDurationMs: number;
133
+ /** Causal LM identifier that loaded. */
134
+ modelId: string;
135
+ }
84
136
  export declare const LOCAL_MLC_CAUSAL_MODEL_ID = "SmolLM2-135M-Instruct-q0f16-MLC";
85
137
  /**
86
138
  * MLC ID for the semantic embedder (Snowflake Arctic Embed S, batch=4 variant).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-autocomplete",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -35,7 +35,7 @@
35
35
  "wink-nlp": "^2.4.0"
36
36
  },
37
37
  "peerDependencies": {
38
- "@atlaskit/editor-common": "^115.8.0",
38
+ "@atlaskit/editor-common": "^115.15.0",
39
39
  "@atlaskit/editor-plugin-analytics": "^11.0.0",
40
40
  "react": "^18.2.0"
41
41
  },
@@ -16,7 +16,12 @@ import type { AutocompletePlugin } from '../autocompletePluginType';
16
16
 
17
17
  import { isAutocompleteDebugEnabled } from './debug-mode';
18
18
  import { createGhostTextDecorationSet } from './ghost-text-decoration';
19
- import { createLocalSlowLaneClient, type LocalSlowLaneClient } from './local-slow-lane-client';
19
+ import {
20
+ createLocalSlowLaneClient,
21
+ type LocalSlowLaneClient,
22
+ type LocalSlowLaneLoadError,
23
+ type LocalSlowLaneLoadSuccess,
24
+ } from './local-slow-lane-client';
20
25
  import { createSlowLaneClient, setDefaultSlowLaneClient, isWordBoundary } from './slow-lane-client';
21
26
  import {
22
27
  predict,
@@ -298,6 +303,43 @@ export const createAutocompletePlugin = (
298
303
  });
299
304
  };
300
305
 
306
+ const fireLocalModelLoadedAnalytics = (info: LocalSlowLaneLoadSuccess): void => {
307
+ api?.analytics?.actions.fireAnalyticsEvent({
308
+ action: ACTION.LOCAL_MODEL_LOADED,
309
+ actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
310
+ eventType: EVENT_TYPE.TRACK,
311
+ attributes: {
312
+ modelId: info.modelId,
313
+ embeddingModelId: info.embeddingModelId,
314
+ loadDurationMs: info.loadDurationMs,
315
+ gpuVendor: info.capabilities.vendor,
316
+ gpuArchitecture: info.capabilities.architecture,
317
+ },
318
+ });
319
+ };
320
+
321
+ const fireLocalModelLoadFailedAnalytics = (error: LocalSlowLaneLoadError): void => {
322
+ const { capabilities } = error;
323
+ api?.analytics?.actions.fireAnalyticsEvent({
324
+ action: ACTION.LOCAL_MODEL_LOAD_FAILED,
325
+ actionSubject: ACTION_SUBJECT.CONTEXTUAL_TYPEAHEAD,
326
+ eventType: EVENT_TYPE.TRACK,
327
+ attributes: {
328
+ reason: error.reason,
329
+ message: error.message,
330
+ modelId: error.modelId,
331
+ embeddingModelId: error.embeddingModelId,
332
+ webgpuAvailable: capabilities.available,
333
+ adapterAvailable: capabilities.adapterAvailable,
334
+ shaderF16Supported: capabilities.shaderF16Supported,
335
+ maxBufferSizeMB: capabilities.maxBufferSizeMB,
336
+ maxStorageBufferBindingSizeMB: capabilities.maxStorageBufferBindingSizeMB,
337
+ gpuVendor: capabilities.vendor,
338
+ gpuArchitecture: capabilities.architecture,
339
+ },
340
+ });
341
+ };
342
+
301
343
  const fireSuggestionInsertedAnalytics = (ghostText: string): void => {
302
344
  const typedLength = lastSuggestionTypedLength;
303
345
  const suggestionLength = lastSuggestionLength || typedLength + ghostText.length;
@@ -319,6 +361,8 @@ export const createAutocompletePlugin = (
319
361
  const slowLaneClient = options?.useLocalModel
320
362
  ? createLocalSlowLaneClient({
321
363
  debounceMs: LOCAL_SLOW_LANE_DEBOUNCE_MS,
364
+ onLoadSuccess: fireLocalModelLoadedAnalytics,
365
+ onLoadError: fireLocalModelLoadFailedAnalytics,
322
366
  })
323
367
  : createSlowLaneClient({
324
368
  baseUrl: '',