@bitkyc08/opencodex 2.6.26-preview.20260705 → 2.6.28-preview.20260707
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 +1 -0
- package/bin/ocx.mjs +4 -4
- package/gui/dist/assets/index-ByGC8-Bm.css +1 -0
- package/gui/dist/assets/index-CkV5xFA8.js +15 -0
- package/gui/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/adapters/anthropic-image-guard.ts +195 -0
- package/src/adapters/anthropic.ts +85 -14
- package/src/adapters/cursor/cursor-errors.ts +2 -2
- package/src/adapters/cursor/live-transport.ts +1 -1
- package/src/adapters/cursor/transport-retry.ts +2 -2
- package/src/adapters/google-errors.ts +1 -1
- package/src/adapters/google-http.ts +1 -1
- package/src/adapters/google-truncation.ts +1 -1
- package/src/adapters/google.ts +1 -1
- package/src/adapters/kiro-errors.ts +1 -1
- package/src/adapters/kiro-retry.ts +1 -1
- package/src/adapters/kiro-truncation.ts +1 -1
- package/src/adapters/kiro.ts +1 -1
- package/src/adapters/openai-chat.ts +12 -2
- package/src/adapters/openai-responses.ts +126 -3
- package/src/bridge.ts +164 -7
- package/src/{doctor.ts → cli/doctor.ts} +21 -4
- package/src/{cli-help.ts → cli/help.ts} +1 -1
- package/src/cli/index.ts +584 -0
- package/src/{init.ts → cli/init.ts} +6 -6
- package/src/{cli-models.ts → cli/models.ts} +2 -2
- package/src/{cli-provider.ts → cli/provider.ts} +12 -8
- package/src/{star-prompt.ts → cli/star-prompt.ts} +1 -1
- package/src/{cli-status.ts → cli/status.ts} +7 -7
- package/src/cli.ts +9 -575
- package/src/{codex-account-label.ts → codex/account-label.ts} +1 -1
- package/src/{codex-account-lifecycle.ts → codex/account-lifecycle.ts} +6 -6
- package/src/{codex-account-store.ts → codex/account-store.ts} +2 -2
- package/src/{codex-account-usability.ts → codex/account-usability.ts} +4 -4
- package/src/{codex-auth-api.ts → codex/auth-api.ts} +18 -18
- package/src/{codex-auth-collision.ts → codex/auth-collision.ts} +4 -4
- package/src/{codex-auth-context.ts → codex/auth-context.ts} +9 -9
- package/src/{codex-catalog.ts → codex/catalog.ts} +49 -20
- package/src/codex/history-migration-guardian.ts +102 -0
- package/src/{codex-history-provider.ts → codex/history-provider.ts} +111 -7
- package/src/{codex-home.ts → codex/home.ts} +1 -1
- package/src/{codex-inject.ts → codex/inject.ts} +204 -26
- package/src/{codex-journal.ts → codex/journal.ts} +2 -2
- package/src/{codex-main-account.ts → codex/main-account.ts} +2 -2
- package/src/{model-cache.ts → codex/model-cache.ts} +1 -1
- package/src/{codex-paths.ts → codex/paths.ts} +2 -2
- package/src/{codex-plugins-doctor.ts → codex/plugins-doctor.ts} +2 -2
- package/src/{codex-refresh.ts → codex/refresh.ts} +4 -4
- package/src/{codex-routing.ts → codex/routing.ts} +8 -8
- package/src/{codex-shim.ts → codex/shim.ts} +6 -5
- package/src/{codex-sync.ts → codex/sync.ts} +4 -4
- package/src/{codex-websocket-registry.ts → codex/websocket-registry.ts} +1 -1
- package/src/config.ts +2 -0
- package/src/generated/jawcode-model-metadata.ts +2 -0
- package/src/{bun-runtime.ts → lib/bun-runtime.ts} +1 -1
- package/src/{crash-guard.ts → lib/crash-guard.ts} +1 -1
- package/src/{process-control.ts → lib/process-control.ts} +1 -1
- package/src/{service-secrets.ts → lib/service-secrets.ts} +1 -1
- package/src/oauth/callback-server.ts +1 -1
- package/src/oauth/google-antigravity.ts +7 -4
- package/src/oauth/index.ts +67 -16
- package/src/oauth/login-cli.ts +2 -2
- package/src/oauth/store.ts +236 -20
- package/src/oauth/token-guardian.ts +24 -20
- package/src/oauth/types.ts +16 -0
- package/src/providers/api-keys.ts +121 -0
- package/src/{provider-context-cap.ts → providers/context-cap.ts} +1 -1
- package/src/providers/derive.ts +2 -0
- package/src/providers/key-failover.ts +145 -0
- package/src/{provider-label.ts → providers/label.ts} +1 -1
- package/src/{provider-quota.ts → providers/quota.ts} +12 -7
- package/src/providers/registry.ts +66 -4
- package/src/responses/compaction.ts +117 -0
- package/src/responses/parser.ts +89 -13
- package/src/responses/reasoning-envelope.ts +52 -0
- package/src/responses/schema.ts +15 -3
- package/src/responses/state.ts +117 -2
- package/src/router.ts +2 -0
- package/src/server/auth-cors.ts +231 -0
- package/src/server/index.ts +523 -0
- package/src/server/lifecycle.ts +73 -0
- package/src/server/management-api.ts +628 -0
- package/src/{proxy-liveness.ts → server/proxy-liveness.ts} +1 -1
- package/src/server/relay.ts +534 -0
- package/src/server/request-decompress.ts +46 -0
- package/src/server/request-log.ts +310 -0
- package/src/server/responses.ts +775 -0
- package/src/{ws-bridge.ts → server/ws-bridge.ts} +44 -13
- package/src/service.ts +19 -11
- package/src/types.ts +27 -0
- package/src/{update.ts → update/index.ts} +5 -5
- package/src/{update-job.ts → update/job.ts} +7 -6
- package/src/{update-notify.ts → update/notify.ts} +3 -3
- package/src/{usage-debug.ts → usage/debug.ts} +3 -3
- package/src/{usage-log.ts → usage/log.ts} +3 -3
- package/src/{usage-summary.ts → usage/summary.ts} +3 -3
- package/src/{usage-totals.ts → usage/totals.ts} +1 -1
- package/src/vision/describe.ts +3 -3
- package/src/vision/index.ts +21 -1
- package/src/web-search/executor.ts +4 -4
- package/src/web-search/index.ts +1 -1
- package/src/web-search/loop.ts +84 -24
- package/gui/dist/assets/index-BcHhxo1I.css +0 -1
- package/gui/dist/assets/index-DCC1q_Jx.js +0 -15
- package/src/server.ts +0 -2501
- /package/src/{codex-account-runtime-state.ts → codex/account-runtime-state.ts} +0 -0
- /package/src/{codex-quota.ts → codex/quota.ts} +0 -0
- /package/src/{abort.ts → lib/abort.ts} +0 -0
- /package/src/{debug.ts → lib/debug.ts} +0 -0
- /package/src/{errors.ts → lib/errors.ts} +0 -0
- /package/src/{open-url.ts → lib/open-url.ts} +0 -0
- /package/src/{privacy.ts → lib/privacy.ts} +0 -0
- /package/src/{redact.ts → lib/redact.ts} +0 -0
- /package/src/{sidecar-tracker.ts → lib/sidecar-tracker.ts} +0 -0
- /package/src/{upstream-retry.ts → lib/upstream-retry.ts} +0 -0
- /package/src/{win-paths.ts → lib/win-paths.ts} +0 -0
- /package/src/{ports.ts → server/ports.ts} +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import type { ResponsesTerminalStatus } from "../bridge";
|
|
3
|
+
import { CODEX_CONFIG_PATH, readRootTomlString } from "../codex/paths";
|
|
4
|
+
import { readCodexCatalogPath } from "../codex/catalog";
|
|
5
|
+
import type { OcxUsage } from "../types";
|
|
6
|
+
import {
|
|
7
|
+
appendUsageEntry,
|
|
8
|
+
usageForFinalLog,
|
|
9
|
+
usageStatusForFinalLog,
|
|
10
|
+
usageTotalTokens,
|
|
11
|
+
type UsageStatus,
|
|
12
|
+
} from "../usage/log";
|
|
13
|
+
import {
|
|
14
|
+
appendUsageDebug,
|
|
15
|
+
isUsageDebugEnabled,
|
|
16
|
+
truncateForDebug,
|
|
17
|
+
USAGE_DEBUG_BODY_SAMPLE_BYTES,
|
|
18
|
+
type UsageDebugBodyKind,
|
|
19
|
+
} from "../usage/debug";
|
|
20
|
+
|
|
21
|
+
export interface RequestLogContext {
|
|
22
|
+
model: string;
|
|
23
|
+
provider: string;
|
|
24
|
+
requestedModel?: string;
|
|
25
|
+
requestedEffort?: string;
|
|
26
|
+
requestedServiceTier?: string;
|
|
27
|
+
requestedSpeedLabel?: string;
|
|
28
|
+
configuredServiceTier?: string;
|
|
29
|
+
configuredSpeedLabel?: string;
|
|
30
|
+
modelSupportsServiceTier?: boolean;
|
|
31
|
+
responseServiceTier?: string;
|
|
32
|
+
resolvedModel?: string;
|
|
33
|
+
usage?: OcxUsage;
|
|
34
|
+
usageLogInputTokens?: number;
|
|
35
|
+
usageDebugBodyKind?: UsageDebugBodyKind;
|
|
36
|
+
usageDebugBodySample?: string;
|
|
37
|
+
usageDebugContentType?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface RequestLogEntry {
|
|
41
|
+
requestId: string;
|
|
42
|
+
timestamp: number;
|
|
43
|
+
model: string;
|
|
44
|
+
provider: string;
|
|
45
|
+
requestedModel?: string;
|
|
46
|
+
requestedEffort?: string;
|
|
47
|
+
requestedServiceTier?: string;
|
|
48
|
+
requestedSpeedLabel?: string;
|
|
49
|
+
configuredServiceTier?: string;
|
|
50
|
+
configuredSpeedLabel?: string;
|
|
51
|
+
modelSupportsServiceTier?: boolean;
|
|
52
|
+
responseServiceTier?: string;
|
|
53
|
+
resolvedModel?: string;
|
|
54
|
+
status: number;
|
|
55
|
+
durationMs: number;
|
|
56
|
+
errorCode?: string;
|
|
57
|
+
terminalStatus?: ResponsesTerminalStatus;
|
|
58
|
+
closeReason?: "terminal" | "client_cancel" | "non_stream";
|
|
59
|
+
usageStatus: UsageStatus;
|
|
60
|
+
usage?: OcxUsage;
|
|
61
|
+
totalTokens?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const requestLog: RequestLogEntry[] = [];
|
|
65
|
+
const MAX_LOG_SIZE = 200;
|
|
66
|
+
let requestLogSeq = 0;
|
|
67
|
+
|
|
68
|
+
export function addRequestLog(entry: RequestLogEntry) {
|
|
69
|
+
requestLog.push(entry);
|
|
70
|
+
if (requestLog.length > MAX_LOG_SIZE) requestLog.shift();
|
|
71
|
+
try {
|
|
72
|
+
appendUsageEntry({
|
|
73
|
+
requestId: entry.requestId,
|
|
74
|
+
timestamp: entry.timestamp,
|
|
75
|
+
provider: entry.provider,
|
|
76
|
+
model: entry.model,
|
|
77
|
+
...(entry.resolvedModel ? { resolvedModel: entry.resolvedModel } : {}),
|
|
78
|
+
status: entry.status,
|
|
79
|
+
durationMs: entry.durationMs,
|
|
80
|
+
usageStatus: entry.usageStatus,
|
|
81
|
+
...(entry.usage ? { usage: entry.usage } : {}),
|
|
82
|
+
...(entry.totalTokens !== undefined ? { totalTokens: entry.totalTokens } : {}),
|
|
83
|
+
});
|
|
84
|
+
} catch {
|
|
85
|
+
/* request logging must never fail a user request */
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function nextRequestLogId(timestamp = Date.now()): string {
|
|
90
|
+
requestLogSeq = (requestLogSeq % 1_000_000) + 1;
|
|
91
|
+
return `ocx-${timestamp.toString(36)}-${requestLogSeq.toString(36)}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function requestLogErrorCode(status: number): string | undefined {
|
|
95
|
+
if (status >= 200 && status < 400) return undefined;
|
|
96
|
+
if (status === 400 || status === 409) return "invalid_request_error";
|
|
97
|
+
if (status === 401 || status === 403) return "invalid_api_key";
|
|
98
|
+
if (status === 429) return "rate_limit_exceeded";
|
|
99
|
+
if (status === 499) return "client_closed_request";
|
|
100
|
+
if (status === 503) return "server_is_overloaded";
|
|
101
|
+
if (status >= 500) return "upstream_server_error";
|
|
102
|
+
return `http_${status}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function requestLogSpeedLabel(serviceTier: string | undefined): string | undefined {
|
|
106
|
+
const normalized = serviceTier?.trim().toLowerCase();
|
|
107
|
+
if (normalized === "priority" || normalized === "fast") return "fast";
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function readConfiguredCodexServiceTier(): string | undefined {
|
|
112
|
+
try {
|
|
113
|
+
if (!existsSync(CODEX_CONFIG_PATH)) return undefined;
|
|
114
|
+
return readRootTomlString(readFileSync(CODEX_CONFIG_PATH, "utf-8"), "service_tier") ?? undefined;
|
|
115
|
+
} catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function catalogModelSupportsServiceTier(modelId: string, serviceTier: string | undefined): boolean | undefined {
|
|
121
|
+
if (!serviceTier) return undefined;
|
|
122
|
+
const requestTier = serviceTier.trim().toLowerCase() === "fast" ? "priority" : serviceTier.trim();
|
|
123
|
+
try {
|
|
124
|
+
const catalogPath = readCodexCatalogPath();
|
|
125
|
+
if (!existsSync(catalogPath)) return undefined;
|
|
126
|
+
const catalog = JSON.parse(readFileSync(catalogPath, "utf-8")) as { models?: unknown };
|
|
127
|
+
const models = Array.isArray(catalog.models) ? catalog.models : [];
|
|
128
|
+
const entry = models.find(model => {
|
|
129
|
+
if (!model || typeof model !== "object") return false;
|
|
130
|
+
return (model as { slug?: unknown; id?: unknown }).slug === modelId
|
|
131
|
+
|| (model as { slug?: unknown; id?: unknown }).id === modelId;
|
|
132
|
+
});
|
|
133
|
+
if (!entry || typeof entry !== "object") return undefined;
|
|
134
|
+
const tiers = (entry as { service_tiers?: unknown }).service_tiers;
|
|
135
|
+
return Array.isArray(tiers) && tiers.some(tier => (
|
|
136
|
+
tier && typeof tier === "object" && (tier as { id?: unknown }).id === requestTier
|
|
137
|
+
));
|
|
138
|
+
} catch {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function applyResponseLogMetadata(logCtx: RequestLogContext, payload: unknown): void {
|
|
144
|
+
if (!payload || typeof payload !== "object") return;
|
|
145
|
+
const source = "response" in payload && typeof (payload as { response?: unknown }).response === "object"
|
|
146
|
+
? (payload as { response?: unknown }).response
|
|
147
|
+
: payload;
|
|
148
|
+
if (!source || typeof source !== "object") return;
|
|
149
|
+
const model = (source as { model?: unknown }).model;
|
|
150
|
+
if (typeof model === "string" && model.trim()) logCtx.resolvedModel = model;
|
|
151
|
+
const serviceTier = (source as { service_tier?: unknown }).service_tier;
|
|
152
|
+
if (typeof serviceTier === "string" && serviceTier.trim()) logCtx.responseServiceTier = serviceTier;
|
|
153
|
+
const usage = usageFromResponsesPayload((source as { usage?: unknown }).usage);
|
|
154
|
+
if (usage) logCtx.usage = usage;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function usageFromResponsesPayload(usage: unknown): OcxUsage | undefined {
|
|
158
|
+
if (!usage || typeof usage !== "object") return undefined;
|
|
159
|
+
const raw = usage as {
|
|
160
|
+
input_tokens?: unknown;
|
|
161
|
+
output_tokens?: unknown;
|
|
162
|
+
input_tokens_details?: { cached_tokens?: unknown };
|
|
163
|
+
output_tokens_details?: { reasoning_tokens?: unknown };
|
|
164
|
+
total_tokens?: unknown;
|
|
165
|
+
prompt_tokens?: unknown;
|
|
166
|
+
completion_tokens?: unknown;
|
|
167
|
+
prompt_tokens_details?: { cached_tokens?: unknown };
|
|
168
|
+
completion_tokens_details?: { reasoning_tokens?: unknown };
|
|
169
|
+
};
|
|
170
|
+
if (typeof raw.input_tokens === "number" && typeof raw.output_tokens === "number") {
|
|
171
|
+
return {
|
|
172
|
+
inputTokens: raw.input_tokens,
|
|
173
|
+
outputTokens: raw.output_tokens,
|
|
174
|
+
...(typeof raw.total_tokens === "number" ? { totalTokens: raw.total_tokens } : {}),
|
|
175
|
+
...(typeof raw.input_tokens_details?.cached_tokens === "number"
|
|
176
|
+
? { cachedInputTokens: raw.input_tokens_details.cached_tokens }
|
|
177
|
+
: {}),
|
|
178
|
+
...(typeof raw.output_tokens_details?.reasoning_tokens === "number"
|
|
179
|
+
? { reasoningOutputTokens: raw.output_tokens_details.reasoning_tokens }
|
|
180
|
+
: {}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (typeof raw.prompt_tokens === "number" && typeof raw.completion_tokens === "number") {
|
|
184
|
+
return {
|
|
185
|
+
inputTokens: raw.prompt_tokens,
|
|
186
|
+
outputTokens: raw.completion_tokens,
|
|
187
|
+
...(typeof raw.total_tokens === "number" ? { totalTokens: raw.total_tokens } : {}),
|
|
188
|
+
...(typeof raw.prompt_tokens_details?.cached_tokens === "number"
|
|
189
|
+
? { cachedInputTokens: raw.prompt_tokens_details.cached_tokens }
|
|
190
|
+
: {}),
|
|
191
|
+
...(typeof raw.completion_tokens_details?.reasoning_tokens === "number"
|
|
192
|
+
? { reasoningOutputTokens: raw.completion_tokens_details.reasoning_tokens }
|
|
193
|
+
: {}),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function inspectResponseLogJson(logCtx: RequestLogContext, text: string): void {
|
|
200
|
+
try {
|
|
201
|
+
applyResponseLogMetadata(logCtx, JSON.parse(text));
|
|
202
|
+
} catch {
|
|
203
|
+
/* body may not be JSON; request log metadata is best-effort only */
|
|
204
|
+
}
|
|
205
|
+
if (isUsageDebugEnabled() && logCtx.usageDebugBodyKind === undefined) {
|
|
206
|
+
logCtx.usageDebugBodyKind = "json";
|
|
207
|
+
logCtx.usageDebugBodySample = truncateForDebug(text);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function inspectResponseLogSsePayload(logCtx: RequestLogContext, payload: string | null): void {
|
|
212
|
+
if (!payload || payload.trim() === "[DONE]") return;
|
|
213
|
+
const debugEnabled = isUsageDebugEnabled();
|
|
214
|
+
const sseAlreadyMarked = logCtx.usageDebugBodyKind === "sse";
|
|
215
|
+
try {
|
|
216
|
+
applyResponseLogMetadata(logCtx, JSON.parse(payload));
|
|
217
|
+
} catch {
|
|
218
|
+
/* SSE block payload may not be JSON; metadata inspection is best-effort */
|
|
219
|
+
}
|
|
220
|
+
if (debugEnabled) {
|
|
221
|
+
if (!sseAlreadyMarked) {
|
|
222
|
+
logCtx.usageDebugBodyKind = "sse";
|
|
223
|
+
logCtx.usageDebugBodySample = truncateForDebug(payload);
|
|
224
|
+
} else if (typeof logCtx.usageDebugBodySample === "string"
|
|
225
|
+
&& logCtx.usageDebugBodySample.length < USAGE_DEBUG_BODY_SAMPLE_BYTES) {
|
|
226
|
+
const combined = `${logCtx.usageDebugBodySample}\n${payload}`;
|
|
227
|
+
logCtx.usageDebugBodySample = truncateForDebug(combined);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function httpStatusForTerminalStatus(status: ResponsesTerminalStatus): number {
|
|
233
|
+
return status === "completed" ? 200 : 502;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function addFinalRequestLog(
|
|
237
|
+
requestId: string,
|
|
238
|
+
start: number,
|
|
239
|
+
logCtx: RequestLogContext,
|
|
240
|
+
status: number,
|
|
241
|
+
meta?: Pick<RequestLogEntry, "terminalStatus" | "closeReason">,
|
|
242
|
+
addLog: (entry: RequestLogEntry) => void = addRequestLog,
|
|
243
|
+
): void {
|
|
244
|
+
const errorCode = requestLogErrorCode(status);
|
|
245
|
+
const finalUsage = usageForFinalLog(logCtx.provider, logCtx.usage);
|
|
246
|
+
const usageFallback = !finalUsage && typeof logCtx.usageLogInputTokens === "number"
|
|
247
|
+
? { inputTokens: logCtx.usageLogInputTokens, outputTokens: 0, estimated: true }
|
|
248
|
+
: undefined;
|
|
249
|
+
const loggedUsage = finalUsage && typeof logCtx.usageLogInputTokens === "number"
|
|
250
|
+
? { ...finalUsage, inputTokens: Math.max(finalUsage.inputTokens, logCtx.usageLogInputTokens) }
|
|
251
|
+
: (finalUsage ?? usageFallback);
|
|
252
|
+
const usageStatus = usageStatusForFinalLog(loggedUsage);
|
|
253
|
+
const totalTokens = usageTotalTokens(loggedUsage);
|
|
254
|
+
addLog({
|
|
255
|
+
requestId,
|
|
256
|
+
timestamp: start,
|
|
257
|
+
model: logCtx.model,
|
|
258
|
+
provider: logCtx.provider,
|
|
259
|
+
...(logCtx.requestedModel ? { requestedModel: logCtx.requestedModel } : {}),
|
|
260
|
+
...(logCtx.requestedEffort ? { requestedEffort: logCtx.requestedEffort } : {}),
|
|
261
|
+
...(logCtx.requestedServiceTier ? { requestedServiceTier: logCtx.requestedServiceTier } : {}),
|
|
262
|
+
...(logCtx.requestedSpeedLabel ? { requestedSpeedLabel: logCtx.requestedSpeedLabel } : {}),
|
|
263
|
+
...(logCtx.configuredServiceTier ? { configuredServiceTier: logCtx.configuredServiceTier } : {}),
|
|
264
|
+
...(logCtx.configuredSpeedLabel ? { configuredSpeedLabel: logCtx.configuredSpeedLabel } : {}),
|
|
265
|
+
...(logCtx.modelSupportsServiceTier !== undefined ? { modelSupportsServiceTier: logCtx.modelSupportsServiceTier } : {}),
|
|
266
|
+
...(logCtx.responseServiceTier ? { responseServiceTier: logCtx.responseServiceTier } : {}),
|
|
267
|
+
...(logCtx.resolvedModel ? { resolvedModel: logCtx.resolvedModel } : {}),
|
|
268
|
+
status,
|
|
269
|
+
durationMs: Date.now() - start,
|
|
270
|
+
...(errorCode ? { errorCode } : {}),
|
|
271
|
+
...(meta?.terminalStatus ? { terminalStatus: meta.terminalStatus } : {}),
|
|
272
|
+
...(meta?.closeReason ? { closeReason: meta.closeReason } : {}),
|
|
273
|
+
usageStatus,
|
|
274
|
+
...(loggedUsage ? { usage: loggedUsage } : {}),
|
|
275
|
+
...(totalTokens !== undefined ? { totalTokens } : {}),
|
|
276
|
+
});
|
|
277
|
+
if (isUsageDebugEnabled()) {
|
|
278
|
+
appendUsageDebug({
|
|
279
|
+
ts: Date.now(),
|
|
280
|
+
requestId,
|
|
281
|
+
provider: logCtx.provider,
|
|
282
|
+
model: logCtx.model,
|
|
283
|
+
upstreamContentType: logCtx.usageDebugContentType ?? null,
|
|
284
|
+
upstreamStatus: status,
|
|
285
|
+
bodyKind: logCtx.usageDebugBodyKind ?? "none",
|
|
286
|
+
bodySample: logCtx.usageDebugBodySample ?? "",
|
|
287
|
+
extractedUsage: loggedUsage ?? null,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function filterRequestLogs(logs: RequestLogEntry[], params: URLSearchParams): RequestLogEntry[] {
|
|
293
|
+
let filtered = logs;
|
|
294
|
+
const provider = params.get("provider")?.trim();
|
|
295
|
+
if (provider) filtered = filtered.filter(entry => entry.provider === provider);
|
|
296
|
+
const status = params.get("status")?.trim().toLowerCase();
|
|
297
|
+
if (status) {
|
|
298
|
+
filtered = /^[1-5]xx$/.test(status)
|
|
299
|
+
? filtered.filter(entry => Math.floor(entry.status / 100) === Number(status[0]))
|
|
300
|
+
: filtered.filter(entry => String(entry.status) === status);
|
|
301
|
+
}
|
|
302
|
+
const tailRaw = params.get("tail")?.trim();
|
|
303
|
+
if (tailRaw) {
|
|
304
|
+
const tail = Number.parseInt(tailRaw, 10);
|
|
305
|
+
if (Number.isFinite(tail) && tail > 0) filtered = filtered.slice(-Math.min(tail, MAX_LOG_SIZE));
|
|
306
|
+
}
|
|
307
|
+
return filtered;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function getRequestLogEntries(): RequestLogEntry[] { return requestLog; }
|