@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
|
@@ -71,6 +71,10 @@ export interface LocalSlowLaneClientConfig {
|
|
|
71
71
|
* `customModelConfig` with the model URL and WASM library URL.
|
|
72
72
|
*/
|
|
73
73
|
modelId?: string;
|
|
74
|
+
/** Callback fired when the engine fails to load/start. */
|
|
75
|
+
onLoadError?: (error: LocalSlowLaneLoadError) => void;
|
|
76
|
+
/** Callback fired when the engine successfully loads and is ready. */
|
|
77
|
+
onLoadSuccess?: (info: LocalSlowLaneLoadSuccess) => void;
|
|
74
78
|
/** Callback fired with status messages (model loading progress, etc.). */
|
|
75
79
|
onStatus?: (message: string) => void;
|
|
76
80
|
/** Callback fired when inference returns new results. */
|
|
@@ -91,6 +95,80 @@ export interface LocalSlowLaneClient {
|
|
|
91
95
|
updateContext: (text: string) => void;
|
|
92
96
|
}
|
|
93
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Why the local engine failed to load/start.
|
|
100
|
+
*
|
|
101
|
+
* The first three are user-machine limitations (WebGPU missing, no compatible
|
|
102
|
+
* GPU adapter, GPU lacks the `shader-f16` feature the model needs);
|
|
103
|
+
* `insufficient_memory` is hit when weights don't fit in VRAM. The rest cover
|
|
104
|
+
* delivery/runtime failures unrelated to hardware.
|
|
105
|
+
*/
|
|
106
|
+
export type LocalSlowLaneLoadErrorReason =
|
|
107
|
+
| 'webgpu_unavailable'
|
|
108
|
+
| 'webgpu_no_adapter'
|
|
109
|
+
| 'missing_shader_f16'
|
|
110
|
+
| 'insufficient_memory'
|
|
111
|
+
| 'model_download_failed'
|
|
112
|
+
| 'module_load_failed'
|
|
113
|
+
| 'init_failed';
|
|
114
|
+
|
|
115
|
+
/** Snapshot of the machine's WebGPU support, used to explain hardware limits. */
|
|
116
|
+
export interface WebGpuCapabilities {
|
|
117
|
+
/** GPU architecture reported by the adapter (e.g. "metal-3", "rdna2"). */
|
|
118
|
+
architecture?: string;
|
|
119
|
+
/** Whether `navigator.gpu.requestAdapter()` returned a usable adapter. */
|
|
120
|
+
adapterAvailable?: boolean;
|
|
121
|
+
/** Whether `navigator.gpu` exists at all. */
|
|
122
|
+
available: boolean;
|
|
123
|
+
/** Largest single GPU buffer the adapter allows, in MB. */
|
|
124
|
+
maxBufferSizeMB?: number;
|
|
125
|
+
/** Largest storage-buffer binding the adapter allows, in MB. */
|
|
126
|
+
maxStorageBufferBindingSizeMB?: number;
|
|
127
|
+
/** Whether the adapter exposes the `shader-f16` feature the model requires. */
|
|
128
|
+
shaderF16Supported?: boolean;
|
|
129
|
+
/** GPU vendor reported by the adapter (e.g. "apple", "intel"). */
|
|
130
|
+
vendor?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface LocalSlowLaneLoadError {
|
|
134
|
+
/** WebGPU support snapshot — explains hardware limitations behind the failure. */
|
|
135
|
+
capabilities: WebGpuCapabilities;
|
|
136
|
+
/** Semantic embedder loaded alongside the causal LM (loads atomically). */
|
|
137
|
+
embeddingModelId: string;
|
|
138
|
+
/** Canonical, controlled failure description (never raw error text). */
|
|
139
|
+
message: string;
|
|
140
|
+
/** Causal LM identifier that failed to load. */
|
|
141
|
+
modelId: string;
|
|
142
|
+
/** Coarse, privacy-safe failure category. */
|
|
143
|
+
reason: LocalSlowLaneLoadErrorReason;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface LocalSlowLaneLoadSuccess {
|
|
147
|
+
/** WebGPU support snapshot for the machine that loaded the model. */
|
|
148
|
+
capabilities: WebGpuCapabilities;
|
|
149
|
+
/** Semantic embedder loaded alongside the causal LM (loads atomically). */
|
|
150
|
+
embeddingModelId: string;
|
|
151
|
+
/** Model engine load time in ms (excludes the WebGPU capability probe). */
|
|
152
|
+
loadDurationMs: number;
|
|
153
|
+
/** Causal LM identifier that loaded. */
|
|
154
|
+
modelId: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Minimal WebGPU shape: lib.dom types aren't guaranteed in this build target.
|
|
158
|
+
interface MinimalGpuAdapterInfo {
|
|
159
|
+
architecture?: string;
|
|
160
|
+
vendor?: string;
|
|
161
|
+
}
|
|
162
|
+
interface MinimalGpuAdapter {
|
|
163
|
+
features: { has: (feature: string) => boolean };
|
|
164
|
+
info?: MinimalGpuAdapterInfo;
|
|
165
|
+
limits?: { maxBufferSize?: number; maxStorageBufferBindingSize?: number };
|
|
166
|
+
requestAdapterInfo?: () => Promise<MinimalGpuAdapterInfo>;
|
|
167
|
+
}
|
|
168
|
+
interface MinimalGpu {
|
|
169
|
+
requestAdapter: () => Promise<MinimalGpuAdapter | null>;
|
|
170
|
+
}
|
|
171
|
+
|
|
94
172
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
95
173
|
|
|
96
174
|
const DEFAULT_DEBOUNCE_MS = 300;
|
|
@@ -584,6 +662,8 @@ export const createLocalSlowLaneClient = (
|
|
|
584
662
|
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
585
663
|
onUpdate,
|
|
586
664
|
onStatus,
|
|
665
|
+
onLoadError,
|
|
666
|
+
onLoadSuccess,
|
|
587
667
|
modelId = LOCAL_MLC_CAUSAL_MODEL_ID,
|
|
588
668
|
customModelConfig,
|
|
589
669
|
} = config;
|
|
@@ -641,7 +721,142 @@ export const createLocalSlowLaneClient = (
|
|
|
641
721
|
onStatus?.(message);
|
|
642
722
|
};
|
|
643
723
|
|
|
724
|
+
const bytesToMB = (bytes?: number): number | undefined =>
|
|
725
|
+
typeof bytes === 'number' ? Math.round(bytes / (1024 * 1024)) : undefined;
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Inspect the machine's WebGPU support so a load failure can be attributed
|
|
729
|
+
* to a concrete hardware/browser limitation rather than a generic error.
|
|
730
|
+
*/
|
|
731
|
+
const probeWebGpuCapabilities = async (): Promise<WebGpuCapabilities> => {
|
|
732
|
+
const gpu = (navigator as Navigator & { gpu?: MinimalGpu }).gpu;
|
|
733
|
+
if (!gpu) {
|
|
734
|
+
return { available: false };
|
|
735
|
+
}
|
|
736
|
+
try {
|
|
737
|
+
const adapter = await gpu.requestAdapter();
|
|
738
|
+
if (!adapter) {
|
|
739
|
+
return { available: true, adapterAvailable: false };
|
|
740
|
+
}
|
|
741
|
+
let vendor: string | undefined;
|
|
742
|
+
let architecture: string | undefined;
|
|
743
|
+
try {
|
|
744
|
+
const info = adapter.info ?? (await adapter.requestAdapterInfo?.());
|
|
745
|
+
vendor = info?.vendor || undefined;
|
|
746
|
+
architecture = info?.architecture || undefined;
|
|
747
|
+
} catch {
|
|
748
|
+
// adapter info is best-effort
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
available: true,
|
|
752
|
+
adapterAvailable: true,
|
|
753
|
+
shaderF16Supported: adapter.features.has('shader-f16'),
|
|
754
|
+
maxBufferSizeMB: bytesToMB(adapter.limits?.maxBufferSize),
|
|
755
|
+
maxStorageBufferBindingSizeMB: bytesToMB(adapter.limits?.maxStorageBufferBindingSize),
|
|
756
|
+
vendor,
|
|
757
|
+
architecture,
|
|
758
|
+
};
|
|
759
|
+
} catch {
|
|
760
|
+
return { available: true, adapterAvailable: false };
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
/** Map an MLC/WebLLM engine-creation error message to a coarse reason. */
|
|
765
|
+
const classifyEngineError = (message: string): LocalSlowLaneLoadErrorReason => {
|
|
766
|
+
const lower = message.toLowerCase();
|
|
767
|
+
if (
|
|
768
|
+
lower.includes('loading chunk') ||
|
|
769
|
+
lower.includes('dynamically imported module') ||
|
|
770
|
+
lower.includes('dynamic import')
|
|
771
|
+
) {
|
|
772
|
+
return 'module_load_failed';
|
|
773
|
+
}
|
|
774
|
+
if (
|
|
775
|
+
lower.includes('out of memory') ||
|
|
776
|
+
/\boom\b/u.test(lower) ||
|
|
777
|
+
lower.includes('allocation') ||
|
|
778
|
+
lower.includes('exceeds') ||
|
|
779
|
+
lower.includes('buffer size') ||
|
|
780
|
+
lower.includes('not enough memory')
|
|
781
|
+
) {
|
|
782
|
+
return 'insufficient_memory';
|
|
783
|
+
}
|
|
784
|
+
// Pre-flight already returns missing_shader_f16 when the feature is absent,
|
|
785
|
+
// so only match the exact feature token here — not bare 'shader' (compile
|
|
786
|
+
// errors) or bare 'f16' (present in model ids like q0f16-MLC).
|
|
787
|
+
if (lower.includes('shader-f16') || lower.includes('shader_f16')) {
|
|
788
|
+
return 'missing_shader_f16';
|
|
789
|
+
}
|
|
790
|
+
if (
|
|
791
|
+
lower.includes('fetch') ||
|
|
792
|
+
lower.includes('network') ||
|
|
793
|
+
lower.includes('download') ||
|
|
794
|
+
lower.includes('http') ||
|
|
795
|
+
lower.includes('cache')
|
|
796
|
+
) {
|
|
797
|
+
return 'model_download_failed';
|
|
798
|
+
}
|
|
799
|
+
return 'init_failed';
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
// Canonical, controlled failure descriptions. We never emit the raw engine
|
|
803
|
+
// error into analytics — it can embed customer-context URLs/paths (HOT-120175)
|
|
804
|
+
// — so the analytics `message` is always one of these fixed strings.
|
|
805
|
+
const LOAD_FAILURE_MESSAGE: Record<LocalSlowLaneLoadErrorReason, string> = {
|
|
806
|
+
webgpu_unavailable: 'WebGPU is not available in this browser',
|
|
807
|
+
webgpu_no_adapter: 'No compatible WebGPU adapter found',
|
|
808
|
+
missing_shader_f16: 'GPU does not support the shader-f16 feature',
|
|
809
|
+
insufficient_memory: 'Insufficient GPU memory to load the model',
|
|
810
|
+
model_download_failed: 'Failed to download model assets',
|
|
811
|
+
module_load_failed: 'Failed to load the web-llm runtime module',
|
|
812
|
+
init_failed: 'Model engine failed to initialise',
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
const handleLoadFailure = (
|
|
816
|
+
reason: LocalSlowLaneLoadErrorReason,
|
|
817
|
+
capabilities: WebGpuCapabilities,
|
|
818
|
+
// Raw engine error — local debug logging only, never sent to analytics.
|
|
819
|
+
debugDetail?: string,
|
|
820
|
+
): void => {
|
|
821
|
+
ready = false;
|
|
822
|
+
const message = LOAD_FAILURE_MESSAGE[reason];
|
|
823
|
+
if (isAutocompleteDebugEnabled()) {
|
|
824
|
+
// eslint-disable-next-line no-console
|
|
825
|
+
console.log(
|
|
826
|
+
`[LocalSlowLane] Engine initialisation failed (${reason}): ${debugDetail ?? message}`,
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
onStatus?.(`Engine initialisation failed: ${message}`);
|
|
830
|
+
onLoadError?.({
|
|
831
|
+
reason,
|
|
832
|
+
message,
|
|
833
|
+
modelId,
|
|
834
|
+
embeddingModelId: LOCAL_MLC_EMBEDDING_MODEL_ID,
|
|
835
|
+
capabilities,
|
|
836
|
+
});
|
|
837
|
+
engineInitPromise = null;
|
|
838
|
+
initFailed = true;
|
|
839
|
+
};
|
|
840
|
+
|
|
644
841
|
const initEngine = async (): Promise<void> => {
|
|
842
|
+
const capabilities = await probeWebGpuCapabilities();
|
|
843
|
+
|
|
844
|
+
// ── Pre-flight: machine limitations short-circuit before the expensive load ──
|
|
845
|
+
if (!capabilities.available) {
|
|
846
|
+
handleLoadFailure('webgpu_unavailable', capabilities);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (capabilities.adapterAvailable === false) {
|
|
850
|
+
handleLoadFailure('webgpu_no_adapter', capabilities);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (capabilities.shaderF16Supported === false) {
|
|
854
|
+
handleLoadFailure('missing_shader_f16', capabilities);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const startTime = performance.now();
|
|
859
|
+
|
|
645
860
|
try {
|
|
646
861
|
if (isAutocompleteDebugEnabled()) {
|
|
647
862
|
// eslint-disable-next-line no-console
|
|
@@ -653,10 +868,6 @@ export const createLocalSlowLaneClient = (
|
|
|
653
868
|
}
|
|
654
869
|
onStatus?.(`Initialising models: ${modelId} + ${LOCAL_MLC_EMBEDDING_MODEL_ID}…`);
|
|
655
870
|
|
|
656
|
-
if (!('gpu' in navigator)) {
|
|
657
|
-
throw new Error('WebGPU not supported');
|
|
658
|
-
}
|
|
659
|
-
|
|
660
871
|
// Fetch the web-llm runtime and the BE-parity lookup tables in parallel;
|
|
661
872
|
// both are dynamically imported so they stay out of the main editor chunk.
|
|
662
873
|
const [{ MLCEngine: MLCEngineCtor, prebuiltAppConfig }] = await Promise.all([
|
|
@@ -714,6 +925,7 @@ export const createLocalSlowLaneClient = (
|
|
|
714
925
|
|
|
715
926
|
engine = newEngine;
|
|
716
927
|
ready = true;
|
|
928
|
+
const loadDurationMs = Math.round(performance.now() - startTime);
|
|
717
929
|
|
|
718
930
|
if (isAutocompleteDebugEnabled()) {
|
|
719
931
|
// eslint-disable-next-line no-console
|
|
@@ -740,16 +952,15 @@ export const createLocalSlowLaneClient = (
|
|
|
740
952
|
);
|
|
741
953
|
}
|
|
742
954
|
onStatus?.('Model loaded and ready.');
|
|
955
|
+
onLoadSuccess?.({
|
|
956
|
+
modelId,
|
|
957
|
+
embeddingModelId: LOCAL_MLC_EMBEDDING_MODEL_ID,
|
|
958
|
+
loadDurationMs,
|
|
959
|
+
capabilities,
|
|
960
|
+
});
|
|
743
961
|
} catch (err) {
|
|
744
962
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
745
|
-
|
|
746
|
-
if (isAutocompleteDebugEnabled()) {
|
|
747
|
-
// eslint-disable-next-line no-console
|
|
748
|
-
console.log(`[LocalSlowLane] Engine initialisation failed: ${errorMsg}`);
|
|
749
|
-
}
|
|
750
|
-
onStatus?.(`Engine initialisation failed: ${errorMsg}`);
|
|
751
|
-
engineInitPromise = null;
|
|
752
|
-
initFailed = true;
|
|
963
|
+
handleLoadFailure(classifyEngineError(errorMsg), capabilities, errorMsg);
|
|
753
964
|
}
|
|
754
965
|
};
|
|
755
966
|
|