@nick3/copilot-api 1.5.5 → 1.5.9
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 +1 -1
- package/README.md +34 -4
- package/dist/{account-AacnHem5.js → account-CbYMFuS4.js} +12 -4
- package/dist/account-CbYMFuS4.js.map +1 -0
- package/dist/accounts-manager-BKG9aZEL.js +2899 -0
- package/dist/accounts-manager-BKG9aZEL.js.map +1 -0
- package/dist/admin/assets/index-BFN8rXmt.css +1 -0
- package/dist/admin/assets/index-HnEqzcKv.js +101 -0
- package/dist/admin/index.html +2 -2
- package/dist/{auth-B7x3wjry.js → auth-Ckj1wD43.js} +3 -3
- package/dist/{auth-B7x3wjry.js.map → auth-Ckj1wD43.js.map} +1 -1
- package/dist/{check-usage-B1cbDEOI.js → check-usage-bIbj_1Q_.js} +3 -3
- package/dist/check-usage-bIbj_1Q_.js.map +1 -0
- package/dist/{get-copilot-token-cha9rQwA.js → get-copilot-token-MAZsr5Vu.js} +2 -2
- package/dist/{get-copilot-token-cha9rQwA.js.map → get-copilot-token-MAZsr5Vu.js.map} +1 -1
- package/dist/main.js +3 -3
- package/dist/{poll-access-token-DFooFWhY.js → poll-access-token-DiwBJNtK.js} +58 -33
- package/dist/poll-access-token-DiwBJNtK.js.map +1 -0
- package/dist/{server-CuXJhEMC.js → server-DAxpfPde.js} +934 -899
- package/dist/server-DAxpfPde.js.map +1 -0
- package/dist/{start-D6O1XcfI.js → start-8dkfsQqd.js} +7 -6
- package/dist/start-8dkfsQqd.js.map +1 -0
- package/package.json +2 -1
- package/dist/account-AacnHem5.js.map +0 -1
- package/dist/accounts-manager-BevCBoaF.js +0 -1449
- package/dist/accounts-manager-BevCBoaF.js.map +0 -1
- package/dist/admin/assets/index-519a65q_.js +0 -66
- package/dist/admin/assets/index-ChMaMig2.css +0 -1
- package/dist/check-usage-B1cbDEOI.js.map +0 -1
- package/dist/poll-access-token-DFooFWhY.js.map +0 -1
- package/dist/server-CuXJhEMC.js.map +0 -1
- package/dist/start-D6O1XcfI.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import {
|
|
1
|
+
import { A as requestContext, D as prepareMessageProxyHeaders, E as prepareInteractionHeaders, O as accountFromState, T as prepareForCompact, _ as HTTPError, b as copilotHeaders, c as getUUID, d as parseUserIdMetadata, f as resolveAffinityKey, g as getCopilotUsage, h as getDeviceCode, j as resolveTraceId, k as state, l as isNullish, m as getGitHubUser, o as generateRequestIdFromPayload, p as sleep, s as getRootSessionId, t as pollAccessToken, u as normalizeStableSessionId, v as forwardError, w as normalizeDomain, y as copilotBaseUrl } from "./poll-access-token-DiwBJNtK.js";
|
|
2
|
+
import { _ as DEFAULT_IDENTITY_ENTERPRISE_DOMAIN, a as getAccountClientIdentityByLoginAndApp, b as getCurrentIdentityEnvironment, d as loadRegistry, g as saveRegistry, h as saveAccountToken, l as listAccountsFromRegistry, m as removeAccountToken, p as removeAccountFromRegistry, r as addAccountToRegistry, t as isAccountType } from "./account-CbYMFuS4.js";
|
|
3
3
|
import { r as ensurePaths, t as PATHS } from "./paths-DGlr310R.js";
|
|
4
|
-
import "./get-copilot-token-
|
|
5
|
-
import {
|
|
4
|
+
import "./get-copilot-token-MAZsr5Vu.js";
|
|
5
|
+
import { A as isResponsesApiWebSearchEnabled, C as getReasoningEffortForModel, D as isMessageStartInputTokensFallbackEnabled, E as isForceAgentEnabled, M as resolveModelAlias, N as shouldCompactUseSmallModel, O as isMessagesApiEnabled, S as getProviderConfig, T as isAccountAffinityEnabled, _ as getExtraPromptForModel, a as getClientIpInfo, b as getModelAliasesInfo, c as normalizeChatCompletionsUsage, d as toLocalDateString, f as PROVIDER_TYPE_ANTHROPIC, g as getConfig, h as getClaudeTokenMultiplier, i as extractResponsesUsageFromStreamEvent, j as mergeConfigWithDefaults, k as isResponsesApiContextManagementModel, l as normalizeEmbeddingsUsage, m as getAnthropicApiKey, n as applySharedSessionAffinityRetention, o as getRequestHistoryStore, p as getAliasTargetSet, r as extractResponsesUsageFromResult, s as getStatsStore, t as accountsManager, u as normalizeMessagesUsage, v as getLogLevel, w as getSmallModel, x as getModelRefreshIntervalMs, y as getModelAliases } from "./accounts-manager-BKG9aZEL.js";
|
|
6
6
|
import consola from "consola";
|
|
7
7
|
import fs, { readFile } from "node:fs/promises";
|
|
8
8
|
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -12,7 +12,6 @@ import { Hono } from "hono";
|
|
|
12
12
|
import { cors } from "hono/cors";
|
|
13
13
|
import { logger } from "hono/logger";
|
|
14
14
|
import fs$1, { existsSync } from "node:fs";
|
|
15
|
-
import { Database } from "bun:sqlite";
|
|
16
15
|
import { fileURLToPath } from "node:url";
|
|
17
16
|
import { streamSSE } from "hono/streaming";
|
|
18
17
|
import util from "node:util";
|
|
@@ -121,577 +120,6 @@ const traceIdMiddleware = async (c, next) => {
|
|
|
121
120
|
});
|
|
122
121
|
};
|
|
123
122
|
|
|
124
|
-
//#endregion
|
|
125
|
-
//#region src/lib/admin-db.ts
|
|
126
|
-
const DEFAULT_DB_PATH = path.join(PATHS.APP_DIR, "admin.sqlite");
|
|
127
|
-
let sharedDb = null;
|
|
128
|
-
let initialized = false;
|
|
129
|
-
const INIT_WARN_THROTTLE_MS = 3e4;
|
|
130
|
-
let lastInitWarnAtMs = 0;
|
|
131
|
-
let suppressedInitWarnCount = 0;
|
|
132
|
-
function warnAdminDbInitFailure(error) {
|
|
133
|
-
const now = Date.now();
|
|
134
|
-
if (now - lastInitWarnAtMs < INIT_WARN_THROTTLE_MS) {
|
|
135
|
-
suppressedInitWarnCount++;
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const suppressed = suppressedInitWarnCount;
|
|
139
|
-
suppressedInitWarnCount = 0;
|
|
140
|
-
lastInitWarnAtMs = now;
|
|
141
|
-
const suffix = suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : "";
|
|
142
|
-
consola.warn(`Failed to initialize admin DB; admin features disabled${suffix}`, error);
|
|
143
|
-
}
|
|
144
|
-
function getAdminDbPath() {
|
|
145
|
-
return DEFAULT_DB_PATH;
|
|
146
|
-
}
|
|
147
|
-
function openAdminDb(filePath = DEFAULT_DB_PATH) {
|
|
148
|
-
return new Database(filePath);
|
|
149
|
-
}
|
|
150
|
-
function initAdminDb(db) {
|
|
151
|
-
db.run("PRAGMA journal_mode = WAL;");
|
|
152
|
-
db.run("PRAGMA synchronous = NORMAL;");
|
|
153
|
-
db.run("PRAGMA busy_timeout = 3000;");
|
|
154
|
-
db.run("PRAGMA foreign_keys = ON;");
|
|
155
|
-
migrateAdminDb(db);
|
|
156
|
-
}
|
|
157
|
-
function getAdminDb() {
|
|
158
|
-
if (!sharedDb) sharedDb = openAdminDb();
|
|
159
|
-
if (!initialized) try {
|
|
160
|
-
initAdminDb(sharedDb);
|
|
161
|
-
initialized = true;
|
|
162
|
-
} catch (error) {
|
|
163
|
-
warnAdminDbInitFailure(error);
|
|
164
|
-
}
|
|
165
|
-
return sharedDb;
|
|
166
|
-
}
|
|
167
|
-
function getAdminDbUserVersion(db = getAdminDb()) {
|
|
168
|
-
try {
|
|
169
|
-
return db.query("PRAGMA user_version;").get()?.user_version ?? 0;
|
|
170
|
-
} catch {
|
|
171
|
-
return 0;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function migrateV1(db) {
|
|
175
|
-
db.run(`
|
|
176
|
-
CREATE TABLE IF NOT EXISTS request_log (
|
|
177
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
178
|
-
request_id TEXT NOT NULL UNIQUE,
|
|
179
|
-
|
|
180
|
-
started_at_ms INTEGER NOT NULL,
|
|
181
|
-
finished_at_ms INTEGER,
|
|
182
|
-
duration_ms INTEGER,
|
|
183
|
-
ttfb_ms INTEGER,
|
|
184
|
-
|
|
185
|
-
method TEXT NOT NULL,
|
|
186
|
-
path TEXT NOT NULL,
|
|
187
|
-
upstream_endpoint TEXT,
|
|
188
|
-
stream INTEGER NOT NULL DEFAULT 0,
|
|
189
|
-
|
|
190
|
-
account_id TEXT,
|
|
191
|
-
account_type TEXT,
|
|
192
|
-
cost_units REAL,
|
|
193
|
-
client_model TEXT,
|
|
194
|
-
upstream_model TEXT,
|
|
195
|
-
|
|
196
|
-
client_ip TEXT,
|
|
197
|
-
client_ip_source TEXT,
|
|
198
|
-
user_agent TEXT,
|
|
199
|
-
|
|
200
|
-
tokens_input INTEGER,
|
|
201
|
-
tokens_output INTEGER,
|
|
202
|
-
tokens_total INTEGER,
|
|
203
|
-
tokens_cached_input INTEGER,
|
|
204
|
-
usage_json TEXT,
|
|
205
|
-
|
|
206
|
-
premium_remaining_before REAL,
|
|
207
|
-
premium_remaining_after REAL,
|
|
208
|
-
premium_remaining_diff REAL,
|
|
209
|
-
premium_unlimited_before INTEGER,
|
|
210
|
-
premium_unlimited_after INTEGER,
|
|
211
|
-
|
|
212
|
-
http_status INTEGER,
|
|
213
|
-
error_name TEXT,
|
|
214
|
-
error_status INTEGER,
|
|
215
|
-
error_message TEXT,
|
|
216
|
-
selection_failure_reason TEXT
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
CREATE INDEX IF NOT EXISTS idx_request_log_started_at
|
|
220
|
-
ON request_log(started_at_ms DESC);
|
|
221
|
-
CREATE INDEX IF NOT EXISTS idx_request_log_account_started_at
|
|
222
|
-
ON request_log(account_id, started_at_ms DESC);
|
|
223
|
-
CREATE INDEX IF NOT EXISTS idx_request_log_model_started_at
|
|
224
|
-
ON request_log(upstream_model, started_at_ms DESC);
|
|
225
|
-
CREATE INDEX IF NOT EXISTS idx_request_log_endpoint_started_at
|
|
226
|
-
ON request_log(upstream_endpoint, started_at_ms DESC);
|
|
227
|
-
CREATE INDEX IF NOT EXISTS idx_request_log_status_started_at
|
|
228
|
-
ON request_log(http_status, started_at_ms DESC);
|
|
229
|
-
|
|
230
|
-
PRAGMA user_version = 1;
|
|
231
|
-
`);
|
|
232
|
-
}
|
|
233
|
-
function migrateAdminDb(db) {
|
|
234
|
-
const current = db.query("PRAGMA user_version;").get()?.user_version ?? 0;
|
|
235
|
-
if (current >= 5) return;
|
|
236
|
-
if (current < 1) migrateV1(db);
|
|
237
|
-
if (current < 2) db.run(`
|
|
238
|
-
ALTER TABLE request_log ADD COLUMN user_id TEXT;
|
|
239
|
-
ALTER TABLE request_log ADD COLUMN safety_identifier TEXT;
|
|
240
|
-
ALTER TABLE request_log ADD COLUMN prompt_cache_key TEXT;
|
|
241
|
-
ALTER TABLE request_log ADD COLUMN initiator TEXT;
|
|
242
|
-
ALTER TABLE request_log ADD COLUMN upstream_request_id TEXT;
|
|
243
|
-
|
|
244
|
-
PRAGMA user_version = 2;
|
|
245
|
-
`);
|
|
246
|
-
if (current < 3) db.run(`
|
|
247
|
-
CREATE INDEX IF NOT EXISTS idx_request_log_session_finished
|
|
248
|
-
ON request_log(
|
|
249
|
-
prompt_cache_key,
|
|
250
|
-
safety_identifier,
|
|
251
|
-
finished_at_ms DESC
|
|
252
|
-
)
|
|
253
|
-
WHERE finished_at_ms IS NOT NULL
|
|
254
|
-
AND tokens_input IS NOT NULL;
|
|
255
|
-
|
|
256
|
-
PRAGMA user_version = 3;
|
|
257
|
-
`);
|
|
258
|
-
if (current < 4) db.run(`
|
|
259
|
-
CREATE INDEX IF NOT EXISTS idx_request_log_session_finished_by_client_model
|
|
260
|
-
ON request_log(
|
|
261
|
-
prompt_cache_key,
|
|
262
|
-
safety_identifier,
|
|
263
|
-
client_model,
|
|
264
|
-
finished_at_ms DESC
|
|
265
|
-
)
|
|
266
|
-
WHERE finished_at_ms IS NOT NULL
|
|
267
|
-
AND tokens_input IS NOT NULL;
|
|
268
|
-
|
|
269
|
-
PRAGMA user_version = 4;
|
|
270
|
-
`);
|
|
271
|
-
if (current < 5) db.run(`
|
|
272
|
-
ALTER TABLE request_log ADD COLUMN affinity_hit INTEGER;
|
|
273
|
-
ALTER TABLE request_log ADD COLUMN affinity_cache_key TEXT;
|
|
274
|
-
|
|
275
|
-
PRAGMA user_version = 5;
|
|
276
|
-
`);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
//#endregion
|
|
280
|
-
//#region src/lib/request-history.ts
|
|
281
|
-
const DEFAULT_RETENTION_DAYS = 14;
|
|
282
|
-
const DEFAULT_MAX_ROWS = 2e5;
|
|
283
|
-
const INSERT_WARN_THROTTLE_MS = 3e4;
|
|
284
|
-
let lastInsertWarnAtMs = 0;
|
|
285
|
-
let suppressedInsertWarnCount = 0;
|
|
286
|
-
function warnInsertFailure(error) {
|
|
287
|
-
const now = Date.now();
|
|
288
|
-
if (now - lastInsertWarnAtMs < INSERT_WARN_THROTTLE_MS) {
|
|
289
|
-
suppressedInsertWarnCount++;
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
const suppressed = suppressedInsertWarnCount;
|
|
293
|
-
suppressedInsertWarnCount = 0;
|
|
294
|
-
lastInsertWarnAtMs = now;
|
|
295
|
-
const suffix = suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : "";
|
|
296
|
-
consola.warn(`Failed to insert request log${suffix}`, error);
|
|
297
|
-
}
|
|
298
|
-
function toDbNull(value) {
|
|
299
|
-
return value === void 0 ? null : value;
|
|
300
|
-
}
|
|
301
|
-
function toDbBool(value) {
|
|
302
|
-
if (value === true) return 1;
|
|
303
|
-
if (value === false) return 0;
|
|
304
|
-
return null;
|
|
305
|
-
}
|
|
306
|
-
function getClientIpInfo(c) {
|
|
307
|
-
const cf = c.req.header("cf-connecting-ip");
|
|
308
|
-
if (cf) return {
|
|
309
|
-
ip: cf.trim(),
|
|
310
|
-
source: "cf-connecting-ip"
|
|
311
|
-
};
|
|
312
|
-
const xff = c.req.header("x-forwarded-for");
|
|
313
|
-
if (xff) {
|
|
314
|
-
const first = xff.split(",")[0]?.trim();
|
|
315
|
-
if (first) return {
|
|
316
|
-
ip: first,
|
|
317
|
-
source: "x-forwarded-for"
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
const xri = c.req.header("x-real-ip");
|
|
321
|
-
if (xri) return {
|
|
322
|
-
ip: xri.trim(),
|
|
323
|
-
source: "x-real-ip"
|
|
324
|
-
};
|
|
325
|
-
return {};
|
|
326
|
-
}
|
|
327
|
-
function normalizeChatCompletionsUsage(usage) {
|
|
328
|
-
if (!usage) return {};
|
|
329
|
-
const cached = usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
330
|
-
const prompt = usage.prompt_tokens;
|
|
331
|
-
const completion = usage.completion_tokens;
|
|
332
|
-
const total = usage.total_tokens;
|
|
333
|
-
return {
|
|
334
|
-
tokensCachedInput: cached,
|
|
335
|
-
tokensInput: Math.max(0, prompt - cached),
|
|
336
|
-
tokensOutput: completion,
|
|
337
|
-
tokensTotal: total,
|
|
338
|
-
usageJson: JSON.stringify(usage)
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
function normalizeResponsesUsage(usage) {
|
|
342
|
-
if (!usage) return {};
|
|
343
|
-
const cached = usage.input_tokens_details?.cached_tokens ?? 0;
|
|
344
|
-
const input = usage.input_tokens;
|
|
345
|
-
const output = usage.output_tokens ?? 0;
|
|
346
|
-
const total = usage.total_tokens;
|
|
347
|
-
return {
|
|
348
|
-
tokensCachedInput: cached,
|
|
349
|
-
tokensInput: Math.max(0, input - cached),
|
|
350
|
-
tokensOutput: output,
|
|
351
|
-
tokensTotal: total,
|
|
352
|
-
usageJson: JSON.stringify(usage)
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
function normalizeMessagesUsage(usage) {
|
|
356
|
-
if (!usage) return {};
|
|
357
|
-
const cached = usage.cache_read_input_tokens ?? 0;
|
|
358
|
-
const input = usage.input_tokens;
|
|
359
|
-
const output = usage.output_tokens;
|
|
360
|
-
const hasInput = typeof input === "number";
|
|
361
|
-
const hasOutput = typeof output === "number";
|
|
362
|
-
return {
|
|
363
|
-
tokensCachedInput: cached,
|
|
364
|
-
tokensInput: hasInput ? Math.max(0, input - cached) : void 0,
|
|
365
|
-
tokensOutput: hasOutput ? output : void 0,
|
|
366
|
-
tokensTotal: hasInput || hasOutput ? (input ?? 0) + (output ?? 0) : void 0,
|
|
367
|
-
usageJson: JSON.stringify(usage)
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
function normalizeEmbeddingsUsage(usage) {
|
|
371
|
-
if (!usage) return {};
|
|
372
|
-
return {
|
|
373
|
-
tokensCachedInput: 0,
|
|
374
|
-
tokensInput: usage.prompt_tokens,
|
|
375
|
-
tokensOutput: 0,
|
|
376
|
-
tokensTotal: usage.total_tokens,
|
|
377
|
-
usageJson: JSON.stringify(usage)
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
var RequestHistoryStore = class {
|
|
381
|
-
db;
|
|
382
|
-
insertStmt;
|
|
383
|
-
getByRequestIdStmt;
|
|
384
|
-
getLastCompletedUsageBySessionStmt;
|
|
385
|
-
constructor(db) {
|
|
386
|
-
this.db = db;
|
|
387
|
-
this.insertStmt = db.query(`
|
|
388
|
-
INSERT INTO request_log (
|
|
389
|
-
request_id,
|
|
390
|
-
started_at_ms,
|
|
391
|
-
finished_at_ms,
|
|
392
|
-
duration_ms,
|
|
393
|
-
ttfb_ms,
|
|
394
|
-
method,
|
|
395
|
-
path,
|
|
396
|
-
upstream_endpoint,
|
|
397
|
-
stream,
|
|
398
|
-
account_id,
|
|
399
|
-
account_type,
|
|
400
|
-
cost_units,
|
|
401
|
-
client_model,
|
|
402
|
-
upstream_model,
|
|
403
|
-
client_ip,
|
|
404
|
-
client_ip_source,
|
|
405
|
-
user_agent,
|
|
406
|
-
user_id,
|
|
407
|
-
safety_identifier,
|
|
408
|
-
prompt_cache_key,
|
|
409
|
-
initiator,
|
|
410
|
-
upstream_request_id,
|
|
411
|
-
tokens_input,
|
|
412
|
-
tokens_output,
|
|
413
|
-
tokens_total,
|
|
414
|
-
tokens_cached_input,
|
|
415
|
-
usage_json,
|
|
416
|
-
premium_remaining_before,
|
|
417
|
-
premium_remaining_after,
|
|
418
|
-
premium_remaining_diff,
|
|
419
|
-
premium_unlimited_before,
|
|
420
|
-
premium_unlimited_after,
|
|
421
|
-
http_status,
|
|
422
|
-
error_name,
|
|
423
|
-
error_status,
|
|
424
|
-
error_message,
|
|
425
|
-
selection_failure_reason,
|
|
426
|
-
affinity_hit,
|
|
427
|
-
affinity_cache_key
|
|
428
|
-
) VALUES (
|
|
429
|
-
?,?,?,?,?,?,?,?,
|
|
430
|
-
?,?,?,?,?,?,?,?,
|
|
431
|
-
?,?,?,?,?,?,?,?,
|
|
432
|
-
?,?,?,?,?,?,?,?,
|
|
433
|
-
?,?,?,?,?,?,?
|
|
434
|
-
);
|
|
435
|
-
`);
|
|
436
|
-
this.getByRequestIdStmt = db.query("SELECT * FROM request_log WHERE request_id = ? LIMIT 1;");
|
|
437
|
-
this.getLastCompletedUsageBySessionStmt = db.query(`
|
|
438
|
-
SELECT
|
|
439
|
-
tokens_input,
|
|
440
|
-
tokens_output,
|
|
441
|
-
tokens_total,
|
|
442
|
-
tokens_cached_input
|
|
443
|
-
FROM request_log
|
|
444
|
-
WHERE prompt_cache_key = ?
|
|
445
|
-
AND safety_identifier = ?
|
|
446
|
-
AND client_model = ?
|
|
447
|
-
AND finished_at_ms IS NOT NULL
|
|
448
|
-
AND tokens_input IS NOT NULL
|
|
449
|
-
ORDER BY finished_at_ms DESC
|
|
450
|
-
LIMIT 1;
|
|
451
|
-
`);
|
|
452
|
-
}
|
|
453
|
-
insert(record) {
|
|
454
|
-
try {
|
|
455
|
-
const args = [
|
|
456
|
-
record.requestId,
|
|
457
|
-
record.startedAtMs,
|
|
458
|
-
toDbNull(record.finishedAtMs),
|
|
459
|
-
toDbNull(record.durationMs),
|
|
460
|
-
toDbNull(record.ttfbMs),
|
|
461
|
-
record.method,
|
|
462
|
-
record.path,
|
|
463
|
-
toDbNull(record.upstreamEndpoint),
|
|
464
|
-
record.stream ? 1 : 0,
|
|
465
|
-
toDbNull(record.accountId),
|
|
466
|
-
toDbNull(record.accountType),
|
|
467
|
-
toDbNull(record.costUnits),
|
|
468
|
-
toDbNull(record.clientModel),
|
|
469
|
-
toDbNull(record.upstreamModel),
|
|
470
|
-
toDbNull(record.clientIp),
|
|
471
|
-
toDbNull(record.clientIpSource),
|
|
472
|
-
toDbNull(record.userAgent),
|
|
473
|
-
toDbNull(record.userId),
|
|
474
|
-
toDbNull(record.safetyIdentifier),
|
|
475
|
-
toDbNull(record.promptCacheKey),
|
|
476
|
-
toDbNull(record.initiator),
|
|
477
|
-
toDbNull(record.upstreamRequestId),
|
|
478
|
-
toDbNull(record.tokensInput),
|
|
479
|
-
toDbNull(record.tokensOutput),
|
|
480
|
-
toDbNull(record.tokensTotal),
|
|
481
|
-
toDbNull(record.tokensCachedInput),
|
|
482
|
-
toDbNull(record.usageJson),
|
|
483
|
-
toDbNull(record.premiumRemainingBefore),
|
|
484
|
-
toDbNull(record.premiumRemainingAfter),
|
|
485
|
-
toDbNull(record.premiumRemainingDiff),
|
|
486
|
-
toDbBool(record.premiumUnlimitedBefore),
|
|
487
|
-
toDbBool(record.premiumUnlimitedAfter),
|
|
488
|
-
toDbNull(record.httpStatus),
|
|
489
|
-
toDbNull(record.errorName),
|
|
490
|
-
toDbNull(record.errorStatus),
|
|
491
|
-
toDbNull(record.errorMessage),
|
|
492
|
-
toDbNull(record.selectionFailureReason),
|
|
493
|
-
toDbBool(record.affinityHit),
|
|
494
|
-
toDbNull(record.affinityCacheKey)
|
|
495
|
-
];
|
|
496
|
-
this.insertStmt.run(...args);
|
|
497
|
-
} catch (error) {
|
|
498
|
-
warnInsertFailure(error);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
getByRequestId(requestId) {
|
|
502
|
-
try {
|
|
503
|
-
return this.getByRequestIdStmt.get(requestId) ?? null;
|
|
504
|
-
} catch (error) {
|
|
505
|
-
consola.debug("Failed to fetch request log by request_id", error);
|
|
506
|
-
return null;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
getLastCompletedUsageBySession(session) {
|
|
510
|
-
if (!session.promptCacheKey || !session.safetyIdentifier || !session.clientModel) return null;
|
|
511
|
-
try {
|
|
512
|
-
const row = this.getLastCompletedUsageBySessionStmt.get(session.promptCacheKey, session.safetyIdentifier, session.clientModel);
|
|
513
|
-
if (!row || row.tokens_input === null) return null;
|
|
514
|
-
const tokensOutput = row.tokens_output === null ? void 0 : row.tokens_output;
|
|
515
|
-
const tokensTotal = row.tokens_total === null ? void 0 : row.tokens_total;
|
|
516
|
-
const tokensCachedInput = row.tokens_cached_input === null ? void 0 : row.tokens_cached_input;
|
|
517
|
-
return {
|
|
518
|
-
tokensInput: row.tokens_input,
|
|
519
|
-
tokensOutput,
|
|
520
|
-
tokensTotal,
|
|
521
|
-
tokensCachedInput
|
|
522
|
-
};
|
|
523
|
-
} catch (error) {
|
|
524
|
-
consola.debug("Failed to fetch last completed usage by session", error);
|
|
525
|
-
return null;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
query(params) {
|
|
529
|
-
const limit = Math.max(1, Math.min(params.limit, 200));
|
|
530
|
-
const where = [];
|
|
531
|
-
const values = [];
|
|
532
|
-
if (params.cursorId !== void 0) {
|
|
533
|
-
where.push("id < ?");
|
|
534
|
-
values.push(params.cursorId);
|
|
535
|
-
}
|
|
536
|
-
if (params.accountId) {
|
|
537
|
-
where.push("account_id = ?");
|
|
538
|
-
values.push(params.accountId);
|
|
539
|
-
}
|
|
540
|
-
if (params.upstreamModel) {
|
|
541
|
-
where.push("upstream_model = ?");
|
|
542
|
-
values.push(params.upstreamModel);
|
|
543
|
-
}
|
|
544
|
-
if (params.clientModel) {
|
|
545
|
-
where.push("client_model = ?");
|
|
546
|
-
values.push(params.clientModel);
|
|
547
|
-
}
|
|
548
|
-
if (params.upstreamEndpoint) {
|
|
549
|
-
where.push("upstream_endpoint = ?");
|
|
550
|
-
values.push(params.upstreamEndpoint);
|
|
551
|
-
}
|
|
552
|
-
if (params.path) {
|
|
553
|
-
where.push("path = ?");
|
|
554
|
-
values.push(params.path);
|
|
555
|
-
}
|
|
556
|
-
if (params.status !== void 0) {
|
|
557
|
-
where.push("http_status = ?");
|
|
558
|
-
values.push(params.status);
|
|
559
|
-
}
|
|
560
|
-
if (params.hasError === true) where.push("http_status >= 400");
|
|
561
|
-
if (params.hasError === false) where.push("http_status < 400");
|
|
562
|
-
if (params.fromMs !== void 0) {
|
|
563
|
-
where.push("started_at_ms >= ?");
|
|
564
|
-
values.push(params.fromMs);
|
|
565
|
-
}
|
|
566
|
-
if (params.toMs !== void 0) {
|
|
567
|
-
where.push("started_at_ms <= ?");
|
|
568
|
-
values.push(params.toMs);
|
|
569
|
-
}
|
|
570
|
-
const sql = `
|
|
571
|
-
SELECT *
|
|
572
|
-
FROM request_log
|
|
573
|
-
${where.length > 0 ? `WHERE ${where.join(" AND ")}` : ""}
|
|
574
|
-
ORDER BY id DESC
|
|
575
|
-
LIMIT ?;
|
|
576
|
-
`;
|
|
577
|
-
const rows = this.db.query(sql).all(...values, limit + 1);
|
|
578
|
-
const items = rows.slice(0, limit);
|
|
579
|
-
const hasMore = rows.length > limit;
|
|
580
|
-
return {
|
|
581
|
-
items,
|
|
582
|
-
nextCursorId: hasMore ? items.at(-1)?.id : void 0,
|
|
583
|
-
hasMore
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
getAccountStatsSince(sinceMs) {
|
|
587
|
-
try {
|
|
588
|
-
const rows = this.db.query(`
|
|
589
|
-
SELECT
|
|
590
|
-
account_id,
|
|
591
|
-
COUNT(*) AS request_count,
|
|
592
|
-
SUM(CASE WHEN http_status >= 400 THEN 1 ELSE 0 END) AS error_count,
|
|
593
|
-
COALESCE(SUM(tokens_total), 0) AS tokens_total,
|
|
594
|
-
COALESCE(AVG(duration_ms), 0) AS avg_duration_ms,
|
|
595
|
-
COALESCE(MAX(started_at_ms), 0) AS last_request_at_ms
|
|
596
|
-
FROM request_log
|
|
597
|
-
WHERE started_at_ms >= ? AND account_id IS NOT NULL
|
|
598
|
-
GROUP BY account_id;
|
|
599
|
-
`).all(sinceMs);
|
|
600
|
-
const map = {};
|
|
601
|
-
for (const row of rows) map[row.account_id] = row;
|
|
602
|
-
return map;
|
|
603
|
-
} catch (error) {
|
|
604
|
-
consola.debug("Failed to fetch account stats", error);
|
|
605
|
-
return {};
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
cleanupRetention(retentionDays = DEFAULT_RETENTION_DAYS, maxRows = DEFAULT_MAX_ROWS) {
|
|
609
|
-
try {
|
|
610
|
-
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
611
|
-
this.db.query("DELETE FROM request_log WHERE started_at_ms < ?;").run(cutoffMs);
|
|
612
|
-
if (this.db.query("SELECT COUNT(*) AS c FROM request_log;").get().c <= maxRows) return;
|
|
613
|
-
const offset = maxRows - 1;
|
|
614
|
-
const thresholdId = this.db.query("SELECT id FROM request_log ORDER BY id DESC LIMIT 1 OFFSET ?;").get(offset)?.id;
|
|
615
|
-
if (!thresholdId) return;
|
|
616
|
-
this.db.query("DELETE FROM request_log WHERE id < ?;").run(thresholdId);
|
|
617
|
-
} catch (error) {
|
|
618
|
-
consola.debug("Failed to cleanup request_log retention", error);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
meta() {
|
|
622
|
-
return {
|
|
623
|
-
dbPath: getAdminDbPath(),
|
|
624
|
-
userVersion: getAdminDbUserVersion(this.db),
|
|
625
|
-
retentionDays: DEFAULT_RETENTION_DAYS,
|
|
626
|
-
maxRows: DEFAULT_MAX_ROWS
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
const STORE_INIT_WARN_THROTTLE_MS = 3e4;
|
|
631
|
-
const STORE_INIT_RETRY_DELAY_MS = 3e4;
|
|
632
|
-
let lastStoreInitWarnAtMs = 0;
|
|
633
|
-
let suppressedStoreInitWarnCount = 0;
|
|
634
|
-
let nextStoreRetryAtMs = 0;
|
|
635
|
-
function warnStoreInitFailure(error) {
|
|
636
|
-
const now = Date.now();
|
|
637
|
-
if (now - lastStoreInitWarnAtMs < STORE_INIT_WARN_THROTTLE_MS) {
|
|
638
|
-
suppressedStoreInitWarnCount++;
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
const suppressed = suppressedStoreInitWarnCount;
|
|
642
|
-
suppressedStoreInitWarnCount = 0;
|
|
643
|
-
lastStoreInitWarnAtMs = now;
|
|
644
|
-
const suffix = suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : "";
|
|
645
|
-
consola.warn(`Request history store is disabled${suffix}`, error);
|
|
646
|
-
}
|
|
647
|
-
const disabledStore = {
|
|
648
|
-
insert: () => {},
|
|
649
|
-
getByRequestId: () => null,
|
|
650
|
-
getLastCompletedUsageBySession: () => null,
|
|
651
|
-
query: () => ({
|
|
652
|
-
items: [],
|
|
653
|
-
hasMore: false
|
|
654
|
-
}),
|
|
655
|
-
getAccountStatsSince: () => ({}),
|
|
656
|
-
cleanupRetention: () => {},
|
|
657
|
-
meta: () => ({
|
|
658
|
-
dbPath: getAdminDbPath(),
|
|
659
|
-
userVersion: 0,
|
|
660
|
-
retentionDays: DEFAULT_RETENTION_DAYS,
|
|
661
|
-
maxRows: DEFAULT_MAX_ROWS
|
|
662
|
-
})
|
|
663
|
-
};
|
|
664
|
-
let sharedStore = null;
|
|
665
|
-
let maintenanceStarted = false;
|
|
666
|
-
function getRequestHistoryStore() {
|
|
667
|
-
if (sharedStore) return sharedStore;
|
|
668
|
-
const now = Date.now();
|
|
669
|
-
if (now < nextStoreRetryAtMs) return disabledStore;
|
|
670
|
-
try {
|
|
671
|
-
sharedStore = new RequestHistoryStore(getAdminDb());
|
|
672
|
-
if (!maintenanceStarted) {
|
|
673
|
-
maintenanceStarted = true;
|
|
674
|
-
sharedStore.cleanupRetention();
|
|
675
|
-
setInterval(() => {
|
|
676
|
-
sharedStore?.cleanupRetention();
|
|
677
|
-
}, 1440 * 60 * 1e3);
|
|
678
|
-
}
|
|
679
|
-
return sharedStore;
|
|
680
|
-
} catch (error) {
|
|
681
|
-
nextStoreRetryAtMs = now + STORE_INIT_RETRY_DELAY_MS;
|
|
682
|
-
warnStoreInitFailure(error);
|
|
683
|
-
return disabledStore;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
function extractResponsesUsageFromStreamEvent(event) {
|
|
687
|
-
if (event.type === "response.completed" || event.type === "response.incomplete") return normalizeResponsesUsage(event.response.usage);
|
|
688
|
-
if (event.type === "response.failed") return normalizeResponsesUsage(event.response.usage);
|
|
689
|
-
return {};
|
|
690
|
-
}
|
|
691
|
-
function extractResponsesUsageFromResult(result) {
|
|
692
|
-
return normalizeResponsesUsage(result.usage);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
123
|
//#endregion
|
|
696
124
|
//#region src/routes/admin-api/auth-sessions.ts
|
|
697
125
|
function buildOauthUrls(enterpriseDomain) {
|
|
@@ -812,6 +240,7 @@ var AuthSessionManager = class {
|
|
|
812
240
|
});
|
|
813
241
|
}
|
|
814
242
|
if (!this.getLiveSession(sessionId)) return;
|
|
243
|
+
await accountsManager.reloadRegistryNow();
|
|
815
244
|
this.completeSession(sessionId, accountId);
|
|
816
245
|
} catch (error) {
|
|
817
246
|
if (session.abortController.signal.aborted) return;
|
|
@@ -910,6 +339,7 @@ const CONFIG_KEYS = new Set([
|
|
|
910
339
|
"auth",
|
|
911
340
|
"extraPrompts",
|
|
912
341
|
"smallModel",
|
|
342
|
+
"logLevel",
|
|
913
343
|
"accountAffinity",
|
|
914
344
|
"apiKey",
|
|
915
345
|
"anthropicApiKey",
|
|
@@ -923,6 +353,7 @@ const CONFIG_KEYS = new Set([
|
|
|
923
353
|
"compactUseSmallModel",
|
|
924
354
|
"messageStartInputTokensFallback",
|
|
925
355
|
"modelRefreshIntervalHours",
|
|
356
|
+
"sessionAffinityRetentionDays",
|
|
926
357
|
"useMessagesApi",
|
|
927
358
|
"useResponsesApiWebSearch"
|
|
928
359
|
]);
|
|
@@ -934,6 +365,12 @@ const REASONING_EFFORTS = new Set([
|
|
|
934
365
|
"high",
|
|
935
366
|
"xhigh"
|
|
936
367
|
]);
|
|
368
|
+
const LOG_LEVELS = new Set([
|
|
369
|
+
"error",
|
|
370
|
+
"warn",
|
|
371
|
+
"info",
|
|
372
|
+
"debug"
|
|
373
|
+
]);
|
|
937
374
|
const BLOCKED_KEYS = new Set([
|
|
938
375
|
"__proto__",
|
|
939
376
|
"constructor",
|
|
@@ -952,6 +389,13 @@ function parseOptionalString(value, field) {
|
|
|
952
389
|
if (!trimmed) return { clear: true };
|
|
953
390
|
return { value: trimmed };
|
|
954
391
|
}
|
|
392
|
+
function parseOptionalLogLevel(value) {
|
|
393
|
+
const parsed = parseOptionalString(value, "logLevel");
|
|
394
|
+
if ("error" in parsed) return parsed;
|
|
395
|
+
if ("clear" in parsed) return parsed;
|
|
396
|
+
if (!LOG_LEVELS.has(parsed.value)) return { error: `logLevel must be one of: ${[...LOG_LEVELS].join(", ")}` };
|
|
397
|
+
return { value: parsed.value };
|
|
398
|
+
}
|
|
955
399
|
function parseOptionalBoolean(value, field) {
|
|
956
400
|
if (value === null || value === void 0) return { clear: true };
|
|
957
401
|
if (typeof value !== "boolean") return { error: `${field} must be a boolean` };
|
|
@@ -1001,13 +445,20 @@ const PROVIDER_MODEL_CONFIG_KEYS = new Set([
|
|
|
1001
445
|
"topP",
|
|
1002
446
|
"topK"
|
|
1003
447
|
]);
|
|
1004
|
-
const
|
|
448
|
+
const PROVIDER_CONFIG_FIELDS = [
|
|
1005
449
|
"type",
|
|
1006
450
|
"enabled",
|
|
1007
451
|
"baseUrl",
|
|
1008
452
|
"apiKey",
|
|
1009
|
-
"
|
|
1010
|
-
|
|
453
|
+
"authType",
|
|
454
|
+
"models",
|
|
455
|
+
"adjustInputTokens"
|
|
456
|
+
];
|
|
457
|
+
const PROVIDER_AUTH_TYPES = ["authorization", "x-api-key"];
|
|
458
|
+
const PROVIDER_CONFIG_KEYS = new Set(PROVIDER_CONFIG_FIELDS);
|
|
459
|
+
function isProviderAuthType(value) {
|
|
460
|
+
return PROVIDER_AUTH_TYPES.includes(value);
|
|
461
|
+
}
|
|
1011
462
|
function validateAllowedObjectKeys(value, field, allowed) {
|
|
1012
463
|
for (const key of Object.keys(value)) if (!allowed.has(key)) return `${field}.${key} is not supported`;
|
|
1013
464
|
}
|
|
@@ -1087,12 +538,27 @@ function applyProviderApiKey(provider, value, field) {
|
|
|
1087
538
|
if ("error" in parsed) return parsed.error;
|
|
1088
539
|
if ("value" in parsed) provider.apiKey = parsed.value;
|
|
1089
540
|
}
|
|
541
|
+
function applyProviderAuthType(provider, value, field) {
|
|
542
|
+
if (!Object.hasOwn(value, "authType")) return void 0;
|
|
543
|
+
const parsed = parseOptionalString(value.authType, `${field}.authType`);
|
|
544
|
+
if ("error" in parsed) return parsed.error;
|
|
545
|
+
if ("value" in parsed) {
|
|
546
|
+
if (!isProviderAuthType(parsed.value)) return `${field}.authType must be one of: ${PROVIDER_AUTH_TYPES.map((item) => `"${item}"`).join(", ")}`;
|
|
547
|
+
provider.authType = parsed.value;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
1090
550
|
function applyProviderModels(provider, value, field) {
|
|
1091
551
|
if (!Object.hasOwn(value, "models")) return void 0;
|
|
1092
552
|
const parsed = parseProviderModelsRecord(value.models, `${field}.models`);
|
|
1093
553
|
if ("error" in parsed) return parsed.error;
|
|
1094
554
|
if ("value" in parsed) provider.models = parsed.value;
|
|
1095
555
|
}
|
|
556
|
+
function applyProviderAdjustInputTokens(provider, value, field) {
|
|
557
|
+
if (!Object.hasOwn(value, "adjustInputTokens")) return void 0;
|
|
558
|
+
const parsed = parseOptionalBoolean(value.adjustInputTokens, `${field}.adjustInputTokens`);
|
|
559
|
+
if ("error" in parsed) return parsed.error;
|
|
560
|
+
if ("value" in parsed) provider.adjustInputTokens = parsed.value;
|
|
561
|
+
}
|
|
1096
562
|
function parseProviderConfig(value, field) {
|
|
1097
563
|
if (value === null || value === void 0) return { error: `${field} must be an object` };
|
|
1098
564
|
if (!isPlainObject(value)) return { error: `${field} must be an object` };
|
|
@@ -1107,8 +573,12 @@ function parseProviderConfig(value, field) {
|
|
|
1107
573
|
if (baseUrlError) return { error: baseUrlError };
|
|
1108
574
|
const apiKeyError = applyProviderApiKey(provider, value, field);
|
|
1109
575
|
if (apiKeyError) return { error: apiKeyError };
|
|
576
|
+
const authTypeError = applyProviderAuthType(provider, value, field);
|
|
577
|
+
if (authTypeError) return { error: authTypeError };
|
|
1110
578
|
const modelsError = applyProviderModels(provider, value, field);
|
|
1111
579
|
if (modelsError) return { error: modelsError };
|
|
580
|
+
const adjustInputTokensError = applyProviderAdjustInputTokens(provider, value, field);
|
|
581
|
+
if (adjustInputTokensError) return { error: adjustInputTokensError };
|
|
1112
582
|
return { value: provider };
|
|
1113
583
|
}
|
|
1114
584
|
function parseProviders(value) {
|
|
@@ -1205,6 +675,15 @@ function applyAuthConfig(next, value) {
|
|
|
1205
675
|
}
|
|
1206
676
|
next.auth = parsed.value;
|
|
1207
677
|
}
|
|
678
|
+
function applyLogLevel(next, value) {
|
|
679
|
+
const parsed = parseOptionalLogLevel(value);
|
|
680
|
+
if ("error" in parsed) return parsed.error;
|
|
681
|
+
if ("clear" in parsed) {
|
|
682
|
+
next.logLevel = void 0;
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
next.logLevel = parsed.value;
|
|
686
|
+
}
|
|
1208
687
|
function applyOptionalBoolean(next, key, value) {
|
|
1209
688
|
const parsed = parseOptionalBoolean(value, key);
|
|
1210
689
|
if ("error" in parsed) return parsed.error;
|
|
@@ -1272,6 +751,7 @@ const CONFIG_PATCH_HANDLERS = {
|
|
|
1272
751
|
auth: applyAuthConfig,
|
|
1273
752
|
extraPrompts: applyExtraPrompts,
|
|
1274
753
|
smallModel: (next, value) => applyOptionalString(next, "smallModel", value),
|
|
754
|
+
logLevel: applyLogLevel,
|
|
1275
755
|
accountAffinity: (next, value) => applyOptionalBoolean(next, "accountAffinity", value),
|
|
1276
756
|
apiKey: (next, value) => applyOptionalString(next, "apiKey", value),
|
|
1277
757
|
anthropicApiKey: (next, value) => applyOptionalString(next, "anthropicApiKey", value),
|
|
@@ -1285,6 +765,7 @@ const CONFIG_PATCH_HANDLERS = {
|
|
|
1285
765
|
compactUseSmallModel: (next, value) => applyOptionalBoolean(next, "compactUseSmallModel", value),
|
|
1286
766
|
messageStartInputTokensFallback: (next, value) => applyOptionalBoolean(next, "messageStartInputTokensFallback", value),
|
|
1287
767
|
modelRefreshIntervalHours: (next, value) => applyOptionalNumber(next, "modelRefreshIntervalHours", value),
|
|
768
|
+
sessionAffinityRetentionDays: (next, value) => applyOptionalNumber(next, "sessionAffinityRetentionDays", value),
|
|
1288
769
|
useMessagesApi: (next, value) => applyOptionalBoolean(next, "useMessagesApi", value),
|
|
1289
770
|
useResponsesApiWebSearch: (next, value) => applyOptionalBoolean(next, "useResponsesApiWebSearch", value)
|
|
1290
771
|
};
|
|
@@ -1367,6 +848,7 @@ adminApiRoutes.post("/config", async (c) => {
|
|
|
1367
848
|
const merged = mergeConfigWithDefaults();
|
|
1368
849
|
accountsManager.setAccountAffinityEnabled(isAccountAffinityEnabled());
|
|
1369
850
|
accountsManager.setModelsRefreshIntervalMs(getModelRefreshIntervalMs());
|
|
851
|
+
applySharedSessionAffinityRetention();
|
|
1370
852
|
return c.json({
|
|
1371
853
|
...merged,
|
|
1372
854
|
_configPath: PATHS.CONFIG_PATH
|
|
@@ -1515,7 +997,8 @@ adminApiRoutes.get("/accounts", async (c) => {
|
|
|
1515
997
|
remaining: s.remaining,
|
|
1516
998
|
unlimited: s.unlimited,
|
|
1517
999
|
failed: s.failed,
|
|
1518
|
-
failureReason: s.failureReason
|
|
1000
|
+
failureReason: s.failureReason,
|
|
1001
|
+
enabled: s.enabled
|
|
1519
1002
|
},
|
|
1520
1003
|
stats
|
|
1521
1004
|
};
|
|
@@ -1575,11 +1058,7 @@ adminApiRoutes.post("/accounts/auth/start", async (c) => {
|
|
|
1575
1058
|
});
|
|
1576
1059
|
const enterpriseDomainRaw = payload.enterpriseDomain;
|
|
1577
1060
|
let enterpriseDomain;
|
|
1578
|
-
if (accountType === "enterprise" && typeof enterpriseDomainRaw === "string") enterpriseDomain = enterpriseDomainRaw.trim();
|
|
1579
|
-
if (accountType === "enterprise" && !enterpriseDomain) return jsonError(c, 400, {
|
|
1580
|
-
message: "enterpriseDomain is required for enterprise accounts.",
|
|
1581
|
-
type: "bad_request"
|
|
1582
|
-
});
|
|
1061
|
+
if (accountType === "enterprise" && typeof enterpriseDomainRaw === "string") enterpriseDomain = enterpriseDomainRaw.trim() || void 0;
|
|
1583
1062
|
try {
|
|
1584
1063
|
const result = await authSessionManager.startAuth({
|
|
1585
1064
|
accountType,
|
|
@@ -1610,6 +1089,40 @@ adminApiRoutes.post("/accounts/auth/cancel/:sessionId", (c) => {
|
|
|
1610
1089
|
});
|
|
1611
1090
|
return c.json({ cancelled: true });
|
|
1612
1091
|
});
|
|
1092
|
+
adminApiRoutes.patch("/accounts/:id", async (c) => {
|
|
1093
|
+
const accountId = c.req.param("id");
|
|
1094
|
+
let payload;
|
|
1095
|
+
try {
|
|
1096
|
+
payload = await c.req.json();
|
|
1097
|
+
} catch {
|
|
1098
|
+
return jsonError(c, 400, {
|
|
1099
|
+
message: "Invalid JSON body.",
|
|
1100
|
+
type: "bad_request"
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload) || typeof payload.enabled !== "boolean") return jsonError(c, 400, {
|
|
1104
|
+
message: "Request body must contain { enabled: boolean }.",
|
|
1105
|
+
type: "bad_request"
|
|
1106
|
+
});
|
|
1107
|
+
const enabled = payload.enabled;
|
|
1108
|
+
try {
|
|
1109
|
+
const registry = await loadRegistry();
|
|
1110
|
+
const entry = registry.accounts.find((a) => a.id === accountId);
|
|
1111
|
+
if (!entry) return jsonError(c, 404, {
|
|
1112
|
+
message: "Account not found.",
|
|
1113
|
+
type: "not_found"
|
|
1114
|
+
});
|
|
1115
|
+
entry.enabled = enabled;
|
|
1116
|
+
await saveRegistry(registry);
|
|
1117
|
+
await accountsManager.reloadRegistryNow();
|
|
1118
|
+
return c.json({ success: true });
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
return jsonError(c, 500, {
|
|
1121
|
+
message: `Failed to update account: ${error instanceof Error ? error.message : String(error)}`,
|
|
1122
|
+
type: "internal_error"
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1613
1126
|
adminApiRoutes.delete("/accounts/:id", async (c) => {
|
|
1614
1127
|
const accountId = c.req.param("id");
|
|
1615
1128
|
try {
|
|
@@ -1623,6 +1136,7 @@ adminApiRoutes.delete("/accounts/:id", async (c) => {
|
|
|
1623
1136
|
} catch (error) {
|
|
1624
1137
|
console.error(`Account ${accountId} deleted but token cleanup failed.`, error);
|
|
1625
1138
|
}
|
|
1139
|
+
await accountsManager.reloadRegistryNow();
|
|
1626
1140
|
return c.json({
|
|
1627
1141
|
deleted: true,
|
|
1628
1142
|
accountId
|
|
@@ -1646,10 +1160,6 @@ adminApiRoutes.post("/accounts/:id/reauth", async (c) => {
|
|
|
1646
1160
|
const resolvedEnterpriseDomain = (await getAccountClientIdentityByLoginAndApp(accountId, oauthApp))?.enterpriseDomain;
|
|
1647
1161
|
let enterpriseDomain;
|
|
1648
1162
|
if (resolvedEnterpriseDomain && resolvedEnterpriseDomain !== DEFAULT_IDENTITY_ENTERPRISE_DOMAIN) enterpriseDomain = resolvedEnterpriseDomain;
|
|
1649
|
-
if (account.accountType === "enterprise" && !enterpriseDomain) return jsonError(c, 400, {
|
|
1650
|
-
message: "Cannot re-authenticate enterprise account: enterprise domain could not be resolved from stored identity.",
|
|
1651
|
-
type: "bad_request"
|
|
1652
|
-
});
|
|
1653
1163
|
const result = await authSessionManager.startAuth({
|
|
1654
1164
|
accountType: account.accountType,
|
|
1655
1165
|
enterpriseDomain,
|
|
@@ -1663,6 +1173,70 @@ adminApiRoutes.post("/accounts/:id/reauth", async (c) => {
|
|
|
1663
1173
|
});
|
|
1664
1174
|
}
|
|
1665
1175
|
});
|
|
1176
|
+
adminApiRoutes.get("/stats/premium-daily", (c) => {
|
|
1177
|
+
const p = new URL(c.req.url, "http://local").searchParams;
|
|
1178
|
+
const from = p.get("from") || void 0;
|
|
1179
|
+
const to = p.get("to") || void 0;
|
|
1180
|
+
const accountId = p.get("account_id") || void 0;
|
|
1181
|
+
const granularity = p.get("granularity") === "hour" ? "hour" : "day";
|
|
1182
|
+
const now = /* @__PURE__ */ new Date();
|
|
1183
|
+
const todayStr = toLocalDateString(now.getTime());
|
|
1184
|
+
const resolvedFrom = from || toLocalDateString(now.getTime() - 10080 * 60 * 1e3);
|
|
1185
|
+
const resolvedTo = to || todayStr;
|
|
1186
|
+
function parseLocalDate(dateStr) {
|
|
1187
|
+
const [y, m, d] = dateStr.split("-").map(Number);
|
|
1188
|
+
return new Date(y, m - 1, d);
|
|
1189
|
+
}
|
|
1190
|
+
if (granularity === "hour") {
|
|
1191
|
+
const fromDate = parseLocalDate(resolvedFrom);
|
|
1192
|
+
if (parseLocalDate(resolvedTo).getTime() - fromDate.getTime() + 1440 * 60 * 1e3 > 2880 * 60 * 1e3) return jsonError(c, 400, {
|
|
1193
|
+
message: "Hourly granularity is only supported for ranges up to 48 hours.",
|
|
1194
|
+
type: "bad_request"
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
const statsStore = getStatsStore();
|
|
1198
|
+
if (!statsStore) return c.json({
|
|
1199
|
+
daily: [],
|
|
1200
|
+
by_account: [],
|
|
1201
|
+
range: {
|
|
1202
|
+
from: resolvedFrom,
|
|
1203
|
+
to: resolvedTo,
|
|
1204
|
+
granularity
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
if (granularity === "hour") {
|
|
1208
|
+
const fromMs = parseLocalDate(resolvedFrom).getTime();
|
|
1209
|
+
const toMs = parseLocalDate(resolvedTo).getTime() + 86399999;
|
|
1210
|
+
const result$1 = statsStore.getHourlyPremiumStats({
|
|
1211
|
+
fromMs,
|
|
1212
|
+
toMs,
|
|
1213
|
+
accountId
|
|
1214
|
+
});
|
|
1215
|
+
return c.json({
|
|
1216
|
+
daily: result$1.daily,
|
|
1217
|
+
by_account: result$1.byAccount,
|
|
1218
|
+
range: {
|
|
1219
|
+
from: resolvedFrom,
|
|
1220
|
+
to: resolvedTo,
|
|
1221
|
+
granularity
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
const result = statsStore.getDailyPremiumStats({
|
|
1226
|
+
from: resolvedFrom,
|
|
1227
|
+
to: resolvedTo,
|
|
1228
|
+
accountId
|
|
1229
|
+
});
|
|
1230
|
+
return c.json({
|
|
1231
|
+
daily: result.daily,
|
|
1232
|
+
by_account: result.byAccount,
|
|
1233
|
+
range: {
|
|
1234
|
+
from: resolvedFrom,
|
|
1235
|
+
to: resolvedTo,
|
|
1236
|
+
granularity
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
});
|
|
1666
1240
|
|
|
1667
1241
|
//#endregion
|
|
1668
1242
|
//#region src/routes/admin/route.ts
|
|
@@ -2332,39 +1906,122 @@ function toAccountContext(account) {
|
|
|
2332
1906
|
clientSessionId: account.clientSessionId
|
|
2333
1907
|
};
|
|
2334
1908
|
}
|
|
1909
|
+
const OWNERSHIP_MISMATCH_PATTERN = /does not belong to this connection/i;
|
|
1910
|
+
const UUID_PATTERN = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi;
|
|
1911
|
+
const OPAQUE_ID_PATTERN = /\b(?:msg|resp|chatcmpl|out|toolu|call|item)_[\w-]+\b/g;
|
|
1912
|
+
const UPSTREAM_ERROR_MAX_LENGTH = 1e3;
|
|
1913
|
+
const UPSTREAM_ERROR_READ_FAILED_SENTINEL = "[upstream body read failed]";
|
|
1914
|
+
function sanitizeUpstreamErrorMessage(value) {
|
|
1915
|
+
return truncate(value.trim().replaceAll(UUID_PATTERN, "<uuid>").replaceAll(OPAQUE_ID_PATTERN, "<opaque_id>"), UPSTREAM_ERROR_MAX_LENGTH);
|
|
1916
|
+
}
|
|
1917
|
+
function readErrorField(value) {
|
|
1918
|
+
return typeof value === "string" ? value : void 0;
|
|
1919
|
+
}
|
|
1920
|
+
function formatUpstreamErrorBody(payload) {
|
|
1921
|
+
if (typeof payload === "string") {
|
|
1922
|
+
const text = payload.trim();
|
|
1923
|
+
return text.length > 0 ? text : void 0;
|
|
1924
|
+
}
|
|
1925
|
+
if (!payload || typeof payload !== "object") return;
|
|
1926
|
+
const root = payload;
|
|
1927
|
+
const message = readErrorField(root.error?.message) ?? readErrorField(root.message);
|
|
1928
|
+
const code = readErrorField(root.error?.code) ?? readErrorField(root.code);
|
|
1929
|
+
if (message && code) return `${message} [code:${code}]`;
|
|
1930
|
+
return message ?? (code ? `[code:${code}]` : void 0);
|
|
1931
|
+
}
|
|
1932
|
+
async function extractUpstreamErrorMessageRaw(error) {
|
|
1933
|
+
if (!(error instanceof HTTPError)) return { upstreamErrorMessageReadFailed: false };
|
|
1934
|
+
try {
|
|
1935
|
+
const bodyText = await error.response.clone().text();
|
|
1936
|
+
if (!bodyText) return { upstreamErrorMessageReadFailed: false };
|
|
1937
|
+
let candidate;
|
|
1938
|
+
try {
|
|
1939
|
+
candidate = formatUpstreamErrorBody(JSON.parse(bodyText));
|
|
1940
|
+
} catch {
|
|
1941
|
+
candidate = bodyText;
|
|
1942
|
+
}
|
|
1943
|
+
if (!candidate) return { upstreamErrorMessageReadFailed: false };
|
|
1944
|
+
const sanitized = sanitizeUpstreamErrorMessage(candidate);
|
|
1945
|
+
return {
|
|
1946
|
+
upstreamErrorMessageRaw: sanitized.length > 0 ? sanitized : void 0,
|
|
1947
|
+
upstreamErrorMessageReadFailed: false
|
|
1948
|
+
};
|
|
1949
|
+
} catch (readError) {
|
|
1950
|
+
consola.warn("Failed to read upstream HTTP error response body:", {
|
|
1951
|
+
status: error.response.status,
|
|
1952
|
+
readError
|
|
1953
|
+
});
|
|
1954
|
+
return { upstreamErrorMessageReadFailed: true };
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
2335
1957
|
function extractErrorDetails(error) {
|
|
2336
1958
|
const errorName = error instanceof Error ? error.name : "Error";
|
|
2337
1959
|
const errorMessage = error instanceof Error ? truncate(error.message) : truncate(String(error));
|
|
2338
1960
|
const errorStatus = error instanceof HTTPError ? error.response.status : void 0;
|
|
1961
|
+
const httpStatus = errorStatus ?? 500;
|
|
1962
|
+
const unauthorized = errorStatus === 401;
|
|
2339
1963
|
return {
|
|
2340
|
-
httpStatus
|
|
1964
|
+
httpStatus,
|
|
2341
1965
|
errorName,
|
|
2342
1966
|
errorStatus,
|
|
2343
1967
|
errorMessage,
|
|
2344
|
-
unauthorized
|
|
1968
|
+
unauthorized,
|
|
1969
|
+
ownershipMismatch: unauthorized && OWNERSHIP_MISMATCH_PATTERN.test(errorMessage)
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
async function extractErrorObservability(error) {
|
|
1973
|
+
const details = extractErrorDetails(error);
|
|
1974
|
+
const { upstreamErrorMessageRaw, upstreamErrorMessageReadFailed } = await extractUpstreamErrorMessageRaw(error);
|
|
1975
|
+
const observableUpstreamErrorMessageRaw = upstreamErrorMessageRaw ?? (upstreamErrorMessageReadFailed ? UPSTREAM_ERROR_READ_FAILED_SENTINEL : void 0);
|
|
1976
|
+
return {
|
|
1977
|
+
...details,
|
|
1978
|
+
ownershipMismatch: details.ownershipMismatch || details.unauthorized && OWNERSHIP_MISMATCH_PATTERN.test(upstreamErrorMessageRaw ?? ""),
|
|
1979
|
+
upstreamErrorMessageRaw: observableUpstreamErrorMessageRaw,
|
|
1980
|
+
upstreamErrorMessageReadFailed
|
|
2345
1981
|
};
|
|
2346
1982
|
}
|
|
1983
|
+
function getUserVisibleErrorMessage(details) {
|
|
1984
|
+
if (details.upstreamErrorMessageReadFailed) return details.errorMessage;
|
|
1985
|
+
return details.upstreamErrorMessageRaw ?? details.errorMessage;
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* Returns true when the 401 indicates a genuine auth/token failure
|
|
1989
|
+
* (not an ownership mismatch) and the account should be marked failed.
|
|
1990
|
+
*/
|
|
1991
|
+
function shouldMarkAccountFailed(details) {
|
|
1992
|
+
return details.unauthorized && !details.ownershipMismatch && details.upstreamErrorMessageReadFailed !== true;
|
|
1993
|
+
}
|
|
2347
1994
|
|
|
2348
1995
|
//#endregion
|
|
2349
1996
|
//#region src/lib/logger.ts
|
|
2350
1997
|
const LOG_RETENTION_MS = 10080 * 60 * 1e3;
|
|
2351
1998
|
const CLEANUP_INTERVAL_MS = 1440 * 60 * 1e3;
|
|
2352
|
-
const LOG_DIR = path.join(PATHS.APP_DIR, "logs");
|
|
2353
1999
|
const FLUSH_INTERVAL_MS = 1e3;
|
|
2354
2000
|
const MAX_BUFFER_SIZE = 100;
|
|
2001
|
+
const FILE_LOG_LEVEL_PRIORITY = {
|
|
2002
|
+
error: 0,
|
|
2003
|
+
warn: 1,
|
|
2004
|
+
info: 2,
|
|
2005
|
+
debug: 3
|
|
2006
|
+
};
|
|
2007
|
+
let logDir = path.join(PATHS.APP_DIR, "logs");
|
|
2008
|
+
let testLogLevelOverride;
|
|
2355
2009
|
const logStreams = /* @__PURE__ */ new Map();
|
|
2356
2010
|
const logBuffers = /* @__PURE__ */ new Map();
|
|
2357
2011
|
let runtimeInitialized = false;
|
|
2358
2012
|
let flushInterval;
|
|
2359
2013
|
let cleanupInterval;
|
|
2014
|
+
let exitHandler;
|
|
2015
|
+
let sigintHandler;
|
|
2016
|
+
let sigtermHandler;
|
|
2360
2017
|
const ensureLogDirectory = () => {
|
|
2361
|
-
if (!fs$1.existsSync(
|
|
2018
|
+
if (!fs$1.existsSync(logDir)) fs$1.mkdirSync(logDir, { recursive: true });
|
|
2362
2019
|
};
|
|
2363
2020
|
const cleanupOldLogs = () => {
|
|
2364
|
-
if (!fs$1.existsSync(
|
|
2021
|
+
if (!fs$1.existsSync(logDir)) return;
|
|
2365
2022
|
const now = Date.now();
|
|
2366
|
-
for (const entry of fs$1.readdirSync(
|
|
2367
|
-
const filePath = path.join(
|
|
2023
|
+
for (const entry of fs$1.readdirSync(logDir)) {
|
|
2024
|
+
const filePath = path.join(logDir, entry);
|
|
2368
2025
|
let stats;
|
|
2369
2026
|
try {
|
|
2370
2027
|
stats = fs$1.statSync(filePath);
|
|
@@ -2387,6 +2044,14 @@ const sanitizeName = (name) => {
|
|
|
2387
2044
|
const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
2388
2045
|
return normalized === "" ? "handler" : normalized;
|
|
2389
2046
|
};
|
|
2047
|
+
const getHandlerLogFilePath = (name, date) => {
|
|
2048
|
+
const dateKey = [
|
|
2049
|
+
date.getFullYear(),
|
|
2050
|
+
String(date.getMonth() + 1).padStart(2, "0"),
|
|
2051
|
+
String(date.getDate()).padStart(2, "0")
|
|
2052
|
+
].join("-");
|
|
2053
|
+
return path.join(logDir, `${sanitizeName(name)}-${dateKey}.log`);
|
|
2054
|
+
};
|
|
2390
2055
|
const maybeUnref = (timer) => {
|
|
2391
2056
|
timer.unref();
|
|
2392
2057
|
};
|
|
@@ -2426,15 +2091,18 @@ const initializeLoggerRuntime = () => {
|
|
|
2426
2091
|
maybeUnref(flushInterval);
|
|
2427
2092
|
cleanupInterval = setInterval(cleanupOldLogs, CLEANUP_INTERVAL_MS);
|
|
2428
2093
|
maybeUnref(cleanupInterval);
|
|
2429
|
-
|
|
2430
|
-
|
|
2094
|
+
exitHandler = cleanup;
|
|
2095
|
+
sigintHandler = () => {
|
|
2431
2096
|
cleanup();
|
|
2432
2097
|
process.exit(0);
|
|
2433
|
-
}
|
|
2434
|
-
|
|
2098
|
+
};
|
|
2099
|
+
sigtermHandler = () => {
|
|
2435
2100
|
cleanup();
|
|
2436
2101
|
process.exit(0);
|
|
2437
|
-
}
|
|
2102
|
+
};
|
|
2103
|
+
process.once("exit", exitHandler);
|
|
2104
|
+
process.once("SIGINT", sigintHandler);
|
|
2105
|
+
process.once("SIGTERM", sigtermHandler);
|
|
2438
2106
|
};
|
|
2439
2107
|
const getLogStream = (filePath) => {
|
|
2440
2108
|
initializeLoggerRuntime();
|
|
@@ -2458,8 +2126,21 @@ const appendLine = (filePath, line) => {
|
|
|
2458
2126
|
buffer.push(line);
|
|
2459
2127
|
if (buffer.length >= MAX_BUFFER_SIZE) flushBuffer(filePath);
|
|
2460
2128
|
};
|
|
2129
|
+
const normalizeLogTypeToLevel = (type) => {
|
|
2130
|
+
switch (type) {
|
|
2131
|
+
case "error": return "error";
|
|
2132
|
+
case "warn": return "warn";
|
|
2133
|
+
case "debug": return "debug";
|
|
2134
|
+
default: return "info";
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
2137
|
+
const shouldWriteFileLog = (type, logLevel = resolveLogLevel()) => {
|
|
2138
|
+
return FILE_LOG_LEVEL_PRIORITY[normalizeLogTypeToLevel(type)] <= FILE_LOG_LEVEL_PRIORITY[logLevel];
|
|
2139
|
+
};
|
|
2140
|
+
const resolveLogLevel = () => testLogLevelOverride ?? getLogLevel();
|
|
2141
|
+
const isDebugFileLoggingEnabled = () => resolveLogLevel() === "debug";
|
|
2461
2142
|
const debugLazy = (logger$7, factory) => {
|
|
2462
|
-
if (!
|
|
2143
|
+
if (!isDebugFileLoggingEnabled()) return;
|
|
2463
2144
|
logger$7.debug(...factory());
|
|
2464
2145
|
};
|
|
2465
2146
|
const debugJson = (logger$7, label, value) => {
|
|
@@ -2468,18 +2149,31 @@ const debugJson = (logger$7, label, value) => {
|
|
|
2468
2149
|
const debugJsonTail = (logger$7, label, { value, tailLength = 400 }) => {
|
|
2469
2150
|
debugLazy(logger$7, () => [label, JSON.stringify(value).slice(-tailLength)]);
|
|
2470
2151
|
};
|
|
2152
|
+
const getConsolaLevel = () => {
|
|
2153
|
+
const logLevel = resolveLogLevel();
|
|
2154
|
+
switch (logLevel) {
|
|
2155
|
+
case "error": return 0;
|
|
2156
|
+
case "warn": return 1;
|
|
2157
|
+
case "info": return 2;
|
|
2158
|
+
case "debug": return 4;
|
|
2159
|
+
default: return logLevel;
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2471
2162
|
const createHandlerLogger = (name) => {
|
|
2472
|
-
const sanitizedName = sanitizeName(name);
|
|
2473
2163
|
const instance = consola.withTag(name);
|
|
2474
|
-
|
|
2164
|
+
Object.defineProperty(instance, "level", {
|
|
2165
|
+
get: getConsolaLevel,
|
|
2166
|
+
configurable: true
|
|
2167
|
+
});
|
|
2475
2168
|
instance.setReporters([]);
|
|
2476
2169
|
instance.addReporter({ log(logObj) {
|
|
2170
|
+
const fileLogLevel = resolveLogLevel();
|
|
2171
|
+
if (!shouldWriteFileLog(logObj.type, fileLogLevel)) return;
|
|
2477
2172
|
initializeLoggerRuntime();
|
|
2478
2173
|
const traceId = requestContext.getStore()?.traceId;
|
|
2479
2174
|
const date = logObj.date;
|
|
2480
|
-
const dateKey = date.toLocaleDateString("sv-SE");
|
|
2481
2175
|
const timestamp = date.toLocaleString("sv-SE", { hour12: false });
|
|
2482
|
-
const filePath =
|
|
2176
|
+
const filePath = getHandlerLogFilePath(name, date);
|
|
2483
2177
|
const message = formatArgs(logObj.args);
|
|
2484
2178
|
const traceIdStr = traceId ? ` [${traceId}]` : "";
|
|
2485
2179
|
appendLine(filePath, `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${traceIdStr}${message ? ` ${message}` : ""}`);
|
|
@@ -2728,6 +2422,13 @@ const getTokenCount = async (payload, model) => {
|
|
|
2728
2422
|
};
|
|
2729
2423
|
};
|
|
2730
2424
|
|
|
2425
|
+
//#endregion
|
|
2426
|
+
//#region src/lib/request-initiator.ts
|
|
2427
|
+
function resolveEffectiveInitiator(baseInitiator, options) {
|
|
2428
|
+
if (options.isCompact || options.isSubagent) return "agent";
|
|
2429
|
+
return baseInitiator;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2731
2432
|
//#endregion
|
|
2732
2433
|
//#region src/services/copilot/create-chat-completions.ts
|
|
2733
2434
|
function isGpt5MiniFamily(modelId) {
|
|
@@ -2751,10 +2452,13 @@ const createChatCompletions = async (payload, account, options) => {
|
|
|
2751
2452
|
const ctx = account ?? accountFromState();
|
|
2752
2453
|
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
2753
2454
|
const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x$1) => x$1.type === "image_url"));
|
|
2754
|
-
const
|
|
2455
|
+
const effectiveInitiator = resolveEffectiveInitiator(options?.initiator ?? getChatInitiator(payload.messages), {
|
|
2456
|
+
isCompact: options?.isCompact,
|
|
2457
|
+
isSubagent: Boolean(options?.subagentMarker)
|
|
2458
|
+
});
|
|
2755
2459
|
const headers = {
|
|
2756
2460
|
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
2757
|
-
"x-initiator":
|
|
2461
|
+
"x-initiator": effectiveInitiator
|
|
2758
2462
|
};
|
|
2759
2463
|
prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
|
|
2760
2464
|
const upstreamPayload = applyDefaultReasoningEffort(payload);
|
|
@@ -2772,26 +2476,136 @@ const createChatCompletions = async (payload, account, options) => {
|
|
|
2772
2476
|
return await response.json();
|
|
2773
2477
|
};
|
|
2774
2478
|
|
|
2479
|
+
//#endregion
|
|
2480
|
+
//#region src/routes/chat-completions/support.ts
|
|
2481
|
+
const CHAT_COMPLETIONS_ENDPOINT$1 = "/chat/completions";
|
|
2482
|
+
const GPT_5_4_MODEL_ID = "gpt-5.4";
|
|
2483
|
+
const GPT_5_4_CHAT_COMPLETIONS_MESSAGE = "Please use `/v1/responses` or `/v1/messages` API";
|
|
2484
|
+
function buildRequestContext$1(c) {
|
|
2485
|
+
const requestId = randomUUID();
|
|
2486
|
+
const startedAtMs = Date.now();
|
|
2487
|
+
const method = c.req.raw.method;
|
|
2488
|
+
const path$2 = new URL(c.req.url, "http://local").pathname;
|
|
2489
|
+
const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
|
|
2490
|
+
return {
|
|
2491
|
+
requestId,
|
|
2492
|
+
startedAtMs,
|
|
2493
|
+
method,
|
|
2494
|
+
path: path$2,
|
|
2495
|
+
clientIp,
|
|
2496
|
+
clientIpSource,
|
|
2497
|
+
userAgent: c.req.header("user-agent") ?? void 0
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
function insertRequestLog$2(store, request, record) {
|
|
2501
|
+
store.insert({
|
|
2502
|
+
requestId: request.requestId,
|
|
2503
|
+
startedAtMs: request.startedAtMs,
|
|
2504
|
+
method: request.method,
|
|
2505
|
+
path: request.path,
|
|
2506
|
+
clientIp: request.clientIp,
|
|
2507
|
+
clientIpSource: request.clientIpSource,
|
|
2508
|
+
userAgent: request.userAgent,
|
|
2509
|
+
userId: request.userId,
|
|
2510
|
+
safetyIdentifier: request.safetyIdentifier,
|
|
2511
|
+
promptCacheKey: request.promptCacheKey,
|
|
2512
|
+
initiator: request.initiator,
|
|
2513
|
+
upstreamRequestId: request.upstreamRequestId,
|
|
2514
|
+
affinityKeyUsed: request.affinityKeyUsed,
|
|
2515
|
+
affinityKeySource: request.affinityKeySource,
|
|
2516
|
+
selectionReason: request.selectionReason,
|
|
2517
|
+
affinityHit: request.affinityHit,
|
|
2518
|
+
affinityCacheKey: request.affinityCacheKey,
|
|
2519
|
+
...record
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
function recordUnsupportedChatCompletionsModel(store, params) {
|
|
2523
|
+
const { request, stream, clientModel, upstreamModel, accountId, accountType, costUnits, premiumRemainingBefore, premiumRemainingAfter, premiumUnlimitedBefore, premiumUnlimitedAfter } = params;
|
|
2524
|
+
const finishedAtMs = Date.now();
|
|
2525
|
+
let premiumRemainingDiff;
|
|
2526
|
+
if (premiumRemainingBefore !== void 0 && premiumRemainingAfter !== void 0) premiumRemainingDiff = premiumRemainingAfter - premiumRemainingBefore;
|
|
2527
|
+
insertRequestLog$2(store, request, {
|
|
2528
|
+
finishedAtMs,
|
|
2529
|
+
durationMs: finishedAtMs - request.startedAtMs,
|
|
2530
|
+
upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
|
|
2531
|
+
stream,
|
|
2532
|
+
accountId,
|
|
2533
|
+
accountType,
|
|
2534
|
+
costUnits,
|
|
2535
|
+
clientModel,
|
|
2536
|
+
upstreamModel,
|
|
2537
|
+
premiumRemainingBefore,
|
|
2538
|
+
premiumRemainingAfter,
|
|
2539
|
+
premiumRemainingDiff,
|
|
2540
|
+
premiumUnlimitedBefore,
|
|
2541
|
+
premiumUnlimitedAfter,
|
|
2542
|
+
httpStatus: 400,
|
|
2543
|
+
errorName: "invalid_request_error",
|
|
2544
|
+
errorMessage: GPT_5_4_CHAT_COMPLETIONS_MESSAGE
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
function unsupportedChatCompletionsModelResponse(c) {
|
|
2548
|
+
return c.json({ error: {
|
|
2549
|
+
message: GPT_5_4_CHAT_COMPLETIONS_MESSAGE,
|
|
2550
|
+
type: "invalid_request_error"
|
|
2551
|
+
} }, 400);
|
|
2552
|
+
}
|
|
2553
|
+
function getSelectionFailureStatus(reason) {
|
|
2554
|
+
if (reason === "MODEL_NOT_SUPPORTED") return 400;
|
|
2555
|
+
if (reason === "NO_ACCOUNTS") return 503;
|
|
2556
|
+
return 429;
|
|
2557
|
+
}
|
|
2558
|
+
function recordSelectionFailure$2(store, params) {
|
|
2559
|
+
const { request, stream, clientModel, reason } = params;
|
|
2560
|
+
const finishedAtMs = Date.now();
|
|
2561
|
+
insertRequestLog$2(store, request, {
|
|
2562
|
+
finishedAtMs,
|
|
2563
|
+
durationMs: finishedAtMs - request.startedAtMs,
|
|
2564
|
+
upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
|
|
2565
|
+
stream,
|
|
2566
|
+
clientModel,
|
|
2567
|
+
httpStatus: getSelectionFailureStatus(reason),
|
|
2568
|
+
selectionFailureReason: reason
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
function selectionFailureResponse$2(c, params) {
|
|
2572
|
+
const { clientModel, reason } = params;
|
|
2573
|
+
if (reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
|
|
2574
|
+
message: `Model "${clientModel}" is not available for any configured account.`,
|
|
2575
|
+
type: "invalid_request_error"
|
|
2576
|
+
} }, 400);
|
|
2577
|
+
if (reason === "NO_ACCOUNTS") return c.json({ error: {
|
|
2578
|
+
message: "No enabled Copilot accounts are configured. Add or re-enable an account.",
|
|
2579
|
+
type: "service_unavailable_error"
|
|
2580
|
+
} }, 503);
|
|
2581
|
+
return c.json({ error: {
|
|
2582
|
+
message: "All accounts have exhausted their quota. Please wait for quota refresh or add additional accounts.",
|
|
2583
|
+
type: "rate_limit_error"
|
|
2584
|
+
} }, 429);
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2775
2587
|
//#endregion
|
|
2776
2588
|
//#region src/routes/chat-completions/handler.ts
|
|
2777
2589
|
const logger$6 = createHandlerLogger("chat-completions-handler");
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
const {
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2590
|
+
function buildChatCompletionCandidates(clientModel) {
|
|
2591
|
+
return [{
|
|
2592
|
+
modelId: clientModel,
|
|
2593
|
+
endpoint: CHAT_COMPLETIONS_ENDPOINT$1
|
|
2594
|
+
}];
|
|
2595
|
+
}
|
|
2596
|
+
function isUnsupportedChatCompletionsModel(modelId) {
|
|
2597
|
+
return resolveModelAlias(modelId).toLowerCase() === GPT_5_4_MODEL_ID;
|
|
2598
|
+
}
|
|
2599
|
+
function maybeRejectChatCompletionsClientModel(c, store, params) {
|
|
2600
|
+
const { request, clientModel, streamRequested } = params;
|
|
2601
|
+
if (isUnsupportedChatCompletionsModel(clientModel)) {
|
|
2602
|
+
recordUnsupportedChatCompletionsModel(store, {
|
|
2603
|
+
request,
|
|
2604
|
+
clientModel,
|
|
2605
|
+
stream: streamRequested
|
|
2606
|
+
});
|
|
2607
|
+
return unsupportedChatCompletionsModelResponse(c);
|
|
2608
|
+
}
|
|
2795
2609
|
if (getAliasTargetSet().has(clientModel.toLowerCase())) {
|
|
2796
2610
|
recordSelectionFailure$2(store, {
|
|
2797
2611
|
request,
|
|
@@ -2804,36 +2618,60 @@ async function handleCompletion$1(c) {
|
|
|
2804
2618
|
reason: "MODEL_NOT_SUPPORTED"
|
|
2805
2619
|
});
|
|
2806
2620
|
}
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2621
|
+
return null;
|
|
2622
|
+
}
|
|
2623
|
+
async function handleCompletion$1(c) {
|
|
2624
|
+
await checkRateLimit(state);
|
|
2625
|
+
const store = getRequestHistoryStore();
|
|
2626
|
+
const request = buildRequestContext$1(c);
|
|
2627
|
+
const payload = await c.req.json();
|
|
2628
|
+
const clientModel = payload.model;
|
|
2629
|
+
const streamRequested = Boolean(payload.stream);
|
|
2630
|
+
const normalizedPromptCacheKey = applyChatRequestMetadata(request, payload, getChatInitiator(payload.messages));
|
|
2631
|
+
const blockedResponse = maybeRejectChatCompletionsClientModel(c, store, {
|
|
2632
|
+
request,
|
|
2633
|
+
clientModel,
|
|
2634
|
+
streamRequested
|
|
2810
2635
|
});
|
|
2811
|
-
|
|
2812
|
-
const
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2636
|
+
if (blockedResponse) return blockedResponse;
|
|
2637
|
+
const selectionResult = await selectChatCompletionAccount({
|
|
2638
|
+
c,
|
|
2639
|
+
store,
|
|
2640
|
+
request,
|
|
2641
|
+
payload,
|
|
2642
|
+
clientModel,
|
|
2643
|
+
streamRequested,
|
|
2644
|
+
normalizedPromptCacheKey
|
|
2645
|
+
});
|
|
2646
|
+
if (selectionResult instanceof Response) return selectionResult;
|
|
2647
|
+
const { headerSessionId, selection, upstreamRequestId } = selectionResult;
|
|
2648
|
+
const { account, reservation, selectedModel } = selection;
|
|
2649
|
+
request.affinityHit = selection.affinityHit;
|
|
2650
|
+
request.affinityCacheKey = selection.affinityCacheKey;
|
|
2651
|
+
request.selectionReason = selection.selectionReason;
|
|
2652
|
+
const premiumRemainingBefore = account.premiumRemaining;
|
|
2653
|
+
const premiumUnlimitedBefore = account.unlimited;
|
|
2654
|
+
if (selectedModel.id === GPT_5_4_MODEL_ID) {
|
|
2655
|
+
await accountsManager.finalizeQuota(account, reservation);
|
|
2656
|
+
recordUnsupportedChatCompletionsModel(store, {
|
|
2818
2657
|
request,
|
|
2819
2658
|
clientModel,
|
|
2820
2659
|
stream: streamRequested,
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2660
|
+
upstreamModel: selectedModel.id,
|
|
2661
|
+
accountId: account.id,
|
|
2662
|
+
accountType: account.accountType,
|
|
2663
|
+
costUnits: selection.costUnits,
|
|
2664
|
+
premiumRemainingBefore,
|
|
2665
|
+
premiumRemainingAfter: account.premiumRemaining,
|
|
2666
|
+
premiumUnlimitedBefore,
|
|
2667
|
+
premiumUnlimitedAfter: account.unlimited
|
|
2826
2668
|
});
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
request.affinityHit = selection.affinityHit;
|
|
2830
|
-
request.affinityCacheKey = selection.affinityCacheKey;
|
|
2669
|
+
return unsupportedChatCompletionsModelResponse(c);
|
|
2670
|
+
}
|
|
2831
2671
|
const upstreamPayload = {
|
|
2832
2672
|
...payload,
|
|
2833
2673
|
model: selectedModel.id
|
|
2834
2674
|
};
|
|
2835
|
-
const premiumRemainingBefore = account.premiumRemaining;
|
|
2836
|
-
const premiumUnlimitedBefore = account.unlimited;
|
|
2837
2675
|
await logTokenCountForRequest({
|
|
2838
2676
|
payload: upstreamPayload,
|
|
2839
2677
|
selectedModel
|
|
@@ -2841,7 +2679,7 @@ async function handleCompletion$1(c) {
|
|
|
2841
2679
|
if (state.manualApprove) await awaitApproval();
|
|
2842
2680
|
const payloadWithMaxTokens = applyDefaultMaxTokens(upstreamPayload, selectedModel);
|
|
2843
2681
|
const accountCtx = toAccountContext(account);
|
|
2844
|
-
const upstreamSessionId = getUUID(upstreamRequestId);
|
|
2682
|
+
const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
|
|
2845
2683
|
request.upstreamRequestId = upstreamRequestId;
|
|
2846
2684
|
request.upstreamSessionId = upstreamSessionId;
|
|
2847
2685
|
if (streamRequested) return handleStreamingRequest({
|
|
@@ -2867,64 +2705,60 @@ async function handleCompletion$1(c) {
|
|
|
2867
2705
|
premiumUnlimitedBefore
|
|
2868
2706
|
});
|
|
2869
2707
|
}
|
|
2870
|
-
function
|
|
2871
|
-
const
|
|
2872
|
-
const
|
|
2873
|
-
const
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
method,
|
|
2880
|
-
path: path$2,
|
|
2881
|
-
clientIp,
|
|
2882
|
-
clientIpSource,
|
|
2883
|
-
userAgent: c.req.header("user-agent") ?? void 0
|
|
2884
|
-
};
|
|
2708
|
+
function applyChatRequestMetadata(request, payload, initiator) {
|
|
2709
|
+
const userId = payload.user ?? void 0;
|
|
2710
|
+
const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
|
|
2711
|
+
const normalizedPromptCacheKey = promptCacheKey ?? void 0;
|
|
2712
|
+
request.userId = userId;
|
|
2713
|
+
request.safetyIdentifier = safetyIdentifier ?? void 0;
|
|
2714
|
+
request.promptCacheKey = normalizedPromptCacheKey;
|
|
2715
|
+
request.initiator = initiator;
|
|
2716
|
+
return normalizedPromptCacheKey;
|
|
2885
2717
|
}
|
|
2886
|
-
function
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
safetyIdentifier: request.safetyIdentifier,
|
|
2897
|
-
promptCacheKey: request.promptCacheKey,
|
|
2898
|
-
initiator: request.initiator,
|
|
2899
|
-
upstreamRequestId: request.upstreamRequestId,
|
|
2900
|
-
affinityHit: request.affinityHit,
|
|
2901
|
-
affinityCacheKey: request.affinityCacheKey,
|
|
2902
|
-
...record
|
|
2903
|
-
});
|
|
2718
|
+
async function writeChatCompletionsStreamError(stream, message) {
|
|
2719
|
+
try {
|
|
2720
|
+
await stream.writeSSE({ data: JSON.stringify({ error: {
|
|
2721
|
+
message,
|
|
2722
|
+
type: "error"
|
|
2723
|
+
} }) });
|
|
2724
|
+
await stream.writeSSE({ data: "[DONE]" });
|
|
2725
|
+
} catch (streamError) {
|
|
2726
|
+
logger$6.warn("Failed to write chat completions stream error event:", streamError);
|
|
2727
|
+
}
|
|
2904
2728
|
}
|
|
2905
|
-
function
|
|
2906
|
-
const { request,
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
durationMs: finishedAtMs - request.startedAtMs,
|
|
2911
|
-
upstreamEndpoint: CHAT_COMPLETIONS_ENDPOINT$1,
|
|
2912
|
-
stream,
|
|
2913
|
-
clientModel,
|
|
2914
|
-
httpStatus: reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
|
|
2915
|
-
selectionFailureReason: reason
|
|
2729
|
+
async function selectChatCompletionAccount(params) {
|
|
2730
|
+
const { c, store, request, payload, clientModel, streamRequested, normalizedPromptCacheKey } = params;
|
|
2731
|
+
debugJsonTail(logger$6, "Request payload:", {
|
|
2732
|
+
value: payload,
|
|
2733
|
+
tailLength: 400
|
|
2916
2734
|
});
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
const
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2735
|
+
const upstreamRequestId = generateRequestIdFromPayload(payload, normalizedPromptCacheKey);
|
|
2736
|
+
const headerSessionId = c.req.header("x-session-id") ?? null;
|
|
2737
|
+
const affinityKey = resolveAffinityKey({
|
|
2738
|
+
metadataSessionId: normalizedPromptCacheKey,
|
|
2739
|
+
headerSessionId,
|
|
2740
|
+
upstreamRequestId
|
|
2741
|
+
});
|
|
2742
|
+
request.affinityKeyUsed = affinityKey.affinityKeyUsed;
|
|
2743
|
+
request.affinityKeySource = affinityKey.affinityKeySource;
|
|
2744
|
+
const selection = await accountsManager.selectAccountForRequest(buildChatCompletionCandidates(clientModel), { requestId: affinityKey.requestId });
|
|
2745
|
+
if (!selection.ok) {
|
|
2746
|
+
recordSelectionFailure$2(store, {
|
|
2747
|
+
request,
|
|
2748
|
+
clientModel,
|
|
2749
|
+
stream: streamRequested,
|
|
2750
|
+
reason: selection.reason
|
|
2751
|
+
});
|
|
2752
|
+
return selectionFailureResponse$2(c, {
|
|
2753
|
+
clientModel,
|
|
2754
|
+
reason: selection.reason
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
return {
|
|
2758
|
+
headerSessionId,
|
|
2759
|
+
selection,
|
|
2760
|
+
upstreamRequestId
|
|
2761
|
+
};
|
|
2928
2762
|
}
|
|
2929
2763
|
async function logTokenCountForRequest(params) {
|
|
2930
2764
|
try {
|
|
@@ -2989,8 +2823,8 @@ async function handleUpstreamCreateError$1(params) {
|
|
|
2989
2823
|
const { store, request, selection, clientModel, premiumRemainingBefore, premiumUnlimitedBefore, error } = params;
|
|
2990
2824
|
const { account, reservation, selectedModel, endpoint, costUnits } = selection;
|
|
2991
2825
|
const finishedAtMs = Date.now();
|
|
2992
|
-
const details =
|
|
2993
|
-
if (details
|
|
2826
|
+
const details = await extractErrorObservability(error);
|
|
2827
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
2994
2828
|
await accountsManager.finalizeQuota(account, reservation);
|
|
2995
2829
|
const premiumRemainingAfter = account.premiumRemaining;
|
|
2996
2830
|
const premiumUnlimitedAfter = account.unlimited;
|
|
@@ -3012,7 +2846,8 @@ async function handleUpstreamCreateError$1(params) {
|
|
|
3012
2846
|
httpStatus: details.httpStatus,
|
|
3013
2847
|
errorName: details.errorName,
|
|
3014
2848
|
errorStatus: details.errorStatus,
|
|
3015
|
-
errorMessage: details.errorMessage
|
|
2849
|
+
errorMessage: details.errorMessage,
|
|
2850
|
+
upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
|
|
3016
2851
|
});
|
|
3017
2852
|
throw error;
|
|
3018
2853
|
}
|
|
@@ -3024,16 +2859,18 @@ async function handleNonStreamingUpstreamResponse(params) {
|
|
|
3024
2859
|
let errorName;
|
|
3025
2860
|
let errorStatus;
|
|
3026
2861
|
let errorMessage;
|
|
2862
|
+
let upstreamErrorMessageRaw;
|
|
3027
2863
|
const finishedAtMs = Date.now();
|
|
3028
2864
|
try {
|
|
3029
2865
|
debugJson(logger$6, "Non-streaming response:", response);
|
|
3030
2866
|
return c.json(response);
|
|
3031
2867
|
} catch (error) {
|
|
3032
|
-
const details =
|
|
2868
|
+
const details = await extractErrorObservability(error);
|
|
3033
2869
|
httpStatus = details.httpStatus;
|
|
3034
2870
|
errorName = details.errorName;
|
|
3035
2871
|
errorStatus = details.errorStatus;
|
|
3036
2872
|
errorMessage = details.errorMessage;
|
|
2873
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
3037
2874
|
throw error;
|
|
3038
2875
|
} finally {
|
|
3039
2876
|
await accountsManager.finalizeQuota(account, reservation);
|
|
@@ -3058,7 +2895,8 @@ async function handleNonStreamingUpstreamResponse(params) {
|
|
|
3058
2895
|
httpStatus,
|
|
3059
2896
|
errorName,
|
|
3060
2897
|
errorStatus,
|
|
3061
|
-
errorMessage
|
|
2898
|
+
errorMessage,
|
|
2899
|
+
upstreamErrorMessageRaw
|
|
3062
2900
|
});
|
|
3063
2901
|
}
|
|
3064
2902
|
}
|
|
@@ -3070,6 +2908,7 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
3070
2908
|
let errorName;
|
|
3071
2909
|
let errorStatus;
|
|
3072
2910
|
let errorMessage;
|
|
2911
|
+
let upstreamErrorMessageRaw;
|
|
3073
2912
|
try {
|
|
3074
2913
|
for await (const rawChunk of response) {
|
|
3075
2914
|
const chunk = rawChunk;
|
|
@@ -3080,11 +2919,14 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
3080
2919
|
await stream.writeSSE(chunk);
|
|
3081
2920
|
}
|
|
3082
2921
|
} catch (error) {
|
|
3083
|
-
const details =
|
|
2922
|
+
const details = await extractErrorObservability(error);
|
|
3084
2923
|
errorName = details.errorName;
|
|
3085
2924
|
errorStatus = details.errorStatus;
|
|
3086
2925
|
errorMessage = details.errorMessage;
|
|
2926
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
3087
2927
|
logger$6.warn("Streaming error:", error);
|
|
2928
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
2929
|
+
await writeChatCompletionsStreamError(stream, getUserVisibleErrorMessage(details));
|
|
3088
2930
|
} finally {
|
|
3089
2931
|
const finishedAtMs = Date.now();
|
|
3090
2932
|
await accountsManager.finalizeQuota(account, reservation);
|
|
@@ -3110,18 +2952,29 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
3110
2952
|
httpStatus: errorStatus ?? (errorName ? 500 : 200),
|
|
3111
2953
|
errorName,
|
|
3112
2954
|
errorStatus,
|
|
3113
|
-
errorMessage
|
|
2955
|
+
errorMessage,
|
|
2956
|
+
upstreamErrorMessageRaw
|
|
3114
2957
|
});
|
|
3115
2958
|
}
|
|
3116
2959
|
}
|
|
3117
2960
|
async function extractUsageFromChunk(chunk) {
|
|
3118
|
-
|
|
2961
|
+
let data;
|
|
2962
|
+
try {
|
|
2963
|
+
data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
|
|
2964
|
+
} catch (error) {
|
|
2965
|
+
logger$6.warn("Failed to read chat completions usage chunk:", error);
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
3119
2968
|
if (!data || data === "[DONE]") return;
|
|
3120
2969
|
try {
|
|
3121
2970
|
const parsed = JSON.parse(data);
|
|
3122
2971
|
if (!parsed.usage) return void 0;
|
|
3123
2972
|
return normalizeChatCompletionsUsage(parsed.usage);
|
|
3124
|
-
} catch {
|
|
2973
|
+
} catch (error) {
|
|
2974
|
+
logger$6.warn("Failed to parse chat completions usage chunk:", {
|
|
2975
|
+
error,
|
|
2976
|
+
data
|
|
2977
|
+
});
|
|
3125
2978
|
return;
|
|
3126
2979
|
}
|
|
3127
2980
|
}
|
|
@@ -3133,31 +2986,28 @@ async function handleNonStreamingRequest(params) {
|
|
|
3133
2986
|
let errorName;
|
|
3134
2987
|
let errorStatus;
|
|
3135
2988
|
let errorMessage;
|
|
2989
|
+
let upstreamErrorMessageRaw;
|
|
3136
2990
|
let finishedAtMs;
|
|
3137
2991
|
try {
|
|
3138
2992
|
const response = await createChatCompletions(payload, accountCtx, {
|
|
3139
2993
|
upstreamRequestId: request.upstreamRequestId,
|
|
3140
2994
|
sessionId: request.upstreamSessionId
|
|
3141
2995
|
});
|
|
2996
|
+
if (!isNonStreaming$1(response)) throw new Error("Upstream returned a stream unexpectedly");
|
|
3142
2997
|
selection.confirmAffinity?.();
|
|
3143
2998
|
finishedAtMs = Date.now();
|
|
3144
|
-
if (!isNonStreaming$1(response)) {
|
|
3145
|
-
logger$6.debug("Unexpected streaming response");
|
|
3146
|
-
return streamSSE(c, async (stream) => {
|
|
3147
|
-
for await (const chunk of response) await stream.writeSSE(chunk);
|
|
3148
|
-
});
|
|
3149
|
-
}
|
|
3150
2999
|
usage = normalizeChatCompletionsUsage(response.usage);
|
|
3151
3000
|
debugJson(logger$6, "Non-streaming response:", response);
|
|
3152
3001
|
return c.json(response);
|
|
3153
3002
|
} catch (error) {
|
|
3154
3003
|
finishedAtMs = Date.now();
|
|
3155
|
-
const details =
|
|
3004
|
+
const details = await extractErrorObservability(error);
|
|
3156
3005
|
httpStatus = details.httpStatus;
|
|
3157
3006
|
errorName = details.errorName;
|
|
3158
3007
|
errorStatus = details.errorStatus;
|
|
3159
3008
|
errorMessage = details.errorMessage;
|
|
3160
|
-
|
|
3009
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
3010
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
3161
3011
|
throw error;
|
|
3162
3012
|
} finally {
|
|
3163
3013
|
const finishedAtMsFinal = finishedAtMs ?? Date.now();
|
|
@@ -3183,7 +3033,8 @@ async function handleNonStreamingRequest(params) {
|
|
|
3183
3033
|
httpStatus,
|
|
3184
3034
|
errorName,
|
|
3185
3035
|
errorStatus,
|
|
3186
|
-
errorMessage
|
|
3036
|
+
errorMessage,
|
|
3037
|
+
upstreamErrorMessageRaw
|
|
3187
3038
|
});
|
|
3188
3039
|
}
|
|
3189
3040
|
}
|
|
@@ -3311,6 +3162,7 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
|
|
|
3311
3162
|
let errorName;
|
|
3312
3163
|
let errorStatus;
|
|
3313
3164
|
let errorMessage;
|
|
3165
|
+
let upstreamErrorMessageRaw;
|
|
3314
3166
|
let finishedAtMs;
|
|
3315
3167
|
try {
|
|
3316
3168
|
const response = await createEmbeddings(payload, toAccountContext(account));
|
|
@@ -3319,12 +3171,13 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
|
|
|
3319
3171
|
return c.json(response);
|
|
3320
3172
|
} catch (error) {
|
|
3321
3173
|
finishedAtMs = Date.now();
|
|
3322
|
-
const details =
|
|
3174
|
+
const details = await extractErrorObservability(error);
|
|
3323
3175
|
httpStatus = details.httpStatus;
|
|
3324
3176
|
errorName = details.errorName;
|
|
3325
3177
|
errorStatus = details.errorStatus;
|
|
3326
3178
|
errorMessage = details.errorMessage;
|
|
3327
|
-
|
|
3179
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
3180
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
3328
3181
|
throw error;
|
|
3329
3182
|
} finally {
|
|
3330
3183
|
const finishedAtMsFinal = finishedAtMs ?? Date.now();
|
|
@@ -3357,22 +3210,23 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
|
|
|
3357
3210
|
httpStatus,
|
|
3358
3211
|
errorName,
|
|
3359
3212
|
errorStatus,
|
|
3360
|
-
errorMessage
|
|
3213
|
+
errorMessage,
|
|
3214
|
+
upstreamErrorMessageRaw
|
|
3361
3215
|
});
|
|
3362
3216
|
}
|
|
3363
3217
|
}
|
|
3364
3218
|
|
|
3365
3219
|
//#endregion
|
|
3366
3220
|
//#region src/lib/models.ts
|
|
3221
|
+
const getAvailableModels = () => (accountsManager.getFirstAccountModels()?.data ?? []).filter((model) => model.model_picker_enabled || model.capabilities.type === "embeddings");
|
|
3367
3222
|
const findEndpointModel = (sdkModelId) => {
|
|
3368
|
-
const models =
|
|
3223
|
+
const models = getAvailableModels();
|
|
3369
3224
|
const exactMatch = models.find((m) => m.id === sdkModelId);
|
|
3370
3225
|
if (exactMatch) return exactMatch;
|
|
3371
3226
|
const normalized = _normalizeSdkModelId(sdkModelId);
|
|
3372
3227
|
if (!normalized) return;
|
|
3373
3228
|
const modelName = `claude-${normalized.family}-${normalized.version}`;
|
|
3374
|
-
|
|
3375
|
-
if (model) return model;
|
|
3229
|
+
return models.find((m) => m.id === modelName);
|
|
3376
3230
|
};
|
|
3377
3231
|
/**
|
|
3378
3232
|
* Normalizes an SDK model ID to extract the model family and version.
|
|
@@ -3578,7 +3432,7 @@ const isWarmupProbeRequest = (payload) => {
|
|
|
3578
3432
|
return false;
|
|
3579
3433
|
};
|
|
3580
3434
|
const handleSelectionFailure = (context) => {
|
|
3581
|
-
const { c, store, requestId, startedAtMs, method, path: path$2, streamRequested, clientModel, clientIp, clientIpSource, userAgent, userId, safetyIdentifier, promptCacheKey, initiator, selection } = context;
|
|
3435
|
+
const { c, store, requestId, startedAtMs, method, path: path$2, streamRequested, clientModel, clientIp, clientIpSource, userAgent, userId, safetyIdentifier, promptCacheKey, initiator, isSubagent, affinityKeyUsed, affinityKeySource, selectionReason, selection } = context;
|
|
3582
3436
|
const finishedAtMs = Date.now();
|
|
3583
3437
|
store.insert({
|
|
3584
3438
|
requestId,
|
|
@@ -3596,7 +3450,11 @@ const handleSelectionFailure = (context) => {
|
|
|
3596
3450
|
safetyIdentifier,
|
|
3597
3451
|
promptCacheKey,
|
|
3598
3452
|
initiator,
|
|
3453
|
+
isSubagent,
|
|
3454
|
+
affinityKeyUsed,
|
|
3455
|
+
affinityKeySource,
|
|
3599
3456
|
httpStatus: selection.reason === "MODEL_NOT_SUPPORTED" ? 400 : 429,
|
|
3457
|
+
selectionReason: selectionReason ?? selection.selectionReason,
|
|
3600
3458
|
selectionFailureReason: selection.reason
|
|
3601
3459
|
});
|
|
3602
3460
|
if (selection.reason === "MODEL_NOT_SUPPORTED") return c.json({ error: {
|
|
@@ -3624,8 +3482,7 @@ const maybeBlockOriginalModelName = (context) => {
|
|
|
3624
3482
|
const THINKING_TEXT = "Thinking...";
|
|
3625
3483
|
function translateToOpenAI(payload) {
|
|
3626
3484
|
const modelId = payload.model;
|
|
3627
|
-
const
|
|
3628
|
-
const thinkingBudget = getThinkingBudget(payload, model);
|
|
3485
|
+
const thinkingBudget = getThinkingBudget(payload, getAvailableModels().find((m) => m.id === modelId));
|
|
3629
3486
|
return {
|
|
3630
3487
|
model: modelId,
|
|
3631
3488
|
messages: translateAnthropicMessagesToOpenAI(payload, modelId, thinkingBudget),
|
|
@@ -3921,9 +3778,13 @@ async function handleCountTokens(c) {
|
|
|
3921
3778
|
const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, isCompact }, account) => {
|
|
3922
3779
|
const ctx = account ?? accountFromState();
|
|
3923
3780
|
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
3781
|
+
const effectiveInitiator = resolveEffectiveInitiator(initiator, {
|
|
3782
|
+
isCompact,
|
|
3783
|
+
isSubagent: Boolean(subagentMarker)
|
|
3784
|
+
});
|
|
3924
3785
|
const headers = {
|
|
3925
3786
|
...copilotHeaders(ctx, vision, upstreamRequestId),
|
|
3926
|
-
"x-initiator":
|
|
3787
|
+
"x-initiator": effectiveInitiator
|
|
3927
3788
|
};
|
|
3928
3789
|
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
3929
3790
|
prepareForCompact(headers, isCompact);
|
|
@@ -4848,6 +4709,44 @@ const getPayloadItems = (payload) => {
|
|
|
4848
4709
|
if (Array.isArray(input)) result.push(...input);
|
|
4849
4710
|
return result;
|
|
4850
4711
|
};
|
|
4712
|
+
const useFunctionApplyPatch = (payload) => {
|
|
4713
|
+
if (!(getConfig().useFunctionApplyPatch ?? true)) return;
|
|
4714
|
+
if (Array.isArray(payload.tools)) {
|
|
4715
|
+
const toolsArr = payload.tools;
|
|
4716
|
+
for (let i = 0; i < toolsArr.length; i++) {
|
|
4717
|
+
const t = toolsArr[i];
|
|
4718
|
+
if (t.type === "custom" && t.name === "apply_patch") toolsArr[i] = {
|
|
4719
|
+
type: "function",
|
|
4720
|
+
name: t.name,
|
|
4721
|
+
description: "Use the `apply_patch` tool to edit files",
|
|
4722
|
+
parameters: {
|
|
4723
|
+
type: "object",
|
|
4724
|
+
properties: { input: {
|
|
4725
|
+
type: "string",
|
|
4726
|
+
description: "The entire contents of the apply_patch command"
|
|
4727
|
+
} },
|
|
4728
|
+
required: ["input"]
|
|
4729
|
+
},
|
|
4730
|
+
strict: false
|
|
4731
|
+
};
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
};
|
|
4735
|
+
const removeWebSearchTool = (payload) => {
|
|
4736
|
+
if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
|
|
4737
|
+
payload.tools = payload.tools.filter((t) => {
|
|
4738
|
+
return t.type !== "web_search";
|
|
4739
|
+
});
|
|
4740
|
+
};
|
|
4741
|
+
function getStreamChunkFields(chunk) {
|
|
4742
|
+
const c = chunk;
|
|
4743
|
+
return {
|
|
4744
|
+
id: c.id,
|
|
4745
|
+
event: c.event,
|
|
4746
|
+
data: c.data
|
|
4747
|
+
};
|
|
4748
|
+
}
|
|
4749
|
+
const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
4851
4750
|
const containsVisionContent = (value) => {
|
|
4852
4751
|
if (!value) return false;
|
|
4853
4752
|
if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
|
|
@@ -4897,9 +4796,13 @@ const shouldUseMessageProxyHeaders = (payload) => {
|
|
|
4897
4796
|
return Boolean(safetyIdentifier && sessionId);
|
|
4898
4797
|
};
|
|
4899
4798
|
const buildMessagesHeaders = ({ ctx, enableVision, initiator, options, payload }) => {
|
|
4799
|
+
const effectiveInitiator = resolveEffectiveInitiator(initiator, {
|
|
4800
|
+
isCompact: options?.isCompact,
|
|
4801
|
+
isSubagent: Boolean(options?.subagentMarker)
|
|
4802
|
+
});
|
|
4900
4803
|
const headers = {
|
|
4901
4804
|
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
4902
|
-
"x-initiator":
|
|
4805
|
+
"x-initiator": effectiveInitiator
|
|
4903
4806
|
};
|
|
4904
4807
|
prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
|
|
4905
4808
|
prepareForCompact(headers, options?.isCompact);
|
|
@@ -5191,44 +5094,105 @@ function closeThinkingBlockIfOpen(state$1, events$1) {
|
|
|
5191
5094
|
//#endregion
|
|
5192
5095
|
//#region src/routes/messages/subagent-marker.ts
|
|
5193
5096
|
const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
|
|
5194
|
-
const
|
|
5097
|
+
const subagentStartContextPrefix = "SubagentStart hook additional context:";
|
|
5098
|
+
const REMINDER_RE = /<system-reminder>([\s\S]*?)<\/system-reminder>/g;
|
|
5099
|
+
const NONE_INSPECTION = {
|
|
5100
|
+
kind: "none",
|
|
5101
|
+
marker: null
|
|
5102
|
+
};
|
|
5103
|
+
const INVALID_INSPECTION = {
|
|
5104
|
+
kind: "invalid",
|
|
5105
|
+
marker: null
|
|
5106
|
+
};
|
|
5107
|
+
const isSubagentMarker = (value) => {
|
|
5108
|
+
if (!value || typeof value !== "object") return false;
|
|
5109
|
+
const candidate = value;
|
|
5110
|
+
return typeof candidate.session_id === "string" && typeof candidate.agent_id === "string" && typeof candidate.agent_type === "string" && candidate.session_id.trim().length > 0 && candidate.agent_id.trim().length > 0 && candidate.agent_type.trim().length > 0;
|
|
5111
|
+
};
|
|
5112
|
+
const inspectSubagentMarkerFromFirstUser = (payload) => {
|
|
5195
5113
|
const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
|
|
5196
|
-
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return
|
|
5114
|
+
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return NONE_INSPECTION;
|
|
5115
|
+
let sawInvalidMarker = false;
|
|
5197
5116
|
for (const block of firstUserMessage.content) {
|
|
5198
5117
|
if (block.type !== "text") continue;
|
|
5199
|
-
const
|
|
5200
|
-
if (
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
const
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5118
|
+
const inspection = inspectSubagentMarkerFromSystemReminder(block.text);
|
|
5119
|
+
if (inspection.kind === "valid") return inspection;
|
|
5120
|
+
if (inspection.kind === "invalid") sawInvalidMarker = true;
|
|
5121
|
+
}
|
|
5122
|
+
return sawInvalidMarker ? INVALID_INSPECTION : NONE_INSPECTION;
|
|
5123
|
+
};
|
|
5124
|
+
const extractMarkerPayloadFromReminderLine = (line) => {
|
|
5125
|
+
const trimmedLine = line.trim();
|
|
5126
|
+
if (!trimmedLine) return null;
|
|
5127
|
+
let markerLine = trimmedLine;
|
|
5128
|
+
if (markerLine.startsWith(subagentStartContextPrefix)) markerLine = markerLine.slice(38).trimStart();
|
|
5129
|
+
if (!markerLine.startsWith(subagentMarkerPrefix)) return null;
|
|
5130
|
+
return markerLine.slice(19).trimStart();
|
|
5131
|
+
};
|
|
5132
|
+
const inspectSubagentMarkerFromSystemReminder = (text) => {
|
|
5133
|
+
let sawInvalidMarker = false;
|
|
5134
|
+
for (const [, content] of text.matchAll(REMINDER_RE)) {
|
|
5135
|
+
const lines = content.split(/\r?\n/);
|
|
5136
|
+
for (const line of lines) {
|
|
5137
|
+
const markerPayload = extractMarkerPayloadFromReminderLine(line);
|
|
5138
|
+
if (markerPayload === null) continue;
|
|
5139
|
+
if (!markerPayload.startsWith("{")) {
|
|
5140
|
+
sawInvalidMarker = true;
|
|
5141
|
+
continue;
|
|
5142
|
+
}
|
|
5143
|
+
const json = extractBalancedJson(markerPayload);
|
|
5144
|
+
if (!json) {
|
|
5145
|
+
sawInvalidMarker = true;
|
|
5146
|
+
continue;
|
|
5147
|
+
}
|
|
5148
|
+
try {
|
|
5149
|
+
const parsed = JSON.parse(json);
|
|
5150
|
+
if (!isSubagentMarker(parsed)) {
|
|
5151
|
+
sawInvalidMarker = true;
|
|
5152
|
+
continue;
|
|
5153
|
+
}
|
|
5154
|
+
return {
|
|
5155
|
+
kind: "valid",
|
|
5156
|
+
marker: parsed
|
|
5157
|
+
};
|
|
5158
|
+
} catch {
|
|
5159
|
+
sawInvalidMarker = true;
|
|
5160
|
+
}
|
|
5219
5161
|
}
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5162
|
+
}
|
|
5163
|
+
return sawInvalidMarker ? INVALID_INSPECTION : NONE_INSPECTION;
|
|
5164
|
+
};
|
|
5165
|
+
/** Extract the first balanced `{...}` object from text that starts with `{`. */
|
|
5166
|
+
const extractBalancedJson = (text) => {
|
|
5167
|
+
let depth = 0;
|
|
5168
|
+
let inString = false;
|
|
5169
|
+
let escaped = false;
|
|
5170
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
5171
|
+
const char = text[index];
|
|
5172
|
+
if (inString) {
|
|
5173
|
+
if (escaped) {
|
|
5174
|
+
escaped = false;
|
|
5225
5175
|
continue;
|
|
5226
5176
|
}
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5177
|
+
if (char === "\\") {
|
|
5178
|
+
escaped = true;
|
|
5179
|
+
continue;
|
|
5180
|
+
}
|
|
5181
|
+
if (char === "\"") inString = false;
|
|
5182
|
+
continue;
|
|
5183
|
+
}
|
|
5184
|
+
if (char === "\"") {
|
|
5185
|
+
inString = true;
|
|
5230
5186
|
continue;
|
|
5231
5187
|
}
|
|
5188
|
+
if (char === "{") {
|
|
5189
|
+
depth += 1;
|
|
5190
|
+
continue;
|
|
5191
|
+
}
|
|
5192
|
+
if (char === "}") {
|
|
5193
|
+
depth -= 1;
|
|
5194
|
+
if (depth === 0) return text.slice(0, index + 1);
|
|
5195
|
+
}
|
|
5232
5196
|
}
|
|
5233
5197
|
return null;
|
|
5234
5198
|
};
|
|
@@ -5250,13 +5214,18 @@ async function handleCompletion(c) {
|
|
|
5250
5214
|
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
5251
5215
|
const anthropicPayload = await c.req.json();
|
|
5252
5216
|
debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
|
|
5253
|
-
const
|
|
5254
|
-
const
|
|
5217
|
+
const markerInspection = inspectSubagentMarkerFromFirstUser(anthropicPayload);
|
|
5218
|
+
const subagentMarker = markerInspection.kind === "valid" ? markerInspection.marker : null;
|
|
5219
|
+
const isSubagentRequest = subagentMarker !== null;
|
|
5220
|
+
const invalidSubagentMarkerSelectionReason = markerInspection.kind === "invalid" ? "subagent_marker_invalid_fallback" : void 0;
|
|
5255
5221
|
if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
|
|
5256
5222
|
const sessionId = getRootSessionId(anthropicPayload, c);
|
|
5257
5223
|
logger$5.debug("Extracted session ID:", sessionId);
|
|
5224
|
+
const ownershipLookupSessionId = markerInspection.kind === "valid" ? normalizeStableSessionId(markerInspection.marker.session_id) : void 0;
|
|
5225
|
+
const ownershipWriteSessionId = markerInspection.kind === "none" ? sessionId : void 0;
|
|
5258
5226
|
const anthropicBeta = c.req.header("anthropic-beta");
|
|
5259
5227
|
const isCompact = isCompactRequest(anthropicPayload);
|
|
5228
|
+
const originalRequestModel = anthropicPayload.model;
|
|
5260
5229
|
if (anthropicBeta && isWarmupProbeRequest(anthropicPayload)) anthropicPayload.model = getSmallModel();
|
|
5261
5230
|
if (isCompact) {
|
|
5262
5231
|
logger$5.debug("Is compact request:", isCompact);
|
|
@@ -5274,6 +5243,11 @@ async function handleCompletion(c) {
|
|
|
5274
5243
|
const { safetyIdentifier, sessionId: promptCacheKey } = parseUserIdMetadata(userId);
|
|
5275
5244
|
const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
|
|
5276
5245
|
const normalizedPromptCacheKey = promptCacheKey ?? void 0;
|
|
5246
|
+
const openAIPayload = translateToOpenAI(anthropicPayload);
|
|
5247
|
+
const fallbackInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
|
|
5248
|
+
isCompact,
|
|
5249
|
+
isSubagent: isSubagentRequest
|
|
5250
|
+
});
|
|
5277
5251
|
const blockedResponse = maybeBlockOriginalModelName({
|
|
5278
5252
|
c,
|
|
5279
5253
|
store,
|
|
@@ -5289,13 +5263,14 @@ async function handleCompletion(c) {
|
|
|
5289
5263
|
userId,
|
|
5290
5264
|
safetyIdentifier: normalizedSafetyIdentifier,
|
|
5291
5265
|
promptCacheKey: normalizedPromptCacheKey,
|
|
5292
|
-
initiator:
|
|
5266
|
+
initiator: fallbackInitiator,
|
|
5267
|
+
isSubagent: isSubagentRequest,
|
|
5268
|
+
selectionReason: invalidSubagentMarkerSelectionReason
|
|
5293
5269
|
});
|
|
5294
5270
|
if (blockedResponse) return blockedResponse;
|
|
5295
|
-
const openAIPayload = translateToOpenAI(anthropicPayload);
|
|
5296
|
-
const fallbackInitiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
|
|
5297
5271
|
const endpointModel = findEndpointModel(clientModel);
|
|
5298
5272
|
const resolvedClientModel = endpointModel?.id ?? clientModel;
|
|
5273
|
+
const affinityModelId = clientModel !== originalRequestModel ? findEndpointModel(originalRequestModel)?.id ?? originalRequestModel : void 0;
|
|
5299
5274
|
const useMessagesApi = isMessagesApiEnabled();
|
|
5300
5275
|
const candidates = [];
|
|
5301
5276
|
if (useMessagesApi) candidates.push({
|
|
@@ -5309,7 +5284,18 @@ async function handleCompletion(c) {
|
|
|
5309
5284
|
modelId: endpointModel?.id ?? openAIPayload.model,
|
|
5310
5285
|
endpoint: CHAT_COMPLETIONS_ENDPOINT
|
|
5311
5286
|
});
|
|
5312
|
-
const
|
|
5287
|
+
const affinityKey = resolveAffinityKey({
|
|
5288
|
+
metadataSessionId: promptCacheKey,
|
|
5289
|
+
headerSessionId: c.req.header("x-session-id") ?? null,
|
|
5290
|
+
upstreamRequestId
|
|
5291
|
+
});
|
|
5292
|
+
const selection = await accountsManager.selectAccountForRequest(candidates, {
|
|
5293
|
+
requestId: affinityKey.requestId,
|
|
5294
|
+
affinityModelId,
|
|
5295
|
+
ownershipLookupSessionId,
|
|
5296
|
+
ownershipWriteSessionId
|
|
5297
|
+
});
|
|
5298
|
+
const selectionReason = invalidSubagentMarkerSelectionReason ?? selection.selectionReason;
|
|
5313
5299
|
if (!selection.ok) return handleSelectionFailure({
|
|
5314
5300
|
c,
|
|
5315
5301
|
store,
|
|
@@ -5326,6 +5312,10 @@ async function handleCompletion(c) {
|
|
|
5326
5312
|
safetyIdentifier: normalizedSafetyIdentifier,
|
|
5327
5313
|
promptCacheKey: normalizedPromptCacheKey,
|
|
5328
5314
|
initiator: fallbackInitiator,
|
|
5315
|
+
isSubagent: isSubagentRequest,
|
|
5316
|
+
affinityKeyUsed: affinityKey.affinityKeyUsed,
|
|
5317
|
+
affinityKeySource: affinityKey.affinityKeySource,
|
|
5318
|
+
selectionReason,
|
|
5329
5319
|
selection
|
|
5330
5320
|
});
|
|
5331
5321
|
const { account, reservation, selectedModel, endpoint, costUnits } = selection;
|
|
@@ -5346,6 +5336,7 @@ async function handleCompletion(c) {
|
|
|
5346
5336
|
userId,
|
|
5347
5337
|
safetyIdentifier: normalizedSafetyIdentifier,
|
|
5348
5338
|
promptCacheKey: normalizedPromptCacheKey,
|
|
5339
|
+
isSubagent: isSubagentRequest,
|
|
5349
5340
|
clientModel,
|
|
5350
5341
|
account,
|
|
5351
5342
|
reservation,
|
|
@@ -5356,14 +5347,17 @@ async function handleCompletion(c) {
|
|
|
5356
5347
|
premiumRemainingBefore,
|
|
5357
5348
|
premiumUnlimitedBefore,
|
|
5358
5349
|
confirmAffinity: selection.confirmAffinity,
|
|
5350
|
+
confirmOwnership: selection.confirmOwnership,
|
|
5359
5351
|
affinityHit: selection.affinityHit,
|
|
5360
|
-
affinityCacheKey: selection.affinityCacheKey
|
|
5352
|
+
affinityCacheKey: selection.affinityCacheKey,
|
|
5353
|
+
affinityKeyUsed: affinityKey.affinityKeyUsed,
|
|
5354
|
+
affinityKeySource: affinityKey.affinityKeySource,
|
|
5355
|
+
selectionReason
|
|
5361
5356
|
};
|
|
5362
5357
|
if (endpoint === MESSAGES_ENDPOINT) return await handleWithMessagesApi({
|
|
5363
5358
|
c,
|
|
5364
5359
|
anthropicPayload,
|
|
5365
5360
|
anthropicBetaHeader: anthropicBeta ?? void 0,
|
|
5366
|
-
initiatorOverride,
|
|
5367
5361
|
subagentMarker,
|
|
5368
5362
|
sessionId,
|
|
5369
5363
|
instr,
|
|
@@ -5374,7 +5368,6 @@ async function handleCompletion(c) {
|
|
|
5374
5368
|
c,
|
|
5375
5369
|
anthropicPayload,
|
|
5376
5370
|
openAIPayload,
|
|
5377
|
-
initiatorOverride,
|
|
5378
5371
|
subagentMarker,
|
|
5379
5372
|
sessionId,
|
|
5380
5373
|
selectedModel,
|
|
@@ -5384,7 +5377,6 @@ async function handleCompletion(c) {
|
|
|
5384
5377
|
return await handleWithChatCompletions({
|
|
5385
5378
|
c,
|
|
5386
5379
|
openAIPayload,
|
|
5387
|
-
initiatorOverride,
|
|
5388
5380
|
subagentMarker,
|
|
5389
5381
|
sessionId,
|
|
5390
5382
|
selectedModel,
|
|
@@ -5393,21 +5385,25 @@ async function handleCompletion(c) {
|
|
|
5393
5385
|
});
|
|
5394
5386
|
}
|
|
5395
5387
|
const handleWithChatCompletions = async (params) => {
|
|
5396
|
-
const { c, openAIPayload,
|
|
5388
|
+
const { c, openAIPayload, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
|
|
5397
5389
|
debugJson(logger$5, "Translated OpenAI request payload:", openAIPayload);
|
|
5398
5390
|
const ctx = toAccountContext(instr.account);
|
|
5399
|
-
const
|
|
5400
|
-
|
|
5391
|
+
const effectiveInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
|
|
5392
|
+
isCompact,
|
|
5393
|
+
isSubagent: Boolean(subagentMarker)
|
|
5394
|
+
});
|
|
5395
|
+
instr.initiator = effectiveInitiator;
|
|
5401
5396
|
let response;
|
|
5402
5397
|
try {
|
|
5403
5398
|
response = await createChatCompletions(openAIPayload, ctx, {
|
|
5404
5399
|
upstreamRequestId: instr.upstreamRequestId,
|
|
5405
|
-
initiator,
|
|
5400
|
+
initiator: effectiveInitiator,
|
|
5406
5401
|
subagentMarker,
|
|
5407
5402
|
sessionId,
|
|
5408
5403
|
isCompact
|
|
5409
5404
|
});
|
|
5410
5405
|
instr.confirmAffinity?.();
|
|
5406
|
+
instr.confirmOwnership?.();
|
|
5411
5407
|
} catch (error) {
|
|
5412
5408
|
return await handleChatCompletionsCreateError({
|
|
5413
5409
|
error,
|
|
@@ -5437,26 +5433,30 @@ const handleWithChatCompletions = async (params) => {
|
|
|
5437
5433
|
}));
|
|
5438
5434
|
};
|
|
5439
5435
|
const handleWithResponsesApi = async (params) => {
|
|
5440
|
-
const { c, anthropicPayload, openAIPayload,
|
|
5436
|
+
const { c, anthropicPayload, openAIPayload, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
|
|
5441
5437
|
const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, selectedModel.id);
|
|
5442
5438
|
applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
|
|
5443
5439
|
compactInputByLatestCompaction(responsesPayload);
|
|
5444
5440
|
debugJson(logger$5, "Translated Responses payload:", responsesPayload);
|
|
5445
5441
|
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
5446
|
-
const
|
|
5442
|
+
const effectiveInitiator = resolveEffectiveInitiator(initiator, {
|
|
5443
|
+
isCompact,
|
|
5444
|
+
isSubagent: Boolean(subagentMarker)
|
|
5445
|
+
});
|
|
5447
5446
|
const ctx = toAccountContext(instr.account);
|
|
5448
|
-
instr.initiator =
|
|
5447
|
+
instr.initiator = effectiveInitiator;
|
|
5449
5448
|
let response;
|
|
5450
5449
|
try {
|
|
5451
5450
|
response = await createResponses(responsesPayload, {
|
|
5452
5451
|
vision,
|
|
5453
|
-
initiator:
|
|
5452
|
+
initiator: effectiveInitiator,
|
|
5454
5453
|
upstreamRequestId: instr.upstreamRequestId,
|
|
5455
5454
|
subagentMarker,
|
|
5456
5455
|
sessionId,
|
|
5457
5456
|
isCompact
|
|
5458
5457
|
}, ctx);
|
|
5459
5458
|
instr.confirmAffinity?.();
|
|
5459
|
+
instr.confirmOwnership?.();
|
|
5460
5460
|
} catch (error) {
|
|
5461
5461
|
return await handleResponsesCreateError({
|
|
5462
5462
|
error,
|
|
@@ -5501,9 +5501,13 @@ function insertRequestLog$1(instr, record) {
|
|
|
5501
5501
|
safetyIdentifier: instr.safetyIdentifier,
|
|
5502
5502
|
promptCacheKey: instr.promptCacheKey,
|
|
5503
5503
|
initiator: instr.initiator,
|
|
5504
|
+
isSubagent: instr.isSubagent,
|
|
5504
5505
|
upstreamRequestId: instr.upstreamRequestId,
|
|
5505
5506
|
affinityHit: instr.affinityHit,
|
|
5506
5507
|
affinityCacheKey: instr.affinityCacheKey,
|
|
5508
|
+
affinityKeyUsed: instr.affinityKeyUsed,
|
|
5509
|
+
affinityKeySource: instr.affinityKeySource,
|
|
5510
|
+
selectionReason: instr.selectionReason,
|
|
5507
5511
|
clientModel,
|
|
5508
5512
|
upstreamEndpoint,
|
|
5509
5513
|
accountId: account.id,
|
|
@@ -5527,8 +5531,8 @@ async function finalizeQuotaAndGetPremiumSnapshot(instr) {
|
|
|
5527
5531
|
async function handleChatCompletionsCreateError(params) {
|
|
5528
5532
|
const { error, instr, stream } = params;
|
|
5529
5533
|
const finishedAtMs = Date.now();
|
|
5530
|
-
const details =
|
|
5531
|
-
if (details
|
|
5534
|
+
const details = await extractErrorObservability(error);
|
|
5535
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5532
5536
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
5533
5537
|
insertRequestLog$1(instr, {
|
|
5534
5538
|
finishedAtMs,
|
|
@@ -5540,7 +5544,8 @@ async function handleChatCompletionsCreateError(params) {
|
|
|
5540
5544
|
httpStatus: details.httpStatus,
|
|
5541
5545
|
errorName: details.errorName,
|
|
5542
5546
|
errorStatus: details.errorStatus,
|
|
5543
|
-
errorMessage: details.errorMessage
|
|
5547
|
+
errorMessage: details.errorMessage,
|
|
5548
|
+
upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
|
|
5544
5549
|
});
|
|
5545
5550
|
throw error;
|
|
5546
5551
|
}
|
|
@@ -5551,6 +5556,7 @@ async function handleChatCompletionsNonStreaming(params) {
|
|
|
5551
5556
|
let errorName;
|
|
5552
5557
|
let errorStatus;
|
|
5553
5558
|
let errorMessage;
|
|
5559
|
+
let upstreamErrorMessageRaw;
|
|
5554
5560
|
const finishedAtMs = Date.now();
|
|
5555
5561
|
try {
|
|
5556
5562
|
logger$5.debug("Non-streaming response from Copilot:", JSON.stringify(response));
|
|
@@ -5558,12 +5564,13 @@ async function handleChatCompletionsNonStreaming(params) {
|
|
|
5558
5564
|
debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
|
|
5559
5565
|
return c.json(anthropicResponse);
|
|
5560
5566
|
} catch (error) {
|
|
5561
|
-
const details =
|
|
5567
|
+
const details = await extractErrorObservability(error);
|
|
5562
5568
|
httpStatus = details.httpStatus;
|
|
5563
5569
|
errorName = details.errorName;
|
|
5564
5570
|
errorStatus = details.errorStatus;
|
|
5565
5571
|
errorMessage = details.errorMessage;
|
|
5566
|
-
|
|
5572
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
5573
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5567
5574
|
throw error;
|
|
5568
5575
|
} finally {
|
|
5569
5576
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
@@ -5578,7 +5585,8 @@ async function handleChatCompletionsNonStreaming(params) {
|
|
|
5578
5585
|
httpStatus,
|
|
5579
5586
|
errorName,
|
|
5580
5587
|
errorStatus,
|
|
5581
|
-
errorMessage
|
|
5588
|
+
errorMessage,
|
|
5589
|
+
upstreamErrorMessageRaw
|
|
5582
5590
|
});
|
|
5583
5591
|
}
|
|
5584
5592
|
}
|
|
@@ -5589,6 +5597,7 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
5589
5597
|
let errorName;
|
|
5590
5598
|
let errorStatus;
|
|
5591
5599
|
let errorMessage;
|
|
5600
|
+
let upstreamErrorMessageRaw;
|
|
5592
5601
|
const streamState = {
|
|
5593
5602
|
messageStartSent: false,
|
|
5594
5603
|
contentBlockIndex: 0,
|
|
@@ -5620,12 +5629,14 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
5620
5629
|
}
|
|
5621
5630
|
}
|
|
5622
5631
|
} catch (error) {
|
|
5623
|
-
const details =
|
|
5632
|
+
const details = await extractErrorObservability(error);
|
|
5624
5633
|
errorName = details.errorName;
|
|
5625
5634
|
errorStatus = details.errorStatus;
|
|
5626
5635
|
errorMessage = details.errorMessage;
|
|
5636
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
5627
5637
|
logger$5.warn("Streaming error:", error);
|
|
5628
|
-
if (details
|
|
5638
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5639
|
+
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
5629
5640
|
} finally {
|
|
5630
5641
|
const finishedAtMs = Date.now();
|
|
5631
5642
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
@@ -5641,15 +5652,16 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
5641
5652
|
httpStatus: errorStatus ?? (errorName ? 500 : 200),
|
|
5642
5653
|
errorName,
|
|
5643
5654
|
errorStatus,
|
|
5644
|
-
errorMessage
|
|
5655
|
+
errorMessage,
|
|
5656
|
+
upstreamErrorMessageRaw
|
|
5645
5657
|
});
|
|
5646
5658
|
}
|
|
5647
5659
|
}
|
|
5648
5660
|
async function handleResponsesCreateError(params) {
|
|
5649
5661
|
const { error, instr, stream } = params;
|
|
5650
5662
|
const finishedAtMs = Date.now();
|
|
5651
|
-
const details =
|
|
5652
|
-
if (details
|
|
5663
|
+
const details = await extractErrorObservability(error);
|
|
5664
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5653
5665
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
5654
5666
|
insertRequestLog$1(instr, {
|
|
5655
5667
|
finishedAtMs,
|
|
@@ -5661,7 +5673,8 @@ async function handleResponsesCreateError(params) {
|
|
|
5661
5673
|
httpStatus: details.httpStatus,
|
|
5662
5674
|
errorName: details.errorName,
|
|
5663
5675
|
errorStatus: details.errorStatus,
|
|
5664
|
-
errorMessage: details.errorMessage
|
|
5676
|
+
errorMessage: details.errorMessage,
|
|
5677
|
+
upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
|
|
5665
5678
|
});
|
|
5666
5679
|
throw error;
|
|
5667
5680
|
}
|
|
@@ -5672,6 +5685,7 @@ async function handleResponsesNonStreaming(params) {
|
|
|
5672
5685
|
let errorName;
|
|
5673
5686
|
let errorStatus;
|
|
5674
5687
|
let errorMessage;
|
|
5688
|
+
let upstreamErrorMessageRaw;
|
|
5675
5689
|
const finishedAtMs = Date.now();
|
|
5676
5690
|
try {
|
|
5677
5691
|
usage = extractResponsesUsageFromResult(result);
|
|
@@ -5680,12 +5694,13 @@ async function handleResponsesNonStreaming(params) {
|
|
|
5680
5694
|
debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
|
|
5681
5695
|
return c.json(anthropicResponse);
|
|
5682
5696
|
} catch (error) {
|
|
5683
|
-
const details =
|
|
5697
|
+
const details = await extractErrorObservability(error);
|
|
5684
5698
|
httpStatus = details.httpStatus;
|
|
5685
5699
|
errorName = details.errorName;
|
|
5686
5700
|
errorStatus = details.errorStatus;
|
|
5687
5701
|
errorMessage = details.errorMessage;
|
|
5688
|
-
|
|
5702
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
5703
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5689
5704
|
throw error;
|
|
5690
5705
|
} finally {
|
|
5691
5706
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
@@ -5700,7 +5715,8 @@ async function handleResponsesNonStreaming(params) {
|
|
|
5700
5715
|
httpStatus,
|
|
5701
5716
|
errorName,
|
|
5702
5717
|
errorStatus,
|
|
5703
|
-
errorMessage
|
|
5718
|
+
errorMessage,
|
|
5719
|
+
upstreamErrorMessageRaw
|
|
5704
5720
|
});
|
|
5705
5721
|
}
|
|
5706
5722
|
}
|
|
@@ -5716,6 +5732,17 @@ async function ensureResponsesStreamCompleted(params) {
|
|
|
5716
5732
|
data: JSON.stringify(errorEvent)
|
|
5717
5733
|
});
|
|
5718
5734
|
}
|
|
5735
|
+
async function writeAnthropicStreamError(stream, message) {
|
|
5736
|
+
try {
|
|
5737
|
+
const errorEvent = buildErrorEvent(message);
|
|
5738
|
+
await stream.writeSSE({
|
|
5739
|
+
event: errorEvent.type,
|
|
5740
|
+
data: JSON.stringify(errorEvent)
|
|
5741
|
+
});
|
|
5742
|
+
} catch (streamError) {
|
|
5743
|
+
logger$5.warn("Failed to write Anthropic stream error event:", streamError);
|
|
5744
|
+
}
|
|
5745
|
+
}
|
|
5719
5746
|
async function streamResponsesAndLog$1(params) {
|
|
5720
5747
|
const { stream, response, instr, estimatedInputTokens, historicalUsage } = params;
|
|
5721
5748
|
let ttfbMs;
|
|
@@ -5723,6 +5750,7 @@ async function streamResponsesAndLog$1(params) {
|
|
|
5723
5750
|
let errorName;
|
|
5724
5751
|
let errorStatus;
|
|
5725
5752
|
let errorMessage;
|
|
5753
|
+
let upstreamErrorMessageRaw;
|
|
5726
5754
|
const streamState = createResponsesStreamState();
|
|
5727
5755
|
streamState.estimatedInputTokens = estimatedInputTokens;
|
|
5728
5756
|
streamState.historicalInputTokens = historicalUsage?.tokensInput;
|
|
@@ -5767,12 +5795,14 @@ async function streamResponsesAndLog$1(params) {
|
|
|
5767
5795
|
}
|
|
5768
5796
|
});
|
|
5769
5797
|
} catch (error) {
|
|
5770
|
-
const details =
|
|
5798
|
+
const details = await extractErrorObservability(error);
|
|
5771
5799
|
errorName = details.errorName;
|
|
5772
5800
|
errorStatus = details.errorStatus;
|
|
5773
5801
|
errorMessage = details.errorMessage;
|
|
5802
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
5774
5803
|
logger$5.warn("Streaming error:", error);
|
|
5775
|
-
if (details
|
|
5804
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5805
|
+
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
5776
5806
|
} finally {
|
|
5777
5807
|
const finishedAtMs = Date.now();
|
|
5778
5808
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
@@ -5788,15 +5818,16 @@ async function streamResponsesAndLog$1(params) {
|
|
|
5788
5818
|
httpStatus: errorStatus ?? (errorName ? 500 : 200),
|
|
5789
5819
|
errorName,
|
|
5790
5820
|
errorStatus,
|
|
5791
|
-
errorMessage
|
|
5821
|
+
errorMessage,
|
|
5822
|
+
upstreamErrorMessageRaw
|
|
5792
5823
|
});
|
|
5793
5824
|
}
|
|
5794
5825
|
}
|
|
5795
5826
|
async function handleMessagesCreateError(params) {
|
|
5796
5827
|
const { error, instr, stream } = params;
|
|
5797
5828
|
const finishedAtMs = Date.now();
|
|
5798
|
-
const details =
|
|
5799
|
-
if (details
|
|
5829
|
+
const details = await extractErrorObservability(error);
|
|
5830
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5800
5831
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
5801
5832
|
insertRequestLog$1(instr, {
|
|
5802
5833
|
finishedAtMs,
|
|
@@ -5808,7 +5839,8 @@ async function handleMessagesCreateError(params) {
|
|
|
5808
5839
|
httpStatus: details.httpStatus,
|
|
5809
5840
|
errorName: details.errorName,
|
|
5810
5841
|
errorStatus: details.errorStatus,
|
|
5811
|
-
errorMessage: details.errorMessage
|
|
5842
|
+
errorMessage: details.errorMessage,
|
|
5843
|
+
upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
|
|
5812
5844
|
});
|
|
5813
5845
|
throw error;
|
|
5814
5846
|
}
|
|
@@ -5819,17 +5851,19 @@ async function handleMessagesNonStreaming(params) {
|
|
|
5819
5851
|
let errorName;
|
|
5820
5852
|
let errorStatus;
|
|
5821
5853
|
let errorMessage;
|
|
5854
|
+
let upstreamErrorMessageRaw;
|
|
5822
5855
|
const finishedAtMs = Date.now();
|
|
5823
5856
|
try {
|
|
5824
5857
|
logger$5.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
|
|
5825
5858
|
return c.json(response);
|
|
5826
5859
|
} catch (error) {
|
|
5827
|
-
const details =
|
|
5860
|
+
const details = await extractErrorObservability(error);
|
|
5828
5861
|
httpStatus = details.httpStatus;
|
|
5829
5862
|
errorName = details.errorName;
|
|
5830
5863
|
errorStatus = details.errorStatus;
|
|
5831
5864
|
errorMessage = details.errorMessage;
|
|
5832
|
-
|
|
5865
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
5866
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5833
5867
|
throw error;
|
|
5834
5868
|
} finally {
|
|
5835
5869
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
@@ -5844,7 +5878,8 @@ async function handleMessagesNonStreaming(params) {
|
|
|
5844
5878
|
httpStatus,
|
|
5845
5879
|
errorName,
|
|
5846
5880
|
errorStatus,
|
|
5847
|
-
errorMessage
|
|
5881
|
+
errorMessage,
|
|
5882
|
+
upstreamErrorMessageRaw
|
|
5848
5883
|
});
|
|
5849
5884
|
}
|
|
5850
5885
|
}
|
|
@@ -5866,6 +5901,7 @@ async function streamMessagesAndLog(params) {
|
|
|
5866
5901
|
let errorName;
|
|
5867
5902
|
let errorStatus;
|
|
5868
5903
|
let errorMessage;
|
|
5904
|
+
let upstreamErrorMessageRaw;
|
|
5869
5905
|
try {
|
|
5870
5906
|
for await (const rawEvent of response) {
|
|
5871
5907
|
if (ttfbMs === void 0) ttfbMs = Date.now() - instr.startedAtMs;
|
|
@@ -5881,12 +5917,14 @@ async function streamMessagesAndLog(params) {
|
|
|
5881
5917
|
});
|
|
5882
5918
|
}
|
|
5883
5919
|
} catch (error) {
|
|
5884
|
-
const details =
|
|
5920
|
+
const details = await extractErrorObservability(error);
|
|
5885
5921
|
errorName = details.errorName;
|
|
5886
5922
|
errorStatus = details.errorStatus;
|
|
5887
5923
|
errorMessage = details.errorMessage;
|
|
5924
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
5888
5925
|
logger$5.warn("Streaming error:", error);
|
|
5889
|
-
if (details
|
|
5926
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
5927
|
+
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
5890
5928
|
} finally {
|
|
5891
5929
|
const finishedAtMs = Date.now();
|
|
5892
5930
|
const { premiumRemainingAfter, premiumUnlimitedAfter, premiumRemainingDiff } = await finalizeQuotaAndGetPremiumSnapshot(instr);
|
|
@@ -5902,28 +5940,33 @@ async function streamMessagesAndLog(params) {
|
|
|
5902
5940
|
httpStatus: errorStatus ?? (errorName ? 500 : 200),
|
|
5903
5941
|
errorName,
|
|
5904
5942
|
errorStatus,
|
|
5905
|
-
errorMessage
|
|
5943
|
+
errorMessage,
|
|
5944
|
+
upstreamErrorMessageRaw
|
|
5906
5945
|
});
|
|
5907
5946
|
}
|
|
5908
5947
|
}
|
|
5909
5948
|
const handleWithMessagesApi = async (params) => {
|
|
5910
|
-
const { c, anthropicPayload, anthropicBetaHeader,
|
|
5949
|
+
const { c, anthropicPayload, anthropicBetaHeader, subagentMarker, sessionId, instr, selectedModel, isCompact } = params;
|
|
5911
5950
|
prepareMessagesApiPayload(anthropicPayload, selectedModel);
|
|
5912
5951
|
debugJson(logger$5, "Translated Messages payload:", anthropicPayload);
|
|
5913
5952
|
const ctx = toAccountContext(instr.account);
|
|
5914
|
-
const
|
|
5915
|
-
|
|
5953
|
+
const effectiveInitiator = resolveEffectiveInitiator(getMessagesInitiator(anthropicPayload), {
|
|
5954
|
+
isCompact,
|
|
5955
|
+
isSubagent: Boolean(subagentMarker)
|
|
5956
|
+
});
|
|
5957
|
+
instr.initiator = effectiveInitiator;
|
|
5916
5958
|
let response;
|
|
5917
5959
|
try {
|
|
5918
5960
|
response = await createMessages(anthropicPayload, ctx, {
|
|
5919
5961
|
anthropicBetaHeader,
|
|
5920
5962
|
upstreamRequestId: instr.upstreamRequestId,
|
|
5921
|
-
initiator,
|
|
5963
|
+
initiator: effectiveInitiator,
|
|
5922
5964
|
subagentMarker,
|
|
5923
5965
|
sessionId,
|
|
5924
5966
|
isCompact
|
|
5925
5967
|
});
|
|
5926
5968
|
instr.confirmAffinity?.();
|
|
5969
|
+
instr.confirmOwnership?.();
|
|
5927
5970
|
} catch (error) {
|
|
5928
5971
|
return await handleMessagesCreateError({
|
|
5929
5972
|
error,
|
|
@@ -5971,9 +6014,8 @@ messageRoutes.post("/count_tokens", async (c) => {
|
|
|
5971
6014
|
const modelRoutes = new Hono();
|
|
5972
6015
|
modelRoutes.get("/", async (c) => {
|
|
5973
6016
|
try {
|
|
5974
|
-
if (!state.models) await cacheModels();
|
|
5975
6017
|
const blockedTargets = getAliasTargetSet();
|
|
5976
|
-
const models =
|
|
6018
|
+
const models = getAvailableModels().filter((model) => !blockedTargets.has(model.id.toLowerCase())).map((model) => ({
|
|
5977
6019
|
id: model.id,
|
|
5978
6020
|
object: "model",
|
|
5979
6021
|
type: "model",
|
|
@@ -5981,7 +6023,7 @@ modelRoutes.get("/", async (c) => {
|
|
|
5981
6023
|
created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
5982
6024
|
owned_by: model.vendor,
|
|
5983
6025
|
display_name: model.name
|
|
5984
|
-
}))
|
|
6026
|
+
}));
|
|
5985
6027
|
const aliasModels = Object.keys(getModelAliases()).map((alias) => ({
|
|
5986
6028
|
id: alias,
|
|
5987
6029
|
object: "model",
|
|
@@ -6030,7 +6072,7 @@ async function handleProviderCountTokens(c) {
|
|
|
6030
6072
|
const anthropicPayload = await c.req.json();
|
|
6031
6073
|
const openAIPayload = translateToOpenAI(anthropicPayload);
|
|
6032
6074
|
const modelId = anthropicPayload.model.trim();
|
|
6033
|
-
let selectedModel =
|
|
6075
|
+
let selectedModel = getAvailableModels().find((model) => model.id === modelId);
|
|
6034
6076
|
if (!selectedModel && modelId) selectedModel = createFallbackModel(modelId);
|
|
6035
6077
|
if (!selectedModel) {
|
|
6036
6078
|
logger$4.warn("provider.count_tokens.model_not_found", {
|
|
@@ -6077,10 +6119,13 @@ const STRIPPED_RESPONSE_HEADERS = [
|
|
|
6077
6119
|
"upgrade"
|
|
6078
6120
|
];
|
|
6079
6121
|
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
6122
|
+
const authHeaders = {};
|
|
6123
|
+
if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
6124
|
+
else authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
6080
6125
|
const headers = {
|
|
6081
6126
|
"content-type": "application/json",
|
|
6082
6127
|
accept: "application/json",
|
|
6083
|
-
|
|
6128
|
+
...authHeaders
|
|
6084
6129
|
};
|
|
6085
6130
|
for (const headerName of FORWARDABLE_HEADERS) {
|
|
6086
6131
|
const headerValue = requestHeaders.get(headerName);
|
|
@@ -6287,9 +6332,10 @@ const handleResponses = async (c) => {
|
|
|
6287
6332
|
const streamRequested = Boolean(payload.stream);
|
|
6288
6333
|
const { initiator: initialInitiator } = getResponsesRequestOptions(payload);
|
|
6289
6334
|
const userId = payload.metadata?.user_id;
|
|
6290
|
-
const
|
|
6335
|
+
const requestBodyPromptCacheKey = typeof payload.prompt_cache_key === "string" ? payload.prompt_cache_key : null;
|
|
6336
|
+
const { safetyIdentifier, sessionId: metadataSessionId } = parseUserIdMetadata(userId);
|
|
6291
6337
|
const normalizedSafetyIdentifier = safetyIdentifier ?? void 0;
|
|
6292
|
-
const normalizedPromptCacheKey =
|
|
6338
|
+
const normalizedPromptCacheKey = requestBodyPromptCacheKey ?? metadataSessionId ?? void 0;
|
|
6293
6339
|
request.userId = userId;
|
|
6294
6340
|
request.safetyIdentifier = normalizedSafetyIdentifier;
|
|
6295
6341
|
request.promptCacheKey = normalizedPromptCacheKey;
|
|
@@ -6307,10 +6353,19 @@ const handleResponses = async (c) => {
|
|
|
6307
6353
|
});
|
|
6308
6354
|
}
|
|
6309
6355
|
const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, normalizedPromptCacheKey);
|
|
6356
|
+
const headerSessionId = c.req.header("x-session-id") ?? null;
|
|
6357
|
+
const affinityKey = resolveAffinityKey({
|
|
6358
|
+
promptCacheKey: requestBodyPromptCacheKey,
|
|
6359
|
+
metadataSessionId,
|
|
6360
|
+
headerSessionId,
|
|
6361
|
+
upstreamRequestId
|
|
6362
|
+
});
|
|
6363
|
+
request.affinityKeyUsed = affinityKey.affinityKeyUsed;
|
|
6364
|
+
request.affinityKeySource = affinityKey.affinityKeySource;
|
|
6310
6365
|
const selection = await accountsManager.selectAccountForRequest([{
|
|
6311
6366
|
modelId: clientModel,
|
|
6312
6367
|
endpoint: RESPONSES_ENDPOINT
|
|
6313
|
-
}], { requestId:
|
|
6368
|
+
}], { requestId: affinityKey.requestId });
|
|
6314
6369
|
if (!selection.ok) {
|
|
6315
6370
|
recordSelectionFailure(store, {
|
|
6316
6371
|
request,
|
|
@@ -6323,6 +6378,7 @@ const handleResponses = async (c) => {
|
|
|
6323
6378
|
const { account, selectedModel } = selection;
|
|
6324
6379
|
request.affinityHit = selection.affinityHit;
|
|
6325
6380
|
request.affinityCacheKey = selection.affinityCacheKey;
|
|
6381
|
+
request.selectionReason = selection.selectionReason;
|
|
6326
6382
|
const upstreamPayload = {
|
|
6327
6383
|
...payload,
|
|
6328
6384
|
model: selectedModel.id
|
|
@@ -6336,7 +6392,7 @@ const handleResponses = async (c) => {
|
|
|
6336
6392
|
request.initiator = initiator;
|
|
6337
6393
|
if (state.manualApprove) await awaitApproval();
|
|
6338
6394
|
const accountCtx = toAccountContext(account);
|
|
6339
|
-
const upstreamSessionId = getUUID(upstreamRequestId);
|
|
6395
|
+
const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
|
|
6340
6396
|
request.upstreamRequestId = upstreamRequestId;
|
|
6341
6397
|
request.upstreamSessionId = upstreamSessionId;
|
|
6342
6398
|
if (streamRequested) return handleStreamingResponses({
|
|
@@ -6366,6 +6422,37 @@ const handleResponses = async (c) => {
|
|
|
6366
6422
|
premiumUnlimitedBefore
|
|
6367
6423
|
});
|
|
6368
6424
|
};
|
|
6425
|
+
async function observeRequestError(accountId, error) {
|
|
6426
|
+
const details = await extractErrorObservability(error);
|
|
6427
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(accountId, "Unauthorized (401)");
|
|
6428
|
+
return {
|
|
6429
|
+
httpStatus: details.httpStatus,
|
|
6430
|
+
errorName: details.errorName,
|
|
6431
|
+
errorStatus: details.errorStatus,
|
|
6432
|
+
errorMessage: details.errorMessage,
|
|
6433
|
+
upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
|
|
6434
|
+
};
|
|
6435
|
+
}
|
|
6436
|
+
function buildResponsesStreamError(message) {
|
|
6437
|
+
return {
|
|
6438
|
+
type: "error",
|
|
6439
|
+
code: null,
|
|
6440
|
+
message,
|
|
6441
|
+
param: null,
|
|
6442
|
+
sequence_number: 0
|
|
6443
|
+
};
|
|
6444
|
+
}
|
|
6445
|
+
async function writeResponsesStreamError(stream, message) {
|
|
6446
|
+
try {
|
|
6447
|
+
const errorEvent = buildResponsesStreamError(message);
|
|
6448
|
+
await stream.writeSSE({
|
|
6449
|
+
event: errorEvent.type,
|
|
6450
|
+
data: JSON.stringify(errorEvent)
|
|
6451
|
+
});
|
|
6452
|
+
} catch (streamError) {
|
|
6453
|
+
logger$1.warn("Failed to write Responses stream error event:", streamError);
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6369
6456
|
function buildRequestContext(c) {
|
|
6370
6457
|
const requestId = randomUUID();
|
|
6371
6458
|
const startedAtMs = Date.now();
|
|
@@ -6396,6 +6483,9 @@ function insertRequestLog(store, request, record) {
|
|
|
6396
6483
|
promptCacheKey: request.promptCacheKey,
|
|
6397
6484
|
initiator: request.initiator,
|
|
6398
6485
|
upstreamRequestId: request.upstreamRequestId,
|
|
6486
|
+
affinityKeyUsed: request.affinityKeyUsed,
|
|
6487
|
+
affinityKeySource: request.affinityKeySource,
|
|
6488
|
+
selectionReason: request.selectionReason,
|
|
6399
6489
|
affinityHit: request.affinityHit,
|
|
6400
6490
|
affinityCacheKey: request.affinityCacheKey,
|
|
6401
6491
|
...record
|
|
@@ -6434,14 +6524,6 @@ function extractUsageFromChunkData(data) {
|
|
|
6434
6524
|
return;
|
|
6435
6525
|
}
|
|
6436
6526
|
}
|
|
6437
|
-
function getStreamChunkFields(chunk) {
|
|
6438
|
-
const c = chunk;
|
|
6439
|
-
return {
|
|
6440
|
-
id: c.id,
|
|
6441
|
-
event: c.event,
|
|
6442
|
-
data: c.data
|
|
6443
|
-
};
|
|
6444
|
-
}
|
|
6445
6527
|
async function handleStreamingResponses(params) {
|
|
6446
6528
|
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore } = params;
|
|
6447
6529
|
let response;
|
|
@@ -6492,8 +6574,8 @@ async function handleUpstreamCreateError(params) {
|
|
|
6492
6574
|
const { store, request, selection, clientModel, premiumRemainingBefore, premiumUnlimitedBefore, error } = params;
|
|
6493
6575
|
const { account, reservation, selectedModel, endpoint, costUnits } = selection;
|
|
6494
6576
|
const finishedAtMs = Date.now();
|
|
6495
|
-
const details =
|
|
6496
|
-
if (details
|
|
6577
|
+
const details = await extractErrorObservability(error);
|
|
6578
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
6497
6579
|
await accountsManager.finalizeQuota(account, reservation);
|
|
6498
6580
|
const premiumRemainingAfter = account.premiumRemaining;
|
|
6499
6581
|
const premiumUnlimitedAfter = account.unlimited;
|
|
@@ -6515,7 +6597,8 @@ async function handleUpstreamCreateError(params) {
|
|
|
6515
6597
|
httpStatus: details.httpStatus,
|
|
6516
6598
|
errorName: details.errorName,
|
|
6517
6599
|
errorStatus: details.errorStatus,
|
|
6518
|
-
errorMessage: details.errorMessage
|
|
6600
|
+
errorMessage: details.errorMessage,
|
|
6601
|
+
upstreamErrorMessageRaw: details.upstreamErrorMessageRaw
|
|
6519
6602
|
});
|
|
6520
6603
|
throw error;
|
|
6521
6604
|
}
|
|
@@ -6527,6 +6610,7 @@ async function handleNonStreamingUpstreamResult(params) {
|
|
|
6527
6610
|
let errorName;
|
|
6528
6611
|
let errorStatus;
|
|
6529
6612
|
let errorMessage;
|
|
6613
|
+
let upstreamErrorMessageRaw;
|
|
6530
6614
|
const finishedAtMs = Date.now();
|
|
6531
6615
|
try {
|
|
6532
6616
|
debugJsonTail(logger$1, "Forwarding native Responses result:", {
|
|
@@ -6535,11 +6619,12 @@ async function handleNonStreamingUpstreamResult(params) {
|
|
|
6535
6619
|
});
|
|
6536
6620
|
return c.json(result);
|
|
6537
6621
|
} catch (error) {
|
|
6538
|
-
const details =
|
|
6622
|
+
const details = await extractErrorObservability(error);
|
|
6539
6623
|
httpStatus = details.httpStatus;
|
|
6540
6624
|
errorName = details.errorName;
|
|
6541
6625
|
errorStatus = details.errorStatus;
|
|
6542
6626
|
errorMessage = details.errorMessage;
|
|
6627
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
6543
6628
|
throw error;
|
|
6544
6629
|
} finally {
|
|
6545
6630
|
await accountsManager.finalizeQuota(account, reservation);
|
|
@@ -6564,7 +6649,8 @@ async function handleNonStreamingUpstreamResult(params) {
|
|
|
6564
6649
|
httpStatus,
|
|
6565
6650
|
errorName,
|
|
6566
6651
|
errorStatus,
|
|
6567
|
-
errorMessage
|
|
6652
|
+
errorMessage,
|
|
6653
|
+
upstreamErrorMessageRaw
|
|
6568
6654
|
});
|
|
6569
6655
|
}
|
|
6570
6656
|
}
|
|
@@ -6577,6 +6663,7 @@ async function streamResponsesAndLog(params) {
|
|
|
6577
6663
|
let errorName;
|
|
6578
6664
|
let errorStatus;
|
|
6579
6665
|
let errorMessage;
|
|
6666
|
+
let upstreamErrorMessageRaw;
|
|
6580
6667
|
try {
|
|
6581
6668
|
for await (const chunk of response) {
|
|
6582
6669
|
if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
|
|
@@ -6592,11 +6679,14 @@ async function streamResponsesAndLog(params) {
|
|
|
6592
6679
|
});
|
|
6593
6680
|
}
|
|
6594
6681
|
} catch (error) {
|
|
6595
|
-
const details =
|
|
6682
|
+
const details = await extractErrorObservability(error);
|
|
6596
6683
|
errorName = details.errorName;
|
|
6597
6684
|
errorStatus = details.errorStatus;
|
|
6598
6685
|
errorMessage = details.errorMessage;
|
|
6686
|
+
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
6599
6687
|
logger$1.warn("Responses streaming error:", error);
|
|
6688
|
+
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
6689
|
+
await writeResponsesStreamError(stream, getUserVisibleErrorMessage(details));
|
|
6600
6690
|
} finally {
|
|
6601
6691
|
const finishedAtMs = Date.now();
|
|
6602
6692
|
await accountsManager.finalizeQuota(account, reservation);
|
|
@@ -6622,18 +6712,16 @@ async function streamResponsesAndLog(params) {
|
|
|
6622
6712
|
httpStatus: errorStatus ?? (errorName ? 500 : 200),
|
|
6623
6713
|
errorName,
|
|
6624
6714
|
errorStatus,
|
|
6625
|
-
errorMessage
|
|
6715
|
+
errorMessage,
|
|
6716
|
+
upstreamErrorMessageRaw
|
|
6626
6717
|
});
|
|
6627
6718
|
}
|
|
6628
6719
|
}
|
|
6629
6720
|
async function handleNonStreamingResponses(params) {
|
|
6630
6721
|
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore } = params;
|
|
6631
6722
|
const { account, reservation, selectedModel, endpoint, costUnits } = selection;
|
|
6632
|
-
let httpStatus = 200;
|
|
6633
6723
|
let usage = {};
|
|
6634
|
-
let
|
|
6635
|
-
let errorStatus;
|
|
6636
|
-
let errorMessage;
|
|
6724
|
+
let errorState = { httpStatus: 200 };
|
|
6637
6725
|
let finishedAtMs;
|
|
6638
6726
|
try {
|
|
6639
6727
|
const response = await createResponses(payload, {
|
|
@@ -6642,10 +6730,9 @@ async function handleNonStreamingResponses(params) {
|
|
|
6642
6730
|
upstreamRequestId: request.upstreamRequestId,
|
|
6643
6731
|
sessionId: request.upstreamSessionId
|
|
6644
6732
|
}, accountCtx);
|
|
6733
|
+
if (isAsyncIterable(response)) throw new Error("Upstream returned a stream unexpectedly");
|
|
6645
6734
|
selection.confirmAffinity?.();
|
|
6646
6735
|
finishedAtMs = Date.now();
|
|
6647
|
-
const streamResponse = handleUnexpectedResponsesStream(c, response);
|
|
6648
|
-
if (streamResponse) return streamResponse;
|
|
6649
6736
|
const result = response;
|
|
6650
6737
|
usage = extractResponsesUsageFromResult(result);
|
|
6651
6738
|
debugJsonTail(logger$1, "Forwarding native Responses result:", {
|
|
@@ -6655,12 +6742,7 @@ async function handleNonStreamingResponses(params) {
|
|
|
6655
6742
|
return c.json(result);
|
|
6656
6743
|
} catch (error) {
|
|
6657
6744
|
finishedAtMs = Date.now();
|
|
6658
|
-
|
|
6659
|
-
httpStatus = details.httpStatus;
|
|
6660
|
-
errorName = details.errorName;
|
|
6661
|
-
errorStatus = details.errorStatus;
|
|
6662
|
-
errorMessage = details.errorMessage;
|
|
6663
|
-
if (details.unauthorized) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
6745
|
+
errorState = await observeRequestError(account.id, error);
|
|
6664
6746
|
throw error;
|
|
6665
6747
|
} finally {
|
|
6666
6748
|
const finishedAtMsFinal = finishedAtMs ?? Date.now();
|
|
@@ -6683,61 +6765,14 @@ async function handleNonStreamingResponses(params) {
|
|
|
6683
6765
|
premiumRemainingDiff: computeDiff(premiumRemainingBefore, premiumRemainingAfter),
|
|
6684
6766
|
premiumUnlimitedBefore,
|
|
6685
6767
|
premiumUnlimitedAfter,
|
|
6686
|
-
httpStatus,
|
|
6687
|
-
errorName,
|
|
6688
|
-
errorStatus,
|
|
6689
|
-
errorMessage
|
|
6768
|
+
httpStatus: errorState.httpStatus,
|
|
6769
|
+
errorName: errorState.errorName,
|
|
6770
|
+
errorStatus: errorState.errorStatus,
|
|
6771
|
+
errorMessage: errorState.errorMessage,
|
|
6772
|
+
upstreamErrorMessageRaw: errorState.upstreamErrorMessageRaw
|
|
6690
6773
|
});
|
|
6691
6774
|
}
|
|
6692
6775
|
}
|
|
6693
|
-
function handleUnexpectedResponsesStream(c, response) {
|
|
6694
|
-
if (!isAsyncIterable(response)) return null;
|
|
6695
|
-
logger$1.debug("Forwarding native Responses stream (unexpected)");
|
|
6696
|
-
return streamSSE(c, async (stream) => {
|
|
6697
|
-
const idTracker = createStreamIdTracker();
|
|
6698
|
-
for await (const chunk of response) {
|
|
6699
|
-
const { id, event, data } = getStreamChunkFields(chunk);
|
|
6700
|
-
const processedData = fixStreamIds(data ?? "", event, idTracker);
|
|
6701
|
-
debugJson(logger$1, "Responses stream chunk:", chunk);
|
|
6702
|
-
await stream.writeSSE({
|
|
6703
|
-
id,
|
|
6704
|
-
event,
|
|
6705
|
-
data: processedData
|
|
6706
|
-
});
|
|
6707
|
-
}
|
|
6708
|
-
});
|
|
6709
|
-
}
|
|
6710
|
-
const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
6711
|
-
const useFunctionApplyPatch = (payload) => {
|
|
6712
|
-
if (!(getConfig().useFunctionApplyPatch ?? true)) return;
|
|
6713
|
-
logger$1.debug("Using function tool apply_patch for responses");
|
|
6714
|
-
if (Array.isArray(payload.tools)) {
|
|
6715
|
-
const toolsArr = payload.tools;
|
|
6716
|
-
for (let i = 0; i < toolsArr.length; i++) {
|
|
6717
|
-
const t = toolsArr[i];
|
|
6718
|
-
if (t.type === "custom" && t.name === "apply_patch") toolsArr[i] = {
|
|
6719
|
-
type: "function",
|
|
6720
|
-
name: t.name,
|
|
6721
|
-
description: "Use the `apply_patch` tool to edit files",
|
|
6722
|
-
parameters: {
|
|
6723
|
-
type: "object",
|
|
6724
|
-
properties: { input: {
|
|
6725
|
-
type: "string",
|
|
6726
|
-
description: "The entire contents of the apply_patch command"
|
|
6727
|
-
} },
|
|
6728
|
-
required: ["input"]
|
|
6729
|
-
},
|
|
6730
|
-
strict: false
|
|
6731
|
-
};
|
|
6732
|
-
}
|
|
6733
|
-
}
|
|
6734
|
-
};
|
|
6735
|
-
const removeWebSearchTool = (payload) => {
|
|
6736
|
-
if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
|
|
6737
|
-
payload.tools = payload.tools.filter((t) => {
|
|
6738
|
-
return t.type !== "web_search";
|
|
6739
|
-
});
|
|
6740
|
-
};
|
|
6741
6776
|
|
|
6742
6777
|
//#endregion
|
|
6743
6778
|
//#region src/routes/responses/route.ts
|
|
@@ -6831,4 +6866,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
6831
6866
|
|
|
6832
6867
|
//#endregion
|
|
6833
6868
|
export { server };
|
|
6834
|
-
//# sourceMappingURL=server-
|
|
6869
|
+
//# sourceMappingURL=server-DAxpfPde.js.map
|