@atlaskit/editor-plugin-autocomplete 2.3.0 → 2.5.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/afm-cc/tsconfig.json +3 -0
  3. package/afm-products/tsconfig.json +3 -0
  4. package/autocompletePlugin/package.json +15 -0
  5. package/autocompletePluginType/package.json +15 -0
  6. package/dist/cjs/analytics/ufo.js +111 -0
  7. package/dist/cjs/entry-points/autocompletePlugin.js +12 -0
  8. package/dist/cjs/entry-points/autocompletePluginType.js +1 -0
  9. package/dist/cjs/entry-points/src-pm-plugins-autocomplete-plugin.js +18 -0
  10. package/dist/cjs/entry-points/src-pm-plugins-slow-lane-client.js +36 -0
  11. package/dist/cjs/entry-points/src-pm-plugins-text-predictor.js +66 -0
  12. package/dist/cjs/pm-plugins/autocomplete-plugin.js +126 -64
  13. package/dist/cjs/pm-plugins/local-slow-lane-client.js +447 -0
  14. package/dist/cjs/pm-plugins/slow-lane-client.js +45 -16
  15. package/dist/cjs/pm-plugins/text-predictor.js +72 -40
  16. package/dist/es2019/analytics/ufo.js +110 -0
  17. package/dist/es2019/entry-points/autocompletePlugin.js +2 -0
  18. package/dist/es2019/entry-points/autocompletePluginType.js +0 -0
  19. package/dist/es2019/entry-points/src-pm-plugins-autocomplete-plugin.js +2 -0
  20. package/dist/es2019/entry-points/src-pm-plugins-slow-lane-client.js +2 -0
  21. package/dist/es2019/entry-points/src-pm-plugins-text-predictor.js +2 -0
  22. package/dist/es2019/pm-plugins/autocomplete-plugin.js +132 -72
  23. package/dist/es2019/pm-plugins/local-slow-lane-client.js +355 -0
  24. package/dist/es2019/pm-plugins/slow-lane-client.js +29 -2
  25. package/dist/es2019/pm-plugins/text-predictor.js +48 -15
  26. package/dist/esm/analytics/ufo.js +105 -0
  27. package/dist/esm/entry-points/autocompletePlugin.js +2 -0
  28. package/dist/esm/entry-points/autocompletePluginType.js +0 -0
  29. package/dist/esm/entry-points/src-pm-plugins-autocomplete-plugin.js +2 -0
  30. package/dist/esm/entry-points/src-pm-plugins-slow-lane-client.js +2 -0
  31. package/dist/esm/entry-points/src-pm-plugins-text-predictor.js +2 -0
  32. package/dist/esm/pm-plugins/autocomplete-plugin.js +126 -64
  33. package/dist/esm/pm-plugins/local-slow-lane-client.js +439 -0
  34. package/dist/esm/pm-plugins/slow-lane-client.js +45 -16
  35. package/dist/esm/pm-plugins/text-predictor.js +73 -40
  36. package/dist/types/analytics/ufo.d.ts +38 -0
  37. package/dist/types/entry-points/autocompletePlugin.d.ts +1 -0
  38. package/dist/types/entry-points/autocompletePluginType.d.ts +1 -0
  39. package/dist/types/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +2 -0
  40. package/dist/types/entry-points/src-pm-plugins-slow-lane-client.d.ts +2 -0
  41. package/dist/types/entry-points/src-pm-plugins-text-predictor.d.ts +2 -0
  42. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +5 -0
  43. package/dist/types/pm-plugins/local-slow-lane-client.d.ts +102 -0
  44. package/dist/types-ts4.5/analytics/ufo.d.ts +38 -0
  45. package/dist/types-ts4.5/entry-points/autocompletePlugin.d.ts +1 -0
  46. package/dist/types-ts4.5/entry-points/autocompletePluginType.d.ts +1 -0
  47. package/dist/types-ts4.5/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +2 -0
  48. package/dist/types-ts4.5/entry-points/src-pm-plugins-slow-lane-client.d.ts +2 -0
  49. package/dist/types-ts4.5/entry-points/src-pm-plugins-text-predictor.d.ts +2 -0
  50. package/dist/types-ts4.5/pm-plugins/autocomplete-plugin.d.ts +5 -0
  51. package/dist/types-ts4.5/pm-plugins/local-slow-lane-client.d.ts +102 -0
  52. package/package.json +8 -3
  53. package/src/analytics/ufo.ts +139 -0
  54. package/src/entry-points/autocompletePlugin.tsx +2 -0
  55. package/src/entry-points/autocompletePluginType.ts +2 -0
  56. package/src/entry-points/src-pm-plugins-autocomplete-plugin.ts +7 -0
  57. package/src/entry-points/src-pm-plugins-slow-lane-client.ts +13 -0
  58. package/src/entry-points/src-pm-plugins-text-predictor.ts +14 -0
  59. package/src/pm-plugins/autocomplete-plugin/package.json +5 -5
  60. package/src/pm-plugins/autocomplete-plugin.ts +126 -64
  61. package/src/pm-plugins/local-slow-lane-client.ts +480 -0
  62. package/src/pm-plugins/slow-lane-client/package.json +5 -5
  63. package/src/pm-plugins/slow-lane-client.ts +28 -2
  64. package/src/pm-plugins/text-predictor/package.json +5 -5
  65. package/src/pm-plugins/text-predictor.ts +42 -12
  66. package/tsconfig.app.json +3 -0
@@ -24,6 +24,8 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
24
24
  * via incrementSessionFreq(), called on word boundaries from the plugin.
25
25
  */
26
26
 
27
+ import { EXPERIENCE_NAME, failExp, startExp, succeedExp } from '../analytics/ufo';
28
+
27
29
  // import bigramsData from './data/bigrams.json';
28
30
  import l3VocabularyData from './data/l3_vocabulary.json';
29
31
  import vocabularyData from './data/vocabulary_10k.json';
@@ -706,38 +708,46 @@ export var loadVectorsAsync = /*#__PURE__*/function () {
706
708
  return _context.abrupt("return");
707
709
  case 5:
708
710
  vectorsLoadStarted = true;
709
- _context.prev = 6;
710
- _context.next = 9;
711
+ startExp(EXPERIENCE_NAME.LOAD_VECTORS, 'singleton');
712
+ _context.prev = 7;
713
+ _context.next = 10;
711
714
  return options.getBinaryUrl();
712
- case 9:
715
+ case 10:
713
716
  url = _context.sent;
714
- _context.next = 17;
717
+ _context.next = 19;
715
718
  break;
716
- case 12:
717
- _context.prev = 12;
718
- _context.t0 = _context["catch"](6);
719
+ case 13:
720
+ _context.prev = 13;
721
+ _context.t0 = _context["catch"](7);
719
722
  vectorsLoadStarted = false;
723
+ failExp(EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
724
+ errorType: 'resolve_url'
725
+ });
720
726
  // eslint-disable-next-line no-console
721
727
  console.warn('[text-predictor] Failed to resolve vectors URL:', _context.t0);
722
728
  return _context.abrupt("return");
723
- case 17:
724
- _context.prev = 17;
725
- _context.next = 20;
729
+ case 19:
730
+ _context.prev = 19;
731
+ _context.next = 22;
726
732
  return fetch(url);
727
- case 20:
733
+ case 22:
728
734
  res = _context.sent;
729
735
  if (res.ok) {
730
- _context.next = 25;
736
+ _context.next = 28;
731
737
  break;
732
738
  }
733
739
  vectorsLoadStarted = false;
740
+ failExp(EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
741
+ status: res.status,
742
+ errorType: 'http_error'
743
+ });
734
744
  // eslint-disable-next-line no-console
735
745
  console.warn("[text-predictor] Failed to load vectors: ".concat(res.status));
736
746
  return _context.abrupt("return");
737
- case 25:
738
- _context.next = 27;
747
+ case 28:
748
+ _context.next = 30;
739
749
  return res.arrayBuffer();
740
- case 27:
750
+ case 30:
741
751
  buffer = _context.sent;
742
752
  float32 = new Float32Array(buffer);
743
753
  wordIndex = wordIndexData;
@@ -748,6 +758,11 @@ export var loadVectorsAsync = /*#__PURE__*/function () {
748
758
  wordIndex: wordIndex,
749
759
  dim: dim
750
760
  };
761
+ succeedExp(EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
762
+ wordCount: nWords,
763
+ dim: dim,
764
+ sizeBytes: float32.byteLength
765
+ });
751
766
  if (isAutocompleteDebugEnabled()) {
752
767
  // eslint-disable-next-line no-console
753
768
  console.log('[text-predictor] Vectors loaded:', {
@@ -756,19 +771,22 @@ export var loadVectorsAsync = /*#__PURE__*/function () {
756
771
  sizeBytes: float32.byteLength
757
772
  });
758
773
  }
759
- _context.next = 40;
774
+ _context.next = 45;
760
775
  break;
761
- case 36:
762
- _context.prev = 36;
763
- _context.t1 = _context["catch"](17);
776
+ case 40:
777
+ _context.prev = 40;
778
+ _context.t1 = _context["catch"](19);
764
779
  vectorsLoadStarted = false;
780
+ failExp(EXPERIENCE_NAME.LOAD_VECTORS, 'singleton', {
781
+ errorType: 'network'
782
+ });
765
783
  // eslint-disable-next-line no-console
766
784
  console.warn('[text-predictor] Failed to load vectors:', _context.t1);
767
- case 40:
785
+ case 45:
768
786
  case "end":
769
787
  return _context.stop();
770
788
  }
771
- }, _callee, null, [[6, 12], [17, 36]]);
789
+ }, _callee, null, [[7, 13], [19, 40]]);
772
790
  }));
773
791
  return function loadVectorsAsync(_x) {
774
792
  return _ref6.apply(this, arguments);
@@ -778,24 +796,39 @@ export var initVectors = function initVectors(store) {
778
796
  vectorStore = store;
779
797
  };
780
798
  export var loadDefaultVocabulary = function loadDefaultVocabulary() {
781
- // 1. Load the Atlassian Domain (L2)
782
- var data = vocabularyData;
783
- var terms = Object.entries(data.words).map(function (_ref7) {
784
- var _ref8 = _slicedToArray(_ref7, 2),
785
- word = _ref8[0],
786
- stats = _ref8[1];
787
- return {
788
- word: word,
789
- freq: stats.freq,
790
- docFreq: stats.doc_freq,
791
- authorFreq: stats.author_freq
792
- };
793
- });
794
- initVocabulary({
795
- terms: terms
796
- });
799
+ if (isInitialized) {
800
+ return;
801
+ }
802
+ startExp(EXPERIENCE_NAME.LOAD_VOCABULARY, 'singleton');
803
+ try {
804
+ // 1. Load the Atlassian Domain (L2)
805
+ var data = vocabularyData;
806
+ var terms = Object.entries(data.words).map(function (_ref7) {
807
+ var _ref8 = _slicedToArray(_ref7, 2),
808
+ word = _ref8[0],
809
+ stats = _ref8[1];
810
+ return {
811
+ word: word,
812
+ freq: stats.freq,
813
+ docFreq: stats.doc_freq,
814
+ authorFreq: stats.author_freq
815
+ };
816
+ });
817
+ initVocabulary({
818
+ terms: terms
819
+ });
797
820
 
798
- // 2. Load General English (L3)
799
- var l3Words = l3VocabularyData;
800
- initL3Vocabulary(l3Words);
821
+ // 2. Load General English (L3)
822
+ var l3Words = l3VocabularyData;
823
+ initL3Vocabulary(l3Words);
824
+ succeedExp(EXPERIENCE_NAME.LOAD_VOCABULARY, 'singleton', {
825
+ l2WordCount: terms.length,
826
+ l3WordCount: l3Words.length
827
+ });
828
+ } catch (e) {
829
+ failExp(EXPERIENCE_NAME.LOAD_VOCABULARY, 'singleton', {
830
+ errorType: 'parse_error'
831
+ });
832
+ throw e;
833
+ }
801
834
  };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * UFO experience tracking helpers for `@atlaskit/editor-plugin-autocomplete`.
3
+ *
4
+ * Pattern follows `packages/linking-platform/smart-card/src/state/analytics/ufoExperiences.ts`
5
+ * (ConcurrentExperience keyed per request/instance) plus a try/catch safety wrap
6
+ * inspired by `packages/editor/collab-provider/src/analytics/ufo.ts` so a UFO
7
+ * runtime error can never break the autocomplete feature.
8
+ *
9
+ * Experience names must be registered in DataPortal's `task` attribute before
10
+ * they can power FE Reliability SLOs:
11
+ * https://hello.atlassian.net/wiki/spaces/AA6/pages/3961393753
12
+ */
13
+ import { type CustomData } from '@atlaskit/ufo';
14
+ /**
15
+ * Experience name strings are surfaced downstream by the UFO pipeline as
16
+ * `platform.fe.<type>.<platform.component>.<name>` (e.g.
17
+ * `platform.fe.operation.editor-plugin-autocomplete.slow-lane-fetch`), so
18
+ * names here should be short, dot-free, and must not repeat the component
19
+ * name. This matches the convention used by `@atlaskit/emoji`'s
20
+ * `ufoExperiences` map (e.g. `'emoji-rendered'`).
21
+ */
22
+ /**
23
+ * Only experiences with meaningful async work and a real success/failure
24
+ * outcome belong on UFO (which measures latency and success rate for FE
25
+ * Reliability SLOs). Per-keystroke suggestion lifecycle counters (viewed /
26
+ * inserted / dismissed) are tracked via analytics-next instead — they would
27
+ * otherwise emit zero-duration events on every word boundary.
28
+ */
29
+ export declare const EXPERIENCE_NAME: {
30
+ readonly SLOW_LANE_FETCH: "slow-lane-fetch";
31
+ readonly LOAD_VOCABULARY: "load-vocabulary";
32
+ readonly LOAD_VECTORS: "load-vectors";
33
+ };
34
+ export type AutocompleteExperienceName = (typeof EXPERIENCE_NAME)[keyof typeof EXPERIENCE_NAME];
35
+ export declare const startExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
36
+ export declare const succeedExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
37
+ export declare const failExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
38
+ export declare const abortExp: (name: AutocompleteExperienceName, id: string, reason?: string) => void;
@@ -0,0 +1 @@
1
+ export { autocompletePlugin } from '../autocompletePlugin';
@@ -0,0 +1 @@
1
+ export type { AutocompletePlugin } from '../autocompletePluginType';
@@ -0,0 +1,2 @@
1
+ export { autocompletePluginKey, createAutocompletePlugin } from '../pm-plugins/autocomplete-plugin';
2
+ export type { AutocompleteContext, AutocompletePluginOptions, AutocompletePluginState, } from '../pm-plugins/autocomplete-plugin';
@@ -0,0 +1,2 @@
1
+ export { createSlowLaneClient, getStoredContextVector, getStoredLmLogits, isWordBoundary, setDefaultSlowLaneClient, } from '../pm-plugins/slow-lane-client';
2
+ export type { SlowLaneClientConfig, TypeaheadEncodingsRequest, TypeaheadEncodingsResponse, } from '../pm-plugins/slow-lane-client';
@@ -0,0 +1,2 @@
1
+ export { getLastPredictionDebug, getPredictorStatus, incrementSessionFreq, ingestDocumentPage, initL3Vocabulary, initVectors, initVocabulary, loadDefaultVocabulary, loadVectorsAsync, predict, } from '../pm-plugins/text-predictor';
2
+ export type { TenantVocabulary, WeightedTerm } from '../pm-plugins/text-predictor';
@@ -40,5 +40,10 @@ 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
+ * When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
45
+ * the network-based slow-lane backend. Defaults to false (network client).
46
+ */
47
+ useLocalModel?: boolean;
43
48
  }
44
49
  export declare const createAutocompletePlugin: (options?: AutocompletePluginOptions, api?: ExtractInjectionAPI<AutocompletePlugin>) => SafePlugin<AutocompletePluginState>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Local Slow Lane Client: On-device inference via @mlc-ai/web-llm.
3
+ *
4
+ * Drop-in replacement for the network-based slow-lane-client. Instead of
5
+ * calling a backend API, this client uses MLC WebLLM to run a small language
6
+ * model (SmolLM 135M) directly in the browser via WebGPU.
7
+ *
8
+ * ── Why main thread (no Web Worker)? ─────────────────────────────────────
9
+ * SmolLM 135M is small enough (~270 MB weights, 350-400 MB VRAM) that
10
+ * WebGPU inference on the main thread is production-viable:
11
+ *
12
+ * - WebGPU GPU compute is inherently async (doesn't block the main thread)
13
+ * - CPU overhead (tokenization + post-processing) is only 5-10 ms
14
+ * - Single forward pass latency is 50-150 ms — well within autocomplete
15
+ * expectations (~250 ms between word boundaries)
16
+ *
17
+ * This avoids all the complexity of Web Workers:
18
+ * - No CSP workarounds (blob URLs, inline scripts)
19
+ * - No bundler configuration (worker-plugin, import.meta.url)
20
+ * - No message passing protocol
21
+ * - Standard npm import — just works
22
+ *
23
+ * ── Interface ────────────────────────────────────────────────────────────
24
+ * Same shape as createSlowLaneClient so text-predictor.ts needs zero changes.
25
+ * The client exposes getContextVector() and getLmLogits() which are populated
26
+ * asynchronously after each updateContext() call.
27
+ */
28
+ export interface LocalSlowLaneClientConfig {
29
+ /**
30
+ * Optional custom model registration for models not in web-llm's
31
+ * built-in list. When provided, the model is appended to the app
32
+ * config before engine creation.
33
+ */
34
+ customModelConfig?: {
35
+ /** Context window size override (optional) */
36
+ contextWindowSize?: number;
37
+ /** HuggingFace URL to the model weights (e.g. "https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-add-basics-q0f16-MLC") */
38
+ model: string;
39
+ /** URL to the compiled WASM library for this model architecture */
40
+ modelLib: string;
41
+ /** VRAM required in MB (optional, for resource planning) */
42
+ vramRequiredMB?: number;
43
+ };
44
+ /** Debounce interval in ms before sending context for inference. */
45
+ debounceMs?: number;
46
+ /**
47
+ * MLC model identifier.
48
+ * Defaults to the built-in SmolLM2-135M-Instruct-q0f16-MLC.
49
+ *
50
+ * To use a custom HuggingFace model, provide both `modelId` and
51
+ * `customModelConfig` with the model URL and WASM library URL.
52
+ */
53
+ modelId?: string;
54
+ /** Callback fired with status messages (model loading progress, etc.). */
55
+ onStatus?: (message: string) => void;
56
+ /** Callback fired when inference returns new results. */
57
+ onUpdate?: (opts: {
58
+ hasLmLogits: boolean;
59
+ hasVector: boolean;
60
+ textLength: number;
61
+ }) => void;
62
+ }
63
+ export interface LocalSlowLaneClient {
64
+ /** Clean up resources. */
65
+ destroy: () => void;
66
+ getContextVector: () => Float32Array | null;
67
+ getLmLogits: () => Record<string, number> | null;
68
+ /** Whether the model is loaded and ready for inference. */
69
+ isReady: () => boolean;
70
+ isWordBoundary: (text: string) => boolean;
71
+ setContextVector: (vector: Float32Array | null) => void;
72
+ setLmLogits: (logits: Record<string, number> | null) => void;
73
+ updateContext: (text: string) => void;
74
+ }
75
+ export declare const LOCAL_MLC_MODEL_ID = "SmolLM2-135M-Instruct-q0f16-MLC";
76
+ /** HF root for the default weights (includes `tensor-cache.json` for WebLLM 0.2+). */
77
+ export declare const LOCAL_MLC_HF_MODEL_REPO = "https://huggingface.co/mlc-ai/SmolLM2-135M-Instruct-q0f16-MLC";
78
+ export declare const LOCAL_MLC_MODEL_LIB_WASM_NAME = "SmolLM2-135M-Instruct-q0f16-ctx4k_cs1k-webgpu.wasm";
79
+ /**
80
+ * Original target repo (add-basics fine-tune). **Not compatible with WebLLM 0.2.x** (no `tensor-cache.json`).
81
+ * @see module doc above
82
+ */
83
+ export declare const HUGGINGFACE_TB_SMOLLM_ADD_BASICS_REPO = "https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-add-basics-q0f16-MLC";
84
+ /**
85
+ * Create a local slow-lane client powered by MLC WebLLM.
86
+ *
87
+ * The engine is initialised lazily — model weights are downloaded (and cached
88
+ * in IndexedDB) on first use. Subsequent page loads skip the download.
89
+ *
90
+ * Usage:
91
+ * ```ts
92
+ * const client = createLocalSlowLaneClient({ debounceMs: 300 });
93
+ * // On word boundaries:
94
+ * client.updateContext(docText);
95
+ * // In scoring pipeline:
96
+ * const vec = client.getContextVector();
97
+ * const logits = client.getLmLogits();
98
+ * // On plugin teardown:
99
+ * client.destroy();
100
+ * ```
101
+ */
102
+ export declare const createLocalSlowLaneClient: (config?: LocalSlowLaneClientConfig) => LocalSlowLaneClient;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * UFO experience tracking helpers for `@atlaskit/editor-plugin-autocomplete`.
3
+ *
4
+ * Pattern follows `packages/linking-platform/smart-card/src/state/analytics/ufoExperiences.ts`
5
+ * (ConcurrentExperience keyed per request/instance) plus a try/catch safety wrap
6
+ * inspired by `packages/editor/collab-provider/src/analytics/ufo.ts` so a UFO
7
+ * runtime error can never break the autocomplete feature.
8
+ *
9
+ * Experience names must be registered in DataPortal's `task` attribute before
10
+ * they can power FE Reliability SLOs:
11
+ * https://hello.atlassian.net/wiki/spaces/AA6/pages/3961393753
12
+ */
13
+ import { type CustomData } from '@atlaskit/ufo';
14
+ /**
15
+ * Experience name strings are surfaced downstream by the UFO pipeline as
16
+ * `platform.fe.<type>.<platform.component>.<name>` (e.g.
17
+ * `platform.fe.operation.editor-plugin-autocomplete.slow-lane-fetch`), so
18
+ * names here should be short, dot-free, and must not repeat the component
19
+ * name. This matches the convention used by `@atlaskit/emoji`'s
20
+ * `ufoExperiences` map (e.g. `'emoji-rendered'`).
21
+ */
22
+ /**
23
+ * Only experiences with meaningful async work and a real success/failure
24
+ * outcome belong on UFO (which measures latency and success rate for FE
25
+ * Reliability SLOs). Per-keystroke suggestion lifecycle counters (viewed /
26
+ * inserted / dismissed) are tracked via analytics-next instead — they would
27
+ * otherwise emit zero-duration events on every word boundary.
28
+ */
29
+ export declare const EXPERIENCE_NAME: {
30
+ readonly SLOW_LANE_FETCH: "slow-lane-fetch";
31
+ readonly LOAD_VOCABULARY: "load-vocabulary";
32
+ readonly LOAD_VECTORS: "load-vectors";
33
+ };
34
+ export type AutocompleteExperienceName = (typeof EXPERIENCE_NAME)[keyof typeof EXPERIENCE_NAME];
35
+ export declare const startExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
36
+ export declare const succeedExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
37
+ export declare const failExp: (name: AutocompleteExperienceName, id: string, metadata?: CustomData) => void;
38
+ export declare const abortExp: (name: AutocompleteExperienceName, id: string, reason?: string) => void;
@@ -0,0 +1 @@
1
+ export { autocompletePlugin } from '../autocompletePlugin';
@@ -0,0 +1 @@
1
+ export type { AutocompletePlugin } from '../autocompletePluginType';
@@ -0,0 +1,2 @@
1
+ export { autocompletePluginKey, createAutocompletePlugin } from '../pm-plugins/autocomplete-plugin';
2
+ export type { AutocompleteContext, AutocompletePluginOptions, AutocompletePluginState, } from '../pm-plugins/autocomplete-plugin';
@@ -0,0 +1,2 @@
1
+ export { createSlowLaneClient, getStoredContextVector, getStoredLmLogits, isWordBoundary, setDefaultSlowLaneClient, } from '../pm-plugins/slow-lane-client';
2
+ export type { SlowLaneClientConfig, TypeaheadEncodingsRequest, TypeaheadEncodingsResponse, } from '../pm-plugins/slow-lane-client';
@@ -0,0 +1,2 @@
1
+ export { getLastPredictionDebug, getPredictorStatus, incrementSessionFreq, ingestDocumentPage, initL3Vocabulary, initVectors, initVocabulary, loadDefaultVocabulary, loadVectorsAsync, predict, } from '../pm-plugins/text-predictor';
2
+ export type { TenantVocabulary, WeightedTerm } from '../pm-plugins/text-predictor';
@@ -40,5 +40,10 @@ 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
+ * When true, uses on-device inference via WebGPU (MLC WebLLM) instead of
45
+ * the network-based slow-lane backend. Defaults to false (network client).
46
+ */
47
+ useLocalModel?: boolean;
43
48
  }
44
49
  export declare const createAutocompletePlugin: (options?: AutocompletePluginOptions, api?: ExtractInjectionAPI<AutocompletePlugin>) => SafePlugin<AutocompletePluginState>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Local Slow Lane Client: On-device inference via @mlc-ai/web-llm.
3
+ *
4
+ * Drop-in replacement for the network-based slow-lane-client. Instead of
5
+ * calling a backend API, this client uses MLC WebLLM to run a small language
6
+ * model (SmolLM 135M) directly in the browser via WebGPU.
7
+ *
8
+ * ── Why main thread (no Web Worker)? ─────────────────────────────────────
9
+ * SmolLM 135M is small enough (~270 MB weights, 350-400 MB VRAM) that
10
+ * WebGPU inference on the main thread is production-viable:
11
+ *
12
+ * - WebGPU GPU compute is inherently async (doesn't block the main thread)
13
+ * - CPU overhead (tokenization + post-processing) is only 5-10 ms
14
+ * - Single forward pass latency is 50-150 ms — well within autocomplete
15
+ * expectations (~250 ms between word boundaries)
16
+ *
17
+ * This avoids all the complexity of Web Workers:
18
+ * - No CSP workarounds (blob URLs, inline scripts)
19
+ * - No bundler configuration (worker-plugin, import.meta.url)
20
+ * - No message passing protocol
21
+ * - Standard npm import — just works
22
+ *
23
+ * ── Interface ────────────────────────────────────────────────────────────
24
+ * Same shape as createSlowLaneClient so text-predictor.ts needs zero changes.
25
+ * The client exposes getContextVector() and getLmLogits() which are populated
26
+ * asynchronously after each updateContext() call.
27
+ */
28
+ export interface LocalSlowLaneClientConfig {
29
+ /**
30
+ * Optional custom model registration for models not in web-llm's
31
+ * built-in list. When provided, the model is appended to the app
32
+ * config before engine creation.
33
+ */
34
+ customModelConfig?: {
35
+ /** Context window size override (optional) */
36
+ contextWindowSize?: number;
37
+ /** HuggingFace URL to the model weights (e.g. "https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-add-basics-q0f16-MLC") */
38
+ model: string;
39
+ /** URL to the compiled WASM library for this model architecture */
40
+ modelLib: string;
41
+ /** VRAM required in MB (optional, for resource planning) */
42
+ vramRequiredMB?: number;
43
+ };
44
+ /** Debounce interval in ms before sending context for inference. */
45
+ debounceMs?: number;
46
+ /**
47
+ * MLC model identifier.
48
+ * Defaults to the built-in SmolLM2-135M-Instruct-q0f16-MLC.
49
+ *
50
+ * To use a custom HuggingFace model, provide both `modelId` and
51
+ * `customModelConfig` with the model URL and WASM library URL.
52
+ */
53
+ modelId?: string;
54
+ /** Callback fired with status messages (model loading progress, etc.). */
55
+ onStatus?: (message: string) => void;
56
+ /** Callback fired when inference returns new results. */
57
+ onUpdate?: (opts: {
58
+ hasLmLogits: boolean;
59
+ hasVector: boolean;
60
+ textLength: number;
61
+ }) => void;
62
+ }
63
+ export interface LocalSlowLaneClient {
64
+ /** Clean up resources. */
65
+ destroy: () => void;
66
+ getContextVector: () => Float32Array | null;
67
+ getLmLogits: () => Record<string, number> | null;
68
+ /** Whether the model is loaded and ready for inference. */
69
+ isReady: () => boolean;
70
+ isWordBoundary: (text: string) => boolean;
71
+ setContextVector: (vector: Float32Array | null) => void;
72
+ setLmLogits: (logits: Record<string, number> | null) => void;
73
+ updateContext: (text: string) => void;
74
+ }
75
+ export declare const LOCAL_MLC_MODEL_ID = "SmolLM2-135M-Instruct-q0f16-MLC";
76
+ /** HF root for the default weights (includes `tensor-cache.json` for WebLLM 0.2+). */
77
+ export declare const LOCAL_MLC_HF_MODEL_REPO = "https://huggingface.co/mlc-ai/SmolLM2-135M-Instruct-q0f16-MLC";
78
+ export declare const LOCAL_MLC_MODEL_LIB_WASM_NAME = "SmolLM2-135M-Instruct-q0f16-ctx4k_cs1k-webgpu.wasm";
79
+ /**
80
+ * Original target repo (add-basics fine-tune). **Not compatible with WebLLM 0.2.x** (no `tensor-cache.json`).
81
+ * @see module doc above
82
+ */
83
+ export declare const HUGGINGFACE_TB_SMOLLM_ADD_BASICS_REPO = "https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-add-basics-q0f16-MLC";
84
+ /**
85
+ * Create a local slow-lane client powered by MLC WebLLM.
86
+ *
87
+ * The engine is initialised lazily — model weights are downloaded (and cached
88
+ * in IndexedDB) on first use. Subsequent page loads skip the download.
89
+ *
90
+ * Usage:
91
+ * ```ts
92
+ * const client = createLocalSlowLaneClient({ debounceMs: 300 });
93
+ * // On word boundaries:
94
+ * client.updateContext(docText);
95
+ * // In scoring pipeline:
96
+ * const vec = client.getContextVector();
97
+ * const logits = client.getLmLogits();
98
+ * // On plugin teardown:
99
+ * client.destroy();
100
+ * ```
101
+ */
102
+ export declare const createLocalSlowLaneClient: (config?: LocalSlowLaneClientConfig) => LocalSlowLaneClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-autocomplete",
3
- "version": "2.3.0",
3
+ "version": "2.5.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",
@@ -28,13 +28,15 @@
28
28
  "atlaskit:src": "src/index.ts",
29
29
  "dependencies": {
30
30
  "@atlaskit/editor-prosemirror": "^7.3.0",
31
+ "@atlaskit/ufo": "^0.5.0",
31
32
  "@babel/runtime": "^7.0.0",
33
+ "@mlc-ai/web-llm": "^0.2.78",
32
34
  "compromise": "^14.15.0",
33
35
  "wink-nlp": "^2.4.0"
34
36
  },
35
37
  "peerDependencies": {
36
- "@atlaskit/editor-common": "^114.22.0",
37
- "@atlaskit/editor-plugin-analytics": "^10.0.0",
38
+ "@atlaskit/editor-common": "^114.43.0",
39
+ "@atlaskit/editor-plugin-analytics": "^10.1.0",
38
40
  "react": "^18.2.0"
39
41
  },
40
42
  "techstack": {
@@ -49,5 +51,8 @@
49
51
  "file-and-folder-level"
50
52
  ]
51
53
  }
54
+ },
55
+ "devDependencies": {
56
+ "react": "^18.2.0"
52
57
  }
53
58
  }