@atlaskit/editor-plugin-autocomplete 8.0.0 → 9.1.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 +108 -0
- package/dist/cjs/analytics/ufo.js +2 -1
- package/dist/cjs/pm-plugins/artifact-loader.js +135 -0
- package/dist/cjs/pm-plugins/artifacts-manifest.js +3 -0
- package/dist/cjs/pm-plugins/autocomplete-plugin.js +498 -160
- package/dist/cjs/pm-plugins/canonical-lm-scoring.js +371 -0
- package/dist/cjs/pm-plugins/debug-mode.js +105 -7
- package/dist/cjs/pm-plugins/ghost-text-decoration.js +12 -3
- package/dist/cjs/pm-plugins/local-slow-lane-client.js +1202 -453
- package/dist/cjs/pm-plugins/scoring-pipeline.js +164 -19
- package/dist/cjs/pm-plugins/slow-lane-client.js +100 -37
- package/dist/cjs/pm-plugins/text-predictor.js +2099 -309
- package/dist/es2019/analytics/ufo.js +2 -0
- package/dist/es2019/pm-plugins/artifact-loader.js +70 -0
- package/dist/es2019/pm-plugins/artifacts-manifest.js +3 -0
- package/dist/es2019/pm-plugins/autocomplete-plugin.js +489 -152
- package/dist/es2019/pm-plugins/canonical-lm-scoring.js +270 -0
- package/dist/es2019/pm-plugins/debug-mode.js +102 -7
- package/dist/es2019/pm-plugins/ghost-text-decoration.js +12 -3
- package/dist/es2019/pm-plugins/local-slow-lane-client.js +940 -326
- package/dist/es2019/pm-plugins/scoring-pipeline.js +137 -13
- package/dist/es2019/pm-plugins/slow-lane-client.js +94 -37
- package/dist/es2019/pm-plugins/text-predictor.js +1692 -200
- package/dist/esm/analytics/ufo.js +2 -1
- package/dist/esm/pm-plugins/artifact-loader.js +128 -0
- package/dist/esm/pm-plugins/artifacts-manifest.js +3 -0
- package/dist/esm/pm-plugins/autocomplete-plugin.js +501 -163
- package/dist/esm/pm-plugins/canonical-lm-scoring.js +364 -0
- package/dist/esm/pm-plugins/debug-mode.js +104 -6
- package/dist/esm/pm-plugins/ghost-text-decoration.js +12 -3
- package/dist/esm/pm-plugins/local-slow-lane-client.js +1200 -451
- package/dist/esm/pm-plugins/scoring-pipeline.js +163 -18
- package/dist/esm/pm-plugins/slow-lane-client.js +100 -37
- package/dist/esm/pm-plugins/text-predictor.js +2101 -313
- package/dist/types/analytics/ufo.d.ts +1 -0
- package/dist/types/pm-plugins/artifact-loader.d.ts +44 -0
- package/dist/types/pm-plugins/artifacts-manifest.d.ts +3 -0
- package/dist/types/pm-plugins/autocomplete-plugin.d.ts +39 -0
- package/dist/types/pm-plugins/canonical-lm-scoring.d.ts +158 -0
- package/dist/types/pm-plugins/debug-mode.d.ts +55 -1
- package/dist/types/pm-plugins/ghost-text-decoration.d.ts +5 -2
- package/dist/types/pm-plugins/local-slow-lane-client.d.ts +87 -54
- package/dist/types/pm-plugins/scoring-pipeline.d.ts +33 -1
- package/dist/types/pm-plugins/slow-lane-client.d.ts +51 -3
- package/dist/types/pm-plugins/text-predictor.d.ts +156 -3
- package/package.json +2 -2
- package/src/analytics/ufo.ts +5 -0
- package/src/pm-plugins/artifact-loader.ts +92 -0
- package/src/pm-plugins/artifacts-manifest.ts +3 -0
- package/src/pm-plugins/autocomplete-plugin.ts +584 -154
- package/src/pm-plugins/canonical-lm-scoring.ts +412 -0
- package/src/pm-plugins/debug-mode.ts +134 -7
- package/src/pm-plugins/ghost-text-decoration.ts +13 -4
- package/src/pm-plugins/local-slow-lane-client.ts +1302 -386
- package/src/pm-plugins/scoring-pipeline.ts +167 -17
- package/src/pm-plugins/slow-lane-client.ts +147 -62
- package/src/pm-plugins/text-predictor.ts +2119 -254
- package/CANONICAL_FIX__DO_NOT_USE_ME_A/package.json +0 -8
- package/CANONICAL_FIX__DO_NOT_USE_ME_B/package.json +0 -8
- package/CANONICAL_FIX__DO_NOT_USE_ME_C/package.json +0 -8
- package/dist/cjs/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -18
- package/dist/cjs/entry-points/src-pm-plugins-slow-lane-client.js +0 -36
- package/dist/cjs/entry-points/src-pm-plugins-text-predictor.js +0 -66
- package/dist/es2019/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
- package/dist/es2019/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
- package/dist/es2019/entry-points/src-pm-plugins-text-predictor.js +0 -2
- package/dist/esm/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
- package/dist/esm/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
- package/dist/esm/entry-points/src-pm-plugins-text-predictor.js +0 -2
- package/dist/types/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +0 -2
- package/dist/types/entry-points/src-pm-plugins-slow-lane-client.d.ts +0 -2
- package/dist/types/entry-points/src-pm-plugins-text-predictor.d.ts +0 -2
- package/scripts/gen_first_token_to_words.py +0 -170
- package/src/entry-points/src-pm-plugins-autocomplete-plugin.ts +0 -7
- package/src/entry-points/src-pm-plugins-slow-lane-client.ts +0 -13
- package/src/entry-points/src-pm-plugins-text-predictor.ts +0 -14
- package/src/pm-plugins/autocomplete-plugin/package.json +0 -8
- package/src/pm-plugins/slow-lane-client/package.json +0 -8
- package/src/pm-plugins/text-predictor/package.json +0 -8
|
@@ -30,6 +30,7 @@ export declare const EXPERIENCE_NAME: {
|
|
|
30
30
|
readonly SLOW_LANE_FETCH: 'slow-lane-fetch';
|
|
31
31
|
readonly LOAD_VOCABULARY: 'load-vocabulary';
|
|
32
32
|
readonly LOAD_VECTORS: 'load-vectors';
|
|
33
|
+
readonly LOAD_PHRASES: 'load-phrases';
|
|
33
34
|
readonly LOAD_GRAMMAR: 'load-grammar';
|
|
34
35
|
};
|
|
35
36
|
export type AutocompleteExperienceName = (typeof EXPERIENCE_NAME)[keyof typeof EXPERIENCE_NAME];
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
import { type ArtifactName } from './artifacts-manifest';
|
|
11
|
+
type ArtifactLoaderOptions<T> = {
|
|
12
|
+
label?: string;
|
|
13
|
+
summarize?: (payload: T) => string;
|
|
14
|
+
validate?: (payload: unknown) => payload is T;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Build a URL for one of the autocomplete gateway's own endpoints (e.g. the
|
|
18
|
+
* network slow lane). Model artifacts do NOT go through here — they are served
|
|
19
|
+
* from the CDN via the manifest.
|
|
20
|
+
*/
|
|
21
|
+
export declare const buildAutocompleteGatewayUrl: (path: string, baseUrl?: string) => string;
|
|
22
|
+
/**
|
|
23
|
+
* Download a JSON artifact and assert its shape before handing it to a loader.
|
|
24
|
+
*
|
|
25
|
+
* :params:
|
|
26
|
+
* artifactName: Manifest key for the artifact
|
|
27
|
+
* options.label: Name used in diagnostics; defaults to the manifest key
|
|
28
|
+
* options.summarize: Renders a short size/count summary for the debug log
|
|
29
|
+
* options.validate: Type guard the payload must satisfy
|
|
30
|
+
* :returns:
|
|
31
|
+
* The parsed, validated payload
|
|
32
|
+
*/
|
|
33
|
+
export declare const fetchAutocompleteArtifactJson: <T>(artifactName: ArtifactName, options?: ArtifactLoaderOptions<T>) => Promise<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Download a binary artifact (the word-vectors blob) as an ArrayBuffer.
|
|
36
|
+
*
|
|
37
|
+
* :params:
|
|
38
|
+
* artifactName: Manifest key for the artifact
|
|
39
|
+
* label: Name used in diagnostics; defaults to the manifest key
|
|
40
|
+
* :returns:
|
|
41
|
+
* The raw bytes of the artifact
|
|
42
|
+
*/
|
|
43
|
+
export declare const fetchAutocompleteArtifactBinary: (artifactName: ArtifactName, label?: string) => Promise<ArrayBuffer>;
|
|
44
|
+
export {};
|
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
* hyphenated, unlike the underscored filenames the artifacts are generated from.
|
|
16
16
|
*/
|
|
17
17
|
export declare const ARTIFACT_NAME: {
|
|
18
|
+
readonly BIGRAMS: 'bigrams.json';
|
|
18
19
|
readonly FIRST_TOKEN_TO_WORDS: 'first-token-to-words.json';
|
|
19
20
|
readonly GHOST_POS_TAGS: 'ghost-pos-tags.json';
|
|
20
21
|
readonly GRAMMAR_TRANSITIONS: 'grammar-transitions-10k.json';
|
|
21
22
|
readonly L3_VOCABULARY: 'l3-vocabulary.json';
|
|
23
|
+
readonly PHRASE_CONTINUATION_TOKENS: 'phrase-continuation-tokens.json';
|
|
24
|
+
readonly PHRASES: 'phrases.json';
|
|
22
25
|
readonly POS_TAGS: 'combined-l2-l3-pos-tags.json';
|
|
23
26
|
readonly VOCABULARY: 'vocabulary-10k.json';
|
|
24
27
|
readonly WORD_INDEX: 'word-index-10k.json';
|
|
@@ -3,7 +3,44 @@ import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
|
3
3
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
4
4
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
5
5
|
import type { AutocompletePlugin } from '../autocompletePluginType';
|
|
6
|
+
import { type PredictionResult } from './text-predictor';
|
|
6
7
|
export declare const autocompletePluginKey: PluginKey;
|
|
8
|
+
export interface CommittedSuggestion {
|
|
9
|
+
/** Wall-clock latency from the triggering editor update to the committed ghost. */
|
|
10
|
+
decisionLatencyMs: number;
|
|
11
|
+
/** Evidence tier that authorized this suggestion for display. */
|
|
12
|
+
evidenceTier: PredictionResult['evidenceTier'];
|
|
13
|
+
/** Ghost tail actually rendered and inserted. */
|
|
14
|
+
ghostText: string;
|
|
15
|
+
/**
|
|
16
|
+
* Mean per-token log-probability of the verified prefix, carried so an
|
|
17
|
+
* acceptance can be attributed to the evidence that authorized the display
|
|
18
|
+
* rather than to whatever the scorer holds by the time Tab arrives.
|
|
19
|
+
*/
|
|
20
|
+
meanTokenLogProbability: number;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a longer candidate in the same pool extended this surface, which
|
|
23
|
+
* hands it that pool's mass undivided under the chain rule and puts it near
|
|
24
|
+
* the top of the posterior range for a structural reason.
|
|
25
|
+
*/
|
|
26
|
+
poolHeldExtension: boolean;
|
|
27
|
+
/** ProseMirror position at which the ghost is anchored. */
|
|
28
|
+
position: number;
|
|
29
|
+
/** Share of its shortlist's mass the model put on this surface. */
|
|
30
|
+
posterior: number;
|
|
31
|
+
/** Final confidence-v2 ranking score. */
|
|
32
|
+
rankScore: number;
|
|
33
|
+
/** Monotonic editor decision revision that owns this suggestion. */
|
|
34
|
+
revision: number;
|
|
35
|
+
/** How many scored candidates this surface's normaliser divided between. */
|
|
36
|
+
shortlistSize: number;
|
|
37
|
+
/** Full candidate surface, used by analytics and cooldown. */
|
|
38
|
+
surface: string;
|
|
39
|
+
/** Whether the surface is a single word, a bigram, or a phrase. */
|
|
40
|
+
termType: PredictionResult['termType'];
|
|
41
|
+
/** Difference between the selected rank score and its closest eligible rival. */
|
|
42
|
+
winnerMargin: number;
|
|
43
|
+
}
|
|
7
44
|
export interface AutocompletePluginState {
|
|
8
45
|
/** The decoration set containing the ghost text widget */
|
|
9
46
|
decorationSet: DecorationSet;
|
|
@@ -11,6 +48,8 @@ export interface AutocompletePluginState {
|
|
|
11
48
|
ghostPosition: number;
|
|
12
49
|
/** The predicted ghost text to display */
|
|
13
50
|
ghostText: string;
|
|
51
|
+
/** Single authoritative snapshot for rendering, insertion, analytics, and cooldown. */
|
|
52
|
+
suggestion: CommittedSuggestion | null;
|
|
14
53
|
}
|
|
15
54
|
/**
|
|
16
55
|
* Context provided to the autocomplete plugin on first editor focus.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { LogitProcessor } from '@mlc-ai/web-llm';
|
|
2
|
+
export type SeparatorKind = 'whitespace' | 'document-start' | 'non-space';
|
|
3
|
+
export type CanonicalLmEvidenceSource = 'canonical-first-token' | 'canonical-full-surface' | 'network-logit';
|
|
4
|
+
export interface BoundaryLmState {
|
|
5
|
+
contextKey: string;
|
|
6
|
+
prompt: string;
|
|
7
|
+
rawLogits: Float32Array;
|
|
8
|
+
}
|
|
9
|
+
export interface SurfaceScore {
|
|
10
|
+
contextKey: string;
|
|
11
|
+
meanTokenLogProbability: number;
|
|
12
|
+
surface: string;
|
|
13
|
+
tokenCount: number;
|
|
14
|
+
totalLogProbability: number;
|
|
15
|
+
}
|
|
16
|
+
export interface CanonicalCandidateContext {
|
|
17
|
+
canonicalTokenIds: number[] | null;
|
|
18
|
+
contextBeforeSurface: string;
|
|
19
|
+
contextKey: string;
|
|
20
|
+
separatorKind: SeparatorKind;
|
|
21
|
+
surfaceStart: number;
|
|
22
|
+
}
|
|
23
|
+
export interface CanonicalBoundaryContext {
|
|
24
|
+
contextKey: string;
|
|
25
|
+
prompt: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CanonicalLmEvidence {
|
|
28
|
+
contextKey: string;
|
|
29
|
+
logProbability?: number;
|
|
30
|
+
meanTokenLogProbability?: number;
|
|
31
|
+
rawLogit?: number;
|
|
32
|
+
score: number;
|
|
33
|
+
separatorKind: 'whitespace';
|
|
34
|
+
source: CanonicalLmEvidenceSource;
|
|
35
|
+
totalLogProbability?: number;
|
|
36
|
+
totalSurfaceCharCount: number;
|
|
37
|
+
totalTokenCount: number;
|
|
38
|
+
verifiedCharCount: number;
|
|
39
|
+
verifiedTokenCount: number;
|
|
40
|
+
}
|
|
41
|
+
export interface BoundaryPrimeRequest {
|
|
42
|
+
contextKey: string;
|
|
43
|
+
familyKey: string;
|
|
44
|
+
priority: number;
|
|
45
|
+
prompt: string;
|
|
46
|
+
}
|
|
47
|
+
export interface SurfaceScoreRequest {
|
|
48
|
+
candidates: Array<{
|
|
49
|
+
rankHint?: number;
|
|
50
|
+
surface: string;
|
|
51
|
+
tokenIds: number[];
|
|
52
|
+
}>;
|
|
53
|
+
contextKey: string;
|
|
54
|
+
familyKey: string;
|
|
55
|
+
prompt: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ProgressiveSurfaceEvidence {
|
|
58
|
+
meanTokenLogProbabilityUpperBound: number;
|
|
59
|
+
scoredTokenCount: number;
|
|
60
|
+
totalLogProbability: number;
|
|
61
|
+
totalTokenCount: number;
|
|
62
|
+
}
|
|
63
|
+
export interface TokenPrefixExpansion {
|
|
64
|
+
contextKey: string;
|
|
65
|
+
rawNextTokenLogits: Float32Array;
|
|
66
|
+
tokenPrefix: number[];
|
|
67
|
+
totalLogProbability: number;
|
|
68
|
+
}
|
|
69
|
+
export interface TokenPrefixGroup {
|
|
70
|
+
surfaces: string[];
|
|
71
|
+
tokenPrefix: number[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Token trie for the producer's canonical leading-space token sequences.
|
|
75
|
+
* It lets the scorer expand a shared token prefix once for every surface below
|
|
76
|
+
* that node (`root cause`, `root directory`, ...), rather than guessing two
|
|
77
|
+
* arbitrary full surfaces from a broad one-character string prefix.
|
|
78
|
+
*/
|
|
79
|
+
export declare class CanonicalSurfaceTokenTrie {
|
|
80
|
+
private readonly root;
|
|
81
|
+
private readonly tokenIdsBySurface;
|
|
82
|
+
constructor(entries?: Iterable<readonly [string, number[]]>);
|
|
83
|
+
insert(surface: string, tokenIds: number[]): void;
|
|
84
|
+
getTokenIds(surface: string): number[] | null;
|
|
85
|
+
groupByScoredPrefix(candidates: Array<{
|
|
86
|
+
scoredTokenCount: number;
|
|
87
|
+
surface: string;
|
|
88
|
+
tokenIds: number[];
|
|
89
|
+
}>): TokenPrefixGroup[];
|
|
90
|
+
}
|
|
91
|
+
/** The part of a candidate's context that depends only on where its surface starts. */
|
|
92
|
+
interface SurfacePositionContext {
|
|
93
|
+
contextBeforeSurface: string;
|
|
94
|
+
contextKey: string;
|
|
95
|
+
separatorKind: SeparatorKind;
|
|
96
|
+
surfaceStart: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Per-pass store for the position-derived half of a candidate's context.
|
|
100
|
+
*
|
|
101
|
+
* Every unigram candidate for a keystroke shares one surface start, and the
|
|
102
|
+
* phrase candidates share a handful more, so without this the same slice,
|
|
103
|
+
* `trimEnd` and context key are rebuilt for each of the couple of hundred
|
|
104
|
+
* candidates. Valid only for a single `textBeforeCursor`.
|
|
105
|
+
*/
|
|
106
|
+
export type CanonicalContextPositionCache = Map<number, SurfacePositionContext>;
|
|
107
|
+
export declare const createCanonicalContextPositionCache: () => CanonicalContextPositionCache;
|
|
108
|
+
/**
|
|
109
|
+
* Derive the causal prompt and artifact eligibility from the exact, untrimmed
|
|
110
|
+
* pre-cursor text. The shipped artifact contains only tokenizations of
|
|
111
|
+
* `" " + surface`, so it is valid only when the surface is actually preceded
|
|
112
|
+
* by whitespace.
|
|
113
|
+
*/
|
|
114
|
+
export declare const deriveCanonicalCandidateContext: (textBeforeCursor: string, matchedPrefixLen: number, surface: string, getTokenIds: (surface: string) => number[] | null, surfaceStartOverride?: number, positionCache?: CanonicalContextPositionCache) => CanonicalCandidateContext;
|
|
115
|
+
/**
|
|
116
|
+
* Derive the next surface's leading-space context at an actual word boundary.
|
|
117
|
+
* This lets the local LM start its one-token prime on the space keystroke,
|
|
118
|
+
* before the user has typed the first character of the next word.
|
|
119
|
+
*/
|
|
120
|
+
export declare const deriveWhitespaceBoundaryContext: (textBeforeCursor: string) => CanonicalBoundaryContext | null;
|
|
121
|
+
/**
|
|
122
|
+
* Return one prime per exact context, ordered from the latest surface start
|
|
123
|
+
* (normally the unigram window) to wider phrase windows.
|
|
124
|
+
*/
|
|
125
|
+
export declare const selectBoundaryPrimeRequests: (familyKey: string, candidates: Array<CanonicalCandidateContext>, maxPrimes: number) => BoundaryPrimeRequest[];
|
|
126
|
+
/**
|
|
127
|
+
* `log(sum(exp(values)))`, shifted by the maximum so the sum cannot overflow or
|
|
128
|
+
* underflow to zero for the sequence log-likelihoods this is called with.
|
|
129
|
+
*
|
|
130
|
+
* Returns `-Infinity` for an empty or wholly non-finite input, which makes
|
|
131
|
+
* `exp(value - logSumExp(values))` evaluate to zero rather than `NaN`.
|
|
132
|
+
*/
|
|
133
|
+
export declare const logSumExp: (values: readonly number[]) => number;
|
|
134
|
+
export declare const logSoftmaxAt: (logits: Float32Array, target: number) => number;
|
|
135
|
+
/**
|
|
136
|
+
* One processor is registered for the causal model so the scheduler can read
|
|
137
|
+
* the next-token distribution off any forward pass.
|
|
138
|
+
*
|
|
139
|
+
* The scheduler drives decoding token by token — it prefills a prompt, then
|
|
140
|
+
* feeds each continuation token itself — so the processor never has to force
|
|
141
|
+
* the model onto a target path. It only has to hand back the distribution the
|
|
142
|
+
* forward pass produced, which it does without altering the logits.
|
|
143
|
+
*/
|
|
144
|
+
export declare class CanonicalLogitProcessor implements LogitProcessor {
|
|
145
|
+
private captured;
|
|
146
|
+
processLogits: (logits: Float32Array) => Float32Array;
|
|
147
|
+
processSampledToken: () => void;
|
|
148
|
+
resetState: () => void;
|
|
149
|
+
startCapture: () => void;
|
|
150
|
+
/**
|
|
151
|
+
* The captured distribution, or null when the forward pass never ran.
|
|
152
|
+
*
|
|
153
|
+
* `processLogits` already copied it, and every capture allocates afresh, so
|
|
154
|
+
* the buffer handed out here is never written to again.
|
|
155
|
+
*/
|
|
156
|
+
getCapturedLogits: () => Float32Array | null;
|
|
157
|
+
}
|
|
158
|
+
export {};
|
|
@@ -18,10 +18,64 @@
|
|
|
18
18
|
declare global {
|
|
19
19
|
interface Window {
|
|
20
20
|
__atlCtcDebug__?: {
|
|
21
|
-
enable: () => void;
|
|
22
21
|
disable: () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Enable CTC debug logging for this session. Pass `'verbose'` to also
|
|
24
|
+
* emit the per-prediction candidate table, canonical LM, and grammar detail:
|
|
25
|
+
* `__atlCtcDebug__.enable('verbose')`.
|
|
26
|
+
*/
|
|
27
|
+
enable: (level?: 'verbose') => void;
|
|
23
28
|
isEnabled: () => boolean;
|
|
29
|
+
/** Whether verbose logging (candidate tables + extra detail) is on. */
|
|
30
|
+
isVerbose: () => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Snapshot of the L1 session boosts — the vocabulary words this session
|
|
33
|
+
* has seen and how often — or one family of them:
|
|
34
|
+
* `__atlCtcDebug__.session('poll')`.
|
|
35
|
+
*
|
|
36
|
+
* Installed by the predictor rather than declared with the rest of the
|
|
37
|
+
* API, and typed as `unknown` so the return shape can live with the module
|
|
38
|
+
* that owns it instead of creating a cycle back to this one.
|
|
39
|
+
*/
|
|
40
|
+
session?: (prefix?: string) => unknown;
|
|
24
41
|
};
|
|
25
42
|
}
|
|
26
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Shared `%c` styles for all CTC console output, so every log — lifecycle,
|
|
46
|
+
* async signals, and per-prediction groups — reads consistently.
|
|
47
|
+
*/
|
|
48
|
+
export declare const CTC_STYLES: {
|
|
49
|
+
brand: string;
|
|
50
|
+
section: string;
|
|
51
|
+
dim: string;
|
|
52
|
+
good: string;
|
|
53
|
+
warn: string;
|
|
54
|
+
bad: string;
|
|
55
|
+
cold: string;
|
|
56
|
+
lm: string;
|
|
57
|
+
body: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Log one aligned section line inside a per-prediction group, e.g.
|
|
61
|
+
* `INPUT raw: "…"`. Label is padded so the bodies line up. No-ops unless
|
|
62
|
+
* debug is enabled, so callers don't need to guard.
|
|
63
|
+
*/
|
|
64
|
+
export declare const ctcSection: (label: string, body: string) => void;
|
|
65
|
+
/**
|
|
66
|
+
* Log a lifecycle / async-signal line (outside the per-prediction groups),
|
|
67
|
+
* tagged `[CTC:<tag>]`. No-ops unless debug is enabled, so callers don't need to
|
|
68
|
+
* guard. Use tags like `init` (loads) and `signal` (slow-lane arrivals).
|
|
69
|
+
*/
|
|
70
|
+
export declare const ctcTag: (tag: string, body: string, tagStyle?: string) => void;
|
|
27
71
|
export declare const isAutocompleteDebugEnabled: () => boolean;
|
|
72
|
+
export declare const isAutocompleteDebugVerbose: () => boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Hang the L1 session-boost snapshot off the console API.
|
|
75
|
+
*
|
|
76
|
+
* Unlike the log helpers this is available whether or not debug is enabled:
|
|
77
|
+
* inspecting state on demand is not logging, and asking someone to turn on
|
|
78
|
+
* logging and retype to find out what the session already holds defeats the
|
|
79
|
+
* point of being able to ask.
|
|
80
|
+
*/
|
|
81
|
+
export declare const registerCtcSessionInspector: (inspect: (prefix?: string) => unknown) => void;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
2
2
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
3
3
|
/**
|
|
4
4
|
* Creates a DecorationSet containing a ghost text widget at the given position.
|
|
5
5
|
* The ghost text is rendered as a styled <span> that appears after the cursor.
|
|
6
|
+
*
|
|
7
|
+
* Takes the document rather than the whole state so it can also be called from
|
|
8
|
+
* `apply`, where only the post-transaction doc exists.
|
|
6
9
|
*/
|
|
7
|
-
export declare const createGhostTextDecorationSet: (
|
|
10
|
+
export declare const createGhostTextDecorationSet: (doc: PMNode, position: number, text: string) => DecorationSet;
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
* a backend API, this client runs two models in the browser via WebGPU, in a
|
|
6
6
|
* single MLCEngine, to reproduce the BE encoder's outputs on-device:
|
|
7
7
|
*
|
|
8
|
-
* - Causal LM (SmolLM2-135M-Instruct): one
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* port of the BE `CausalLMEncoder._get_top_k_probs` (masked softmax over the
|
|
12
|
-
* vocab's first-tokens, prefix expansion, L2 reservation, log-space pooling).
|
|
8
|
+
* - Causal LM (SmolLM2-135M-Instruct): context-keyed one-step boundary primes
|
|
9
|
+
* provide canonical first-token logits. A persistent token-prefix scheduler
|
|
10
|
+
* expands shared paths and exact-scores only plausible finalists.
|
|
13
11
|
* - Semantic embedder (Snowflake Arctic Embed S): produces the real 384-d
|
|
14
12
|
* `semantic_vector`. Inputs are wrapped as passages (see `wrapForArctic`) so
|
|
15
13
|
* the runtime vector lands in the same space as the precomputed word bin.
|
|
@@ -19,7 +17,7 @@
|
|
|
19
17
|
* the main thread is viable:
|
|
20
18
|
*
|
|
21
19
|
* - WebGPU GPU compute is inherently async (doesn't block the main thread)
|
|
22
|
-
* - CPU overhead
|
|
20
|
+
* - CPU overhead for named-token reads and cache bookkeeping is small
|
|
23
21
|
* - Per-inference latency is well within autocomplete expectations
|
|
24
22
|
* (~250 ms between word boundaries)
|
|
25
23
|
*
|
|
@@ -30,10 +28,11 @@
|
|
|
30
28
|
* - Standard npm import — just works
|
|
31
29
|
*
|
|
32
30
|
* ── Interface ────────────────────────────────────────────────────────────
|
|
33
|
-
* Same shape as createSlowLaneClient
|
|
34
|
-
*
|
|
35
|
-
*
|
|
31
|
+
* Same base shape as createSlowLaneClient, plus on-device canonical-surface
|
|
32
|
+
* scoring. Semantic updates remain word-boundary timed; causal work is requested
|
|
33
|
+
* independently for exact pre-surface contexts.
|
|
36
34
|
*/
|
|
35
|
+
import { type BoundaryLmState, type BoundaryPrimeRequest, type ProgressiveSurfaceEvidence, type SurfaceScore, type SurfaceScoreRequest } from './canonical-lm-scoring';
|
|
37
36
|
export interface LocalSlowLaneClientConfig {
|
|
38
37
|
/**
|
|
39
38
|
* Optional custom model registration for models not in web-llm's
|
|
@@ -60,12 +59,25 @@ export interface LocalSlowLaneClientConfig {
|
|
|
60
59
|
* `customModelConfig` with the model URL and WASM library URL.
|
|
61
60
|
*/
|
|
62
61
|
modelId?: string;
|
|
62
|
+
/** Callback fired when a context-keyed Tier-A raw-logit vector is available. */
|
|
63
|
+
onBoundaryLmUpdate?: (opts: {
|
|
64
|
+
contextKey: string;
|
|
65
|
+
familyKey: string;
|
|
66
|
+
latencyMs: number;
|
|
67
|
+
}) => void;
|
|
63
68
|
/** Callback fired when the engine fails to load/start. */
|
|
64
69
|
onLoadError?: (error: LocalSlowLaneLoadError) => void;
|
|
65
70
|
/** Callback fired when the engine successfully loads and is ready. */
|
|
66
71
|
onLoadSuccess?: (info: LocalSlowLaneLoadSuccess) => void;
|
|
67
72
|
/** Callback fired with status messages (model loading progress, etc.). */
|
|
68
73
|
onStatus?: (message: string) => void;
|
|
74
|
+
/** Callback fired when grouped progress or exact surface scores become available. */
|
|
75
|
+
onSurfaceScoreUpdate?: (opts: {
|
|
76
|
+
contextKey: string;
|
|
77
|
+
count: number;
|
|
78
|
+
familyKey: string;
|
|
79
|
+
latencyMs: number;
|
|
80
|
+
}) => void;
|
|
69
81
|
/** Callback fired when inference returns new results. */
|
|
70
82
|
onUpdate?: (opts: {
|
|
71
83
|
hasLmLogits: boolean;
|
|
@@ -78,11 +90,19 @@ export interface LocalSlowLaneClientConfig {
|
|
|
78
90
|
export interface LocalSlowLaneClient {
|
|
79
91
|
/** Clean up resources. */
|
|
80
92
|
destroy: () => void;
|
|
93
|
+
getBoundaryLmState: (contextKey: string) => BoundaryLmState | null;
|
|
94
|
+
getCanonicalSurfaceCount: () => number;
|
|
95
|
+
getCanonicalSurfaceTokenIds: (surface: string) => number[] | null;
|
|
96
|
+
getContextInput: () => string | null;
|
|
81
97
|
getContextVector: () => Float32Array | null;
|
|
82
98
|
getLmLogits: () => Record<string, number> | null;
|
|
99
|
+
getProgressiveSurfaceEvidence: (contextKey: string, surface: string) => ProgressiveSurfaceEvidence | null;
|
|
100
|
+
getSurfaceScore: (contextKey: string, surface: string) => SurfaceScore | null;
|
|
83
101
|
/** Whether the model is loaded and ready for inference. */
|
|
84
102
|
isReady: () => boolean;
|
|
85
103
|
isWordBoundary: (text: string) => boolean;
|
|
104
|
+
primeBoundaryLm: (input: BoundaryPrimeRequest) => void;
|
|
105
|
+
requestProgressiveSurfaceScores: (input: SurfaceScoreRequest) => void;
|
|
86
106
|
setContextVector: (vector: Float32Array | null) => void;
|
|
87
107
|
setLmLogits: (logits: Record<string, number> | null) => void;
|
|
88
108
|
updateContext: (text: string) => void;
|
|
@@ -160,21 +180,7 @@ export declare const LOCAL_MLC_EMBEDDING_MODEL_ID = "snowflake-arctic-embed-s-q0
|
|
|
160
180
|
* the relationship. Encode both sides as passages. See implementation.md §4.3.
|
|
161
181
|
*/
|
|
162
182
|
export declare const wrapForArctic: (text: string) => string;
|
|
163
|
-
|
|
164
|
-
* BE-parity constants — must match `CausalLMEncoder` defaults in the Python
|
|
165
|
-
* sidecar (`cc-smarts/python-sidecar/src/causal_lm_encoder.py`) and
|
|
166
|
-
* `SlowLaneEngine` (`typeahead_context_encoding.py`) so local payloads behave
|
|
167
|
-
* identically to the server-client setup.
|
|
168
|
-
*/
|
|
169
|
-
export declare const BE_PARITY: {
|
|
170
|
-
/** Final payload size cap (BE: `top_k_words`). */
|
|
171
|
-
readonly TOP_K_WORDS: 2000;
|
|
172
|
-
/** L2 (domain) words admitted unconditionally before pooling (BE: `reserved_l2_slots`). */
|
|
173
|
-
readonly RESERVED_L2_SLOTS: 500;
|
|
174
|
-
/** Log-space additive bias favouring L2 over L3 in the pool (BE: `l2_bias`). */
|
|
175
|
-
readonly L2_BIAS: 1;
|
|
176
|
-
/** Drop words below this probability from the final payload (BE: `> 0.00001`). */
|
|
177
|
-
readonly MIN_PROB: 0.00001;
|
|
183
|
+
export declare const LOCAL_INFERENCE: {
|
|
178
184
|
/**
|
|
179
185
|
* Word-level approximation of the BE causal LM token limit.
|
|
180
186
|
*
|
|
@@ -195,33 +201,61 @@ export declare const BE_PARITY: {
|
|
|
195
201
|
*/
|
|
196
202
|
readonly MAX_CONTEXT_WORDS: 100;
|
|
197
203
|
};
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
204
|
+
export declare const CANONICAL_SCORING: {
|
|
205
|
+
/**
|
|
206
|
+
* How many contexts keep their prefilled state before the oldest is dropped.
|
|
207
|
+
*
|
|
208
|
+
* Sizes every context-keyed cache together on purpose: an expansion needs
|
|
209
|
+
* both the boundary logits and the candidate list to still be resident, so
|
|
210
|
+
* bounding them separately would evict half of a context and strand the
|
|
211
|
+
* other half.
|
|
212
|
+
*
|
|
213
|
+
* The context of a keystroke is the text *before* the word being typed,
|
|
214
|
+
* which does not change while that word is typed, so one word should cost
|
|
215
|
+
* one prefill and then hit. Measured hit rate was 45% over the first 84s of
|
|
216
|
+
* a session and 24% over the following four minutes, well short of that, and
|
|
217
|
+
* prompt prefills per decision rose 0.80 → 1.25 across the same split while
|
|
218
|
+
* cost per prefill stayed flat. Contexts were being dropped while still live.
|
|
219
|
+
*
|
|
220
|
+
* A boundary entry holds a `Float32Array` over the 49,152-token vocabulary,
|
|
221
|
+
* so each one is ~192KB and this bound is the dominant term in the scorer's
|
|
222
|
+
* footprint: ~24MB resident here, against ~6MB at the 32 this replaced. That
|
|
223
|
+
* cost is what kept the bound low, not a hit rate anyone had measured.
|
|
224
|
+
*/
|
|
225
|
+
readonly BOUNDARY_CACHE_MAX: 128;
|
|
226
|
+
readonly EXACT_MAX_TARGET_TOKENS: 8;
|
|
227
|
+
readonly PREFIX_CACHE_MAX: 256;
|
|
228
|
+
/**
|
|
229
|
+
* How many candidates compete for expansion in one context.
|
|
230
|
+
*
|
|
231
|
+
* Each distinct token prefix among them is a separate branch, and a branch
|
|
232
|
+
* the engine is not already standing on costs a prompt prefill before its
|
|
233
|
+
* first decode. A wide field therefore spreads a decision's round trips
|
|
234
|
+
* across candidates and finishes none of them inside the budget.
|
|
235
|
+
*/
|
|
236
|
+
readonly PROGRESSIVE_CANDIDATES_MAX: 8;
|
|
237
|
+
/**
|
|
238
|
+
* Ceiling on distinct token prefixes explored per context.
|
|
239
|
+
*
|
|
240
|
+
* Raising this to 8 to give long surfaces more room did the opposite: model
|
|
241
|
+
* calls per decision went 2.35 → 4.32, the warm-KV extend share fell 40% →
|
|
242
|
+
* 31% as the extra branches displaced the live path, the prefix queue backed
|
|
243
|
+
* up to 29 deep, and acceptances per thousand decisions fell 9.3 → 6.7. No
|
|
244
|
+
* phrase was shown either way, so breadth was never the binding constraint.
|
|
245
|
+
*
|
|
246
|
+
* What blocked them then was normalisation against a short word's per-token
|
|
247
|
+
* mean, which has since been replaced by a posterior over sequence
|
|
248
|
+
* log-likelihoods. That removes the bias towards short surfaces but does not
|
|
249
|
+
* by itself make phrases reachable: a longer surface is strictly less likely
|
|
250
|
+
* than a shorter one, so a phrase sharing a shortlist with a unigram still
|
|
251
|
+
* holds little of its mass. Whether the remaining gap is the threshold or the
|
|
252
|
+
* comparison is still open.
|
|
253
|
+
*/
|
|
254
|
+
readonly PROGRESSIVE_EXPANSIONS_PER_CONTEXT_MAX: 4;
|
|
255
|
+
readonly PROGRESSIVE_INPUT_MAX: 400;
|
|
256
|
+
readonly SURFACE_CACHE_MAX: 128;
|
|
257
|
+
readonly TIER_A_PRIMES_MAX: 3;
|
|
258
|
+
};
|
|
225
259
|
/**
|
|
226
260
|
* Create a local slow-lane client powered by MLC WebLLM.
|
|
227
261
|
*
|
|
@@ -233,9 +267,8 @@ validTokenIds?: number[]) => Record<string, number>;
|
|
|
233
267
|
* const client = createLocalSlowLaneClient({ debounceMs: 300 });
|
|
234
268
|
* // On word boundaries:
|
|
235
269
|
* client.updateContext(docText);
|
|
236
|
-
* //
|
|
237
|
-
*
|
|
238
|
-
* const logits = client.getLmLogits();
|
|
270
|
+
* // Candidate scoring independently calls
|
|
271
|
+
* // primeBoundaryLm/requestProgressiveSurfaceScores.
|
|
239
272
|
* // On plugin teardown:
|
|
240
273
|
* client.destroy();
|
|
241
274
|
* ```
|
|
@@ -7,18 +7,35 @@
|
|
|
7
7
|
* The grammar/POS payloads are fetched from the CDN by loadGrammarDataAsync, so
|
|
8
8
|
* the grammar filter is a pass-through until that resolves.
|
|
9
9
|
*/
|
|
10
|
+
import type { CanonicalLmEvidence } from './canonical-lm-scoring';
|
|
11
|
+
/**
|
|
12
|
+
* A completion term is `(term, termType)`. `word` is the v1 default; `bigram`
|
|
13
|
+
* (2 tokens) and `phrase` (3 tokens) are multi-word units mined by the producer.
|
|
14
|
+
*/
|
|
15
|
+
export type TermType = 'word' | 'bigram' | 'phrase';
|
|
10
16
|
export interface ScoringCandidate {
|
|
17
|
+
/** NPMI-style association strength (bigram/phrase only). */
|
|
18
|
+
association?: number | null;
|
|
11
19
|
authorFreq: number;
|
|
12
20
|
docFreq: number;
|
|
21
|
+
/** Last-token POS of a multi-word term (bigram/phrase only). */
|
|
22
|
+
headPos?: string | null;
|
|
23
|
+
/** Per-token POS sequence of a multi-word term (bigram/phrase only). */
|
|
24
|
+
posSeq?: string[] | null;
|
|
13
25
|
sessionFreq: number;
|
|
14
26
|
tenantFreq: number;
|
|
27
|
+
/** Defaults to `'word'` when omitted (back-compat with word-only callers). */
|
|
28
|
+
termType?: TermType;
|
|
15
29
|
word: string;
|
|
16
30
|
}
|
|
17
31
|
export interface ScoredCandidate {
|
|
18
32
|
finalScore: number;
|
|
19
33
|
freqScore: number;
|
|
34
|
+
lmEvidence: CanonicalLmEvidence | null;
|
|
20
35
|
lmScore: number;
|
|
21
36
|
semanticScore: number;
|
|
37
|
+
stage1Score: number;
|
|
38
|
+
termType: TermType;
|
|
22
39
|
word: string;
|
|
23
40
|
}
|
|
24
41
|
/** Metadata returned by the grammar filter for debug logging in the caller. */
|
|
@@ -32,6 +49,18 @@ export interface GrammarFilterMeta {
|
|
|
32
49
|
export declare const STAGE1_WEIGHT = 0.35;
|
|
33
50
|
export declare const STAGE2_WEIGHT = 0.65;
|
|
34
51
|
export declare const MIN_STAGE1_SCORE = 0.35;
|
|
52
|
+
/**
|
|
53
|
+
* How far the leading candidate must sit above the runner-up on the blended
|
|
54
|
+
* score before a ghost is committed.
|
|
55
|
+
*
|
|
56
|
+
* Lives beside the weights it is measured in, because it is only meaningful on
|
|
57
|
+
* that scale — and because both the arbitration that applies it and the
|
|
58
|
+
* scheduler that decides when reading more tokens can no longer change the
|
|
59
|
+
* outcome have to use the same number. When those two drifted apart, the
|
|
60
|
+
* scheduler stopped early on a margin the arbitration did not recognise and the
|
|
61
|
+
* abstention that followed was invisible from either side.
|
|
62
|
+
*/
|
|
63
|
+
export declare const MIN_WINNER_MARGIN = 0.08;
|
|
35
64
|
export declare const isGrammarDataLoaded: () => boolean;
|
|
36
65
|
/**
|
|
37
66
|
* Fetch the POS tag and grammar transition payloads from the CDN.
|
|
@@ -54,4 +83,7 @@ export interface RankCandidatesResult {
|
|
|
54
83
|
grammarMeta: GrammarFilterMeta | null;
|
|
55
84
|
pipelineDebug: PipelineDebug;
|
|
56
85
|
}
|
|
57
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Apply Stage 1, grammar, and nullable context-local LM evidence to candidates.
|
|
88
|
+
*/
|
|
89
|
+
export declare function rankCandidates(candidates: ScoringCandidate[], contextVector: Float32Array | null, getWordVector: (word: string) => Float32Array | null, lmLogits: Record<string, number> | null, maxFreqByType: Record<TermType, number>, previousWord: string, getCanonicalLmEvidence?: (surface: string) => CanonicalLmEvidence | null): RankCandidatesResult;
|