codex-copilot-dx 0.4.43 → 0.5.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/README.md +31 -13
- package/bin/cli.mjs +31 -7
- package/bin/codex-copilot-dx.mjs +2 -0
- package/package.json +3 -2
- package/src/adapter.mjs +16 -8
- package/src/anthropic-handler.mjs +13 -5
- package/src/anthropic.mjs +40 -5
- package/src/auth.mjs +2 -2
- package/src/bounded-result-cache.mjs +180 -0
- package/src/claude-config.mjs +1 -1
- package/src/cli-options.mjs +30 -6
- package/src/cli-status.mjs +122 -0
- package/src/copilot.mjs +11 -4
- package/src/doctor.mjs +11 -2
- package/src/http-transport.mjs +4 -1
- package/src/image-optimization.mjs +74 -65
- package/src/observability.mjs +4 -3
- package/src/responses-bridge.mjs +46 -10
- package/src/responses-chat-payload.mjs +2 -1
- package/src/responses-compaction.mjs +113 -0
- package/src/responses-handler.mjs +28 -14
- package/src/responses-proxy.mjs +123 -23
- package/src/running-adapter.mjs +1 -0
- package/src/stream-contract.mjs +102 -0
- package/src/stream-errors.mjs +3 -1
- package/src/stream-performance.mjs +177 -0
- package/src/upstream-headers.mjs +67 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { currentRequestContext } from "./request-context.mjs";
|
|
3
|
+
|
|
4
|
+
const PERFORMANCE_ROUTES = Object.freeze(["responses", "responses_compact", "messages"]);
|
|
5
|
+
const TTFT_EDGES_MS = Object.freeze([
|
|
6
|
+
100, 200, 300, 500, 700, 1_000, 1_400, 2_000, 2_800, 4_000,
|
|
7
|
+
5_500, 8_000, 12_000, 16_000, 24_000, 36_000, 60_000, 120_000, 300_000,
|
|
8
|
+
]);
|
|
9
|
+
const TPOT_EDGES_US = Object.freeze([
|
|
10
|
+
500, 1_000, 2_000, 3_333, 5_000, 6_667, 8_333, 10_000, 12_500, 14_286,
|
|
11
|
+
16_667, 20_000, 25_000, 33_333, 40_000, 50_000, 66_667, 100_000,
|
|
12
|
+
150_000, 250_000, 500_000, 1_000_000, 2_500_000, 10_000_000,
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const RESPONSES_OUTPUT_EVENT_TYPES = new Set([
|
|
16
|
+
"response.output_text.delta",
|
|
17
|
+
"response.function_call_arguments.delta",
|
|
18
|
+
"response.custom_tool_call_input.delta",
|
|
19
|
+
"response.refusal.delta",
|
|
20
|
+
"response.reasoning_text.delta",
|
|
21
|
+
"response.reasoning_summary_text.delta",
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
function nonEmptyString(value) {
|
|
25
|
+
return typeof value === "string" && value.length > 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isResponsesOutputEvent(event, eventType = event?.type) {
|
|
29
|
+
return RESPONSES_OUTPUT_EVENT_TYPES.has(eventType) && nonEmptyString(event?.delta);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isChatOutputDelta(delta) {
|
|
33
|
+
if (!delta || typeof delta !== "object") return false;
|
|
34
|
+
return nonEmptyString(delta.content)
|
|
35
|
+
|| (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0)
|
|
36
|
+
|| nonEmptyString(delta.refusal)
|
|
37
|
+
|| nonEmptyString(delta.reasoning)
|
|
38
|
+
|| nonEmptyString(delta.reasoning_content)
|
|
39
|
+
|| nonEmptyString(delta.reasoning_text);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createHistogram(edges) {
|
|
43
|
+
return {
|
|
44
|
+
edges,
|
|
45
|
+
counts: new Uint32Array(edges.length + 1),
|
|
46
|
+
samples: 0,
|
|
47
|
+
total: 0,
|
|
48
|
+
max: 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function observe(histogram, value) {
|
|
53
|
+
if (!Number.isFinite(value) || value < 0) return;
|
|
54
|
+
let index = 0;
|
|
55
|
+
while (index < histogram.edges.length && value > histogram.edges[index]) index += 1;
|
|
56
|
+
histogram.counts[index] += 1;
|
|
57
|
+
histogram.samples += 1;
|
|
58
|
+
histogram.total += value;
|
|
59
|
+
histogram.max = Math.max(histogram.max, value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function histogramSnapshot(histogram, unit) {
|
|
63
|
+
let lower = 0;
|
|
64
|
+
const buckets = histogram.edges.map((upper, index) => {
|
|
65
|
+
const bucket = { lower, upper, count: histogram.counts[index] };
|
|
66
|
+
lower = upper;
|
|
67
|
+
return bucket;
|
|
68
|
+
});
|
|
69
|
+
buckets.push({ lower, upper: null, count: histogram.counts.at(-1) });
|
|
70
|
+
return {
|
|
71
|
+
unit,
|
|
72
|
+
samples: histogram.samples,
|
|
73
|
+
avg: histogram.samples > 0 ? Number((histogram.total / histogram.samples).toFixed(1)) : 0,
|
|
74
|
+
max: histogram.max,
|
|
75
|
+
buckets,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function createRoutePerformance() {
|
|
80
|
+
return {
|
|
81
|
+
ttft: createHistogram(TTFT_EDGES_MS),
|
|
82
|
+
tpot: createHistogram(TPOT_EDGES_US),
|
|
83
|
+
success_with_output: 0,
|
|
84
|
+
errors_with_output: 0,
|
|
85
|
+
zero_output_errors: 0,
|
|
86
|
+
neutral: 0,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function routeSnapshot(route) {
|
|
91
|
+
return {
|
|
92
|
+
success_with_output: route.success_with_output,
|
|
93
|
+
errors_with_output: route.errors_with_output,
|
|
94
|
+
zero_output_errors: route.zero_output_errors,
|
|
95
|
+
neutral: route.neutral,
|
|
96
|
+
ttft_ms: histogramSnapshot(route.ttft, "ms"),
|
|
97
|
+
tpot_us: histogramSnapshot(route.tpot, "us"),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function createStreamPerformanceMetrics({ now = () => performance.now() } = {}) {
|
|
102
|
+
const routes = Object.fromEntries(PERFORMANCE_ROUTES.map((name) => [name, createRoutePerformance()]));
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
begin(routeName) {
|
|
106
|
+
const route = routes[routeName];
|
|
107
|
+
if (!route) return null;
|
|
108
|
+
let upstreamStartedAt = null;
|
|
109
|
+
let firstOutputAt = null;
|
|
110
|
+
let outputTokens = null;
|
|
111
|
+
let failed = false;
|
|
112
|
+
let finished = false;
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
upstreamStarted() {
|
|
116
|
+
if (upstreamStartedAt === null) upstreamStartedAt = now();
|
|
117
|
+
},
|
|
118
|
+
firstOutput() {
|
|
119
|
+
if (firstOutputAt === null) firstOutputAt = now();
|
|
120
|
+
},
|
|
121
|
+
setOutputTokens(value) {
|
|
122
|
+
const tokens = Number(value);
|
|
123
|
+
if (Number.isFinite(tokens) && tokens >= 0) outputTokens = tokens;
|
|
124
|
+
},
|
|
125
|
+
fail() {
|
|
126
|
+
failed = true;
|
|
127
|
+
},
|
|
128
|
+
finish({ failed: finishFailed = false } = {}) {
|
|
129
|
+
if (finished) return;
|
|
130
|
+
finished = true;
|
|
131
|
+
failed ||= finishFailed;
|
|
132
|
+
const finishedAt = now();
|
|
133
|
+
if (upstreamStartedAt === null) {
|
|
134
|
+
route.neutral += 1;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (firstOutputAt === null) {
|
|
138
|
+
if (failed) route.zero_output_errors += 1;
|
|
139
|
+
else route.neutral += 1;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
observe(route.ttft, Math.max(0, firstOutputAt - upstreamStartedAt));
|
|
143
|
+
if (outputTokens !== null && outputTokens >= 2) {
|
|
144
|
+
observe(route.tpot, Math.max(0, ((finishedAt - firstOutputAt) * 1_000) / (outputTokens - 1)));
|
|
145
|
+
}
|
|
146
|
+
if (failed) route.errors_with_output += 1;
|
|
147
|
+
else route.success_with_output += 1;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
snapshot() {
|
|
152
|
+
return {
|
|
153
|
+
by_route: Object.fromEntries(PERFORMANCE_ROUTES.map((name) => [name, routeSnapshot(routes[name])])),
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function tracker() {
|
|
160
|
+
return currentRequestContext()?.streamPerformance || null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function markUpstreamStarted() {
|
|
164
|
+
tracker()?.upstreamStarted();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function markFirstOutput() {
|
|
168
|
+
tracker()?.firstOutput();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function markOutputTokens(value) {
|
|
172
|
+
tracker()?.setOutputTokens(value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function markStreamFailure() {
|
|
176
|
+
tracker()?.fail();
|
|
177
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const REQUEST_ID_HEADERS = [
|
|
2
|
+
"x-request-id",
|
|
3
|
+
"x-github-request-id",
|
|
4
|
+
"x-ms-request-id",
|
|
5
|
+
"request-id",
|
|
6
|
+
"x-correlation-id",
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
const SAFE_EXACT_HEADERS = new Set([
|
|
10
|
+
"retry-after",
|
|
11
|
+
"ratelimit-limit",
|
|
12
|
+
"ratelimit-remaining",
|
|
13
|
+
"ratelimit-reset",
|
|
14
|
+
"ratelimit-policy",
|
|
15
|
+
"openai-model",
|
|
16
|
+
"openai-processing-ms",
|
|
17
|
+
"traceparent",
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const SAFE_HEADER_PREFIXES = [
|
|
21
|
+
"x-ratelimit-",
|
|
22
|
+
"x-ms-ratelimit-",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
function headerValue(headers, name) {
|
|
26
|
+
const value = headers?.get?.(name);
|
|
27
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function canonicalHeaderName(name) {
|
|
31
|
+
return String(name).split("-").map((part) => (
|
|
32
|
+
part ? `${part[0].toUpperCase()}${part.slice(1).toLowerCase()}` : part
|
|
33
|
+
)).join("-");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isSafeMetadataHeader(name) {
|
|
37
|
+
return SAFE_EXACT_HEADERS.has(name)
|
|
38
|
+
|| SAFE_HEADER_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function safeUpstreamResponseHeaders(headers, {
|
|
42
|
+
contentType,
|
|
43
|
+
defaultContentType,
|
|
44
|
+
} = {}) {
|
|
45
|
+
const out = {};
|
|
46
|
+
const resolvedContentType = contentType
|
|
47
|
+
?? headerValue(headers, "content-type")
|
|
48
|
+
?? defaultContentType;
|
|
49
|
+
if (resolvedContentType) out["Content-Type"] = resolvedContentType;
|
|
50
|
+
|
|
51
|
+
if (headers && typeof headers[Symbol.iterator] === "function") {
|
|
52
|
+
for (const [rawName, value] of headers) {
|
|
53
|
+
const name = String(rawName).toLowerCase();
|
|
54
|
+
if (!isSafeMetadataHeader(name)) continue;
|
|
55
|
+
out[canonicalHeaderName(name)] = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const name of REQUEST_ID_HEADERS) {
|
|
60
|
+
const value = headerValue(headers, name);
|
|
61
|
+
if (value) {
|
|
62
|
+
out["X-Upstream-Request-Id"] = value;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|