@nick3/copilot-api 1.10.9 → 1.10.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -45
- package/README.zh-CN.md +140 -45
- package/dist/{account-COtMmvzU.js → account-DpW8RaT6.js} +3 -3
- package/dist/{account-COtMmvzU.js.map → account-DpW8RaT6.js.map} +1 -1
- package/dist/admin/AGENTS.md +19 -0
- package/dist/admin/assets/{index-BAh4eOwM.js → index-BGmG-ckX.js} +34 -34
- package/dist/admin/index.html +1 -1
- package/dist/auth-CnwhQOu6.js +327 -0
- package/dist/auth-CnwhQOu6.js.map +1 -0
- package/dist/{check-usage-DdevqHE5.js → check-usage-CseltoFJ.js} +4 -42
- package/dist/check-usage-CseltoFJ.js.map +1 -0
- package/dist/config-XZv75uoU.js +591 -0
- package/dist/config-XZv75uoU.js.map +1 -0
- package/dist/{debug-BMo6ltbp.js → debug-D8xHblDV.js} +18 -7
- package/dist/debug-D8xHblDV.js.map +1 -0
- package/dist/main.js +5 -10
- package/dist/main.js.map +1 -1
- package/dist/mcp-http-BhELuvog.js +2 -0
- package/dist/mcp-http-DI4Vz01p.js +82 -0
- package/dist/mcp-http-DI4Vz01p.js.map +1 -0
- package/dist/mcp-http-config-DMdUDz1D.js +39 -0
- package/dist/mcp-http-config-DMdUDz1D.js.map +1 -0
- package/dist/mcp-pLTPS0tO.js +79 -0
- package/dist/mcp-pLTPS0tO.js.map +1 -0
- package/dist/{tool-search-BrN7M0Dd.js → mcp-server-DEqHrXFq.js} +25 -2
- package/dist/mcp-server-DEqHrXFq.js.map +1 -0
- package/dist/{paths-CclKwouX.js → paths-Bpsb62LK.js} +3 -1
- package/dist/paths-Bpsb62LK.js.map +1 -0
- package/dist/{poll-access-token-BAgM2-7k.js → poll-access-token-GzVkiTH8.js} +71 -4
- package/dist/poll-access-token-GzVkiTH8.js.map +1 -0
- package/dist/{request-outbound-BJjWS_jF.js → request-outbound-BkEA8Wgb.js} +1 -1
- package/dist/{request-outbound-Pu1kp2x8.js → request-outbound-DZTxxtcx.js} +3 -3
- package/dist/{request-outbound-Pu1kp2x8.js.map → request-outbound-DZTxxtcx.js.map} +1 -1
- package/dist/{proxy-_U-hgwIn.js → responses-bridge-registry-JjUvTCST.js} +127 -581
- package/dist/responses-bridge-registry-JjUvTCST.js.map +1 -0
- package/dist/{server-GxNB5Syq.js → server-BYKxAFro.js} +678 -249
- package/dist/server-BYKxAFro.js.map +1 -0
- package/dist/start-DVlCJ0Ma.js +526 -0
- package/dist/start-DVlCJ0Ma.js.map +1 -0
- package/dist/token-9O2KJcF5.js +946 -0
- package/dist/token-9O2KJcF5.js.map +1 -0
- package/package.json +2 -2
- package/dist/auth-B0y-2njL.js +0 -226
- package/dist/auth-B0y-2njL.js.map +0 -1
- package/dist/check-usage-DdevqHE5.js.map +0 -1
- package/dist/debug-BMo6ltbp.js.map +0 -1
- package/dist/get-copilot-token-8Rm-rVsp.js +0 -17
- package/dist/get-copilot-token-8Rm-rVsp.js.map +0 -1
- package/dist/mcp-9Hgepkc5.js +0 -37
- package/dist/mcp-9Hgepkc5.js.map +0 -1
- package/dist/paths-CclKwouX.js.map +0 -1
- package/dist/poll-access-token-BAgM2-7k.js.map +0 -1
- package/dist/proxy-_U-hgwIn.js.map +0 -1
- package/dist/server-GxNB5Syq.js.map +0 -1
- package/dist/start-DdrurmQ3.js +0 -274
- package/dist/start-DdrurmQ3.js.map +0 -1
- package/dist/tool-search-BrN7M0Dd.js.map +0 -1
|
@@ -1,109 +1,27 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { 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-
|
|
3
|
-
import { r as ensurePaths, t as PATHS } from "./paths-
|
|
4
|
-
import { i as
|
|
5
|
-
import { a as
|
|
6
|
-
import {
|
|
1
|
+
import { A as accountFromState, B as compactSystemPromptStarts, C as copilotHeaders, D as prepareForCompact, E as normalizeDomain, F as resolveTraceId, L as compactAutoContinuePromptStarts, M as captureOutboundHeadersSnapshot, O as prepareInteractionHeaders, P as requestContext, R as compactMessageSections, S as copilotBaseUrl, T as copilotWebSocketHeaders, b as HTTPError, c as getUUID, d as parseUserIdMetadata, f as resolveAffinityKey, g as getCopilotUsage, h as getDeviceCode, j as state, k as prepareMessageProxyHeaders, l as isNullish, m as getGitHubUser, o as generateRequestIdFromPayload, p as sleep, s as getRootSessionId, t as pollAccessToken, u as normalizeStableSessionId, v as getProxyEnvDispatcher, x as forwardError } from "./poll-access-token-GzVkiTH8.js";
|
|
2
|
+
import { 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-DpW8RaT6.js";
|
|
3
|
+
import { r as ensurePaths, t as PATHS } from "./paths-Bpsb62LK.js";
|
|
4
|
+
import { C as isResponsesApiWebSocketEnabled, D as resolveModelAlias, E as resolveMappedModel, S as isResponsesApiWebSearchEnabled, T as mergeConfigWithDefaults, _ as isAccountAffinityEnabled, a as getConfig, b as isMessagesApiEnabled, c as getModelAliases, d as getProviderConfig, g as getSmallModel, i as getClaudeTokenMultiplier, k as shouldCompactUseSmallModel, l as getModelAliasesInfo, m as getReasoningEffortForModel, n as getAliasTargetSet, o as getExtraPromptForModel, p as getRawProviderConfig, r as getAnthropicApiKey, s as getLogLevel, t as PROVIDER_TYPE_ANTHROPIC, u as getModelRefreshIntervalMs, v as isForceAgentEnabled, x as isResponsesApiContextManagementEnabled, y as isMessageStartInputTokensFallbackEnabled } from "./config-XZv75uoU.js";
|
|
5
|
+
import { a as forwardCodexResponses, n as setupCodexToken } from "./token-9O2KJcF5.js";
|
|
6
|
+
import { i as getRequestOutboundStore, r as getRedactedHeaderKeys } from "./request-outbound-DZTxxtcx.js";
|
|
7
|
+
import { i as isMcpHttpEnabledFromEnv, n as DEFAULT_MCP_HTTP_PATH } from "./mcp-http-config-DMdUDz1D.js";
|
|
8
|
+
import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, i as isBridgeToolSearchName, l as selectDeferredToolsByNames, n as BRIDGE_TOOL_SEARCH_NAME, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, u as shouldEnableResponsesToolSearch } from "./mcp-server-DEqHrXFq.js";
|
|
9
|
+
import { n as handleStreamableHttpMcpRequest, r as mcpHttpCorsOptions } from "./mcp-http-DI4Vz01p.js";
|
|
10
|
+
import { C as createAuthMiddleware, S as isDevModeEnabled, _ as normalizeMessagesUsage, b as copilotFetch, c as accountsManager, d as extractResponsesUsageFromStreamEvent, f as getClientIpInfo, g as normalizeEmbeddingsUsage, h as normalizeChatCompletionsUsage, l as applySharedSessionAffinityRetention, m as getStatsStore, p as getRequestHistoryStore, s as updateQuotaRefreshSchedulerFromConfig, t as closeResponsesBridge, u as extractResponsesUsageFromResult, v as normalizeResponsesUsage, x as flushPendingCapture, y as toLocalDateString } from "./responses-bridge-registry-JjUvTCST.js";
|
|
7
11
|
import consola from "consola";
|
|
8
12
|
import fs, { readFile } from "node:fs/promises";
|
|
9
|
-
import { createHash, randomUUID
|
|
13
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
10
14
|
import * as path$1 from "node:path";
|
|
11
15
|
import path from "node:path";
|
|
12
|
-
import {
|
|
16
|
+
import { events } from "fetch-event-stream";
|
|
13
17
|
import fs$1, { existsSync } from "node:fs";
|
|
18
|
+
import { WebSocket } from "undici";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
14
20
|
import { Hono } from "hono";
|
|
15
21
|
import { cors } from "hono/cors";
|
|
16
22
|
import { logger } from "hono/logger";
|
|
17
23
|
import { streamSSE } from "hono/streaming";
|
|
18
|
-
import { events } from "fetch-event-stream";
|
|
19
24
|
import util from "node:util";
|
|
20
|
-
import { WebSocket } from "undici";
|
|
21
|
-
//#region src/lib/request-auth.ts
|
|
22
|
-
const LEGACY_API_KEY_ENV_VAR = "COPILOT_API_KEY";
|
|
23
|
-
let warnedLegacyEnvFallback = false;
|
|
24
|
-
let warnedLegacyConfigFallback = false;
|
|
25
|
-
function normalizeApiKeys(apiKeys) {
|
|
26
|
-
if (!Array.isArray(apiKeys)) {
|
|
27
|
-
if (apiKeys !== void 0) consola.warn("Invalid auth.apiKeys config. Expected an array of strings.");
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
const normalizedKeys = apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0);
|
|
31
|
-
if (normalizedKeys.length !== apiKeys.length) consola.warn("Invalid auth.apiKeys entries found. Only non-empty strings are allowed.");
|
|
32
|
-
return [...new Set(normalizedKeys)];
|
|
33
|
-
}
|
|
34
|
-
function getConfiguredApiKeys() {
|
|
35
|
-
const config = getConfig();
|
|
36
|
-
const configuredApiKeys = normalizeApiKeys(config.auth?.apiKeys);
|
|
37
|
-
if (configuredApiKeys.length > 0) return configuredApiKeys;
|
|
38
|
-
const envApiKey = process.env[LEGACY_API_KEY_ENV_VAR]?.trim();
|
|
39
|
-
if (envApiKey) {
|
|
40
|
-
if (!warnedLegacyEnvFallback) {
|
|
41
|
-
warnedLegacyEnvFallback = true;
|
|
42
|
-
consola.warn(`Using legacy ${LEGACY_API_KEY_ENV_VAR}. Please migrate to config.auth.apiKeys.`);
|
|
43
|
-
}
|
|
44
|
-
return [envApiKey];
|
|
45
|
-
}
|
|
46
|
-
const legacyConfigApiKey = config.apiKey?.trim();
|
|
47
|
-
if (legacyConfigApiKey) {
|
|
48
|
-
if (!warnedLegacyConfigFallback) {
|
|
49
|
-
warnedLegacyConfigFallback = true;
|
|
50
|
-
consola.warn("Using deprecated config.apiKey. Please migrate to config.auth.apiKeys.");
|
|
51
|
-
}
|
|
52
|
-
return [legacyConfigApiKey];
|
|
53
|
-
}
|
|
54
|
-
return configuredApiKeys;
|
|
55
|
-
}
|
|
56
|
-
function extractRequestApiKey(c) {
|
|
57
|
-
const xApiKey = c.req.header("x-api-key")?.trim();
|
|
58
|
-
if (xApiKey) return xApiKey;
|
|
59
|
-
const authorization = c.req.header("authorization");
|
|
60
|
-
if (!authorization) return null;
|
|
61
|
-
const [scheme, ...rest] = authorization.trim().split(/\s+/);
|
|
62
|
-
if (scheme.toLowerCase() !== "bearer") return null;
|
|
63
|
-
return rest.join(" ").trim() || null;
|
|
64
|
-
}
|
|
65
|
-
function createUnauthorizedResponse(c) {
|
|
66
|
-
c.header("WWW-Authenticate", "Bearer realm=\"copilot-api\"");
|
|
67
|
-
return c.json({ error: {
|
|
68
|
-
message: "Unauthorized. Provide Authorization: Bearer <key> or x-api-key.",
|
|
69
|
-
type: "unauthorized"
|
|
70
|
-
} }, 401);
|
|
71
|
-
}
|
|
72
|
-
function normalizePathname(pathname) {
|
|
73
|
-
if (pathname.length > 1 && pathname.endsWith("/")) return pathname.slice(0, -1);
|
|
74
|
-
return pathname;
|
|
75
|
-
}
|
|
76
|
-
function hasPrefixBoundary(pathname, prefix) {
|
|
77
|
-
return pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
78
|
-
}
|
|
79
|
-
function timingSafeKeyCompare(a, b) {
|
|
80
|
-
try {
|
|
81
|
-
const aBuf = Buffer.from(a);
|
|
82
|
-
const bBuf = Buffer.from(b);
|
|
83
|
-
if (aBuf.length !== bBuf.length) return false;
|
|
84
|
-
return timingSafeEqual(aBuf, bBuf);
|
|
85
|
-
} catch {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function createAuthMiddleware(options = {}) {
|
|
90
|
-
const getApiKeys = options.getApiKeys ?? getConfiguredApiKeys;
|
|
91
|
-
const allowUnauthenticatedPaths = new Set((options.allowUnauthenticatedPaths ?? ["/"]).map((path) => normalizePathname(path)));
|
|
92
|
-
const allowUnauthenticatedPathPrefixes = (options.allowUnauthenticatedPathPrefixes ?? []).map((path) => normalizePathname(path));
|
|
93
|
-
const allowOptionsBypass = options.allowOptionsBypass ?? true;
|
|
94
|
-
return async (c, next) => {
|
|
95
|
-
if (allowOptionsBypass && c.req.method === "OPTIONS") return next();
|
|
96
|
-
const pathname = normalizePathname(new URL(c.req.url, "http://local").pathname);
|
|
97
|
-
if (allowUnauthenticatedPaths.has(pathname)) return next();
|
|
98
|
-
if (allowUnauthenticatedPathPrefixes.some((prefix) => hasPrefixBoundary(pathname, prefix))) return next();
|
|
99
|
-
const apiKeys = getApiKeys();
|
|
100
|
-
if (apiKeys.length === 0) return next();
|
|
101
|
-
const requestApiKey = extractRequestApiKey(c);
|
|
102
|
-
if (!(requestApiKey ? apiKeys.some((apiKey) => timingSafeKeyCompare(requestApiKey, apiKey)) : false)) return createUnauthorizedResponse(c);
|
|
103
|
-
return next();
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
//#endregion
|
|
107
25
|
//#region src/lib/trace.ts
|
|
108
26
|
const traceIdMiddleware = async (c, next) => {
|
|
109
27
|
const traceId = resolveTraceId(c.req.header("x-trace-id"));
|
|
@@ -112,7 +30,7 @@ const traceIdMiddleware = async (c, next) => {
|
|
|
112
30
|
traceId,
|
|
113
31
|
startTime: Date.now(),
|
|
114
32
|
userAgent: c.req.header("user-agent") || "",
|
|
115
|
-
sessionAffinity: c.req.header("x-session-affinity"),
|
|
33
|
+
sessionAffinity: c.req.header("x-session-affinity") ?? c.req.header("x-client-request-id"),
|
|
116
34
|
parentSessionId: c.req.header("x-parent-session-id")
|
|
117
35
|
};
|
|
118
36
|
await requestContext.run(context, async () => {
|
|
@@ -302,7 +220,7 @@ const findEndpointModel = (sdkModelId) => {
|
|
|
302
220
|
const models = getAvailableModels();
|
|
303
221
|
const exactMatch = models.find((m) => m.id === sdkModelId);
|
|
304
222
|
if (exactMatch) return exactMatch;
|
|
305
|
-
const normalized =
|
|
223
|
+
const normalized = normalizeSdkModelId(sdkModelId);
|
|
306
224
|
if (!normalized) return;
|
|
307
225
|
const modelName = `claude-${normalized.family}-${normalized.version}`;
|
|
308
226
|
return models.find((m) => m.id === modelName);
|
|
@@ -317,22 +235,22 @@ const findEndpointModel = (sdkModelId) => {
|
|
|
317
235
|
* - "claude-haiku-3-5-20250514" -> { family: "haiku", version: "3.5" }
|
|
318
236
|
* - "claude-haiku-4.5" -> { family: "haiku", version: "4.5" }
|
|
319
237
|
*/
|
|
320
|
-
const
|
|
238
|
+
const normalizeSdkModelId = (sdkModelId) => {
|
|
321
239
|
const withoutDate = sdkModelId.toLowerCase().replace(/-\d{8}$/, "");
|
|
322
|
-
const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)
|
|
240
|
+
const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
|
|
323
241
|
if (pattern1) return {
|
|
324
242
|
family: pattern1[1],
|
|
325
243
|
version: `${pattern1[2]}.${pattern1[3]}`
|
|
326
244
|
};
|
|
327
|
-
const pattern2 = withoutDate.match(/^claude-(\
|
|
245
|
+
const pattern2 = withoutDate.match(/^claude-(\w+)-(\d+)-(\d+)$/);
|
|
328
246
|
if (pattern2) return {
|
|
329
|
-
family: pattern2[
|
|
330
|
-
version: `${pattern2[
|
|
247
|
+
family: pattern2[1],
|
|
248
|
+
version: `${pattern2[2]}.${pattern2[3]}`
|
|
331
249
|
};
|
|
332
|
-
const pattern3 = withoutDate.match(/^claude-(\
|
|
250
|
+
const pattern3 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
|
|
333
251
|
if (pattern3) return {
|
|
334
|
-
family: pattern3[
|
|
335
|
-
version: `${pattern3[
|
|
252
|
+
family: pattern3[3],
|
|
253
|
+
version: `${pattern3[1]}.${pattern3[2]}`
|
|
336
254
|
};
|
|
337
255
|
const pattern4 = withoutDate.match(/^claude-(\w+)-(\d+)$/);
|
|
338
256
|
if (pattern4) return {
|
|
@@ -411,7 +329,7 @@ const logCopilotQuotaSnapshots = (snapshots) => {
|
|
|
411
329
|
const logCopilotRateLimitUsage = (usage) => {
|
|
412
330
|
const d = new Date(usage.resetAt);
|
|
413
331
|
const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
|
|
414
|
-
consola.
|
|
332
|
+
consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
|
|
415
333
|
};
|
|
416
334
|
const isCopilotQuotaSnapshot = (value) => {
|
|
417
335
|
if (!value || typeof value !== "object") return false;
|
|
@@ -693,10 +611,95 @@ const getTokenCount = async (payload, model) => {
|
|
|
693
611
|
output: outputTokens
|
|
694
612
|
};
|
|
695
613
|
};
|
|
614
|
+
const SYSTEM_REMINDER_START = "<system-reminder>";
|
|
615
|
+
const SYSTEM_REMINDER_END = "</system-reminder>";
|
|
616
|
+
const SUBAGENT_START_HOOK_ADDITIONAL_PREFIX = "SubagentStart hook additional";
|
|
696
617
|
const IDE_EXECUTE_CODE_TOOL = "mcp__ide__executeCode";
|
|
697
618
|
const IDE_GET_DIAGNOSTICS_TOOL = "mcp__ide__getDiagnostics";
|
|
698
619
|
const IDE_GET_DIAGNOSTICS_DESCRIPTION = "Get language diagnostics from VS Code. Returns errors, warnings, information, and hints for files in the workspace.";
|
|
699
620
|
const PDF_FILE_READ_PREFIX = "PDF file read:";
|
|
621
|
+
const createTextBlock = (text) => ({
|
|
622
|
+
type: "text",
|
|
623
|
+
text
|
|
624
|
+
});
|
|
625
|
+
const appendTextSegment = (base, addition) => {
|
|
626
|
+
if (base.length === 0) return addition;
|
|
627
|
+
if (addition.length === 0) return base;
|
|
628
|
+
return `${base}\n\n${addition}`;
|
|
629
|
+
};
|
|
630
|
+
const ensureSystemReminderText = (text) => {
|
|
631
|
+
if (text.startsWith(SYSTEM_REMINDER_START)) return text;
|
|
632
|
+
return `${SYSTEM_REMINDER_START}\n${text.trim()}\n${SYSTEM_REMINDER_END}`;
|
|
633
|
+
};
|
|
634
|
+
const normalizeSystemStringForMerge = (text) => {
|
|
635
|
+
if (!text.startsWith(SUBAGENT_START_HOOK_ADDITIONAL_PREFIX)) return ensureSystemReminderText(text);
|
|
636
|
+
const lineBreakMatch = /\r?\n/.exec(text);
|
|
637
|
+
if (!lineBreakMatch) return [createTextBlock(ensureSystemReminderText(text))];
|
|
638
|
+
const firstLine = text.slice(0, lineBreakMatch.index);
|
|
639
|
+
const rest = text.slice(lineBreakMatch.index + lineBreakMatch[0].length);
|
|
640
|
+
return [createTextBlock(ensureSystemReminderText(firstLine)), ...rest.length > 0 ? [createTextBlock(ensureSystemReminderText(rest))] : []];
|
|
641
|
+
};
|
|
642
|
+
const normalizeSystemContentForMerge = (content) => {
|
|
643
|
+
if (typeof content === "string") return normalizeSystemStringForMerge(content);
|
|
644
|
+
return content.map((block) => block.text.startsWith(SYSTEM_REMINDER_START) ? block : {
|
|
645
|
+
...block,
|
|
646
|
+
text: ensureSystemReminderText(block.text)
|
|
647
|
+
});
|
|
648
|
+
};
|
|
649
|
+
const toSystemTextBlocks = (content) => {
|
|
650
|
+
return typeof content === "string" ? [createTextBlock(content)] : [...content];
|
|
651
|
+
};
|
|
652
|
+
const mergeSystemPromptContent = (current, addition) => {
|
|
653
|
+
if (current === void 0) return typeof addition === "string" ? addition : [...addition];
|
|
654
|
+
if (typeof current === "string" && typeof addition === "string") return appendTextSegment(current, addition);
|
|
655
|
+
return [...toSystemTextBlocks(current), ...toSystemTextBlocks(addition)];
|
|
656
|
+
};
|
|
657
|
+
const prependSystemContentToUserMessage = (message, addition) => {
|
|
658
|
+
if (typeof message.content === "string" && typeof addition === "string") {
|
|
659
|
+
message.content = appendTextSegment(addition, message.content);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (Array.isArray(message.content)) {
|
|
663
|
+
const lastToolResultIndex = message.content.findLastIndex((block) => block.type === "tool_result");
|
|
664
|
+
if (lastToolResultIndex >= 0) {
|
|
665
|
+
message.content = [
|
|
666
|
+
...message.content.slice(0, lastToolResultIndex + 1),
|
|
667
|
+
...toSystemTextBlocks(addition),
|
|
668
|
+
...message.content.slice(lastToolResultIndex + 1)
|
|
669
|
+
];
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
message.content = [...toSystemTextBlocks(addition), ...typeof message.content === "string" ? [createTextBlock(message.content)] : message.content];
|
|
674
|
+
};
|
|
675
|
+
const normalizeSystemMessages = (payload) => {
|
|
676
|
+
if (!Array.isArray(payload.messages) || !payload.messages.some((msg) => msg.role === "system")) return;
|
|
677
|
+
const normalizedMessages = [];
|
|
678
|
+
let system = payload.system;
|
|
679
|
+
for (const message of payload.messages) {
|
|
680
|
+
if (message.role === "system") {
|
|
681
|
+
const normalizedContent = normalizeSystemContentForMerge(message.content);
|
|
682
|
+
const previousMessage = normalizedMessages.at(-1);
|
|
683
|
+
if (previousMessage?.role === "user") prependSystemContentToUserMessage(previousMessage, normalizedContent);
|
|
684
|
+
else if (!previousMessage) system = mergeSystemPromptContent(system, normalizedContent);
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
normalizedMessages.push(message);
|
|
688
|
+
}
|
|
689
|
+
payload.messages = normalizedMessages;
|
|
690
|
+
payload.system = system;
|
|
691
|
+
};
|
|
692
|
+
const isVersionAtLeast = (version, minimumMajor, minimumMinor) => {
|
|
693
|
+
const [majorPart, minorPart = "0"] = version.split(".");
|
|
694
|
+
const major = Number.parseInt(majorPart, 10);
|
|
695
|
+
const minor = Number.parseInt(minorPart, 10);
|
|
696
|
+
if (!Number.isInteger(major) || !Number.isInteger(minor)) return false;
|
|
697
|
+
return major > minimumMajor || major === minimumMajor && minor >= minimumMinor;
|
|
698
|
+
};
|
|
699
|
+
const shouldSummarizeThinkingDisplayForModel = (model) => {
|
|
700
|
+
const normalized = normalizeSdkModelId(model);
|
|
701
|
+
return Boolean(normalized && isVersionAtLeast(normalized.version, 4, 7));
|
|
702
|
+
};
|
|
700
703
|
const getBlockCacheControl = (block) => {
|
|
701
704
|
if (!block || block.type === "thinking") return;
|
|
702
705
|
const cacheControl = block.cache_control;
|
|
@@ -966,7 +969,7 @@ const prepareMessagesApiPayload = (payload, selectedModel) => {
|
|
|
966
969
|
if (selectedModel?.capabilities.supports.adaptive_thinking && !disableThink) {
|
|
967
970
|
payload.thinking = { type: "adaptive" };
|
|
968
971
|
if (!hasThinking) payload.thinking.display = "summarized";
|
|
969
|
-
if (payload.model
|
|
972
|
+
if (shouldSummarizeThinkingDisplayForModel(payload.model)) payload.thinking.display = "summarized";
|
|
970
973
|
let effort = getReasoningEffortForModel(payload.model);
|
|
971
974
|
if (effort === "none" || effort === "minimal") effort = "low";
|
|
972
975
|
const reasoningEffort = selectedModel.capabilities.supports.reasoning_effort;
|
|
@@ -1884,7 +1887,11 @@ const PROVIDER_CONFIG_FIELDS = [
|
|
|
1884
1887
|
"models",
|
|
1885
1888
|
"adjustInputTokens"
|
|
1886
1889
|
];
|
|
1887
|
-
const PROVIDER_AUTH_TYPES = [
|
|
1890
|
+
const PROVIDER_AUTH_TYPES = [
|
|
1891
|
+
"authorization",
|
|
1892
|
+
"oauth2",
|
|
1893
|
+
"x-api-key"
|
|
1894
|
+
];
|
|
1888
1895
|
const PROVIDER_CONFIG_KEYS = new Set(PROVIDER_CONFIG_FIELDS);
|
|
1889
1896
|
function isProviderAuthType(value) {
|
|
1890
1897
|
return PROVIDER_AUTH_TYPES.includes(value);
|
|
@@ -2493,13 +2500,22 @@ adminApiRoutes.get("/accounts", async (c) => {
|
|
|
2493
2500
|
unlimited: s.unlimited,
|
|
2494
2501
|
failed: s.failed,
|
|
2495
2502
|
failureReason: s.failureReason,
|
|
2496
|
-
enabled: s.enabled
|
|
2503
|
+
enabled: s.enabled,
|
|
2504
|
+
lastModelsFetch: s.lastModelsFetch,
|
|
2505
|
+
isRefreshingModels: s.isRefreshingModels
|
|
2497
2506
|
},
|
|
2498
2507
|
stats
|
|
2499
2508
|
};
|
|
2500
2509
|
});
|
|
2501
2510
|
return c.json({ items });
|
|
2502
2511
|
});
|
|
2512
|
+
adminApiRoutes.post("/accounts/models/refresh", async (c) => {
|
|
2513
|
+
const { failedCount } = await accountsManager.refreshAllModelsNow();
|
|
2514
|
+
return c.json({
|
|
2515
|
+
ok: true,
|
|
2516
|
+
failedCount
|
|
2517
|
+
});
|
|
2518
|
+
});
|
|
2503
2519
|
adminApiRoutes.get("/requests", (c) => {
|
|
2504
2520
|
const p = new URL(c.req.url, "http://local").searchParams;
|
|
2505
2521
|
const limit = parseFiniteNumber(p.get("limit")) ?? 50;
|
|
@@ -3864,7 +3880,7 @@ function selectionFailureResponse$2(c, params) {
|
|
|
3864
3880
|
}
|
|
3865
3881
|
//#endregion
|
|
3866
3882
|
//#region src/routes/chat-completions/handler.ts
|
|
3867
|
-
const logger$
|
|
3883
|
+
const logger$7 = createHandlerLogger("chat-completions-handler");
|
|
3868
3884
|
function buildChatCompletionCandidates(clientModel) {
|
|
3869
3885
|
return [{
|
|
3870
3886
|
modelId: clientModel,
|
|
@@ -4001,12 +4017,12 @@ async function writeChatCompletionsStreamError(stream, message) {
|
|
|
4001
4017
|
} }) });
|
|
4002
4018
|
await stream.writeSSE({ data: "[DONE]" });
|
|
4003
4019
|
} catch (streamError) {
|
|
4004
|
-
logger$
|
|
4020
|
+
logger$7.warn("Failed to write chat completions stream error event:", streamError);
|
|
4005
4021
|
}
|
|
4006
4022
|
}
|
|
4007
4023
|
async function selectChatCompletionAccount(params) {
|
|
4008
4024
|
const { c, store, request, payload, clientModel, streamRequested, normalizedPromptCacheKey } = params;
|
|
4009
|
-
debugJsonTail(logger$
|
|
4025
|
+
debugJsonTail(logger$7, "Request payload:", {
|
|
4010
4026
|
value: payload,
|
|
4011
4027
|
tailLength: 400
|
|
4012
4028
|
});
|
|
@@ -4041,9 +4057,9 @@ async function selectChatCompletionAccount(params) {
|
|
|
4041
4057
|
async function logTokenCountForRequest(params) {
|
|
4042
4058
|
try {
|
|
4043
4059
|
const tokenCount = await getTokenCount(params.payload, params.selectedModel);
|
|
4044
|
-
logger$
|
|
4060
|
+
logger$7.info("Current token count:", tokenCount);
|
|
4045
4061
|
} catch (error) {
|
|
4046
|
-
logger$
|
|
4062
|
+
logger$7.warn("Failed to calculate token count:", error);
|
|
4047
4063
|
}
|
|
4048
4064
|
}
|
|
4049
4065
|
function applyDefaultMaxTokens(payload, selectedModel) {
|
|
@@ -4052,7 +4068,7 @@ function applyDefaultMaxTokens(payload, selectedModel) {
|
|
|
4052
4068
|
...payload,
|
|
4053
4069
|
max_tokens: selectedModel.capabilities.limits.max_output_tokens
|
|
4054
4070
|
};
|
|
4055
|
-
debugJson(logger$
|
|
4071
|
+
debugJson(logger$7, "Set max_tokens to:", updated.max_tokens);
|
|
4056
4072
|
return updated;
|
|
4057
4073
|
}
|
|
4058
4074
|
async function handleStreamingRequest(params) {
|
|
@@ -4086,7 +4102,7 @@ async function handleStreamingRequest(params) {
|
|
|
4086
4102
|
premiumUnlimitedBefore,
|
|
4087
4103
|
response
|
|
4088
4104
|
});
|
|
4089
|
-
logger$
|
|
4105
|
+
logger$7.debug("Streaming response");
|
|
4090
4106
|
return streamSSE(c, (stream) => streamChatCompletionsAndLog$1({
|
|
4091
4107
|
stream,
|
|
4092
4108
|
response,
|
|
@@ -4141,7 +4157,7 @@ async function handleNonStreamingUpstreamResponse(params) {
|
|
|
4141
4157
|
let upstreamErrorMessageRaw;
|
|
4142
4158
|
const finishedAtMs = Date.now();
|
|
4143
4159
|
try {
|
|
4144
|
-
debugJson(logger$
|
|
4160
|
+
debugJson(logger$7, "Non-streaming response:", response);
|
|
4145
4161
|
return c.json(response);
|
|
4146
4162
|
} catch (error) {
|
|
4147
4163
|
const details = await extractErrorObservability(error);
|
|
@@ -4194,7 +4210,7 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
4194
4210
|
if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
|
|
4195
4211
|
const usage = await extractUsageFromChunk(chunk);
|
|
4196
4212
|
if (usage) lastUsage = usage;
|
|
4197
|
-
debugJson(logger$
|
|
4213
|
+
debugJson(logger$7, "Streaming chunk:", chunk);
|
|
4198
4214
|
await stream.writeSSE(chunk);
|
|
4199
4215
|
}
|
|
4200
4216
|
} catch (error) {
|
|
@@ -4203,7 +4219,7 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
4203
4219
|
errorStatus = details.errorStatus;
|
|
4204
4220
|
errorMessage = details.errorMessage;
|
|
4205
4221
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
4206
|
-
logger$
|
|
4222
|
+
logger$7.warn("Streaming error:", error);
|
|
4207
4223
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
4208
4224
|
await writeChatCompletionsStreamError(stream, getUserVisibleErrorMessage(details));
|
|
4209
4225
|
} finally {
|
|
@@ -4241,7 +4257,7 @@ async function extractUsageFromChunk(chunk) {
|
|
|
4241
4257
|
try {
|
|
4242
4258
|
data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
|
|
4243
4259
|
} catch (error) {
|
|
4244
|
-
logger$
|
|
4260
|
+
logger$7.warn("Failed to read chat completions usage chunk:", error);
|
|
4245
4261
|
return;
|
|
4246
4262
|
}
|
|
4247
4263
|
if (!data || data === "[DONE]") return;
|
|
@@ -4250,7 +4266,7 @@ async function extractUsageFromChunk(chunk) {
|
|
|
4250
4266
|
if (!parsed.usage) return void 0;
|
|
4251
4267
|
return normalizeChatCompletionsUsage(parsed.usage);
|
|
4252
4268
|
} catch (error) {
|
|
4253
|
-
logger$
|
|
4269
|
+
logger$7.warn("Failed to parse chat completions usage chunk:", {
|
|
4254
4270
|
error,
|
|
4255
4271
|
data
|
|
4256
4272
|
});
|
|
@@ -4277,7 +4293,7 @@ async function handleNonStreamingRequest(params) {
|
|
|
4277
4293
|
selection.confirmAffinity?.();
|
|
4278
4294
|
finishedAtMs = Date.now();
|
|
4279
4295
|
usage = normalizeChatCompletionsUsage(response.usage);
|
|
4280
|
-
debugJson(logger$
|
|
4296
|
+
debugJson(logger$7, "Non-streaming response:", response);
|
|
4281
4297
|
return c.json(response);
|
|
4282
4298
|
} catch (error) {
|
|
4283
4299
|
finishedAtMs = Date.now();
|
|
@@ -4535,8 +4551,8 @@ const createFallbackModel = (modelId) => ({
|
|
|
4535
4551
|
});
|
|
4536
4552
|
//#endregion
|
|
4537
4553
|
//#region src/routes/provider/messages/count-tokens-handler.ts
|
|
4538
|
-
const logger$
|
|
4539
|
-
const resolveProviderConfig$
|
|
4554
|
+
const logger$6 = createHandlerLogger("provider-count-tokens-handler");
|
|
4555
|
+
const resolveProviderConfig$3 = (c, provider) => {
|
|
4540
4556
|
return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
|
|
4541
4557
|
};
|
|
4542
4558
|
async function handleProviderCountTokens(c) {
|
|
@@ -4548,7 +4564,8 @@ async function handleProviderCountTokens(c) {
|
|
|
4548
4564
|
}
|
|
4549
4565
|
async function handleProviderCountTokensForProvider(c, options) {
|
|
4550
4566
|
const { payload: anthropicPayload, provider } = options;
|
|
4551
|
-
|
|
4567
|
+
normalizeSystemMessages(anthropicPayload);
|
|
4568
|
+
const providerConfig = resolveProviderConfig$3(c, provider);
|
|
4552
4569
|
if (!providerConfig) return c.json({ error: {
|
|
4553
4570
|
message: `Provider '${provider}' not found or disabled`,
|
|
4554
4571
|
type: "invalid_request_error"
|
|
@@ -4559,21 +4576,21 @@ async function handleProviderCountTokensForProvider(c, options) {
|
|
|
4559
4576
|
} }, 400);
|
|
4560
4577
|
const modelId = anthropicPayload.model.trim();
|
|
4561
4578
|
const modelConfig = providerConfig.models?.[modelId];
|
|
4562
|
-
const translationOptions = providerConfig.type === "openai-compatible" ? {
|
|
4579
|
+
const translationOptions = providerConfig.type === "openai-compatible" || providerConfig.type === "openai-responses" ? {
|
|
4563
4580
|
supportPdf: modelConfig?.supportPdf,
|
|
4564
4581
|
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
4565
4582
|
} : void 0;
|
|
4566
4583
|
try {
|
|
4567
4584
|
const tokenCount = await getTokenCount(translateToOpenAI(anthropicPayload, translationOptions), findEndpointModel(modelId) ?? createFallbackModel(modelId));
|
|
4568
4585
|
const finalTokenCount = tokenCount.input + tokenCount.output;
|
|
4569
|
-
logger$
|
|
4586
|
+
logger$6.debug("provider.count_tokens.success", {
|
|
4570
4587
|
provider,
|
|
4571
4588
|
model: anthropicPayload.model,
|
|
4572
4589
|
input_tokens: finalTokenCount
|
|
4573
4590
|
});
|
|
4574
4591
|
return c.json({ input_tokens: finalTokenCount });
|
|
4575
4592
|
} catch (error) {
|
|
4576
|
-
logger$
|
|
4593
|
+
logger$6.error("provider.count_tokens.error", {
|
|
4577
4594
|
provider,
|
|
4578
4595
|
error
|
|
4579
4596
|
});
|
|
@@ -4639,6 +4656,7 @@ async function countTokensViaAnthropic(c, payload) {
|
|
|
4639
4656
|
async function handleCountTokens(c) {
|
|
4640
4657
|
const anthropicPayload = await c.req.json();
|
|
4641
4658
|
anthropicPayload.model = resolveModelAlias(anthropicPayload.model);
|
|
4659
|
+
normalizeSystemMessages(anthropicPayload);
|
|
4642
4660
|
const providerModelAlias = resolveExistingProviderModelAlias(anthropicPayload.model, getProviderConfigResolver$1(c));
|
|
4643
4661
|
if (providerModelAlias) {
|
|
4644
4662
|
anthropicPayload.model = providerModelAlias.model;
|
|
@@ -4676,7 +4694,7 @@ async function handleCountTokens(c) {
|
|
|
4676
4694
|
//#endregion
|
|
4677
4695
|
//#region src/services/copilot/create-responses.ts
|
|
4678
4696
|
const RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS = 6e4;
|
|
4679
|
-
const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, compactType, requestId, fetchImpl, transport = "http" }, account) => {
|
|
4697
|
+
const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, compactType, requestId, fetchImpl, transport = "http", bridgeId }, account) => {
|
|
4680
4698
|
const ctx = account ?? accountFromState();
|
|
4681
4699
|
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
4682
4700
|
const effectiveInitiator = resolveEffectiveInitiator(initiator, {
|
|
@@ -4696,7 +4714,9 @@ const createResponses = async (payload, { vision, initiator, upstreamRequestId,
|
|
|
4696
4714
|
const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
|
|
4697
4715
|
copilotToken: ctx.copilotToken,
|
|
4698
4716
|
requestId: requestId ?? upstreamRequestId ?? "missing-request-id",
|
|
4699
|
-
|
|
4717
|
+
sessionId,
|
|
4718
|
+
subagentMarker,
|
|
4719
|
+
bridgeId
|
|
4700
4720
|
}), copilotBaseUrl(ctx));
|
|
4701
4721
|
if (payload.stream) return stream;
|
|
4702
4722
|
return await consumeResponsesWebSocketStream(stream);
|
|
@@ -4729,20 +4749,22 @@ const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) =>
|
|
|
4729
4749
|
return {
|
|
4730
4750
|
headers: copilotWebSocketHeaders(preparedHeaders),
|
|
4731
4751
|
poolKey: buildResponsesWebSocketPoolKey(payload, options),
|
|
4732
|
-
payload: buildResponsesWebSocketPayload(payload, initiator)
|
|
4752
|
+
payload: buildResponsesWebSocketPayload(payload, initiator),
|
|
4753
|
+
bridgeId: options.bridgeId
|
|
4733
4754
|
};
|
|
4734
4755
|
};
|
|
4735
|
-
const buildResponsesWebSocketPoolKey = (payload, { copilotToken, requestId, subagentMarker }) => {
|
|
4756
|
+
const buildResponsesWebSocketPoolKey = (payload, { copilotToken, requestId, sessionId, subagentMarker }) => {
|
|
4736
4757
|
const tokenFingerprint = copilotToken ? createHash("sha256").update(copilotToken).digest("hex").slice(0, 16) : "missing-token";
|
|
4737
4758
|
const subagentKey = subagentMarker ? [
|
|
4738
4759
|
subagentMarker.session_id,
|
|
4739
4760
|
subagentMarker.agent_id,
|
|
4740
4761
|
subagentMarker.agent_type
|
|
4741
4762
|
].join(":") : "main";
|
|
4763
|
+
const connectionAffinityKey = sessionId ?? requestId;
|
|
4742
4764
|
return [
|
|
4743
4765
|
tokenFingerprint,
|
|
4744
4766
|
payload.model,
|
|
4745
|
-
|
|
4767
|
+
connectionAffinityKey,
|
|
4746
4768
|
subagentKey
|
|
4747
4769
|
].map(encodePoolKeyPart).join("|");
|
|
4748
4770
|
};
|
|
@@ -4797,6 +4819,7 @@ const getResponsesWebSocketRequestTarget = (request, baseUrl) => {
|
|
|
4797
4819
|
const existing = responsesWebSocketPool.get(request.poolKey);
|
|
4798
4820
|
if (existing && !existing.closed) {
|
|
4799
4821
|
clearResponsesWebSocketIdleTimer(existing);
|
|
4822
|
+
existing.bridgeId = request.bridgeId;
|
|
4800
4823
|
return {
|
|
4801
4824
|
entry: existing,
|
|
4802
4825
|
pooled: true
|
|
@@ -4817,13 +4840,16 @@ const createResponsesWebSocketEntry = (request, baseUrl) => {
|
|
|
4817
4840
|
websocketPromise: openResponsesWebSocket({
|
|
4818
4841
|
headers: request.headers,
|
|
4819
4842
|
url: buildResponsesWebSocketUrl(baseUrl)
|
|
4820
|
-
})
|
|
4843
|
+
}),
|
|
4844
|
+
bridgeId: request.bridgeId
|
|
4821
4845
|
};
|
|
4822
4846
|
entry.websocketPromise.then((websocket) => {
|
|
4823
4847
|
websocket.addEventListener("close", () => {
|
|
4848
|
+
maybeCloseResponsesBridgeForReapedEntry(request.poolKey, entry);
|
|
4824
4849
|
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
4825
4850
|
});
|
|
4826
4851
|
websocket.addEventListener("error", () => {
|
|
4852
|
+
maybeCloseResponsesBridgeForReapedEntry(request.poolKey, entry);
|
|
4827
4853
|
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
4828
4854
|
});
|
|
4829
4855
|
}).catch(() => {
|
|
@@ -4831,6 +4857,10 @@ const createResponsesWebSocketEntry = (request, baseUrl) => {
|
|
|
4831
4857
|
});
|
|
4832
4858
|
return entry;
|
|
4833
4859
|
};
|
|
4860
|
+
const maybeCloseResponsesBridgeForReapedEntry = (poolKey, entry) => {
|
|
4861
|
+
if (entry.bridgeId === void 0 || entry.requestCount > 0 || responsesWebSocketPool.get(poolKey) !== entry) return;
|
|
4862
|
+
closeResponsesBridge(entry.bridgeId);
|
|
4863
|
+
};
|
|
4834
4864
|
const acquireResponsesWebSocketEntry = (poolKey, entry, pooled) => {
|
|
4835
4865
|
clearResponsesWebSocketIdleTimer(entry);
|
|
4836
4866
|
incrementResponsesWebSocketActiveRequestCount(poolKey);
|
|
@@ -4862,6 +4892,7 @@ const getReadyResponsesWebSocket = async (poolKey, entry, pooled) => {
|
|
|
4862
4892
|
const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
|
|
4863
4893
|
clearResponsesWebSocketIdleTimer(entry);
|
|
4864
4894
|
entry.idleTimer = setTimeout(() => {
|
|
4895
|
+
maybeCloseResponsesBridgeForReapedEntry(poolKey, entry);
|
|
4865
4896
|
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
4866
4897
|
}, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
|
|
4867
4898
|
unrefTimer(entry.idleTimer);
|
|
@@ -5408,7 +5439,7 @@ const translateResponsesResultToAnthropic = (response, options) => {
|
|
|
5408
5439
|
const usage = mapResponsesUsage(response);
|
|
5409
5440
|
let anthropicContent = fallbackContentBlocks(response.output_text);
|
|
5410
5441
|
if (contentBlocks.length > 0) anthropicContent = contentBlocks;
|
|
5411
|
-
const stopReason = mapResponsesStopReason(response);
|
|
5442
|
+
const stopReason = mapResponsesStopReason(response, options);
|
|
5412
5443
|
return {
|
|
5413
5444
|
id: response.id,
|
|
5414
5445
|
type: "message",
|
|
@@ -5422,6 +5453,7 @@ const translateResponsesResultToAnthropic = (response, options) => {
|
|
|
5422
5453
|
};
|
|
5423
5454
|
const mapOutputToAnthropicContent = (output, options) => {
|
|
5424
5455
|
const contentBlocks = [];
|
|
5456
|
+
if (!output) output = [];
|
|
5425
5457
|
for (const item of output) switch (item.type) {
|
|
5426
5458
|
case "reasoning": {
|
|
5427
5459
|
const thinkingText = extractReasoningText(item);
|
|
@@ -5562,9 +5594,10 @@ const fallbackContentBlocks = (outputText) => {
|
|
|
5562
5594
|
text: outputText
|
|
5563
5595
|
}];
|
|
5564
5596
|
};
|
|
5565
|
-
const mapResponsesStopReason = (response) => {
|
|
5597
|
+
const mapResponsesStopReason = (response, options) => {
|
|
5566
5598
|
const { status, incomplete_details: incompleteDetails } = response;
|
|
5567
5599
|
if (status === "completed") {
|
|
5600
|
+
if (!response.output || response.output.length === 0) return options?.hasToolCall ? "tool_use" : "end_turn";
|
|
5568
5601
|
if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
|
|
5569
5602
|
return "end_turn";
|
|
5570
5603
|
}
|
|
@@ -5992,7 +6025,7 @@ function closeThinkingBlockIfOpen(state, events) {
|
|
|
5992
6025
|
}
|
|
5993
6026
|
}
|
|
5994
6027
|
//#endregion
|
|
5995
|
-
//#region src/services/providers/
|
|
6028
|
+
//#region src/services/providers/provider-proxy.ts
|
|
5996
6029
|
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
5997
6030
|
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
5998
6031
|
const STRIPPED_RESPONSE_HEADERS = [
|
|
@@ -6009,8 +6042,8 @@ const STRIPPED_RESPONSE_HEADERS = [
|
|
|
6009
6042
|
];
|
|
6010
6043
|
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
6011
6044
|
const authHeaders = {};
|
|
6012
|
-
if (providerConfig.authType === "
|
|
6013
|
-
else authHeaders
|
|
6045
|
+
if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
6046
|
+
else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
6014
6047
|
const headers = {
|
|
6015
6048
|
"content-type": "application/json",
|
|
6016
6049
|
accept: "application/json",
|
|
@@ -6027,10 +6060,10 @@ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
|
6027
6060
|
}
|
|
6028
6061
|
return headers;
|
|
6029
6062
|
}
|
|
6030
|
-
function createProviderProxyResponse(upstreamResponse) {
|
|
6063
|
+
function createProviderProxyResponse(upstreamResponse, body) {
|
|
6031
6064
|
const headers = new Headers(upstreamResponse.headers);
|
|
6032
6065
|
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
6033
|
-
return new Response(upstreamResponse.body, {
|
|
6066
|
+
return new Response(body ?? upstreamResponse.body, {
|
|
6034
6067
|
headers,
|
|
6035
6068
|
status: upstreamResponse.status,
|
|
6036
6069
|
statusText: upstreamResponse.statusText
|
|
@@ -6050,6 +6083,13 @@ async function forwardProviderChatCompletions(providerConfig, payload, requestHe
|
|
|
6050
6083
|
body: JSON.stringify(payload)
|
|
6051
6084
|
});
|
|
6052
6085
|
}
|
|
6086
|
+
async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
|
|
6087
|
+
return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
|
|
6088
|
+
method: "POST",
|
|
6089
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
6090
|
+
body: JSON.stringify(payload)
|
|
6091
|
+
});
|
|
6092
|
+
}
|
|
6053
6093
|
async function forwardProviderModels(providerConfig, requestHeaders, fetchImpl = fetch) {
|
|
6054
6094
|
return await fetchImpl(`${providerConfig.baseUrl}/v1/models`, {
|
|
6055
6095
|
method: "GET",
|
|
@@ -6058,9 +6098,9 @@ async function forwardProviderModels(providerConfig, requestHeaders, fetchImpl =
|
|
|
6058
6098
|
}
|
|
6059
6099
|
//#endregion
|
|
6060
6100
|
//#region src/routes/provider/messages/handler.ts
|
|
6061
|
-
const logger$
|
|
6101
|
+
const logger$5 = createHandlerLogger("provider-messages-handler");
|
|
6062
6102
|
const getProviderFetch$1 = (c) => c.get("providerFetch") ?? fetch;
|
|
6063
|
-
const resolveProviderConfig$
|
|
6103
|
+
const resolveProviderConfig$2 = (c, provider) => {
|
|
6064
6104
|
return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
|
|
6065
6105
|
};
|
|
6066
6106
|
const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
|
|
@@ -6084,7 +6124,7 @@ const writeProviderStreamError = async (stream, message) => {
|
|
|
6084
6124
|
})
|
|
6085
6125
|
});
|
|
6086
6126
|
} catch (error) {
|
|
6087
|
-
logger$
|
|
6127
|
+
logger$5.warn("Failed to write provider stream error event", error);
|
|
6088
6128
|
}
|
|
6089
6129
|
};
|
|
6090
6130
|
async function handleProviderMessages(c) {
|
|
@@ -6096,7 +6136,7 @@ async function handleProviderMessages(c) {
|
|
|
6096
6136
|
}
|
|
6097
6137
|
async function handleProviderMessagesForProvider(c, options) {
|
|
6098
6138
|
const { instrumentation, payload, provider } = options;
|
|
6099
|
-
const providerConfig = resolveProviderConfig$
|
|
6139
|
+
const providerConfig = resolveProviderConfig$2(c, provider);
|
|
6100
6140
|
if (!providerConfig) {
|
|
6101
6141
|
const message = `Provider '${provider}' not found or disabled`;
|
|
6102
6142
|
instrumentation?.onError?.({
|
|
@@ -6113,7 +6153,7 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
6113
6153
|
try {
|
|
6114
6154
|
const modelConfig = providerConfig.models?.[payload.model];
|
|
6115
6155
|
applyModelDefaults(payload, modelConfig);
|
|
6116
|
-
debugJson(logger$
|
|
6156
|
+
debugJson(logger$5, "provider.messages.request", {
|
|
6117
6157
|
payload,
|
|
6118
6158
|
provider
|
|
6119
6159
|
});
|
|
@@ -6127,7 +6167,7 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
6127
6167
|
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
6128
6168
|
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers, getProviderFetch$1(c));
|
|
6129
6169
|
if (!upstreamResponse.ok) {
|
|
6130
|
-
logger$
|
|
6170
|
+
logger$5.error("Failed to create responses", upstreamResponse);
|
|
6131
6171
|
throw new HTTPError("Failed to create responses", upstreamResponse);
|
|
6132
6172
|
}
|
|
6133
6173
|
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
@@ -6147,7 +6187,7 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
6147
6187
|
providerConfig
|
|
6148
6188
|
});
|
|
6149
6189
|
} catch (error) {
|
|
6150
|
-
logger$
|
|
6190
|
+
logger$5.error("provider.messages.error", {
|
|
6151
6191
|
provider,
|
|
6152
6192
|
error
|
|
6153
6193
|
});
|
|
@@ -6184,13 +6224,13 @@ const applyOpenAICompatibleExtraBodyThinkingBudget = (payload, options) => {
|
|
|
6184
6224
|
const handleOpenAICompatibleProviderMessages = async (c, options) => {
|
|
6185
6225
|
const { instrumentation, modelConfig, payload, provider, providerConfig } = options;
|
|
6186
6226
|
const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
|
|
6187
|
-
debugJson(logger$
|
|
6227
|
+
debugJson(logger$5, "provider.messages.openai_compatible.request", {
|
|
6188
6228
|
payload: openAIPayload,
|
|
6189
6229
|
provider
|
|
6190
6230
|
});
|
|
6191
6231
|
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers, getProviderFetch$1(c));
|
|
6192
6232
|
if (!upstreamResponse.ok) {
|
|
6193
|
-
logger$
|
|
6233
|
+
logger$5.error("Failed to create openai-compatible responses", upstreamResponse);
|
|
6194
6234
|
throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
|
|
6195
6235
|
}
|
|
6196
6236
|
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
@@ -6274,13 +6314,13 @@ const setContextCacheControl = (part) => {
|
|
|
6274
6314
|
part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
|
|
6275
6315
|
};
|
|
6276
6316
|
const streamProviderMessages = ({ c, instrumentation, providerConfig, upstreamResponse }) => {
|
|
6277
|
-
logger$
|
|
6317
|
+
logger$5.debug("provider.messages.streaming");
|
|
6278
6318
|
return streamSSE(c, async (stream) => {
|
|
6279
6319
|
let usage = {};
|
|
6280
6320
|
try {
|
|
6281
6321
|
let completed = false;
|
|
6282
6322
|
for await (const chunk of events(upstreamResponse)) {
|
|
6283
|
-
logger$
|
|
6323
|
+
logger$5.debug("provider.messages.raw_stream_event:", chunk.data);
|
|
6284
6324
|
const eventName = chunk.event;
|
|
6285
6325
|
if (eventName === "ping") {
|
|
6286
6326
|
await stream.writeSSE({
|
|
@@ -6316,14 +6356,14 @@ const streamProviderMessages = ({ c, instrumentation, providerConfig, upstreamRe
|
|
|
6316
6356
|
instrumentation?.onComplete?.(usage);
|
|
6317
6357
|
} catch (error) {
|
|
6318
6358
|
const details = await extractErrorObservability(error);
|
|
6319
|
-
logger$
|
|
6359
|
+
logger$5.warn("provider.messages.streaming.error", error);
|
|
6320
6360
|
instrumentation?.onError?.(details);
|
|
6321
6361
|
await writeProviderStreamError(stream, getUserVisibleErrorMessage(details));
|
|
6322
6362
|
}
|
|
6323
6363
|
});
|
|
6324
6364
|
};
|
|
6325
6365
|
const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamResponse }) => {
|
|
6326
|
-
logger$
|
|
6366
|
+
logger$5.debug("provider.messages.openai_compatible.streaming");
|
|
6327
6367
|
return streamSSE(c, async (stream) => {
|
|
6328
6368
|
let usage = {};
|
|
6329
6369
|
const streamState = {
|
|
@@ -6336,7 +6376,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6336
6376
|
try {
|
|
6337
6377
|
let completed = false;
|
|
6338
6378
|
for await (const chunk of events(upstreamResponse)) {
|
|
6339
|
-
logger$
|
|
6379
|
+
logger$5.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
|
|
6340
6380
|
if (chunk.event === "ping") {
|
|
6341
6381
|
await stream.writeSSE({
|
|
6342
6382
|
event: "ping",
|
|
@@ -6354,7 +6394,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6354
6394
|
const events = translateChunkToAnthropicEvents(parsed, streamState);
|
|
6355
6395
|
for (const event of events) {
|
|
6356
6396
|
const eventData = JSON.stringify(event);
|
|
6357
|
-
debugLazy(logger$
|
|
6397
|
+
debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
6358
6398
|
await stream.writeSSE({
|
|
6359
6399
|
event: event.type,
|
|
6360
6400
|
data: eventData
|
|
@@ -6364,7 +6404,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6364
6404
|
}
|
|
6365
6405
|
for (const event of flushPendingAnthropicStreamEvents(streamState)) {
|
|
6366
6406
|
const eventData = JSON.stringify(event);
|
|
6367
|
-
debugLazy(logger$
|
|
6407
|
+
debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
6368
6408
|
await stream.writeSSE({
|
|
6369
6409
|
event: event.type,
|
|
6370
6410
|
data: eventData
|
|
@@ -6375,7 +6415,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6375
6415
|
instrumentation?.onComplete?.(usage);
|
|
6376
6416
|
} catch (error) {
|
|
6377
6417
|
const details = await extractErrorObservability(error);
|
|
6378
|
-
logger$
|
|
6418
|
+
logger$5.warn("provider.messages.openai_compatible.streaming.error", error);
|
|
6379
6419
|
instrumentation?.onError?.(details);
|
|
6380
6420
|
await writeProviderStreamError(stream, getUserVisibleErrorMessage(details));
|
|
6381
6421
|
}
|
|
@@ -6386,7 +6426,7 @@ const parseOpenAICompatibleStreamChunk = (data) => {
|
|
|
6386
6426
|
try {
|
|
6387
6427
|
parsed = JSON.parse(data);
|
|
6388
6428
|
} catch (error) {
|
|
6389
|
-
logger$
|
|
6429
|
+
logger$5.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
6390
6430
|
data,
|
|
6391
6431
|
error
|
|
6392
6432
|
});
|
|
@@ -6443,7 +6483,7 @@ const parseProviderStreamEvent = (data, providerConfig) => {
|
|
|
6443
6483
|
usage: {}
|
|
6444
6484
|
};
|
|
6445
6485
|
} catch (error) {
|
|
6446
|
-
logger$
|
|
6486
|
+
logger$5.error("provider.messages.streaming.adjust_tokens_error", {
|
|
6447
6487
|
error,
|
|
6448
6488
|
originalData: data
|
|
6449
6489
|
});
|
|
@@ -6453,7 +6493,7 @@ const parseProviderStreamEvent = (data, providerConfig) => {
|
|
|
6453
6493
|
const respondProviderMessagesJson = (c, options) => {
|
|
6454
6494
|
const { body, instrumentation, providerConfig } = options;
|
|
6455
6495
|
adjustInputTokens(providerConfig, body.usage);
|
|
6456
|
-
debugJson(logger$
|
|
6496
|
+
debugJson(logger$5, "provider.messages.no_stream result:", body);
|
|
6457
6497
|
const response = c.json(body);
|
|
6458
6498
|
instrumentation?.onComplete?.(normalizeAnthropicUsage(body.usage));
|
|
6459
6499
|
return response;
|
|
@@ -6461,7 +6501,7 @@ const respondProviderMessagesJson = (c, options) => {
|
|
|
6461
6501
|
const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
|
|
6462
6502
|
const { body, instrumentation } = options;
|
|
6463
6503
|
const anthropicResponse = translateToAnthropic(body);
|
|
6464
|
-
debugJson(logger$
|
|
6504
|
+
debugJson(logger$5, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
|
|
6465
6505
|
const response = c.json(anthropicResponse);
|
|
6466
6506
|
instrumentation?.onComplete?.(normalizeOpenAIUsage(body.usage));
|
|
6467
6507
|
return response;
|
|
@@ -6491,7 +6531,7 @@ const mergeAnthropicUsage = (current, next) => ({
|
|
|
6491
6531
|
const adjustInputTokens = (providerConfig, usage) => {
|
|
6492
6532
|
if (!providerConfig.adjustInputTokens || !usage) return;
|
|
6493
6533
|
usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
|
|
6494
|
-
debugJson(logger$
|
|
6534
|
+
debugJson(logger$5, "provider.messages.adjusted_usage:", usage);
|
|
6495
6535
|
};
|
|
6496
6536
|
//#endregion
|
|
6497
6537
|
//#region src/routes/messages/responses-stream-translation.ts
|
|
@@ -6528,7 +6568,8 @@ const createResponsesStreamState = (options) => ({
|
|
|
6528
6568
|
openBlocks: /* @__PURE__ */ new Set(),
|
|
6529
6569
|
blockHasDelta: /* @__PURE__ */ new Set(),
|
|
6530
6570
|
functionCallStateByOutputIndex: /* @__PURE__ */ new Map(),
|
|
6531
|
-
toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search"
|
|
6571
|
+
toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search",
|
|
6572
|
+
hasToolCall: false
|
|
6532
6573
|
});
|
|
6533
6574
|
const translateResponsesStreamEvent = (rawEvent, state) => {
|
|
6534
6575
|
switch (rawEvent.type) {
|
|
@@ -6776,7 +6817,10 @@ const handleResponseCompleted = (rawEvent, state) => {
|
|
|
6776
6817
|
const events = new Array();
|
|
6777
6818
|
closeAllOpenBlocks(state, events);
|
|
6778
6819
|
state.responseStatus = response.status;
|
|
6779
|
-
const anthropic = translateResponsesResultToAnthropic(response
|
|
6820
|
+
const anthropic = translateResponsesResultToAnthropic(response, {
|
|
6821
|
+
hasToolCall: state.hasToolCall,
|
|
6822
|
+
toolSearchName: state.toolSearchName
|
|
6823
|
+
});
|
|
6780
6824
|
events.push({
|
|
6781
6825
|
type: "message_delta",
|
|
6782
6826
|
delta: {
|
|
@@ -6908,6 +6952,7 @@ const buildErrorEvent = (message) => ({
|
|
|
6908
6952
|
const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
|
|
6909
6953
|
const openFunctionCallBlock = (state, params) => {
|
|
6910
6954
|
const { outputIndex, toolCallId, name, events } = params;
|
|
6955
|
+
state.hasToolCall = true;
|
|
6911
6956
|
let functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
|
|
6912
6957
|
if (!functionCallState) {
|
|
6913
6958
|
const blockIndex = state.nextContentBlockIndex;
|
|
@@ -6961,8 +7006,9 @@ const stringifyToolSearchArguments = (argumentsValue) => {
|
|
|
6961
7006
|
return;
|
|
6962
7007
|
}
|
|
6963
7008
|
};
|
|
7009
|
+
const DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO = .9;
|
|
6964
7010
|
const responsesUtilsDependencies = {
|
|
6965
|
-
|
|
7011
|
+
isResponsesApiContextManagementEnabled,
|
|
6966
7012
|
isResponsesApiWebSocketEnabled
|
|
6967
7013
|
};
|
|
6968
7014
|
const getResponsesRequestOptions = (payload) => {
|
|
@@ -6992,18 +7038,92 @@ const isAgentRole = (item) => {
|
|
|
6992
7038
|
const hasVisionInput$1 = (payload) => {
|
|
6993
7039
|
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
6994
7040
|
};
|
|
6995
|
-
const
|
|
6996
|
-
|
|
6997
|
-
|
|
7041
|
+
const DATA_URL_PREFIX = "data:";
|
|
7042
|
+
const REDACTED_IMAGE_PLACEHOLDER_DATA_URL = "data:image/png;base64," + [
|
|
7043
|
+
"iVBORw0KGgoAAAANSUhEUgAAAGAAAAAgCAMAAADaHo1mAAADAFBMVEX///8fKTfR1dsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7044
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7045
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7046
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7047
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7048
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7049
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7050
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7051
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7052
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7053
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7054
|
+
"AAAAAAAAAAAAAAAAAAAAAACae8QWAAAAvElEQVR42u1WixKAIAhj/f9Hdz2BXJiVed3pVSYtpgwsGSo3GaRq6wSd4F8EyIJx",
|
|
7055
|
+
"ydSUAMB8il51sHT2fiVQu8czguQwXWAyFvswIJhmoS9gmzYlcFiHj1aAgzcJVgCyguYhAhNZmMhYQZs1EJnnIAqKiuHjSrZT",
|
|
7056
|
+
"ucSQ4s8JkKDDIYr3IuR8vEWgqroKP9b1bYKk2wfgeVmqATQLXdXamsXdEKkz3QXEEeTTuWWImMhW6qci94/+hwSVf99HqVoD",
|
|
7057
|
+
"OAuj2SEAAAAASUVORK5CYII="
|
|
7058
|
+
].join("");
|
|
7059
|
+
const sanitizeOversizedInputImages = (payload, maxPromptImageSize) => {
|
|
7060
|
+
const limit = typeof maxPromptImageSize === "number" && maxPromptImageSize > 0 ? maxPromptImageSize : void 0;
|
|
7061
|
+
if (limit === void 0 || !Array.isArray(payload.input)) return 0;
|
|
7062
|
+
return sanitizeInputImages(payload.input, (image) => image.decodedBytes > limit);
|
|
7063
|
+
};
|
|
7064
|
+
const sanitizeInputImages = (input, shouldReplace) => {
|
|
7065
|
+
let count = 0;
|
|
7066
|
+
for (const image of collectInputImageDataUrls(input)) {
|
|
7067
|
+
if (!shouldReplace(image)) continue;
|
|
7068
|
+
replaceInputImageWithPlaceholder(image);
|
|
7069
|
+
count += 1;
|
|
7070
|
+
}
|
|
7071
|
+
return count;
|
|
7072
|
+
};
|
|
7073
|
+
const collectInputImageDataUrls = (input, images = []) => {
|
|
7074
|
+
for (const item of input) collectInputItemImageDataUrls(item, images);
|
|
7075
|
+
return images;
|
|
7076
|
+
};
|
|
7077
|
+
const collectInputItemImageDataUrls = (item, images) => {
|
|
7078
|
+
if (isResponseInputMessage(item)) collectContentImageDataUrls(item.content, images);
|
|
7079
|
+
else if (isResponseFunctionCallOutputItem(item)) collectContentImageDataUrls(item.output, images);
|
|
7080
|
+
};
|
|
7081
|
+
const collectContentImageDataUrls = (content, images) => {
|
|
7082
|
+
if (!Array.isArray(content)) return;
|
|
7083
|
+
for (const block of content) {
|
|
7084
|
+
const image = getInputImageDataUrl(block);
|
|
7085
|
+
if (image) images.push(image);
|
|
7086
|
+
}
|
|
7087
|
+
};
|
|
7088
|
+
const getInputImageDataUrl = (content) => {
|
|
7089
|
+
if (!isResponseInputImage(content) || typeof content.image_url !== "string") return null;
|
|
7090
|
+
const imageUrl = content.image_url;
|
|
7091
|
+
if (!imageUrl.startsWith(DATA_URL_PREFIX)) return null;
|
|
7092
|
+
return {
|
|
7093
|
+
decodedBytes: estimateDataUrlByteLength(imageUrl),
|
|
7094
|
+
record: content
|
|
7095
|
+
};
|
|
7096
|
+
};
|
|
7097
|
+
const estimateDataUrlByteLength = (value) => {
|
|
7098
|
+
return Math.max(0, Math.floor(value.length * 3 / 4));
|
|
7099
|
+
};
|
|
7100
|
+
const replaceInputImageWithPlaceholder = (image) => {
|
|
7101
|
+
image.record.type = "input_image";
|
|
7102
|
+
image.record.image_url = REDACTED_IMAGE_PLACEHOLDER_DATA_URL;
|
|
7103
|
+
image.record.detail = "low";
|
|
7104
|
+
delete image.record.file_id;
|
|
7105
|
+
};
|
|
7106
|
+
const isResponseInputMessage = (item) => {
|
|
7107
|
+
return typeof item === "object" && item !== null && "role" in item && typeof item.role === "string";
|
|
7108
|
+
};
|
|
7109
|
+
const isResponseFunctionCallOutputItem = (item) => {
|
|
7110
|
+
return typeof item === "object" && item !== null && "type" in item && item.type === "function_call_output";
|
|
7111
|
+
};
|
|
7112
|
+
const isResponseInputImage = (content) => {
|
|
7113
|
+
return typeof content === "object" && content !== null && "type" in content && content.type === "input_image";
|
|
7114
|
+
};
|
|
7115
|
+
const resolveResponsesCompactThreshold = (maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
|
|
7116
|
+
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * compactThresholdRatio);
|
|
7117
|
+
return 2e5 * compactThresholdRatio;
|
|
6998
7118
|
};
|
|
6999
7119
|
const createCompactionContextManagement = (compactThreshold) => [{
|
|
7000
7120
|
type: "compaction",
|
|
7001
7121
|
compact_threshold: compactThreshold
|
|
7002
7122
|
}];
|
|
7003
|
-
const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
|
|
7123
|
+
const applyResponsesApiContextManagement = (payload, maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
|
|
7004
7124
|
if (payload.context_management !== void 0) return;
|
|
7005
|
-
if (!responsesUtilsDependencies.
|
|
7006
|
-
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
|
|
7125
|
+
if (!responsesUtilsDependencies.isResponsesApiContextManagementEnabled()) return;
|
|
7126
|
+
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens, compactThresholdRatio));
|
|
7007
7127
|
};
|
|
7008
7128
|
const compactInputByLatestCompaction = (payload) => {
|
|
7009
7129
|
if (!Array.isArray(payload.input) || payload.input.length === 0) return;
|
|
@@ -7106,7 +7226,7 @@ const buildMessagesHeaders = ({ ctx, enableVision, initiator, options, payload }
|
|
|
7106
7226
|
};
|
|
7107
7227
|
prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
|
|
7108
7228
|
prepareForCompact(headers, options?.compactType);
|
|
7109
|
-
if (shouldUseMessageProxyHeaders(payload)) prepareMessageProxyHeaders(headers);
|
|
7229
|
+
if (shouldUseMessageProxyHeaders(payload) && payload.model !== "claude-opus-4.8") prepareMessageProxyHeaders(headers);
|
|
7110
7230
|
const anthropicBeta = buildAnthropicBetaHeader(options?.anthropicBetaHeader, payload.thinking, payload.model);
|
|
7111
7231
|
if (anthropicBeta) headers["anthropic-beta"] = anthropicBeta;
|
|
7112
7232
|
return headers;
|
|
@@ -7245,7 +7365,7 @@ const extractBalancedJson = (text) => {
|
|
|
7245
7365
|
};
|
|
7246
7366
|
//#endregion
|
|
7247
7367
|
//#region src/routes/messages/handler.ts
|
|
7248
|
-
const logger$
|
|
7368
|
+
const logger$4 = createHandlerLogger("messages-handler");
|
|
7249
7369
|
const CHAT_COMPLETIONS_ENDPOINT = "/chat/completions";
|
|
7250
7370
|
const RESPONSES_ENDPOINT$1 = "/responses";
|
|
7251
7371
|
const MESSAGES_ENDPOINT = "/v1/messages";
|
|
@@ -7291,7 +7411,7 @@ async function handleProviderAliasCompletion(c, options) {
|
|
|
7291
7411
|
let requestRecorded = false;
|
|
7292
7412
|
const insertProviderAliasLog = (record) => {
|
|
7293
7413
|
if (requestRecorded) {
|
|
7294
|
-
logger$
|
|
7414
|
+
logger$4.warn("provider alias request already recorded", { requestId });
|
|
7295
7415
|
return;
|
|
7296
7416
|
}
|
|
7297
7417
|
requestRecorded = true;
|
|
@@ -7364,15 +7484,16 @@ async function handleCompletion(c) {
|
|
|
7364
7484
|
const path = new URL(c.req.url, "http://local").pathname;
|
|
7365
7485
|
const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
|
|
7366
7486
|
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
7487
|
+
normalizeSystemMessages(anthropicPayload);
|
|
7367
7488
|
sanitizeIdeTools(anthropicPayload);
|
|
7368
|
-
debugJson(logger$
|
|
7489
|
+
debugJson(logger$4, "Anthropic request payload:", anthropicPayload);
|
|
7369
7490
|
const markerInspection = inspectSubagentMarkerFromFirstUser(anthropicPayload);
|
|
7370
7491
|
const subagentMarker = markerInspection.kind === "valid" ? markerInspection.marker : null;
|
|
7371
7492
|
const isSubagentRequest = subagentMarker !== null;
|
|
7372
7493
|
const invalidSubagentMarkerSelectionReason = markerInspection.kind === "invalid" ? "subagent_marker_invalid_fallback" : void 0;
|
|
7373
|
-
if (subagentMarker) debugJson(logger$
|
|
7494
|
+
if (subagentMarker) debugJson(logger$4, "Detected Subagent marker:", subagentMarker);
|
|
7374
7495
|
const sessionId = getRootSessionId(anthropicPayload, c);
|
|
7375
|
-
logger$
|
|
7496
|
+
logger$4.debug("Extracted session ID:", sessionId);
|
|
7376
7497
|
const ownershipLookupSessionId = markerInspection.kind === "valid" ? normalizeStableSessionId(markerInspection.marker.session_id) : void 0;
|
|
7377
7498
|
const ownershipWriteSessionId = markerInspection.kind === "none" ? sessionId : void 0;
|
|
7378
7499
|
const anthropicBeta = c.req.header("anthropic-beta");
|
|
@@ -7380,7 +7501,7 @@ async function handleCompletion(c) {
|
|
|
7380
7501
|
const isCompact = compactType !== 0;
|
|
7381
7502
|
const originalRequestModel = anthropicPayload.model;
|
|
7382
7503
|
if (anthropicBeta && isWarmupProbeRequest(anthropicPayload)) anthropicPayload.model = getSmallModel();
|
|
7383
|
-
if (compactType !== 0) logger$
|
|
7504
|
+
if (compactType !== 0) logger$4.debug("Compact request type:", compactType);
|
|
7384
7505
|
const lastMessageCacheControl = getLastMessageContentCacheControl(anthropicPayload.messages.at(-1));
|
|
7385
7506
|
if (compactType === 1 && shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
|
|
7386
7507
|
if (compactType === 0) {
|
|
@@ -7389,7 +7510,7 @@ async function handleCompletion(c) {
|
|
|
7389
7510
|
}
|
|
7390
7511
|
applyLastMessageCacheControl(anthropicPayload, lastMessageCacheControl);
|
|
7391
7512
|
const upstreamRequestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
|
|
7392
|
-
logger$
|
|
7513
|
+
logger$4.debug("Generated request ID:", upstreamRequestId);
|
|
7393
7514
|
const clientModel = anthropicPayload.model;
|
|
7394
7515
|
anthropicPayload.model = resolveModelAlias(anthropicPayload.model);
|
|
7395
7516
|
const routingModel = anthropicPayload.model;
|
|
@@ -7546,7 +7667,7 @@ async function handleCompletion(c) {
|
|
|
7546
7667
|
}
|
|
7547
7668
|
const handleWithChatCompletions = async (params) => {
|
|
7548
7669
|
const { c, openAIPayload, subagentMarker, sessionId, selectedModel, instr, compactType } = params;
|
|
7549
|
-
debugJson(logger$
|
|
7670
|
+
debugJson(logger$4, "Translated OpenAI request payload:", openAIPayload);
|
|
7550
7671
|
const ctx = toAccountContext(instr.account);
|
|
7551
7672
|
const effectiveInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
|
|
7552
7673
|
isCompact: compactType !== 0,
|
|
@@ -7577,9 +7698,9 @@ const handleWithChatCompletions = async (params) => {
|
|
|
7577
7698
|
response,
|
|
7578
7699
|
instr
|
|
7579
7700
|
});
|
|
7580
|
-
logger$
|
|
7701
|
+
logger$4.debug("Streaming response from Copilot");
|
|
7581
7702
|
const fallbackEnabled = isMessageStartInputTokensFallbackEnabled();
|
|
7582
|
-
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$
|
|
7703
|
+
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$4) : void 0;
|
|
7583
7704
|
const historicalUsage = fallbackEnabled && instr.promptCacheKey && instr.safetyIdentifier ? instr.store.getLastCompletedUsageBySession({
|
|
7584
7705
|
promptCacheKey: instr.promptCacheKey,
|
|
7585
7706
|
safetyIdentifier: instr.safetyIdentifier,
|
|
@@ -7601,7 +7722,7 @@ const handleWithResponsesApi = async (params) => {
|
|
|
7601
7722
|
});
|
|
7602
7723
|
applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
|
|
7603
7724
|
compactInputByLatestCompaction(responsesPayload);
|
|
7604
|
-
debugJson(logger$
|
|
7725
|
+
debugJson(logger$4, "Translated Responses payload:", responsesPayload);
|
|
7605
7726
|
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
7606
7727
|
const transport = getResponsesTransportForModel(selectedModel, { compactType }) ?? "http";
|
|
7607
7728
|
const effectiveInitiator = resolveEffectiveInitiator(initiator, {
|
|
@@ -7632,9 +7753,9 @@ const handleWithResponsesApi = async (params) => {
|
|
|
7632
7753
|
});
|
|
7633
7754
|
}
|
|
7634
7755
|
if (responsesPayload.stream && isAsyncIterable(response)) {
|
|
7635
|
-
logger$
|
|
7756
|
+
logger$4.debug("Streaming response from Copilot (Responses API)");
|
|
7636
7757
|
const fallbackEnabled = isMessageStartInputTokensFallbackEnabled();
|
|
7637
|
-
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$
|
|
7758
|
+
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$4) : void 0;
|
|
7638
7759
|
const historicalUsage = fallbackEnabled && instr.promptCacheKey && instr.safetyIdentifier ? instr.store.getLastCompletedUsageBySession({
|
|
7639
7760
|
promptCacheKey: instr.promptCacheKey,
|
|
7640
7761
|
safetyIdentifier: instr.safetyIdentifier,
|
|
@@ -7732,9 +7853,9 @@ async function handleChatCompletionsNonStreaming(params) {
|
|
|
7732
7853
|
let upstreamErrorMessageRaw;
|
|
7733
7854
|
const finishedAtMs = Date.now();
|
|
7734
7855
|
try {
|
|
7735
|
-
logger$
|
|
7856
|
+
logger$4.debug("Non-streaming response from Copilot:", JSON.stringify(response));
|
|
7736
7857
|
const anthropicResponse = translateToAnthropic(response);
|
|
7737
|
-
debugJson(logger$
|
|
7858
|
+
debugJson(logger$4, "Translated Anthropic response:", anthropicResponse);
|
|
7738
7859
|
return c.json(anthropicResponse);
|
|
7739
7860
|
} catch (error) {
|
|
7740
7861
|
const details = await extractErrorObservability(error);
|
|
@@ -7785,7 +7906,7 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
7785
7906
|
try {
|
|
7786
7907
|
for await (const rawEvent of response) {
|
|
7787
7908
|
if (ttfbMs === void 0) ttfbMs = Date.now() - instr.startedAtMs;
|
|
7788
|
-
logger$
|
|
7909
|
+
logger$4.debug("Copilot raw stream event:", JSON.stringify(rawEvent));
|
|
7789
7910
|
const { data: rawData } = rawEvent;
|
|
7790
7911
|
const data = typeof rawData === "string" ? rawData : await rawData;
|
|
7791
7912
|
if (data === "[DONE]") break;
|
|
@@ -7794,7 +7915,7 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
7794
7915
|
if (chunk.usage) lastUsage = normalizeChatCompletionsUsage(chunk.usage);
|
|
7795
7916
|
const events = translateChunkToAnthropicEvents(chunk, streamState);
|
|
7796
7917
|
for (const event of events) {
|
|
7797
|
-
logger$
|
|
7918
|
+
logger$4.debug("Translated Anthropic event:", JSON.stringify(event));
|
|
7798
7919
|
await stream.writeSSE({
|
|
7799
7920
|
event: event.type,
|
|
7800
7921
|
data: JSON.stringify(event)
|
|
@@ -7807,7 +7928,7 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
7807
7928
|
errorStatus = details.errorStatus;
|
|
7808
7929
|
errorMessage = details.errorMessage;
|
|
7809
7930
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
7810
|
-
logger$
|
|
7931
|
+
logger$4.warn("Streaming error:", error);
|
|
7811
7932
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
7812
7933
|
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
7813
7934
|
} finally {
|
|
@@ -7868,9 +7989,9 @@ async function handleResponsesNonStreaming(params) {
|
|
|
7868
7989
|
usage = extractResponsesUsageFromResult(result);
|
|
7869
7990
|
const responseOwnerKeys = extractResponsesResultOwnerKeys(result);
|
|
7870
7991
|
instr.responsesItemOwnerRecordedKeys = responseOwnerKeys;
|
|
7871
|
-
logger$
|
|
7992
|
+
logger$4.debug("Non-streaming Responses result:", JSON.stringify(result).slice(-400));
|
|
7872
7993
|
const anthropicResponse = translateResponsesResultToAnthropic(result);
|
|
7873
|
-
debugJson(logger$
|
|
7994
|
+
debugJson(logger$4, "Translated Anthropic response:", anthropicResponse);
|
|
7874
7995
|
const response = c.json(anthropicResponse);
|
|
7875
7996
|
if (result.status === "completed") accountsManager.recordResponsesItemOwnership(responseOwnerKeys, instr.account.id);
|
|
7876
7997
|
return response;
|
|
@@ -7904,7 +8025,7 @@ async function handleResponsesNonStreaming(params) {
|
|
|
7904
8025
|
async function ensureResponsesStreamCompleted(params) {
|
|
7905
8026
|
const { stream, streamState, setStreamError } = params;
|
|
7906
8027
|
if (streamState.messageCompleted) return;
|
|
7907
|
-
logger$
|
|
8028
|
+
logger$4.warn("Responses stream ended without completion; sending error event");
|
|
7908
8029
|
const msg = "Responses stream ended without completion";
|
|
7909
8030
|
const errorEvent = buildErrorEvent(msg);
|
|
7910
8031
|
setStreamError("StreamIncomplete", msg);
|
|
@@ -7921,7 +8042,7 @@ async function writeAnthropicStreamError(stream, message) {
|
|
|
7921
8042
|
data: JSON.stringify(errorEvent)
|
|
7922
8043
|
});
|
|
7923
8044
|
} catch (streamError) {
|
|
7924
|
-
logger$
|
|
8045
|
+
logger$4.warn("Failed to write Anthropic stream error event:", streamError);
|
|
7925
8046
|
}
|
|
7926
8047
|
}
|
|
7927
8048
|
function collectResponsesStreamOwnerKeys(event, responseOwnerKeys) {
|
|
@@ -7964,7 +8085,7 @@ function getResponsesStreamEventError(event) {
|
|
|
7964
8085
|
async function writeTranslatedAnthropicStreamEvents(stream, events) {
|
|
7965
8086
|
for (const event of events) {
|
|
7966
8087
|
const eventData = JSON.stringify(event);
|
|
7967
|
-
logger$
|
|
8088
|
+
logger$4.debug("Translated Anthropic event:", eventData);
|
|
7968
8089
|
await stream.writeSSE({
|
|
7969
8090
|
event: event.type,
|
|
7970
8091
|
data: eventData
|
|
@@ -7993,7 +8114,7 @@ async function streamResponsesAndLog$1(params) {
|
|
|
7993
8114
|
}
|
|
7994
8115
|
const data = chunk.data;
|
|
7995
8116
|
if (!data) continue;
|
|
7996
|
-
logger$
|
|
8117
|
+
logger$4.debug("Responses raw stream event:", data);
|
|
7997
8118
|
const parsed = JSON.parse(data);
|
|
7998
8119
|
const streamEventError = getResponsesStreamEventError(parsed);
|
|
7999
8120
|
if (streamEventError) {
|
|
@@ -8007,7 +8128,7 @@ async function streamResponsesAndLog$1(params) {
|
|
|
8007
8128
|
if (u.usageJson) lastUsage = u;
|
|
8008
8129
|
await writeTranslatedAnthropicStreamEvents(stream, translateResponsesStreamEvent(parsed, streamState));
|
|
8009
8130
|
if (streamState.messageCompleted) {
|
|
8010
|
-
logger$
|
|
8131
|
+
logger$4.debug("Message completed, ending stream");
|
|
8011
8132
|
break;
|
|
8012
8133
|
}
|
|
8013
8134
|
}
|
|
@@ -8026,7 +8147,7 @@ async function streamResponsesAndLog$1(params) {
|
|
|
8026
8147
|
errorStatus = details.errorStatus;
|
|
8027
8148
|
errorMessage = details.errorMessage;
|
|
8028
8149
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
8029
|
-
logger$
|
|
8150
|
+
logger$4.warn("Streaming error:", error);
|
|
8030
8151
|
invalidateAffinityOnOwnershipMismatch(details.ownershipMismatch, instr);
|
|
8031
8152
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
8032
8153
|
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
@@ -8081,7 +8202,7 @@ async function handleMessagesNonStreaming(params) {
|
|
|
8081
8202
|
let upstreamErrorMessageRaw;
|
|
8082
8203
|
const finishedAtMs = Date.now();
|
|
8083
8204
|
try {
|
|
8084
|
-
logger$
|
|
8205
|
+
logger$4.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
|
|
8085
8206
|
return c.json(response);
|
|
8086
8207
|
} catch (error) {
|
|
8087
8208
|
const details = await extractErrorObservability(error);
|
|
@@ -8118,7 +8239,7 @@ const parseMessagesStreamUsage = (data) => {
|
|
|
8118
8239
|
if (parsed.type !== "message_delta" || !parsed.usage) return null;
|
|
8119
8240
|
return normalizeMessagesUsage(parsed.usage);
|
|
8120
8241
|
} catch (error) {
|
|
8121
|
-
logger$
|
|
8242
|
+
logger$4.warn("Failed to parse messages stream event", error);
|
|
8122
8243
|
throw new Error("Failed to parse messages stream event", { cause: error });
|
|
8123
8244
|
}
|
|
8124
8245
|
};
|
|
@@ -8136,7 +8257,7 @@ async function streamMessagesAndLog(params) {
|
|
|
8136
8257
|
const eventNameRaw = rawEvent.event;
|
|
8137
8258
|
const eventName = typeof eventNameRaw === "string" && eventNameRaw.length > 0 ? eventNameRaw : "message";
|
|
8138
8259
|
const data = rawEvent.data ?? "";
|
|
8139
|
-
logger$
|
|
8260
|
+
logger$4.debug("Messages raw stream event:", data);
|
|
8140
8261
|
const usage = parseMessagesStreamUsage(data);
|
|
8141
8262
|
if (usage) lastUsage = usage;
|
|
8142
8263
|
await stream.writeSSE({
|
|
@@ -8150,7 +8271,7 @@ async function streamMessagesAndLog(params) {
|
|
|
8150
8271
|
errorStatus = details.errorStatus;
|
|
8151
8272
|
errorMessage = details.errorMessage;
|
|
8152
8273
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
8153
|
-
logger$
|
|
8274
|
+
logger$4.warn("Streaming error:", error);
|
|
8154
8275
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
8155
8276
|
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
8156
8277
|
} finally {
|
|
@@ -8176,7 +8297,7 @@ async function streamMessagesAndLog(params) {
|
|
|
8176
8297
|
const handleWithMessagesApi = async (params) => {
|
|
8177
8298
|
const { c, anthropicPayload, anthropicBetaHeader, subagentMarker, sessionId, instr, selectedModel, compactType } = params;
|
|
8178
8299
|
prepareMessagesApiPayload(anthropicPayload, selectedModel);
|
|
8179
|
-
debugJson(logger$
|
|
8300
|
+
debugJson(logger$4, "Translated Messages payload:", anthropicPayload);
|
|
8180
8301
|
const ctx = toAccountContext(instr.account);
|
|
8181
8302
|
const effectiveInitiator = resolveEffectiveInitiator(getMessagesInitiator(anthropicPayload), {
|
|
8182
8303
|
isCompact: compactType !== 0,
|
|
@@ -8204,7 +8325,7 @@ const handleWithMessagesApi = async (params) => {
|
|
|
8204
8325
|
});
|
|
8205
8326
|
}
|
|
8206
8327
|
if (isAsyncIterable(response)) {
|
|
8207
|
-
logger$
|
|
8328
|
+
logger$4.debug("Streaming response from Copilot (Messages API)");
|
|
8208
8329
|
return streamSSE(c, (stream) => streamMessagesAndLog({
|
|
8209
8330
|
stream,
|
|
8210
8331
|
response,
|
|
@@ -8292,29 +8413,113 @@ providerMessageRoutes.post("/count_tokens", async (c) => {
|
|
|
8292
8413
|
}
|
|
8293
8414
|
});
|
|
8294
8415
|
//#endregion
|
|
8416
|
+
//#region src/services/codex/get-models.ts
|
|
8417
|
+
const CODEX_MODELS = [
|
|
8418
|
+
{
|
|
8419
|
+
contextWindow: 1e5,
|
|
8420
|
+
id: "gpt-5.3-codex-spark",
|
|
8421
|
+
input: ["text"],
|
|
8422
|
+
maxTokens: 32e3,
|
|
8423
|
+
name: "GPT-5.3 Codex Spark"
|
|
8424
|
+
},
|
|
8425
|
+
{
|
|
8426
|
+
contextWindow: 4e5,
|
|
8427
|
+
id: "gpt-5.4",
|
|
8428
|
+
input: ["text", "image"],
|
|
8429
|
+
maxTokens: 128e3,
|
|
8430
|
+
name: "GPT-5.4"
|
|
8431
|
+
},
|
|
8432
|
+
{
|
|
8433
|
+
contextWindow: 4e5,
|
|
8434
|
+
id: "gpt-5.4-mini",
|
|
8435
|
+
input: ["text", "image"],
|
|
8436
|
+
maxTokens: 128e3,
|
|
8437
|
+
name: "GPT-5.4 mini"
|
|
8438
|
+
},
|
|
8439
|
+
{
|
|
8440
|
+
contextWindow: 272e3,
|
|
8441
|
+
id: "gpt-5.5",
|
|
8442
|
+
input: ["text", "image"],
|
|
8443
|
+
maxTokens: 128e3,
|
|
8444
|
+
name: "GPT-5.5"
|
|
8445
|
+
}
|
|
8446
|
+
];
|
|
8447
|
+
function normalizeCodexModel(model) {
|
|
8448
|
+
const supportsVision = model.input.includes("image");
|
|
8449
|
+
return {
|
|
8450
|
+
capabilities: {
|
|
8451
|
+
family: "gpt",
|
|
8452
|
+
limits: {
|
|
8453
|
+
max_context_window_tokens: model.contextWindow,
|
|
8454
|
+
max_output_tokens: model.maxTokens,
|
|
8455
|
+
max_prompt_tokens: model.contextWindow
|
|
8456
|
+
},
|
|
8457
|
+
object: "model_capabilities",
|
|
8458
|
+
supports: {
|
|
8459
|
+
adaptive_thinking: true,
|
|
8460
|
+
parallel_tool_calls: true,
|
|
8461
|
+
reasoning_effort: [
|
|
8462
|
+
"minimal",
|
|
8463
|
+
"low",
|
|
8464
|
+
"medium",
|
|
8465
|
+
"high",
|
|
8466
|
+
"xhigh"
|
|
8467
|
+
],
|
|
8468
|
+
streaming: true,
|
|
8469
|
+
tool_calls: true,
|
|
8470
|
+
vision: supportsVision
|
|
8471
|
+
},
|
|
8472
|
+
tokenizer: "o200k_base",
|
|
8473
|
+
type: "chat"
|
|
8474
|
+
},
|
|
8475
|
+
id: model.id,
|
|
8476
|
+
model_picker_enabled: true,
|
|
8477
|
+
name: model.name,
|
|
8478
|
+
object: "model",
|
|
8479
|
+
preview: false,
|
|
8480
|
+
supported_endpoints: ["/v1/messages", "/v1/responses"],
|
|
8481
|
+
vendor: "openai",
|
|
8482
|
+
version: "chatgpt-codex"
|
|
8483
|
+
};
|
|
8484
|
+
}
|
|
8485
|
+
function getModels() {
|
|
8486
|
+
return {
|
|
8487
|
+
object: "list",
|
|
8488
|
+
data: CODEX_MODELS.map((model) => normalizeCodexModel(model))
|
|
8489
|
+
};
|
|
8490
|
+
}
|
|
8491
|
+
//#endregion
|
|
8295
8492
|
//#region src/routes/provider/models/route.ts
|
|
8296
|
-
const logger$
|
|
8493
|
+
const logger$3 = createHandlerLogger("provider-models-handler");
|
|
8297
8494
|
const getProviderFetch = (c) => c.get("providerFetch") ?? fetch;
|
|
8298
|
-
const resolveProviderConfig = (c, provider) => {
|
|
8495
|
+
const resolveProviderConfig$1 = (c, provider) => {
|
|
8299
8496
|
return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
|
|
8300
8497
|
};
|
|
8301
8498
|
const providerModelRoutes = new Hono();
|
|
8302
8499
|
providerModelRoutes.get("/", async (c) => {
|
|
8303
8500
|
const provider = c.req.param("provider") ?? "";
|
|
8304
8501
|
try {
|
|
8305
|
-
const providerConfig = resolveProviderConfig(c, provider);
|
|
8502
|
+
const providerConfig = resolveProviderConfig$1(c, provider);
|
|
8306
8503
|
if (!providerConfig) return c.json({ error: {
|
|
8307
8504
|
message: `Provider '${provider}' not found or disabled`,
|
|
8308
8505
|
type: "invalid_request_error"
|
|
8309
8506
|
} }, 404);
|
|
8507
|
+
if (providerConfig.name === "codex") {
|
|
8508
|
+
const models = getModels();
|
|
8509
|
+
return c.json({
|
|
8510
|
+
object: "list",
|
|
8511
|
+
data: models.data,
|
|
8512
|
+
has_more: false
|
|
8513
|
+
});
|
|
8514
|
+
}
|
|
8310
8515
|
const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers, getProviderFetch(c));
|
|
8311
|
-
logger$
|
|
8516
|
+
logger$3.debug("provider.models.response", {
|
|
8312
8517
|
provider,
|
|
8313
8518
|
statusCode: upstreamResponse.status
|
|
8314
8519
|
});
|
|
8315
8520
|
return createProviderProxyResponse(upstreamResponse);
|
|
8316
8521
|
} catch (error) {
|
|
8317
|
-
logger$
|
|
8522
|
+
logger$3.error("provider.models.error", {
|
|
8318
8523
|
provider,
|
|
8319
8524
|
error
|
|
8320
8525
|
});
|
|
@@ -8322,6 +8527,166 @@ providerModelRoutes.get("/", async (c) => {
|
|
|
8322
8527
|
}
|
|
8323
8528
|
});
|
|
8324
8529
|
//#endregion
|
|
8530
|
+
//#region src/lib/codex-rate-limit.ts
|
|
8531
|
+
const codexRateLimitScopes = ["primary", "secondary"];
|
|
8532
|
+
const formatCodexRateLimitResetAt = (resetAt) => {
|
|
8533
|
+
const date = /* @__PURE__ */ new Date(resetAt * 1e3);
|
|
8534
|
+
return Number.isNaN(date.getTime()) ? String(resetAt) : date.toLocaleString();
|
|
8535
|
+
};
|
|
8536
|
+
const logCodexRateLimitsEvent = (event) => {
|
|
8537
|
+
if (!event || typeof event !== "object") return;
|
|
8538
|
+
const eventRecord = event;
|
|
8539
|
+
if (eventRecord.type !== "codex.rate_limits") return;
|
|
8540
|
+
const rateLimits = eventRecord.rate_limits;
|
|
8541
|
+
if (!rateLimits || typeof rateLimits !== "object") return;
|
|
8542
|
+
const planType = typeof eventRecord.plan_type === "string" ? eventRecord.plan_type : null;
|
|
8543
|
+
const rateLimitRecord = rateLimits;
|
|
8544
|
+
const allowed = typeof rateLimitRecord.allowed === "boolean" ? rateLimitRecord.allowed : null;
|
|
8545
|
+
const limitReached = typeof rateLimitRecord.limit_reached === "boolean" ? rateLimitRecord.limit_reached : null;
|
|
8546
|
+
for (const scope of codexRateLimitScopes) {
|
|
8547
|
+
const window = rateLimitRecord[scope];
|
|
8548
|
+
if (!isCodexRateLimitWindow(window)) continue;
|
|
8549
|
+
const summary = [];
|
|
8550
|
+
if (allowed !== null) summary.push(`allowed=${allowed}`);
|
|
8551
|
+
if (limitReached !== null) summary.push(`limit_reached=${limitReached}`);
|
|
8552
|
+
summary.push(`used=${window.used_percent}%`, `reset_at=${formatCodexRateLimitResetAt(window.reset_at)}`);
|
|
8553
|
+
const label = planType ? `Codex ${scope} rate limit (${planType})` : `Codex ${scope} rate limit`;
|
|
8554
|
+
consola.log(`${label}: ${summary.join(", ")}`);
|
|
8555
|
+
}
|
|
8556
|
+
};
|
|
8557
|
+
const isCodexRateLimitWindow = (value) => {
|
|
8558
|
+
if (!value || typeof value !== "object") return false;
|
|
8559
|
+
const record = value;
|
|
8560
|
+
return typeof record.reset_after_seconds === "number" && typeof record.reset_at === "number" && typeof record.used_percent === "number" && typeof record.window_minutes === "number";
|
|
8561
|
+
};
|
|
8562
|
+
//#endregion
|
|
8563
|
+
//#region src/lib/provider-resolver.ts
|
|
8564
|
+
function isMissingCodexCredentialsError(error) {
|
|
8565
|
+
return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
|
|
8566
|
+
}
|
|
8567
|
+
async function resolveProviderConfig(providerName) {
|
|
8568
|
+
const normalizedProviderName = providerName.trim();
|
|
8569
|
+
if (!normalizedProviderName) return null;
|
|
8570
|
+
if (normalizedProviderName === "codex") {
|
|
8571
|
+
if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
|
|
8572
|
+
try {
|
|
8573
|
+
await setupCodexToken();
|
|
8574
|
+
} catch (error) {
|
|
8575
|
+
if (isMissingCodexCredentialsError(error)) return null;
|
|
8576
|
+
throw error;
|
|
8577
|
+
}
|
|
8578
|
+
const providerConfig = getProviderConfig(normalizedProviderName);
|
|
8579
|
+
if (!providerConfig) return null;
|
|
8580
|
+
return {
|
|
8581
|
+
...providerConfig,
|
|
8582
|
+
apiKey: state.codexAccessToken ?? providerConfig.apiKey
|
|
8583
|
+
};
|
|
8584
|
+
}
|
|
8585
|
+
return getProviderConfig(normalizedProviderName);
|
|
8586
|
+
}
|
|
8587
|
+
//#endregion
|
|
8588
|
+
//#region src/routes/provider/responses/handler.ts
|
|
8589
|
+
const logger$2 = createHandlerLogger("provider-responses-handler");
|
|
8590
|
+
async function handleProviderResponsesForProvider(c, options) {
|
|
8591
|
+
const { payload, provider } = options;
|
|
8592
|
+
debugJson(logger$2, "Responses request payload:", {
|
|
8593
|
+
payload,
|
|
8594
|
+
provider
|
|
8595
|
+
});
|
|
8596
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
8597
|
+
if (providerConfig?.type !== "openai-responses") return c.json({ error: {
|
|
8598
|
+
message: `Provider '${provider}' does not support the /v1/responses endpoint`,
|
|
8599
|
+
type: "invalid_request_error"
|
|
8600
|
+
} }, 400);
|
|
8601
|
+
applyResponsesApiContextManagement(payload, (providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0)?.capabilities.limits.max_prompt_tokens ?? 0, .8);
|
|
8602
|
+
const contextManagement = payload.context_management;
|
|
8603
|
+
debugJson(logger$2, "Translated Responses request payload:", {
|
|
8604
|
+
contextManagement,
|
|
8605
|
+
provider
|
|
8606
|
+
});
|
|
8607
|
+
compactInputByLatestCompaction(payload);
|
|
8608
|
+
if (providerConfig.name === "codex") {
|
|
8609
|
+
const upstreamResponse = await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl);
|
|
8610
|
+
const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
|
|
8611
|
+
if (payload.stream && isResponsesStream(upstreamResponse)) return streamProviderResponses(c, upstreamResponse, {
|
|
8612
|
+
normalizeCodex: true,
|
|
8613
|
+
provider,
|
|
8614
|
+
recordUsage
|
|
8615
|
+
});
|
|
8616
|
+
const responseBody = upstreamResponse;
|
|
8617
|
+
recordUsage(normalizeResponsesUsage(responseBody.usage));
|
|
8618
|
+
return c.json(responseBody);
|
|
8619
|
+
}
|
|
8620
|
+
const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
|
|
8621
|
+
if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${provider} responses`, upstreamResponse);
|
|
8622
|
+
const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
|
|
8623
|
+
if (payload.stream) return streamProviderResponses(c, getResponsesEvents(upstreamResponse), {
|
|
8624
|
+
normalizeCodex: false,
|
|
8625
|
+
provider,
|
|
8626
|
+
recordUsage
|
|
8627
|
+
});
|
|
8628
|
+
recordUsage(normalizeResponsesUsage((await upstreamResponse.clone().json()).usage));
|
|
8629
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
8630
|
+
}
|
|
8631
|
+
const createProviderResponsesUsageRecorder = (_payload, _provider) => {
|
|
8632
|
+
return (_usage) => {};
|
|
8633
|
+
};
|
|
8634
|
+
const streamProviderResponses = (c, upstreamResponse, options) => {
|
|
8635
|
+
return streamSSE(c, async (stream) => {
|
|
8636
|
+
let usage = {};
|
|
8637
|
+
try {
|
|
8638
|
+
for await (const chunk of upstreamResponse) {
|
|
8639
|
+
debugJson(logger$2, "Responses stream chunk:", chunk);
|
|
8640
|
+
let responseChunk = chunk;
|
|
8641
|
+
let event = null;
|
|
8642
|
+
if (chunk.data && chunk.data !== "[DONE]") {
|
|
8643
|
+
event = parseProviderResponsesStreamEvent(chunk.data, {
|
|
8644
|
+
normalizeCodex: options.normalizeCodex,
|
|
8645
|
+
provider: options.provider
|
|
8646
|
+
});
|
|
8647
|
+
if (event && options.normalizeCodex) responseChunk = {
|
|
8648
|
+
...chunk,
|
|
8649
|
+
data: JSON.stringify(event),
|
|
8650
|
+
event: event.type
|
|
8651
|
+
};
|
|
8652
|
+
}
|
|
8653
|
+
if (event) {
|
|
8654
|
+
const nextUsage = getResponsesStreamEventUsage(event);
|
|
8655
|
+
if (nextUsage) usage = nextUsage;
|
|
8656
|
+
}
|
|
8657
|
+
await stream.writeSSE({
|
|
8658
|
+
data: responseChunk.data ?? "",
|
|
8659
|
+
event: responseChunk.event
|
|
8660
|
+
});
|
|
8661
|
+
}
|
|
8662
|
+
} finally {
|
|
8663
|
+
options.recordUsage(usage);
|
|
8664
|
+
}
|
|
8665
|
+
});
|
|
8666
|
+
};
|
|
8667
|
+
const parseProviderResponsesStreamEvent = (data, options) => {
|
|
8668
|
+
try {
|
|
8669
|
+
const parsed = JSON.parse(data);
|
|
8670
|
+
if (options.normalizeCodex) logCodexRateLimitsEvent(parsed);
|
|
8671
|
+
return parsed;
|
|
8672
|
+
} catch (error) {
|
|
8673
|
+
logger$2.error("provider.responses.parse_chunk_error", {
|
|
8674
|
+
provider: options.provider,
|
|
8675
|
+
data,
|
|
8676
|
+
error
|
|
8677
|
+
});
|
|
8678
|
+
return null;
|
|
8679
|
+
}
|
|
8680
|
+
};
|
|
8681
|
+
const getResponsesStreamEventUsage = (event) => {
|
|
8682
|
+
if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return normalizeResponsesUsage(event.response.usage);
|
|
8683
|
+
return null;
|
|
8684
|
+
};
|
|
8685
|
+
const getResponsesEvents = (response) => events(response);
|
|
8686
|
+
const isResponsesStream = (value) => {
|
|
8687
|
+
return Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
8688
|
+
};
|
|
8689
|
+
//#endregion
|
|
8325
8690
|
//#region src/routes/responses/stream-id-sync.ts
|
|
8326
8691
|
const createStreamIdTracker = () => ({ outputItems: /* @__PURE__ */ new Map() });
|
|
8327
8692
|
const fixStreamIds = (data, event, tracker) => {
|
|
@@ -8362,16 +8727,31 @@ const handleItemId = (parsed, tracker) => {
|
|
|
8362
8727
|
const logger$1 = createHandlerLogger("responses-handler");
|
|
8363
8728
|
const RESPONSES_ENDPOINT = "/responses";
|
|
8364
8729
|
const handleResponses = async (c) => {
|
|
8730
|
+
const payload = await c.req.json();
|
|
8731
|
+
debugJson(logger$1, "Responses request payload:", payload);
|
|
8732
|
+
const requestedModel = payload.model;
|
|
8733
|
+
payload.model = resolveMappedModel(payload.model);
|
|
8734
|
+
if (payload.model !== requestedModel) logger$1.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
|
|
8735
|
+
const providerModelAlias = parseProviderModelAlias(payload.model);
|
|
8736
|
+
if (providerModelAlias) {
|
|
8737
|
+
payload.model = providerModelAlias.model;
|
|
8738
|
+
return await handleProviderResponsesForProvider(c, {
|
|
8739
|
+
payload,
|
|
8740
|
+
provider: providerModelAlias.provider
|
|
8741
|
+
});
|
|
8742
|
+
}
|
|
8743
|
+
const subagentMarker = getCodexResponsesSubagentMarker(c);
|
|
8744
|
+
if (subagentMarker) debugJson(logger$1, "Detected Codex subagent headers:", subagentMarker);
|
|
8745
|
+
const incomingSessionId = subagentMarker ? getIncomingResponsesSessionId(c) : void 0;
|
|
8365
8746
|
await checkRateLimit(state);
|
|
8366
8747
|
const store = getRequestHistoryStore();
|
|
8367
8748
|
const request = buildRequestContext(c);
|
|
8368
|
-
const payload = await c.req.json();
|
|
8369
8749
|
const clientModel = payload.model;
|
|
8370
|
-
debugJson(logger$1, "Responses request payload:", payload);
|
|
8371
8750
|
if (!isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
|
|
8372
8751
|
compactInputByLatestCompaction(payload);
|
|
8373
8752
|
const streamRequested = Boolean(payload.stream);
|
|
8374
|
-
const { initiator:
|
|
8753
|
+
const { initiator: inferredInitiator } = getResponsesRequestOptions(payload);
|
|
8754
|
+
const initialInitiator = subagentMarker ? "agent" : inferredInitiator;
|
|
8375
8755
|
const userId = payload.metadata?.user_id;
|
|
8376
8756
|
const requestBodyPromptCacheKey = typeof payload.prompt_cache_key === "string" ? payload.prompt_cache_key : null;
|
|
8377
8757
|
const { safetyIdentifier, sessionId: metadataSessionId } = parseUserIdMetadata(userId);
|
|
@@ -8393,8 +8773,8 @@ const handleResponses = async (c) => {
|
|
|
8393
8773
|
message: "This model is only available via an alias. Please use the alias model name."
|
|
8394
8774
|
});
|
|
8395
8775
|
}
|
|
8396
|
-
const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, normalizedPromptCacheKey);
|
|
8397
8776
|
const headerSessionId = c.req.header("x-session-id") ?? null;
|
|
8777
|
+
const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, incomingSessionId ?? normalizedPromptCacheKey);
|
|
8398
8778
|
const affinityKey = resolveAffinityKey({
|
|
8399
8779
|
promptCacheKey: requestBodyPromptCacheKey,
|
|
8400
8780
|
metadataSessionId,
|
|
@@ -8425,18 +8805,22 @@ const handleResponses = async (c) => {
|
|
|
8425
8805
|
model: selectedModel.id
|
|
8426
8806
|
};
|
|
8427
8807
|
removeUnsupportedTools(upstreamPayload);
|
|
8808
|
+
const sanitizedImageCount = sanitizeOversizedInputImages(upstreamPayload, selectedModel.capabilities.limits.vision?.max_prompt_image_size);
|
|
8809
|
+
if (sanitizedImageCount > 0) logger$1.warn(`Omitted ${sanitizedImageCount} oversized input image(s) before forwarding to Copilot Responses`);
|
|
8428
8810
|
applyResponsesApiContextManagement(upstreamPayload, selectedModel.capabilities.limits.max_prompt_tokens);
|
|
8429
8811
|
compactInputByLatestCompaction(upstreamPayload);
|
|
8430
8812
|
const premiumRemainingBefore = account.premiumRemaining;
|
|
8431
8813
|
const premiumUnlimitedBefore = account.unlimited;
|
|
8432
8814
|
const transport = getResponsesTransportForModel(selectedModel) ?? "http";
|
|
8433
|
-
const { vision, initiator } = getResponsesRequestOptions(upstreamPayload);
|
|
8815
|
+
const { vision, initiator: inferredUpstreamInitiator } = getResponsesRequestOptions(upstreamPayload);
|
|
8816
|
+
const initiator = subagentMarker ? "agent" : inferredUpstreamInitiator;
|
|
8434
8817
|
request.initiator = initiator;
|
|
8435
8818
|
if (state.manualApprove) await awaitApproval();
|
|
8436
8819
|
const accountCtx = toAccountContext(account);
|
|
8437
|
-
const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
|
|
8820
|
+
const upstreamSessionId = getUUID(incomingSessionId ?? normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
|
|
8438
8821
|
request.upstreamRequestId = upstreamRequestId;
|
|
8439
8822
|
request.upstreamSessionId = upstreamSessionId;
|
|
8823
|
+
const bridgeId = c.req.header("x-responses-bridge-id") ?? void 0;
|
|
8440
8824
|
if (streamRequested) return handleStreamingResponses({
|
|
8441
8825
|
c,
|
|
8442
8826
|
store,
|
|
@@ -8447,9 +8831,11 @@ const handleResponses = async (c) => {
|
|
|
8447
8831
|
accountCtx,
|
|
8448
8832
|
vision,
|
|
8449
8833
|
initiator,
|
|
8834
|
+
subagentMarker,
|
|
8450
8835
|
premiumRemainingBefore,
|
|
8451
8836
|
premiumUnlimitedBefore,
|
|
8452
|
-
transport
|
|
8837
|
+
transport,
|
|
8838
|
+
bridgeId
|
|
8453
8839
|
});
|
|
8454
8840
|
return handleNonStreamingResponses({
|
|
8455
8841
|
c,
|
|
@@ -8461,9 +8847,11 @@ const handleResponses = async (c) => {
|
|
|
8461
8847
|
accountCtx,
|
|
8462
8848
|
vision,
|
|
8463
8849
|
initiator,
|
|
8850
|
+
subagentMarker,
|
|
8464
8851
|
premiumRemainingBefore,
|
|
8465
8852
|
premiumUnlimitedBefore,
|
|
8466
|
-
transport
|
|
8853
|
+
transport,
|
|
8854
|
+
bridgeId
|
|
8467
8855
|
});
|
|
8468
8856
|
};
|
|
8469
8857
|
async function observeRequestError(accountId, error, affinity) {
|
|
@@ -8571,16 +8959,18 @@ function extractUsageFromChunkData(data) {
|
|
|
8571
8959
|
}
|
|
8572
8960
|
}
|
|
8573
8961
|
async function handleStreamingResponses(params) {
|
|
8574
|
-
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore, transport } = params;
|
|
8962
|
+
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, subagentMarker, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
|
|
8575
8963
|
let response;
|
|
8576
8964
|
try {
|
|
8577
8965
|
response = await createResponses(payload, {
|
|
8578
8966
|
vision,
|
|
8579
8967
|
initiator,
|
|
8968
|
+
subagentMarker,
|
|
8580
8969
|
upstreamRequestId: request.upstreamRequestId,
|
|
8581
8970
|
sessionId: request.upstreamSessionId,
|
|
8582
8971
|
requestId: request.requestId,
|
|
8583
|
-
transport
|
|
8972
|
+
transport,
|
|
8973
|
+
bridgeId
|
|
8584
8974
|
}, accountCtx);
|
|
8585
8975
|
selection.confirmAffinity?.();
|
|
8586
8976
|
} catch (error) {
|
|
@@ -8768,7 +9158,7 @@ async function streamResponsesAndLog(params) {
|
|
|
8768
9158
|
}
|
|
8769
9159
|
}
|
|
8770
9160
|
async function handleNonStreamingResponses(params) {
|
|
8771
|
-
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore, transport } = params;
|
|
9161
|
+
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, subagentMarker, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
|
|
8772
9162
|
const { account, reservation, selectedModel, endpoint, costUnits } = selection;
|
|
8773
9163
|
let usage = {};
|
|
8774
9164
|
let errorState = { httpStatus: 200 };
|
|
@@ -8777,10 +9167,12 @@ async function handleNonStreamingResponses(params) {
|
|
|
8777
9167
|
const response = await createResponses(payload, {
|
|
8778
9168
|
vision,
|
|
8779
9169
|
initiator,
|
|
9170
|
+
subagentMarker,
|
|
8780
9171
|
upstreamRequestId: request.upstreamRequestId,
|
|
8781
9172
|
sessionId: request.upstreamSessionId,
|
|
8782
9173
|
requestId: request.requestId,
|
|
8783
|
-
transport
|
|
9174
|
+
transport,
|
|
9175
|
+
bridgeId
|
|
8784
9176
|
}, accountCtx);
|
|
8785
9177
|
if (isAsyncIterable$1(response)) throw new Error("Upstream returned a stream unexpectedly");
|
|
8786
9178
|
selection.confirmAffinity?.();
|
|
@@ -8842,6 +9234,31 @@ const removeUnsupportedTools = (payload) => {
|
|
|
8842
9234
|
});
|
|
8843
9235
|
if (dropped.length > 0) logger$1.debug("Removed unsupported tools:", dropped);
|
|
8844
9236
|
};
|
|
9237
|
+
const getTrimmedHeader = (c, name) => {
|
|
9238
|
+
const value = c.req.header(name)?.trim();
|
|
9239
|
+
return value ? value : void 0;
|
|
9240
|
+
};
|
|
9241
|
+
const getIncomingResponsesSessionId = (c) => getTrimmedHeader(c, "session-id") ?? getTrimmedHeader(c, "x-session-id");
|
|
9242
|
+
const codexSubagentHeaderValues = new Set([
|
|
9243
|
+
"collab_spawn",
|
|
9244
|
+
"compact",
|
|
9245
|
+
"memory_consolidation",
|
|
9246
|
+
"review"
|
|
9247
|
+
]);
|
|
9248
|
+
const getCodexResponsesSubagentMarker = (c) => {
|
|
9249
|
+
const agentType = getTrimmedHeader(c, "x-openai-subagent");
|
|
9250
|
+
if (!agentType || !codexSubagentHeaderValues.has(agentType)) return null;
|
|
9251
|
+
const threadId = getTrimmedHeader(c, "thread-id");
|
|
9252
|
+
const rootSessionId = getIncomingResponsesSessionId(c);
|
|
9253
|
+
const parentThreadId = getTrimmedHeader(c, "x-codex-parent-thread-id");
|
|
9254
|
+
if (!threadId && !rootSessionId && !parentThreadId) return null;
|
|
9255
|
+
const agentId = threadId ?? parentThreadId ?? rootSessionId;
|
|
9256
|
+
return {
|
|
9257
|
+
agent_id: agentId,
|
|
9258
|
+
agent_type: agentType,
|
|
9259
|
+
session_id: threadId ?? rootSessionId ?? agentId
|
|
9260
|
+
};
|
|
9261
|
+
};
|
|
8845
9262
|
//#endregion
|
|
8846
9263
|
//#region src/routes/responses/route.ts
|
|
8847
9264
|
const responsesRoutes = new Hono();
|
|
@@ -8904,31 +9321,43 @@ usageRoute.get("/:accountIndex", async (c) => {
|
|
|
8904
9321
|
});
|
|
8905
9322
|
//#endregion
|
|
8906
9323
|
//#region src/server.ts
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
9324
|
+
function createServer(options = {}) {
|
|
9325
|
+
const app = new Hono();
|
|
9326
|
+
const enableMcpHttp = options.enableMcpHttp ?? isMcpHttpEnabledFromEnv();
|
|
9327
|
+
app.use(traceIdMiddleware);
|
|
9328
|
+
app.use(logger());
|
|
9329
|
+
if (enableMcpHttp) {
|
|
9330
|
+
app.use(DEFAULT_MCP_HTTP_PATH, cors(mcpHttpCorsOptions));
|
|
9331
|
+
app.all(DEFAULT_MCP_HTTP_PATH, (c) => handleStreamableHttpMcpRequest(c.req.raw));
|
|
9332
|
+
} else app.all(DEFAULT_MCP_HTTP_PATH, (c) => c.json({ error: {
|
|
9333
|
+
type: "mcp_http_disabled",
|
|
9334
|
+
message: "MCP Streamable HTTP is disabled. Start with --enable-mcp-http to expose /mcp."
|
|
9335
|
+
} }, 404));
|
|
9336
|
+
app.use(cors());
|
|
9337
|
+
app.use("*", createAuthMiddleware({
|
|
9338
|
+
allowUnauthenticatedPaths: ["/", DEFAULT_MCP_HTTP_PATH],
|
|
9339
|
+
allowUnauthenticatedPathPrefixes: ["/admin", "/api/admin"]
|
|
9340
|
+
}));
|
|
9341
|
+
app.get("/", (c) => c.text("Server running"));
|
|
9342
|
+
app.route("/chat/completions", completionRoutes);
|
|
9343
|
+
app.route("/models", modelRoutes);
|
|
9344
|
+
app.route("/embeddings", embeddingRoutes);
|
|
9345
|
+
app.route("/usage", usageRoute);
|
|
9346
|
+
app.route("/token", tokenRoute);
|
|
9347
|
+
app.route("/responses", responsesRoutes);
|
|
9348
|
+
app.route("/admin", adminRoutes);
|
|
9349
|
+
app.route("/api/admin", adminApiRoutes);
|
|
9350
|
+
app.route("/v1/chat/completions", completionRoutes);
|
|
9351
|
+
app.route("/v1/models", modelRoutes);
|
|
9352
|
+
app.route("/v1/embeddings", embeddingRoutes);
|
|
9353
|
+
app.route("/v1/responses", responsesRoutes);
|
|
9354
|
+
app.route("/v1/messages", messageRoutes);
|
|
9355
|
+
app.route("/:provider/v1/messages", providerMessageRoutes);
|
|
9356
|
+
app.route("/:provider/v1/models", providerModelRoutes);
|
|
9357
|
+
return app;
|
|
9358
|
+
}
|
|
9359
|
+
createServer();
|
|
8931
9360
|
//#endregion
|
|
8932
|
-
export {
|
|
9361
|
+
export { createServer };
|
|
8933
9362
|
|
|
8934
|
-
//# sourceMappingURL=server-
|
|
9363
|
+
//# sourceMappingURL=server-BYKxAFro.js.map
|