pi-ghost-autocomplete 0.3.1 → 0.4.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/LICENSE +561 -0
- package/README.md +464 -236
- package/dist/bench-cli.js +1 -47
- package/dist/bench.d.ts +137 -5
- package/dist/bench.js +1 -241
- package/dist/config.d.ts +47 -1
- package/dist/context-aggregator.d.ts +18 -6
- package/dist/cycle-manager.d.ts +83 -0
- package/dist/entropy-stop.d.ts +105 -0
- package/dist/eval-dataset.d.ts +55 -0
- package/dist/fuzzy-matcher.d.ts +12 -1
- package/dist/ghost-editor.d.ts +332 -6
- package/dist/index.d.ts +21 -1
- package/dist/index.js +1 -210
- package/dist/intent.d.ts +179 -0
- package/dist/metrics.d.ts +103 -3
- package/dist/path-index.d.ts +4 -1
- package/dist/path-matcher.d.ts +13 -1
- package/dist/profile-store.d.ts +85 -0
- package/dist/profile.d.ts +167 -0
- package/dist/providers/cloud-provider.d.ts +14 -2
- package/dist/providers/local-provider.d.ts +12 -2
- package/dist/providers/openai-compat-provider.d.ts +35 -2
- package/dist/providers/race.d.ts +62 -2
- package/dist/providers/types.d.ts +109 -2
- package/dist/recent-paths.d.ts +4 -1
- package/dist/replay-cli.js +2 -0
- package/dist/replay.d.ts +102 -0
- package/package.json +90 -79
- package/dist/bench-cli.d.ts +0 -3
- package/dist/bench-cli.d.ts.map +0 -1
- package/dist/bench-cli.js.map +0 -1
- package/dist/bench.d.ts.map +0 -1
- package/dist/bench.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -187
- package/dist/config.js.map +0 -1
- package/dist/context-aggregator.d.ts.map +0 -1
- package/dist/context-aggregator.js +0 -58
- package/dist/context-aggregator.js.map +0 -1
- package/dist/fuzzy-matcher.d.ts.map +0 -1
- package/dist/fuzzy-matcher.js +0 -160
- package/dist/fuzzy-matcher.js.map +0 -1
- package/dist/ghost-editor.d.ts.map +0 -1
- package/dist/ghost-editor.js +0 -445
- package/dist/ghost-editor.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/metrics.d.ts.map +0 -1
- package/dist/metrics.js +0 -98
- package/dist/metrics.js.map +0 -1
- package/dist/path-index.d.ts.map +0 -1
- package/dist/path-index.js +0 -148
- package/dist/path-index.js.map +0 -1
- package/dist/path-matcher.d.ts.map +0 -1
- package/dist/path-matcher.js +0 -85
- package/dist/path-matcher.js.map +0 -1
- package/dist/providers/cloud-provider.d.ts.map +0 -1
- package/dist/providers/cloud-provider.js +0 -52
- package/dist/providers/cloud-provider.js.map +0 -1
- package/dist/providers/local-provider.d.ts.map +0 -1
- package/dist/providers/local-provider.js +0 -36
- package/dist/providers/local-provider.js.map +0 -1
- package/dist/providers/openai-compat-provider.d.ts.map +0 -1
- package/dist/providers/openai-compat-provider.js +0 -128
- package/dist/providers/openai-compat-provider.js.map +0 -1
- package/dist/providers/openai-completions-runner.d.ts +0 -28
- package/dist/providers/openai-completions-runner.d.ts.map +0 -1
- package/dist/providers/openai-completions-runner.js +0 -46
- package/dist/providers/openai-completions-runner.js.map +0 -1
- package/dist/providers/race.d.ts.map +0 -1
- package/dist/providers/race.js +0 -128
- package/dist/providers/race.js.map +0 -1
- package/dist/providers/truncate.d.ts +0 -7
- package/dist/providers/truncate.d.ts.map +0 -1
- package/dist/providers/truncate.js +0 -39
- package/dist/providers/truncate.js.map +0 -1
- package/dist/providers/types.d.ts.map +0 -1
- package/dist/providers/types.js +0 -2
- package/dist/providers/types.js.map +0 -1
- package/dist/recent-paths.d.ts.map +0 -1
- package/dist/recent-paths.js +0 -114
- package/dist/recent-paths.js.map +0 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-License-Identifier: SSPL-1.0
|
|
3
|
+
* SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
|
|
4
|
+
*/
|
|
1
5
|
import type { OpenAICompatProviderConfig } from "../config.js";
|
|
2
|
-
import type { GhostProvider, GhostRequest } from "./types.js";
|
|
6
|
+
import type { CandidateSet, GhostProvider, GhostRequest } from "./types.js";
|
|
7
|
+
import { type PromptIntent } from "../intent.js";
|
|
3
8
|
export interface PreflightResult {
|
|
4
9
|
ok: boolean;
|
|
5
10
|
status?: number;
|
|
@@ -25,5 +30,33 @@ export declare class OpenAICompatProvider implements GhostProvider {
|
|
|
25
30
|
*/
|
|
26
31
|
preflight(): Promise<PreflightResult>;
|
|
27
32
|
predict(req: GhostRequest): Promise<string | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Multi-candidate prediction. Fires `config.parallelN` FIM requests in
|
|
35
|
+
* parallel and dedupes by exact text. PRD §M3b: FIM does not support
|
|
36
|
+
* the `n` parameter, so parallel POSTs are the only n>1 path. Parallel
|
|
37
|
+
* fire is only useful at temperature > 0 — at temp=0 every call returns
|
|
38
|
+
* the same greedy result. We downgrade silently to n=1 in that case.
|
|
39
|
+
*/
|
|
40
|
+
predictMulti(req: GhostRequest): Promise<CandidateSet | null>;
|
|
41
|
+
private effectiveParallelN;
|
|
42
|
+
private fireOne;
|
|
28
43
|
}
|
|
29
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Whether the conversation context should be included in the FIM prompt for a
|
|
46
|
+
* given intent. Pure + exported for direct testing. Set
|
|
47
|
+
* `PI_GHOST_FIM_CONTEXT_ALL_INTENTS=1` to force-include context for every
|
|
48
|
+
* intent (A/B against the empty-rate fix).
|
|
49
|
+
*/
|
|
50
|
+
export declare function fimContextIncluded(intent: PromptIntent | undefined, allIntentsRaw: string | undefined): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Build the FIM prompt/suffix pair from the ghost request.
|
|
53
|
+
*
|
|
54
|
+
* Recent conversation messages are prepended as comments in the prompt so the
|
|
55
|
+
* model has context about what the user is working on — except for `prose` /
|
|
56
|
+
* `question` intents, where the context is dropped to avoid the empty-rate
|
|
57
|
+
* spike described on {@link FIM_CONTEXT_SUPPRESSED_INTENTS}.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildFIMPrompt(req: GhostRequest, maxRecent: number): {
|
|
60
|
+
prompt: string;
|
|
61
|
+
suffix: string;
|
|
62
|
+
};
|
package/dist/providers/race.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-License-Identifier: SSPL-1.0
|
|
3
|
+
* SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
|
|
4
|
+
*/
|
|
1
5
|
import type { ProviderLatency } from "../metrics.js";
|
|
2
|
-
import type
|
|
6
|
+
import { type GhostProvider, type GhostRequest, type StreamDelta } from "./types.js";
|
|
3
7
|
export interface RaceConfig {
|
|
4
8
|
/** Pause/debounce before race starts. Reported as `debounceMs`. */
|
|
5
9
|
pauseMs: number;
|
|
@@ -11,6 +15,8 @@ export interface RaceConfig {
|
|
|
11
15
|
export interface RaceTrace {
|
|
12
16
|
providers: ProviderLatency[];
|
|
13
17
|
winner?: string;
|
|
18
|
+
/** Members that the weighting policy declined to fire (PRD §M8). */
|
|
19
|
+
skipped?: string[];
|
|
14
20
|
}
|
|
15
21
|
/**
|
|
16
22
|
* Implements {@link GhostProvider} by racing several providers in parallel.
|
|
@@ -26,10 +32,65 @@ export declare class RaceProvider implements GhostProvider {
|
|
|
26
32
|
readonly name: string;
|
|
27
33
|
private cooldownUntil;
|
|
28
34
|
private lastTrace;
|
|
35
|
+
private readonly stats;
|
|
29
36
|
constructor(members: GhostProvider[], cfg: RaceConfig);
|
|
37
|
+
/** Snapshot of per-member weighting stats. Test/diagnostic only. */
|
|
38
|
+
getMemberStats(): Array<{
|
|
39
|
+
name: string;
|
|
40
|
+
slowCount: number;
|
|
41
|
+
wins: number;
|
|
42
|
+
skipRemaining: number;
|
|
43
|
+
}>;
|
|
30
44
|
get debounceMs(): number;
|
|
31
45
|
consumeLastTrace(): RaceTrace | null;
|
|
46
|
+
/**
|
|
47
|
+
* Cooldown snapshot for speculative cache warm. The editor consults this
|
|
48
|
+
* synchronously in `handleTextChange` to decide whether the backspace-of-N
|
|
49
|
+
* branch may serve a result (it is the most aggressive branch and is only
|
|
50
|
+
* appropriate when a fresh race would no-op anyway). See PRD §6 M1b.
|
|
51
|
+
*/
|
|
52
|
+
getCooldownState(now?: number): {
|
|
53
|
+
active: boolean;
|
|
54
|
+
remainingMs: number;
|
|
55
|
+
};
|
|
32
56
|
predict(req: GhostRequest): Promise<string | null>;
|
|
57
|
+
/**
|
|
58
|
+
* After a race resolves, update per-member rolling stats and decide who
|
|
59
|
+
* gets a skip penalty for the next `SKIP_RACES`. A member earns "slow"
|
|
60
|
+
* if it ran but didn't win this race (when there was a winner).
|
|
61
|
+
*
|
|
62
|
+
* `userAborted` distinguishes user-keystroke cancellation from
|
|
63
|
+
* race-cancellation-by-winner. Member's trace result is "aborted" in
|
|
64
|
+
* both cases; we only penalize the latter (the member could have run
|
|
65
|
+
* to completion but lost the race). User keystrokes shouldn't drift
|
|
66
|
+
* the weighting at all.
|
|
67
|
+
*/
|
|
68
|
+
private recordRaceOutcome;
|
|
69
|
+
/**
|
|
70
|
+
* PRD §M3.5a — streaming variant of `predict`. Each member runs its own
|
|
71
|
+
* stream under a dedicated AbortController; first member to emit a
|
|
72
|
+
* non-empty delta wins. Losing members are aborted and their iterators
|
|
73
|
+
* released. The winner's remaining chunks pass through untouched.
|
|
74
|
+
*
|
|
75
|
+
* Trace + stats are recorded identically to `predict()` so M8 weighting
|
|
76
|
+
* works the same way: winner = first to first-chunk; losers that also
|
|
77
|
+
* produced are still marked `produced` (they did emit), losers aborted
|
|
78
|
+
* before producing are `aborted`.
|
|
79
|
+
*/
|
|
80
|
+
predictStream(req: GhostRequest): AsyncIterable<StreamDelta>;
|
|
81
|
+
/**
|
|
82
|
+
* Pump a member's stream until its first non-empty delta. Resolves to
|
|
83
|
+
* the parked iterator + that delta when produced; null on
|
|
84
|
+
* empty/aborted/error. Trace is appended for terminal outcomes; the
|
|
85
|
+
* `produced` entry is pushed once the first non-empty delta arrives so
|
|
86
|
+
* its latency reflects TTFT, not end-of-stream.
|
|
87
|
+
*/
|
|
88
|
+
private firstChunkOf;
|
|
89
|
+
/**
|
|
90
|
+
* Reset all rolling stats (PRD §M8: "Reset state on `setProvider` /
|
|
91
|
+
* extension reload"). Public for editor reset paths.
|
|
92
|
+
*/
|
|
93
|
+
resetStats(): void;
|
|
33
94
|
private runMember;
|
|
34
95
|
}
|
|
35
96
|
/**
|
|
@@ -37,4 +98,3 @@ export declare class RaceProvider implements GhostProvider {
|
|
|
37
98
|
* all promises produce null (or throw). Never rejects.
|
|
38
99
|
*/
|
|
39
100
|
export declare function firstNonNull<T>(promises: Array<Promise<T | null>>): Promise<T | null>;
|
|
40
|
-
//# sourceMappingURL=race.d.ts.map
|
|
@@ -1,18 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-License-Identifier: SSPL-1.0
|
|
3
|
+
* SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
|
|
4
|
+
*/
|
|
1
5
|
import type { PreflightResult } from "./openai-compat-provider.js";
|
|
6
|
+
import type { PromptIntent } from "../intent.js";
|
|
2
7
|
export interface GhostRequest {
|
|
3
8
|
editorText: string;
|
|
4
9
|
editorTextBeforeCursor: string;
|
|
10
|
+
/**
|
|
11
|
+
* Recent conversation messages, oldest-first. `role: "tool"` entries
|
|
12
|
+
* may carry a `toolName` for richer prompt formatting (PRD §M6a). The
|
|
13
|
+
* editor only surfaces tool entries when `includeTools` is enabled in
|
|
14
|
+
* `context-aggregator.getRecentMessages`.
|
|
15
|
+
*/
|
|
5
16
|
recentMessages: Array<{
|
|
6
|
-
role: "user" | "assistant";
|
|
17
|
+
role: "user" | "assistant" | "tool";
|
|
7
18
|
text: string;
|
|
19
|
+
toolName?: string;
|
|
8
20
|
}>;
|
|
9
21
|
cwd: string;
|
|
10
22
|
signal: AbortSignal;
|
|
23
|
+
/**
|
|
24
|
+
* Classified intent of the current prompt line. When set, providers use
|
|
25
|
+
* it to tailor the system prompt / FIM prompt so the model receives
|
|
26
|
+
* explicit signals about what kind of completion to produce.
|
|
27
|
+
* Undefined for backward compatibility (providers built before intent
|
|
28
|
+
* classification was added).
|
|
29
|
+
*/
|
|
30
|
+
intent?: PromptIntent;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One candidate completion. `avgLogprob` is the average per-token logprob if
|
|
34
|
+
* the provider returned them; undefined when not supported. Reranker treats
|
|
35
|
+
* missing logprobs as the neutral mid-value (0.5 after normalization) per
|
|
36
|
+
* PRD §M3c.
|
|
37
|
+
*/
|
|
38
|
+
export interface GhostCandidate {
|
|
39
|
+
text: string;
|
|
40
|
+
avgLogprob?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of a multi-candidate prediction. `candidates` is ordered as the
|
|
44
|
+
* provider returned them (no rerank applied yet). Single-candidate providers
|
|
45
|
+
* return a single-element array.
|
|
46
|
+
*/
|
|
47
|
+
export interface CandidateSet {
|
|
48
|
+
candidates: GhostCandidate[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* One streaming delta from a provider. PRD §M3.5a — captures both the
|
|
52
|
+
* incremental piece (`delta`) and the running concatenation (`cumulative`)
|
|
53
|
+
* so consumers don't have to track state. `firstChunk` flags the very
|
|
54
|
+
* first non-empty delta, which is what the editor stamps as `ttftMs`.
|
|
55
|
+
*/
|
|
56
|
+
export interface StreamDelta {
|
|
57
|
+
delta: string;
|
|
58
|
+
cumulative: string;
|
|
59
|
+
firstChunk: boolean;
|
|
11
60
|
}
|
|
12
61
|
export interface GhostProvider {
|
|
13
62
|
readonly name: string;
|
|
14
63
|
readonly debounceMs: number;
|
|
15
64
|
predict(req: GhostRequest): Promise<string | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Optional multi-candidate variant. When absent the editor wraps
|
|
67
|
+
* `predict()` via `wrapAsMulti`. See PRD §M3a.
|
|
68
|
+
*/
|
|
69
|
+
predictMulti?(req: GhostRequest): Promise<CandidateSet | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Optional streaming variant. When present the editor uses it on
|
|
72
|
+
* single-candidate paths to capture `ttftMs` and (in a follow-up
|
|
73
|
+
* iteration) drive incremental UI rendering. Providers that don't
|
|
74
|
+
* stream (e.g. Mercury Edit FIM) omit this method.
|
|
75
|
+
*/
|
|
76
|
+
predictStream?(req: GhostRequest): AsyncIterable<StreamDelta>;
|
|
16
77
|
preflight?(): Promise<boolean | PreflightResult>;
|
|
78
|
+
/**
|
|
79
|
+
* PRD §M3g / §M7 capability flag — true iff the provider populates
|
|
80
|
+
* per-token logprob values on streamed `StreamDelta` events (M7
|
|
81
|
+
* entropy-stop input). `StreamDelta` will be extended in a future
|
|
82
|
+
* change to include per-token logprob data. No provider currently sets
|
|
83
|
+
* this since `@earendil-works/pi-ai` does not surface logprobs; left in
|
|
84
|
+
* place so M7 wiring can flip on once upstream support lands without
|
|
85
|
+
* changing the diagnostics surface. Wording aligns with
|
|
86
|
+
* `GhostCandidate.avgLogprob`.
|
|
87
|
+
*/
|
|
88
|
+
readonly supportsLogprobs?: boolean;
|
|
17
89
|
}
|
|
18
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Adapt a `predict()`-only provider into the `CandidateSet | null` shape.
|
|
92
|
+
* Used by the editor to give every provider a uniform multi-candidate
|
|
93
|
+
* surface without forcing third-party implementations to upgrade.
|
|
94
|
+
*
|
|
95
|
+
* Wrapped candidates intentionally have `avgLogprob: undefined` — the
|
|
96
|
+
* `predict()` signature returns only text, so logprobs aren't recoverable.
|
|
97
|
+
* The reranker normalizes missing logprobs to the neutral mid-value (0.5)
|
|
98
|
+
* per PRD §M3c, so single-candidate wrappers aren't penalized against
|
|
99
|
+
* providers that do return logprobs.
|
|
100
|
+
*/
|
|
101
|
+
export declare function wrapAsMulti(provider: GhostProvider, req: GhostRequest): Promise<CandidateSet | null>;
|
|
102
|
+
/**
|
|
103
|
+
* Adapt any provider to the streaming surface. Providers with
|
|
104
|
+
* `predictStream` pass through unchanged; non-streaming providers get a
|
|
105
|
+
* single-delta stream emitted after `predict()` resolves. `firstChunk`
|
|
106
|
+
* is true on the only emission in the non-streaming case.
|
|
107
|
+
*
|
|
108
|
+
* Yields nothing when the provider returns null/empty. Consumer code
|
|
109
|
+
* pattern:
|
|
110
|
+
*
|
|
111
|
+
* ```ts
|
|
112
|
+
* let full = "";
|
|
113
|
+
* for await (const { cumulative } of wrapAsStream(p, req)) full = cumulative;
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export declare function wrapAsStream(provider: GhostProvider, req: GhostRequest): AsyncIterable<StreamDelta>;
|
|
117
|
+
/**
|
|
118
|
+
* Drain a stream into its final cumulative text. Records the timestamp
|
|
119
|
+
* of the first non-empty chunk on `out.ttftMs` (relative to `startedAt`)
|
|
120
|
+
* for the caller. The result is the full assembled text, or null if the
|
|
121
|
+
* stream produced no content.
|
|
122
|
+
*/
|
|
123
|
+
export declare function streamToText(stream: AsyncIterable<StreamDelta>, startedAt: number, out?: {
|
|
124
|
+
ttftMs?: number;
|
|
125
|
+
}): Promise<string | null>;
|
package/dist/recent-paths.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-License-Identifier: SSPL-1.0
|
|
3
|
+
* SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
|
|
4
|
+
*/
|
|
1
5
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
6
|
import { type RecentMessage } from "./context-aggregator.js";
|
|
3
7
|
/**
|
|
@@ -22,4 +26,3 @@ export declare function recentBoost(matchPath: string, recent: Set<string> | nul
|
|
|
22
26
|
* assistant messages. Oldest-first.
|
|
23
27
|
*/
|
|
24
28
|
export declare function getRoleWeightedRecent(ctx: ExtensionContext, userMax: number, assistantMax: number): RecentMessage[];
|
|
25
|
-
//# sourceMappingURL=recent-paths.d.ts.map
|