@atlaskit/editor-plugin-autocomplete 8.0.0 → 9.0.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 (79) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/cjs/analytics/ufo.js +2 -1
  3. package/dist/cjs/pm-plugins/artifact-loader.js +135 -0
  4. package/dist/cjs/pm-plugins/artifacts-manifest.js +3 -0
  5. package/dist/cjs/pm-plugins/autocomplete-plugin.js +416 -129
  6. package/dist/cjs/pm-plugins/canonical-lm-scoring.js +371 -0
  7. package/dist/cjs/pm-plugins/debug-mode.js +90 -7
  8. package/dist/cjs/pm-plugins/ghost-text-decoration.js +12 -3
  9. package/dist/cjs/pm-plugins/local-slow-lane-client.js +1202 -453
  10. package/dist/cjs/pm-plugins/scoring-pipeline.js +164 -19
  11. package/dist/cjs/pm-plugins/slow-lane-client.js +100 -37
  12. package/dist/cjs/pm-plugins/text-predictor.js +1951 -273
  13. package/dist/es2019/analytics/ufo.js +2 -0
  14. package/dist/es2019/pm-plugins/artifact-loader.js +70 -0
  15. package/dist/es2019/pm-plugins/artifacts-manifest.js +3 -0
  16. package/dist/es2019/pm-plugins/autocomplete-plugin.js +425 -130
  17. package/dist/es2019/pm-plugins/canonical-lm-scoring.js +270 -0
  18. package/dist/es2019/pm-plugins/debug-mode.js +87 -7
  19. package/dist/es2019/pm-plugins/ghost-text-decoration.js +12 -3
  20. package/dist/es2019/pm-plugins/local-slow-lane-client.js +940 -326
  21. package/dist/es2019/pm-plugins/scoring-pipeline.js +137 -13
  22. package/dist/es2019/pm-plugins/slow-lane-client.js +94 -37
  23. package/dist/es2019/pm-plugins/text-predictor.js +1603 -199
  24. package/dist/esm/analytics/ufo.js +2 -1
  25. package/dist/esm/pm-plugins/artifact-loader.js +128 -0
  26. package/dist/esm/pm-plugins/artifacts-manifest.js +3 -0
  27. package/dist/esm/pm-plugins/autocomplete-plugin.js +419 -132
  28. package/dist/esm/pm-plugins/canonical-lm-scoring.js +364 -0
  29. package/dist/esm/pm-plugins/debug-mode.js +89 -6
  30. package/dist/esm/pm-plugins/ghost-text-decoration.js +12 -3
  31. package/dist/esm/pm-plugins/local-slow-lane-client.js +1200 -451
  32. package/dist/esm/pm-plugins/scoring-pipeline.js +163 -18
  33. package/dist/esm/pm-plugins/slow-lane-client.js +100 -37
  34. package/dist/esm/pm-plugins/text-predictor.js +1953 -277
  35. package/dist/types/analytics/ufo.d.ts +1 -0
  36. package/dist/types/pm-plugins/artifact-loader.d.ts +44 -0
  37. package/dist/types/pm-plugins/artifacts-manifest.d.ts +3 -0
  38. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +39 -0
  39. package/dist/types/pm-plugins/canonical-lm-scoring.d.ts +158 -0
  40. package/dist/types/pm-plugins/debug-mode.d.ts +36 -1
  41. package/dist/types/pm-plugins/ghost-text-decoration.d.ts +5 -2
  42. package/dist/types/pm-plugins/local-slow-lane-client.d.ts +87 -54
  43. package/dist/types/pm-plugins/scoring-pipeline.d.ts +33 -1
  44. package/dist/types/pm-plugins/slow-lane-client.d.ts +51 -3
  45. package/dist/types/pm-plugins/text-predictor.d.ts +120 -2
  46. package/package.json +2 -2
  47. package/src/analytics/ufo.ts +5 -0
  48. package/src/pm-plugins/artifact-loader.ts +92 -0
  49. package/src/pm-plugins/artifacts-manifest.ts +3 -0
  50. package/src/pm-plugins/autocomplete-plugin.ts +517 -133
  51. package/src/pm-plugins/canonical-lm-scoring.ts +412 -0
  52. package/src/pm-plugins/debug-mode.ts +109 -7
  53. package/src/pm-plugins/ghost-text-decoration.ts +13 -4
  54. package/src/pm-plugins/local-slow-lane-client.ts +1302 -386
  55. package/src/pm-plugins/scoring-pipeline.ts +167 -17
  56. package/src/pm-plugins/slow-lane-client.ts +147 -62
  57. package/src/pm-plugins/text-predictor.ts +2006 -255
  58. package/CANONICAL_FIX__DO_NOT_USE_ME_A/package.json +0 -8
  59. package/CANONICAL_FIX__DO_NOT_USE_ME_B/package.json +0 -8
  60. package/CANONICAL_FIX__DO_NOT_USE_ME_C/package.json +0 -8
  61. package/dist/cjs/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -18
  62. package/dist/cjs/entry-points/src-pm-plugins-slow-lane-client.js +0 -36
  63. package/dist/cjs/entry-points/src-pm-plugins-text-predictor.js +0 -66
  64. package/dist/es2019/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  65. package/dist/es2019/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  66. package/dist/es2019/entry-points/src-pm-plugins-text-predictor.js +0 -2
  67. package/dist/esm/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  68. package/dist/esm/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  69. package/dist/esm/entry-points/src-pm-plugins-text-predictor.js +0 -2
  70. package/dist/types/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +0 -2
  71. package/dist/types/entry-points/src-pm-plugins-slow-lane-client.d.ts +0 -2
  72. package/dist/types/entry-points/src-pm-plugins-text-predictor.d.ts +0 -2
  73. package/scripts/gen_first_token_to_words.py +0 -170
  74. package/src/entry-points/src-pm-plugins-autocomplete-plugin.ts +0 -7
  75. package/src/entry-points/src-pm-plugins-slow-lane-client.ts +0 -13
  76. package/src/entry-points/src-pm-plugins-text-predictor.ts +0 -14
  77. package/src/pm-plugins/autocomplete-plugin/package.json +0 -8
  78. package/src/pm-plugins/slow-lane-client/package.json +0 -8
  79. package/src/pm-plugins/text-predictor/package.json +0 -8
@@ -6,6 +6,7 @@
6
6
  * Request: { text, session_id }
7
7
  * Response: { semantic_vector: number[], lm_logits: Record<string, number> }
8
8
  */
9
+ import type { BoundaryLmState, BoundaryPrimeRequest, ProgressiveSurfaceEvidence, SurfaceScore, SurfaceScoreRequest } from './canonical-lm-scoring';
9
10
  /** Request payload for typeahead-encodings endpoint. */
10
11
  export interface TypeaheadEncodingsRequest {
11
12
  session_id: string;
@@ -34,14 +35,61 @@ export interface SlowLaneClientConfig {
34
35
  sessionId?: string;
35
36
  surface?: string;
36
37
  }
37
- export declare const createSlowLaneClient: (config: SlowLaneClientConfig) => {
38
+ export interface SlowLaneClient {
39
+ getBoundaryLmState?: (contextKey: string) => BoundaryLmState | null;
40
+ getCanonicalSurfaceCount?: () => number;
41
+ getCanonicalSurfaceTokenIds?: (surface: string) => number[] | null;
42
+ getContextInput?: () => string | null;
38
43
  getContextVector: () => Float32Array | null;
39
44
  getLmLogits: () => Record<string, number> | null;
45
+ getProgressiveSurfaceEvidence?: (contextKey: string, surface: string) => ProgressiveSurfaceEvidence | null;
46
+ getSurfaceScore?: (contextKey: string, surface: string) => SurfaceScore | null;
47
+ isReady?: () => boolean;
40
48
  isWordBoundary: (text: string) => boolean;
49
+ primeBoundaryLm?: (input: BoundaryPrimeRequest) => void;
50
+ requestProgressiveSurfaceScores?: (input: SurfaceScoreRequest) => void;
41
51
  setContextVector: (vector: Float32Array | null) => void;
42
52
  setLmLogits: (logits: Record<string, number> | null) => void;
43
53
  updateContext: (text: string) => void;
44
- };
45
- export declare const setDefaultSlowLaneClient: (client: ReturnType<typeof createSlowLaneClient> | null) => void;
54
+ }
55
+ export declare const createSlowLaneClient: (config: SlowLaneClientConfig) => SlowLaneClient;
56
+ export type SlowLaneClientKind = 'localLlm' | 'server';
57
+ export interface SlowLaneClientRegistration {
58
+ clientId: string;
59
+ kind: SlowLaneClientKind;
60
+ }
61
+ export interface SlowLaneClientStatus {
62
+ canonicalScoringSupported: boolean;
63
+ canonicalSurfaceCount: number | null;
64
+ clientId: string | null;
65
+ clientKind: SlowLaneClientKind | null;
66
+ localModelReady: boolean | null;
67
+ registered: boolean;
68
+ }
69
+ export declare const setDefaultSlowLaneClient: (client: SlowLaneClient | null, registration?: SlowLaneClientRegistration) => void;
70
+ /**
71
+ * Clear the shared client only when the caller still owns the registration.
72
+ *
73
+ * Multiple editor instances can briefly overlap during a React/editor remount.
74
+ * An older instance must not disconnect the newer instance when its delayed
75
+ * teardown runs after the newer client has registered.
76
+ */
77
+ export declare const clearDefaultSlowLaneClient: (client: SlowLaneClient) => boolean;
78
+ export declare const getDefaultSlowLaneClientStatus: () => SlowLaneClientStatus;
46
79
  export declare const getStoredContextVector: () => Float32Array | null;
80
+ export declare const getStoredContextInput: () => string | null;
47
81
  export declare const getStoredLmLogits: () => Record<string, number> | null;
82
+ export declare const getBoundaryLmState: (contextKey: string) => BoundaryLmState | null;
83
+ export declare const getCanonicalSurfaceTokenIds: (surface: string) => number[] | null;
84
+ export declare const getSurfaceScore: (contextKey: string, surface: string) => SurfaceScore | null;
85
+ export declare const getProgressiveSurfaceEvidence: (contextKey: string, surface: string) => ProgressiveSurfaceEvidence | null;
86
+ export declare const primeBoundaryLm: (input: BoundaryPrimeRequest) => void;
87
+ export declare const requestProgressiveSurfaceScores: (input: SurfaceScoreRequest) => void;
88
+ export declare const isCanonicalSurfaceScoringSupported: () => boolean;
89
+ /**
90
+ * How many surfaces the canonical token map currently holds. Goes from zero to
91
+ * its final size once the artifact lands, which is the only point at which
92
+ * `getCanonicalSurfaceTokenIds` starts answering — so callers memoising over
93
+ * that lookup can use this to detect the transition.
94
+ */
95
+ export declare const getCanonicalSurfaceCount: () => number;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Two prediction modes:
5
5
  * 1. Word boundary → bigram-based next-word suggestion (grammar-filtered)
6
- * 2. Mid-word (≥3 chars) → trie prefix search → scoring pipeline → top result
6
+ * 2. Mid-word (≥1 char) → grouped prefetch; ≥3 chars + exact evidence → ghost result
7
7
  *
8
8
  * Scoring is delegated to scoring-pipeline.ts which handles:
9
9
  * Stage 1 (semantic + frequency), grammar filter, Stage 2 (optional LM re-ranking).
@@ -14,6 +14,7 @@
14
14
  * Session personalization (L1): words the user types are incrementally boosted
15
15
  * via incrementSessionFreq(), called on word boundaries from the plugin.
16
16
  */
17
+ import type { TermType } from './scoring-pipeline';
17
18
  export interface WeightedTerm {
18
19
  authorFreq: number;
19
20
  docFreq: number;
@@ -33,13 +34,32 @@ interface VectorStore {
33
34
  * expects a simple array of strings: ["about", "above", "actually", ...]
34
35
  */
35
36
  export declare const initL3Vocabulary: (l3Words: string[]) => void;
37
+ /**
38
+ * Start a short cooldown for the exact surface the editor inserted. The caller
39
+ * passes the committed snapshot's surface so background re-ranking can never
40
+ * move cooldown bookkeeping away from what the user actually accepted.
41
+ */
42
+ export declare const noteSuggestionAccepted: (surface: string) => void;
36
43
  /**
37
44
  * Get predictor status for debugging.
38
45
  * vectorsLoaded: true when semantic scoring is active
39
46
  * wordCount: number of words in vector store (0 if not loaded)
40
47
  */
41
48
  export declare const getPredictorStatus: () => {
49
+ canonicalScoringSupported: boolean;
50
+ canonicalSurfaceCount: number | null;
51
+ clientId: string | null;
52
+ clientKind: 'localLlm' | 'server' | null;
42
53
  isInitialized: boolean;
54
+ localModelReady: boolean | null;
55
+ maxFreqByType: {
56
+ bigram: number;
57
+ phrase: number;
58
+ word: number;
59
+ };
60
+ phraseCount: number;
61
+ phrasesLoaded: boolean;
62
+ slowLaneRegistered: boolean;
43
63
  vectorsLoaded: boolean;
44
64
  vectorsLoadStarted: boolean;
45
65
  wordCount: number;
@@ -49,8 +69,10 @@ export declare const getPredictorStatus: () => {
49
69
  * Returns null if no prediction has run yet or debug was off.
50
70
  */
51
71
  export declare const getLastPredictionDebug: () => {
72
+ awaitingAsyncEvidence: boolean;
52
73
  contextWords: string[];
53
74
  currentWord: string;
75
+ decision: string;
54
76
  mode: 'cold' | 'warm';
55
77
  suggestion: string | null;
56
78
  textBefore: string;
@@ -63,6 +85,16 @@ export declare const getLastPredictionDebug: () => {
63
85
  }>;
64
86
  } | null;
65
87
  export declare const initVocabulary: (vocabulary: TenantVocabulary) => void;
88
+ /**
89
+ * Insert a producer-shipped bigram/phrase artifact into the phrase trie.
90
+ *
91
+ * :params:
92
+ * artifact: Normalized `{term: {freq, doc_freq, author_freq, pos_seq, head_pos, association}}` map
93
+ * termType: Whether these terms are `bigram` (2 tokens) or `phrase` (3 tokens)
94
+ * :returns:
95
+ * The number of terms inserted
96
+ */
97
+ export declare const initPhrases: (artifact: PhraseArtifactJson, termType: TermType) => number;
66
98
  /**
67
99
  * Increment L1 session frequency for a single word.
68
100
  * Called from the plugin on word boundaries for efficient incremental boosting.
@@ -79,14 +111,100 @@ export declare const incrementSessionFreq: (word: string) => void;
79
111
  * calling context does not yet have a page value available.
80
112
  */
81
113
  export declare const ingestDocumentPage: (pageContent: string | undefined) => void;
82
- export declare const predict: (textBefore: string) => string | null;
114
+ /**
115
+ * Result of a prediction: the ghost tail to insert plus an immutable record of
116
+ * the evidence that authorized the UI commitment.
117
+ */
118
+ export interface PredictionResult {
119
+ /** Canonical evidence depth for the selected full surface. */
120
+ evidenceDepth: {
121
+ totalChars: number;
122
+ totalTokens: number;
123
+ verifiedChars: number;
124
+ verifiedTokens: number;
125
+ };
126
+ /** Evidence tier that authorized display. Tier A is never display-eligible. */
127
+ evidenceTier: 'canonical-full-surface' | 'network-logit';
128
+ /**
129
+ * Mean per-token log-probability of the verified prefix.
130
+ *
131
+ * Carried alongside the posterior because the two answer different
132
+ * questions and a plausibility floor is placed on this one. A surface alone
133
+ * in its normaliser holds the whole pool whatever the model thinks of it,
134
+ * so its posterior cannot say whether it was worth showing and this can.
135
+ */
136
+ meanTokenLogProbability: number;
137
+ /**
138
+ * Whether a longer candidate in the same pool extends the selected surface.
139
+ *
140
+ * The chain rule leaves such a surface holding its pool's mass undivided, so
141
+ * its posterior is high for a structural reason rather than a modelled one —
142
+ * whichever continuation was meant, the prefix was right. Carried so the top
143
+ * posterior bucket can be split on it, since a lifted nested leader and a
144
+ * genuinely preferred surface are otherwise the same number.
145
+ */
146
+ poolHeldExtension: boolean;
147
+ /** Share of its shortlist's mass the model put on the selected surface. */
148
+ posterior: number;
149
+ /** Final Stage-1 + LM ranking score. */
150
+ rankScore: number;
151
+ /** How many scored candidates the selected surface's normaliser divided between. */
152
+ shortlistSize: number;
153
+ /** Full surface of the selected candidate (identity for keep-vs-swap checks). */
154
+ surface: string;
155
+ /** Whether the selected surface is a single word, a bigram, or a phrase. */
156
+ termType: TermType;
157
+ /** Ghost tail actually shown/inserted (surface minus the already-typed prefix). */
158
+ text: string;
159
+ /** Final-score lead over the strongest evidence-backed runner-up. */
160
+ winnerMargin: number;
161
+ }
162
+ export declare const predict: (textBefore: string) => PredictionResult | null;
163
+ /**
164
+ * Per-term stats shape for `bigrams.json` / `phrases.json`. Field names/types
165
+ * confirmed against the producer (`l2_vocabulary_creation._build_ngram_stat_payload`
166
+ * + `artifact_release._validate_ngram_payload`): `freq`/`doc_freq`/`author_freq`
167
+ * are ints, `pos_seq` a string array, `head_pos` a string or null, `association`
168
+ * a number.
169
+ */
170
+ interface PhraseStatsJson {
171
+ association?: number;
172
+ author_freq: number;
173
+ doc_freq: number;
174
+ freq: number;
175
+ head_pos?: string | null;
176
+ pos_seq?: string[];
177
+ }
178
+ /** Normalized `{term: stats}` map after unwrapping any producer wrapper key. */
179
+ type PhraseArtifactJson = Record<string, PhraseStatsJson>;
83
180
  export declare const loadVectorsAsync: (options?: {
84
181
  isLocalLLM?: boolean;
85
182
  surface?: string;
86
183
  }) => Promise<void>;
87
184
  export declare const initVectors: (store: VectorStore) => void;
185
+ /**
186
+ * Load the producer's `bigrams.json` + `phrases.json` completion-term artifacts
187
+ * over the TDP-OS gateway and insert them into the phrase trie.
188
+ *
189
+ * Each file is fetched independently (`Promise.allSettled`) so a missing or
190
+ * late-published artifact only skips that term type — word completion and the
191
+ * other term type are unaffected. Phrase VECTORS are not fetched here: per the
192
+ * producer's `extend` decision they are appended into the existing
193
+ * `word-index-10k.json` / `word-vectors-10k.bin`, so `loadVectorsAsync` already
194
+ * covers them.
195
+ *
196
+ * :params:
197
+ * options.isLocalLLM: Tags the UFO experience so latency/success feeds the same SLO
198
+ * :returns:
199
+ * A promise that resolves once both fetches have settled
200
+ */
201
+ export declare const loadPhraseArtifacts: (options?: {
202
+ isLocalLLM?: boolean;
203
+ }) => Promise<void>;
204
+ type VocabularyLoadSource = 'focus' | 'predict';
88
205
  export declare const loadDefaultVocabulary: (options?: {
89
206
  isLocalLLM?: boolean;
207
+ source?: VocabularyLoadSource;
90
208
  surface?: string;
91
209
  }) => Promise<void>;
92
210
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-autocomplete",
3
- "version": "8.0.0",
3
+ "version": "9.0.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",
@@ -27,7 +27,7 @@
27
27
  "wink-nlp": "^2.4.0"
28
28
  },
29
29
  "peerDependencies": {
30
- "@atlaskit/editor-common": "^119.0.0",
30
+ "@atlaskit/editor-common": "^119.4.0",
31
31
  "@atlaskit/editor-plugin-analytics": "^15.0.0",
32
32
  "react": "^18.2.0 || ^19.2.0"
33
33
  },
@@ -37,6 +37,7 @@ export const EXPERIENCE_NAME = {
37
37
  SLOW_LANE_FETCH: 'slow-lane-fetch',
38
38
  LOAD_VOCABULARY: 'load-vocabulary',
39
39
  LOAD_VECTORS: 'load-vectors',
40
+ LOAD_PHRASES: 'load-phrases',
40
41
  LOAD_GRAMMAR: 'load-grammar',
41
42
  } as const;
42
43
 
@@ -70,6 +71,10 @@ const experiences: Record<AutocompleteExperienceName, ConcurrentExperience> = {
70
71
  EXPERIENCE_NAME.LOAD_VECTORS,
71
72
  operationConfig,
72
73
  ),
74
+ [EXPERIENCE_NAME.LOAD_PHRASES]: new ConcurrentExperience(
75
+ EXPERIENCE_NAME.LOAD_PHRASES,
76
+ operationConfig,
77
+ ),
73
78
  [EXPERIENCE_NAME.LOAD_GRAMMAR]: new ConcurrentExperience(
74
79
  EXPERIENCE_NAME.LOAD_GRAMMAR,
75
80
  operationConfig,
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Validation and diagnostics layer over the artifacts manifest.
3
+ *
4
+ * `artifacts-manifest` owns *where* a payload comes from (one manifest request,
5
+ * then a presigned CDN download). This module owns *whether the payload is
6
+ * usable*: every loader declares the shape it expects, so a mis-published
7
+ * artifact fails at the boundary with the artifact's name rather than surfacing
8
+ * later as an empty trie or a silent no-op in scoring.
9
+ */
10
+
11
+ import { type ArtifactName, fetchArtifactJson, getArtifactUrl } from './artifacts-manifest';
12
+ import { ctcTag } from './debug-mode';
13
+
14
+ const TRAILING_SLASH_REGEX = /\/$/u;
15
+ const DEFAULT_BASE_URL = '';
16
+ const AUTOCOMPLETE_GATEWAY_BASE = '/gateway/api/v1/autocomplete';
17
+
18
+ type ArtifactLoaderOptions<T> = {
19
+ label?: string;
20
+ summarize?: (payload: T) => string;
21
+ validate?: (payload: unknown) => payload is T;
22
+ };
23
+
24
+ /**
25
+ * Build a URL for one of the autocomplete gateway's own endpoints (e.g. the
26
+ * network slow lane). Model artifacts do NOT go through here — they are served
27
+ * from the CDN via the manifest.
28
+ */
29
+ export const buildAutocompleteGatewayUrl = (
30
+ path: string,
31
+ baseUrl: string = DEFAULT_BASE_URL,
32
+ ): string => {
33
+ const normalizedBase = baseUrl.replace(TRAILING_SLASH_REGEX, '');
34
+ return `${normalizedBase}${AUTOCOMPLETE_GATEWAY_BASE}${path}`;
35
+ };
36
+
37
+ /**
38
+ * Download a JSON artifact and assert its shape before handing it to a loader.
39
+ *
40
+ * :params:
41
+ * artifactName: Manifest key for the artifact
42
+ * options.label: Name used in diagnostics; defaults to the manifest key
43
+ * options.summarize: Renders a short size/count summary for the debug log
44
+ * options.validate: Type guard the payload must satisfy
45
+ * :returns:
46
+ * The parsed, validated payload
47
+ */
48
+ export const fetchAutocompleteArtifactJson = async <T>(
49
+ artifactName: ArtifactName,
50
+ options: ArtifactLoaderOptions<T> = {},
51
+ ): Promise<T> => {
52
+ const label = options.label ?? artifactName;
53
+ // Presigned CDN URLs carry an auth token, so only the artifact name is logged.
54
+ ctcTag('init', `↓ loading ${label}`);
55
+
56
+ const payload = await fetchArtifactJson<unknown>(artifactName);
57
+ if (options.validate && !options.validate(payload)) {
58
+ throw new Error(`[autocomplete-artifacts] ${label} payload shape was invalid`);
59
+ }
60
+
61
+ const summary = options.summarize?.(payload as T);
62
+ ctcTag('init', `✓ loaded ${label}${summary ? ` (${summary})` : ''}`);
63
+
64
+ return payload as T;
65
+ };
66
+
67
+ /**
68
+ * Download a binary artifact (the word-vectors blob) as an ArrayBuffer.
69
+ *
70
+ * :params:
71
+ * artifactName: Manifest key for the artifact
72
+ * label: Name used in diagnostics; defaults to the manifest key
73
+ * :returns:
74
+ * The raw bytes of the artifact
75
+ */
76
+ export const fetchAutocompleteArtifactBinary = async (
77
+ artifactName: ArtifactName,
78
+ label: string = artifactName,
79
+ ): Promise<ArrayBuffer> => {
80
+ ctcTag('init', `↓ loading ${label}`);
81
+
82
+ const url = await getArtifactUrl(artifactName);
83
+ const response = await fetch(url);
84
+ if (!response.ok) {
85
+ throw new Error(`[autocomplete-artifacts] Failed to fetch ${label}: ${response.status}`);
86
+ }
87
+
88
+ const payload = await response.arrayBuffer();
89
+ ctcTag('init', `✓ loaded ${label} (${payload.byteLength} bytes)`);
90
+
91
+ return payload;
92
+ };
@@ -20,10 +20,13 @@ import { isAutocompleteDebugEnabled } from './debug-mode';
20
20
  * hyphenated, unlike the underscored filenames the artifacts are generated from.
21
21
  */
22
22
  export const ARTIFACT_NAME = {
23
+ BIGRAMS: 'bigrams.json',
23
24
  FIRST_TOKEN_TO_WORDS: 'first-token-to-words.json',
24
25
  GHOST_POS_TAGS: 'ghost-pos-tags.json',
25
26
  GRAMMAR_TRANSITIONS: 'grammar-transitions-10k.json',
26
27
  L3_VOCABULARY: 'l3-vocabulary.json',
28
+ PHRASE_CONTINUATION_TOKENS: 'phrase-continuation-tokens.json',
29
+ PHRASES: 'phrases.json',
27
30
  POS_TAGS: 'combined-l2-l3-pos-tags.json',
28
31
  VOCABULARY: 'vocabulary-10k.json',
29
32
  WORD_INDEX: 'word-index-10k.json',