llm-cli-gateway 2.15.0 → 2.16.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 +50 -1
- package/dist/acp/process-manager.js +2 -1
- package/dist/acp/provider-registry.js +8 -8
- package/dist/async-job-manager.d.ts +4 -1
- package/dist/async-job-manager.js +15 -6
- package/dist/compressor/estimate.d.ts +5 -0
- package/dist/compressor/estimate.js +21 -0
- package/dist/compressor/index.d.ts +23 -0
- package/dist/compressor/index.js +77 -0
- package/dist/compressor/router.d.ts +2 -0
- package/dist/compressor/router.js +57 -0
- package/dist/compressor/transforms/ansi.d.ts +3 -0
- package/dist/compressor/transforms/ansi.js +89 -0
- package/dist/compressor/transforms/json.d.ts +1 -0
- package/dist/compressor/transforms/json.js +156 -0
- package/dist/compressor/transforms/log.d.ts +12 -0
- package/dist/compressor/transforms/log.js +55 -0
- package/dist/compressor/transforms/whitespace.d.ts +8 -0
- package/dist/compressor/transforms/whitespace.js +79 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +25 -0
- package/dist/executor.d.ts +1 -0
- package/dist/executor.js +5 -2
- package/dist/flight-recorder.d.ts +9 -0
- package/dist/flight-recorder.js +42 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.js +273 -66
- package/dist/job-store.d.ts +4 -0
- package/dist/job-store.js +16 -0
- package/dist/provider-definitions.d.ts +1 -0
- package/dist/provider-definitions.js +28 -10
- package/dist/provider-tool-capabilities.js +3 -3
- package/dist/request-helpers.js +1 -1
- package/dist/spawn-env-isolation.d.ts +10 -0
- package/dist/spawn-env-isolation.js +55 -0
- package/dist/upstream-contracts.js +47 -27
- package/npm-shrinkwrap.json +2 -2
- package/package.json +7 -3
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
const WS = new Set([" ", "\t", "\n", "\r"]);
|
|
2
|
+
function skipWs(c) {
|
|
3
|
+
while (c.pos < c.text.length && WS.has(c.text[c.pos]))
|
|
4
|
+
c.pos += 1;
|
|
5
|
+
}
|
|
6
|
+
function fail() {
|
|
7
|
+
throw new SyntaxError("json-minify: not valid JSON");
|
|
8
|
+
}
|
|
9
|
+
const SIMPLE_ESCAPES = new Set(['"', "\\", "/", "b", "f", "n", "r", "t"]);
|
|
10
|
+
const HEX = /^[0-9a-fA-F]{4}$/;
|
|
11
|
+
function copyString(c) {
|
|
12
|
+
const start = c.pos;
|
|
13
|
+
if (c.text[c.pos] !== '"')
|
|
14
|
+
fail();
|
|
15
|
+
c.pos += 1;
|
|
16
|
+
while (c.pos < c.text.length) {
|
|
17
|
+
const ch = c.text[c.pos];
|
|
18
|
+
if (ch === "\\") {
|
|
19
|
+
const esc = c.text[c.pos + 1];
|
|
20
|
+
if (esc === undefined)
|
|
21
|
+
fail();
|
|
22
|
+
if (esc === "u") {
|
|
23
|
+
if (!HEX.test(c.text.slice(c.pos + 2, c.pos + 6)))
|
|
24
|
+
fail();
|
|
25
|
+
c.pos += 6;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (!SIMPLE_ESCAPES.has(esc))
|
|
29
|
+
fail();
|
|
30
|
+
c.pos += 2;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (ch === '"') {
|
|
34
|
+
c.pos += 1;
|
|
35
|
+
c.out.push(c.text.slice(start, c.pos));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (ch.charCodeAt(0) < 0x20)
|
|
39
|
+
fail();
|
|
40
|
+
c.pos += 1;
|
|
41
|
+
}
|
|
42
|
+
fail();
|
|
43
|
+
}
|
|
44
|
+
const NUMBER = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/;
|
|
45
|
+
function copyNumber(c) {
|
|
46
|
+
const match = NUMBER.exec(c.text.slice(c.pos));
|
|
47
|
+
if (!match || match[0].length === 0)
|
|
48
|
+
fail();
|
|
49
|
+
c.out.push(match[0]);
|
|
50
|
+
c.pos += match[0].length;
|
|
51
|
+
}
|
|
52
|
+
function copyLiteral(c, word) {
|
|
53
|
+
if (c.text.startsWith(word, c.pos)) {
|
|
54
|
+
c.out.push(word);
|
|
55
|
+
c.pos += word.length;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
fail();
|
|
59
|
+
}
|
|
60
|
+
function copyValue(c) {
|
|
61
|
+
skipWs(c);
|
|
62
|
+
const ch = c.text[c.pos];
|
|
63
|
+
if (ch === undefined)
|
|
64
|
+
fail();
|
|
65
|
+
if (ch === "{") {
|
|
66
|
+
c.out.push("{");
|
|
67
|
+
c.pos += 1;
|
|
68
|
+
skipWs(c);
|
|
69
|
+
if (c.text[c.pos] === "}") {
|
|
70
|
+
c.out.push("}");
|
|
71
|
+
c.pos += 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
for (;;) {
|
|
75
|
+
skipWs(c);
|
|
76
|
+
copyString(c);
|
|
77
|
+
skipWs(c);
|
|
78
|
+
if (c.text[c.pos] !== ":")
|
|
79
|
+
fail();
|
|
80
|
+
c.out.push(":");
|
|
81
|
+
c.pos += 1;
|
|
82
|
+
copyValue(c);
|
|
83
|
+
skipWs(c);
|
|
84
|
+
if (c.text[c.pos] === ",") {
|
|
85
|
+
c.out.push(",");
|
|
86
|
+
c.pos += 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (c.text[c.pos] === "}") {
|
|
90
|
+
c.out.push("}");
|
|
91
|
+
c.pos += 1;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
fail();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (ch === "[") {
|
|
98
|
+
c.out.push("[");
|
|
99
|
+
c.pos += 1;
|
|
100
|
+
skipWs(c);
|
|
101
|
+
if (c.text[c.pos] === "]") {
|
|
102
|
+
c.out.push("]");
|
|
103
|
+
c.pos += 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
for (;;) {
|
|
107
|
+
copyValue(c);
|
|
108
|
+
skipWs(c);
|
|
109
|
+
if (c.text[c.pos] === ",") {
|
|
110
|
+
c.out.push(",");
|
|
111
|
+
c.pos += 1;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (c.text[c.pos] === "]") {
|
|
115
|
+
c.out.push("]");
|
|
116
|
+
c.pos += 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
fail();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (ch === '"') {
|
|
123
|
+
copyString(c);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (ch === "t") {
|
|
127
|
+
copyLiteral(c, "true");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (ch === "f") {
|
|
131
|
+
copyLiteral(c, "false");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (ch === "n") {
|
|
135
|
+
copyLiteral(c, "null");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (ch === "-" || (ch >= "0" && ch <= "9")) {
|
|
139
|
+
copyNumber(c);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
fail();
|
|
143
|
+
}
|
|
144
|
+
export function minifyJson(text) {
|
|
145
|
+
const c = { text, pos: 0, out: [] };
|
|
146
|
+
try {
|
|
147
|
+
copyValue(c);
|
|
148
|
+
skipWs(c);
|
|
149
|
+
if (c.pos !== text.length)
|
|
150
|
+
return null;
|
|
151
|
+
return c.out.join("");
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const SENTINEL_PREFIX = "[[gateway-";
|
|
2
|
+
export declare const LIT_MARKER = "[[gateway-lit:v1]] ";
|
|
3
|
+
export declare function repeatMarker(lines: number, count: number): string;
|
|
4
|
+
export declare function crMarker(frames: number): string;
|
|
5
|
+
export declare function noteLine(folded: number, escaped: number): string;
|
|
6
|
+
export interface MarkerCounts {
|
|
7
|
+
folded: number;
|
|
8
|
+
escaped: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function escapeSentinelLikeLines(text: string, counts: MarkerCounts): string;
|
|
11
|
+
export declare const MIN_RUN = 3;
|
|
12
|
+
export declare function dedupAdjacentLines(text: string, counts: MarkerCounts): string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { mapUnfenced } from "./whitespace.js";
|
|
2
|
+
export const SENTINEL_PREFIX = "[[gateway-";
|
|
3
|
+
export const LIT_MARKER = "[[gateway-lit:v1]] ";
|
|
4
|
+
export function repeatMarker(lines, count) {
|
|
5
|
+
return `[[gateway-repeat:v1 lines=${lines} count=${count}]]`;
|
|
6
|
+
}
|
|
7
|
+
export function crMarker(frames) {
|
|
8
|
+
return `[[gateway-cr:v1 frames=${frames}]]`;
|
|
9
|
+
}
|
|
10
|
+
export function noteLine(folded, escaped) {
|
|
11
|
+
return (`[[gateway-note:v1 folded=${folded} escaped=${escaped}]] ` +
|
|
12
|
+
"Gateway compressor markers follow: [[gateway-repeat:v1 ...]] folds byte-identical repeated lines (count included), " +
|
|
13
|
+
"[[gateway-cr:v1 ...]] keeps the final frame of carriage-return-rewritten lines, " +
|
|
14
|
+
"and lines opening with [[gateway-lit:v1]] are verbatim input with that prefix added.");
|
|
15
|
+
}
|
|
16
|
+
export function escapeSentinelLikeLines(text, counts) {
|
|
17
|
+
return mapUnfenced(text, segment => segment
|
|
18
|
+
.split("\n")
|
|
19
|
+
.map(line => {
|
|
20
|
+
if (line.trimStart().startsWith(SENTINEL_PREFIX)) {
|
|
21
|
+
counts.escaped += 1;
|
|
22
|
+
return LIT_MARKER + line;
|
|
23
|
+
}
|
|
24
|
+
return line;
|
|
25
|
+
})
|
|
26
|
+
.join("\n"));
|
|
27
|
+
}
|
|
28
|
+
export const MIN_RUN = 3;
|
|
29
|
+
export function dedupAdjacentLines(text, counts) {
|
|
30
|
+
return mapUnfenced(text, segment => {
|
|
31
|
+
const lines = segment.split("\n");
|
|
32
|
+
const out = [];
|
|
33
|
+
let i = 0;
|
|
34
|
+
while (i < lines.length) {
|
|
35
|
+
const line = lines[i];
|
|
36
|
+
let runEnd = i + 1;
|
|
37
|
+
if (line.trim() !== "") {
|
|
38
|
+
while (runEnd < lines.length && lines[runEnd] === line)
|
|
39
|
+
runEnd += 1;
|
|
40
|
+
}
|
|
41
|
+
const runLength = runEnd - i;
|
|
42
|
+
if (runLength >= MIN_RUN) {
|
|
43
|
+
out.push(line);
|
|
44
|
+
out.push(repeatMarker(1, runLength));
|
|
45
|
+
counts.folded += 1;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
for (let j = i; j < runEnd; j += 1)
|
|
49
|
+
out.push(lines[j]);
|
|
50
|
+
}
|
|
51
|
+
i = runEnd;
|
|
52
|
+
}
|
|
53
|
+
return out.join("\n");
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface FenceSegment {
|
|
2
|
+
text: string;
|
|
3
|
+
fenced: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function splitFences(text: string): FenceSegment[];
|
|
6
|
+
export declare function joinFences(segments: FenceSegment[]): string;
|
|
7
|
+
export declare function mapUnfenced(text: string, op: (segment: string) => string): string;
|
|
8
|
+
export declare function normalizeWhitespace(text: string): string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;
|
|
2
|
+
export function splitFences(text) {
|
|
3
|
+
const segments = [];
|
|
4
|
+
const lines = text.split("\n");
|
|
5
|
+
let current = [];
|
|
6
|
+
let fenced = false;
|
|
7
|
+
let fenceMarker = "";
|
|
8
|
+
const flush = (nextFenced) => {
|
|
9
|
+
if (current.length > 0) {
|
|
10
|
+
segments.push({ text: current.join("\n"), fenced });
|
|
11
|
+
current = [];
|
|
12
|
+
}
|
|
13
|
+
fenced = nextFenced;
|
|
14
|
+
};
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
if (!fenced) {
|
|
17
|
+
const open = FENCE_OPEN.exec(line);
|
|
18
|
+
if (open) {
|
|
19
|
+
flush(true);
|
|
20
|
+
fenceMarker = open[1];
|
|
21
|
+
current.push(line);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
current.push(line);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
current.push(line);
|
|
28
|
+
const marker = FENCE_OPEN.exec(line);
|
|
29
|
+
if (marker &&
|
|
30
|
+
marker[1][0] === fenceMarker[0] &&
|
|
31
|
+
marker[1].length >= fenceMarker.length &&
|
|
32
|
+
line.trim() === marker[1]) {
|
|
33
|
+
flush(false);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
flush(false);
|
|
38
|
+
return segments;
|
|
39
|
+
}
|
|
40
|
+
export function joinFences(segments) {
|
|
41
|
+
return segments.map(s => s.text).join("\n");
|
|
42
|
+
}
|
|
43
|
+
export function mapUnfenced(text, op) {
|
|
44
|
+
const segments = splitFences(text);
|
|
45
|
+
return joinFences(segments.map(s => (s.fenced ? s : { ...s, text: op(s.text) })));
|
|
46
|
+
}
|
|
47
|
+
export function normalizeWhitespace(text) {
|
|
48
|
+
return mapUnfenced(text, segment => {
|
|
49
|
+
const lines = segment.split("\n").map(line => {
|
|
50
|
+
if (line.includes("`"))
|
|
51
|
+
return line;
|
|
52
|
+
const crlf = line.endsWith("\r");
|
|
53
|
+
const body = crlf ? line.slice(0, -1) : line;
|
|
54
|
+
const stripped = body.replace(/[ \t]+$/, "");
|
|
55
|
+
return crlf ? `${stripped}\r` : stripped;
|
|
56
|
+
});
|
|
57
|
+
const out = [];
|
|
58
|
+
let blankRun = 0;
|
|
59
|
+
const collapse = () => {
|
|
60
|
+
if (blankRun < 3)
|
|
61
|
+
return;
|
|
62
|
+
const keep = out[out.length - blankRun];
|
|
63
|
+
out.splice(out.length - blankRun, blankRun, keep);
|
|
64
|
+
};
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
const isBlank = line === "" || line === "\r";
|
|
67
|
+
if (isBlank) {
|
|
68
|
+
blankRun += 1;
|
|
69
|
+
out.push(line);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
collapse();
|
|
73
|
+
blankRun = 0;
|
|
74
|
+
out.push(line);
|
|
75
|
+
}
|
|
76
|
+
collapse();
|
|
77
|
+
return out.join("\n");
|
|
78
|
+
});
|
|
79
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -111,6 +111,14 @@ export interface CacheAwarenessConfig {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
export declare function loadCacheAwarenessConfig(logger?: Logger): CacheAwarenessConfig;
|
|
114
|
+
export interface CompressionConfig {
|
|
115
|
+
enabled: boolean;
|
|
116
|
+
sources: {
|
|
117
|
+
configFile: string | null;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export declare const DEFAULT_COMPRESSION_CONFIG: CompressionConfig;
|
|
121
|
+
export declare function loadCompressionConfig(logger?: Logger): CompressionConfig;
|
|
114
122
|
export declare function minStableTokensForModel(config: CacheAwarenessConfig, modelName: string): number;
|
|
115
123
|
export declare const DEFAULT_XAI_API_KEY_ENV = "XAI_API_KEY";
|
|
116
124
|
export declare const DEFAULT_XAI_BASE_URL = "https://api.x.ai/v1";
|
package/dist/config.js
CHANGED
|
@@ -413,6 +413,31 @@ export function loadCacheAwarenessConfig(logger = noopLogger) {
|
|
|
413
413
|
sources: { configFile: sourcePath },
|
|
414
414
|
};
|
|
415
415
|
}
|
|
416
|
+
const CompressionSchema = z
|
|
417
|
+
.object({
|
|
418
|
+
enabled: z.boolean().default(false),
|
|
419
|
+
})
|
|
420
|
+
.strict();
|
|
421
|
+
export const DEFAULT_COMPRESSION_CONFIG = {
|
|
422
|
+
enabled: false,
|
|
423
|
+
sources: { configFile: null },
|
|
424
|
+
};
|
|
425
|
+
export function loadCompressionConfig(logger = noopLogger) {
|
|
426
|
+
const configPath = defaultGatewayConfigPath();
|
|
427
|
+
const { parsed, sourcePath } = readGatewayTomlFile(configPath, logger, "compression");
|
|
428
|
+
const raw = parsed?.compression ?? {};
|
|
429
|
+
let parsedCompression;
|
|
430
|
+
try {
|
|
431
|
+
parsedCompression = CompressionSchema.parse(raw);
|
|
432
|
+
}
|
|
433
|
+
catch (err) {
|
|
434
|
+
throw new Error(`Invalid [compression] config: ${err instanceof Error ? err.message : String(err)}`);
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
enabled: parsedCompression.enabled,
|
|
438
|
+
sources: { configFile: sourcePath },
|
|
439
|
+
};
|
|
440
|
+
}
|
|
416
441
|
export function minStableTokensForModel(config, modelName) {
|
|
417
442
|
const lower = modelName.toLowerCase();
|
|
418
443
|
const table = config.minStableTokensForCacheControl;
|
package/dist/executor.d.ts
CHANGED
|
@@ -35,5 +35,6 @@ export declare function spawnCliProcess(command: string, args: string[], options
|
|
|
35
35
|
cwd?: string;
|
|
36
36
|
env: NodeJS.ProcessEnv;
|
|
37
37
|
stdio: SpawnOptions["stdio"];
|
|
38
|
+
logger?: Logger;
|
|
38
39
|
}): ChildProcess;
|
|
39
40
|
export declare function executeCli(command: string, args: string[], options?: ExecuteOptions): Promise<ExecuteResult>;
|
package/dist/executor.js
CHANGED
|
@@ -3,6 +3,7 @@ import { homedir } from "os";
|
|
|
3
3
|
import { delimiter, join, dirname, extname, win32 } from "path";
|
|
4
4
|
import { readdirSync, existsSync } from "fs";
|
|
5
5
|
import { createCircuitBreaker, withRetry } from "./retry.js";
|
|
6
|
+
import { applySpawnEnvIsolation } from "./spawn-env-isolation.js";
|
|
6
7
|
export function providerCommandName(command) {
|
|
7
8
|
if (command === "gemini")
|
|
8
9
|
return "agy";
|
|
@@ -309,9 +310,10 @@ function killWindowsProcessTree(pid) {
|
|
|
309
310
|
return result.status === 0;
|
|
310
311
|
}
|
|
311
312
|
export function spawnCliProcess(command, args, options) {
|
|
313
|
+
const env = applySpawnEnvIsolation(options.env, options.logger);
|
|
312
314
|
const detached = shouldDetachProviderProcess();
|
|
313
315
|
const resolved = resolveCommandForSpawn(command, args, {
|
|
314
|
-
envPath: pathValueFromEnv(
|
|
316
|
+
envPath: pathValueFromEnv(env, process.platform),
|
|
315
317
|
});
|
|
316
318
|
const proc = spawn(resolved.command, resolved.args, {
|
|
317
319
|
cwd: options.cwd,
|
|
@@ -319,7 +321,7 @@ export function spawnCliProcess(command, args, options) {
|
|
|
319
321
|
windowsHide: true,
|
|
320
322
|
windowsVerbatimArguments: resolved.windowsVerbatimArguments,
|
|
321
323
|
stdio: options.stdio,
|
|
322
|
-
env
|
|
324
|
+
env,
|
|
323
325
|
});
|
|
324
326
|
if (proc.pid)
|
|
325
327
|
registerProcessGroup(proc.pid);
|
|
@@ -337,6 +339,7 @@ export async function executeCli(command, args, options = {}) {
|
|
|
337
339
|
cwd,
|
|
338
340
|
stdio,
|
|
339
341
|
env: { ...baseEnv, ...(extraEnv ?? {}) },
|
|
342
|
+
logger: options.logger,
|
|
340
343
|
});
|
|
341
344
|
if (stdin !== undefined && proc.stdin) {
|
|
342
345
|
proc.stdin.write(stdin);
|
|
@@ -37,6 +37,13 @@ interface LoggerLike {
|
|
|
37
37
|
info: (message: string, ...args: any[]) => void;
|
|
38
38
|
error: (message: string, ...args: any[]) => void;
|
|
39
39
|
}
|
|
40
|
+
export interface CompressionTelemetry {
|
|
41
|
+
route: string;
|
|
42
|
+
transforms: string[];
|
|
43
|
+
originalChars: number;
|
|
44
|
+
compressedChars: number;
|
|
45
|
+
estimatedTokensSaved: number;
|
|
46
|
+
}
|
|
40
47
|
export declare function resolveFlightRecorderDbPath(): string | null;
|
|
41
48
|
export declare class FlightRecorder {
|
|
42
49
|
private db;
|
|
@@ -51,6 +58,7 @@ export declare class FlightRecorder {
|
|
|
51
58
|
});
|
|
52
59
|
logStart(entry: FlightLogStart): void;
|
|
53
60
|
logComplete(correlationId: string, result: FlightLogResult): void;
|
|
61
|
+
recordCompressionTelemetry(correlationId: string, telemetry: CompressionTelemetry): void;
|
|
54
62
|
private redactStart;
|
|
55
63
|
private redactResult;
|
|
56
64
|
queryRequests<T = Record<string, unknown>>(sql: string, ...params: unknown[]): T[];
|
|
@@ -60,6 +68,7 @@ export declare class FlightRecorder {
|
|
|
60
68
|
export declare class NoopFlightRecorder {
|
|
61
69
|
logStart(_entry: FlightLogStart): void;
|
|
62
70
|
logComplete(_correlationId: string, _result: FlightLogResult): void;
|
|
71
|
+
recordCompressionTelemetry(_correlationId: string, _telemetry: CompressionTelemetry): void;
|
|
63
72
|
queryRequests<T = Record<string, unknown>>(_sql: string, ..._params: unknown[]): T[];
|
|
64
73
|
flush(): void;
|
|
65
74
|
close(): void;
|
package/dist/flight-recorder.js
CHANGED
|
@@ -64,6 +64,25 @@ function ensureMetadataProviderSessionColumns(db) {
|
|
|
64
64
|
db.exec("ALTER TABLE gateway_metadata ADD COLUMN stop_reason TEXT");
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
function ensureCompressionColumns(db) {
|
|
68
|
+
const rows = db.prepare("PRAGMA table_info(gateway_metadata)").all();
|
|
69
|
+
const names = new Set(rows.map((row) => (row && typeof row.name === "string" ? row.name : "")));
|
|
70
|
+
if (!names.has("compression_route")) {
|
|
71
|
+
db.exec("ALTER TABLE gateway_metadata ADD COLUMN compression_route TEXT");
|
|
72
|
+
}
|
|
73
|
+
if (!names.has("compression_transforms")) {
|
|
74
|
+
db.exec("ALTER TABLE gateway_metadata ADD COLUMN compression_transforms TEXT");
|
|
75
|
+
}
|
|
76
|
+
if (!names.has("compression_original_chars")) {
|
|
77
|
+
db.exec("ALTER TABLE gateway_metadata ADD COLUMN compression_original_chars INTEGER");
|
|
78
|
+
}
|
|
79
|
+
if (!names.has("compression_compressed_chars")) {
|
|
80
|
+
db.exec("ALTER TABLE gateway_metadata ADD COLUMN compression_compressed_chars INTEGER");
|
|
81
|
+
}
|
|
82
|
+
if (!names.has("compression_tokens_saved_est")) {
|
|
83
|
+
db.exec("ALTER TABLE gateway_metadata ADD COLUMN compression_tokens_saved_est INTEGER");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
67
86
|
export function resolveFlightRecorderDbPath() {
|
|
68
87
|
const configured = process.env.LLM_GATEWAY_LOGS_DB;
|
|
69
88
|
if (configured !== undefined) {
|
|
@@ -196,6 +215,10 @@ export class FlightRecorder {
|
|
|
196
215
|
this.db
|
|
197
216
|
.prepare("INSERT OR IGNORE INTO _migrations(version, applied_at) VALUES(7, ?)")
|
|
198
217
|
.run(new Date().toISOString());
|
|
218
|
+
ensureCompressionColumns(this.db);
|
|
219
|
+
this.db
|
|
220
|
+
.prepare("INSERT OR IGNORE INTO _migrations(version, applied_at) VALUES(8, ?)")
|
|
221
|
+
.run(new Date().toISOString());
|
|
199
222
|
if (process.platform !== "win32") {
|
|
200
223
|
try {
|
|
201
224
|
chmodSync(dbPath, 0o600);
|
|
@@ -297,6 +320,24 @@ export class FlightRecorder {
|
|
|
297
320
|
logComplete(correlationId, result) {
|
|
298
321
|
this.updateCompleteTxn(correlationId, this.redactEnabled ? this.redactResult(result) : result);
|
|
299
322
|
}
|
|
323
|
+
recordCompressionTelemetry(correlationId, telemetry) {
|
|
324
|
+
this.db
|
|
325
|
+
.prepare(`UPDATE gateway_metadata
|
|
326
|
+
SET compression_route = @route,
|
|
327
|
+
compression_transforms = @transforms,
|
|
328
|
+
compression_original_chars = @original_chars,
|
|
329
|
+
compression_compressed_chars = @compressed_chars,
|
|
330
|
+
compression_tokens_saved_est = @tokens_saved_est
|
|
331
|
+
WHERE request_id = @id AND compression_route IS NULL`)
|
|
332
|
+
.run({
|
|
333
|
+
id: correlationId,
|
|
334
|
+
route: telemetry.route,
|
|
335
|
+
transforms: telemetry.transforms.join(","),
|
|
336
|
+
original_chars: telemetry.originalChars,
|
|
337
|
+
compressed_chars: telemetry.compressedChars,
|
|
338
|
+
tokens_saved_est: telemetry.estimatedTokensSaved,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
300
341
|
redactStart(entry) {
|
|
301
342
|
return {
|
|
302
343
|
...entry,
|
|
@@ -330,6 +371,7 @@ export class FlightRecorder {
|
|
|
330
371
|
export class NoopFlightRecorder {
|
|
331
372
|
logStart(_entry) { }
|
|
332
373
|
logComplete(_correlationId, _result) { }
|
|
374
|
+
recordCompressionTelemetry(_correlationId, _telemetry) { }
|
|
333
375
|
queryRequests(_sql, ..._params) {
|
|
334
376
|
return [];
|
|
335
377
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { z } from "zod/v3";
|
|
|
4
4
|
import { ISessionManager, type CliType, type ProviderType } from "./session-manager.js";
|
|
5
5
|
import { ResourceProvider } from "./resources.js";
|
|
6
6
|
import { PerformanceMetrics } from "./metrics.js";
|
|
7
|
-
import { type
|
|
7
|
+
import { type CompressResult } from "./compressor/index.js";
|
|
8
|
+
import { type PersistenceConfig, type CacheAwarenessConfig, type CompressionConfig, type ProvidersConfig, type ApiProviderRuntime, type AcpConfig, type AdminConfig } from "./config.js";
|
|
8
9
|
import { DatabaseConnection } from "./db.js";
|
|
9
10
|
import { type DevinAcpAgentType } from "./provider-definitions.js";
|
|
10
11
|
import { AsyncJobManager } from "./async-job-manager.js";
|
|
@@ -38,6 +39,7 @@ type ExtendedToolResponse = {
|
|
|
38
39
|
};
|
|
39
40
|
reviewIntegrity?: ReviewIntegrityResult;
|
|
40
41
|
warnings?: WarningEntry[];
|
|
42
|
+
compression?: CompressResult;
|
|
41
43
|
};
|
|
42
44
|
export declare const logger: {
|
|
43
45
|
info: (message: string, ...args: unknown[]) => void;
|
|
@@ -76,6 +78,7 @@ export interface GatewayServerDeps {
|
|
|
76
78
|
logger?: GatewayLogger;
|
|
77
79
|
persistence?: PersistenceConfig;
|
|
78
80
|
cacheAwareness?: CacheAwarenessConfig;
|
|
81
|
+
compression?: CompressionConfig;
|
|
79
82
|
providers?: ProvidersConfig;
|
|
80
83
|
acpConfig?: AcpConfig;
|
|
81
84
|
adminConfig?: AdminConfig;
|
|
@@ -92,6 +95,7 @@ export interface GatewayServerRuntime {
|
|
|
92
95
|
logger: GatewayLogger;
|
|
93
96
|
persistence: PersistenceConfig;
|
|
94
97
|
cacheAwareness: CacheAwarenessConfig;
|
|
98
|
+
compression: CompressionConfig;
|
|
95
99
|
providers: ProvidersConfig;
|
|
96
100
|
acpConfig: AcpConfig;
|
|
97
101
|
adminConfig: AdminConfig;
|
|
@@ -169,6 +173,11 @@ export declare function extractUsageAndCost(cli: CliType, output: string, output
|
|
|
169
173
|
cacheCreationTokens?: number;
|
|
170
174
|
costUsd?: number;
|
|
171
175
|
};
|
|
176
|
+
export declare function resolveEffectiveCompression(compression: CompressionConfig, input: {
|
|
177
|
+
compressResponse?: boolean;
|
|
178
|
+
outputFormat?: string;
|
|
179
|
+
outputSchemaDeclared?: boolean;
|
|
180
|
+
}): boolean;
|
|
172
181
|
export declare function registerBaseResources(server: McpServer, runtime: GatewayServerRuntime): void;
|
|
173
182
|
interface CliRequestPrep {
|
|
174
183
|
corrId: string;
|
|
@@ -344,6 +353,7 @@ export declare function prepareGrokRequest(params: {
|
|
|
344
353
|
forkSession?: boolean;
|
|
345
354
|
jsonSchema?: string | Record<string, unknown>;
|
|
346
355
|
}, runtime?: GatewayServerRuntime): CliRequestPrep | ExtendedToolResponse;
|
|
356
|
+
export declare function resolveMistralAgentMode(approvalStrategy: "legacy" | "mcp_managed" | undefined, callerMode: MistralAgentMode | undefined): MistralAgentMode;
|
|
347
357
|
export declare function prepareMistralRequest(params: {
|
|
348
358
|
prompt?: string;
|
|
349
359
|
promptParts?: PromptParts;
|
|
@@ -374,7 +384,7 @@ export declare function buildMistralRetryPrep(params: Pick<MistralRequestParams,
|
|
|
374
384
|
env: Record<string, string>;
|
|
375
385
|
ignoredDisallowedTools: boolean;
|
|
376
386
|
};
|
|
377
|
-
export declare function buildCliResponse(cli: CliType, stdout: string, optimizeResponse: boolean, corrId: string, sessionId: string | undefined, prep: CliRequestPrep, durationMs: number, resumable?: boolean, outputFormat?: string, warnings?: WarningEntry[]): ExtendedToolResponse;
|
|
387
|
+
export declare function buildCliResponse(cli: CliType, stdout: string, optimizeResponse: boolean, corrId: string, sessionId: string | undefined, prep: CliRequestPrep, durationMs: number, resumable?: boolean, outputFormat?: string, warnings?: WarningEntry[], compressResponse?: boolean): ExtendedToolResponse;
|
|
378
388
|
export interface GrokApiRequestParams {
|
|
379
389
|
prompt?: string;
|
|
380
390
|
promptParts?: PromptParts;
|
|
@@ -427,6 +437,7 @@ export interface GeminiRequestParams {
|
|
|
427
437
|
correlationId?: string;
|
|
428
438
|
optimizePrompt: boolean;
|
|
429
439
|
optimizeResponse?: boolean;
|
|
440
|
+
compressResponse?: boolean;
|
|
430
441
|
idleTimeoutMs?: number;
|
|
431
442
|
forceRefresh?: boolean;
|
|
432
443
|
outputFormat?: "text" | "json" | "stream-json";
|
|
@@ -482,6 +493,7 @@ export interface GrokRequestParams {
|
|
|
482
493
|
correlationId?: string;
|
|
483
494
|
optimizePrompt: boolean;
|
|
484
495
|
optimizeResponse?: boolean;
|
|
496
|
+
compressResponse?: boolean;
|
|
485
497
|
idleTimeoutMs?: number;
|
|
486
498
|
forceRefresh?: boolean;
|
|
487
499
|
maxTurns?: number;
|
|
@@ -541,6 +553,7 @@ export interface DevinRequestParams {
|
|
|
541
553
|
correlationId?: string;
|
|
542
554
|
optimizePrompt: boolean;
|
|
543
555
|
optimizeResponse?: boolean;
|
|
556
|
+
compressResponse?: boolean;
|
|
544
557
|
idleTimeoutMs?: number;
|
|
545
558
|
forceRefresh?: boolean;
|
|
546
559
|
}
|
|
@@ -580,6 +593,7 @@ export interface CursorRequestParams {
|
|
|
580
593
|
correlationId?: string;
|
|
581
594
|
optimizePrompt: boolean;
|
|
582
595
|
optimizeResponse?: boolean;
|
|
596
|
+
compressResponse?: boolean;
|
|
583
597
|
idleTimeoutMs?: number;
|
|
584
598
|
forceRefresh?: boolean;
|
|
585
599
|
}
|
|
@@ -620,6 +634,7 @@ export interface MistralRequestParams {
|
|
|
620
634
|
correlationId?: string;
|
|
621
635
|
optimizePrompt: boolean;
|
|
622
636
|
optimizeResponse?: boolean;
|
|
637
|
+
compressResponse?: boolean;
|
|
623
638
|
idleTimeoutMs?: number;
|
|
624
639
|
forceRefresh?: boolean;
|
|
625
640
|
trust?: boolean;
|
|
@@ -653,6 +668,7 @@ export declare function handleCodexRequestAsync(deps: AsyncHandlerDeps, params:
|
|
|
653
668
|
createNewSession: boolean;
|
|
654
669
|
correlationId?: string;
|
|
655
670
|
optimizePrompt: boolean;
|
|
671
|
+
compressResponse?: boolean;
|
|
656
672
|
idleTimeoutMs?: number;
|
|
657
673
|
forceRefresh?: boolean;
|
|
658
674
|
outputFormat?: "text" | "json";
|