@jeffreycao/copilot-api 1.10.9 → 1.10.11
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 +53 -213
- package/README.zh-CN.md +47 -209
- package/dist/auth-BO_SkMVw.js +116 -0
- package/dist/auth-BO_SkMVw.js.map +1 -0
- package/dist/{check-usage-BdXGp1Wr.js → check-usage-D-W6VD7k.js} +3 -4
- package/dist/{check-usage-BdXGp1Wr.js.map → check-usage-D-W6VD7k.js.map} +1 -1
- package/dist/{proxy-DvlF9a-7.js → config-ztdkLu9o.js} +83 -70
- package/dist/config-ztdkLu9o.js.map +1 -0
- package/dist/{debug-C_TBkyUw.js → debug-BVHmoCzY.js} +17 -7
- package/dist/debug-BVHmoCzY.js.map +1 -0
- package/dist/main.js +5 -5
- package/dist/{mcp-CTb-DbQH.js → mcp-DZgcvqQY.js} +2 -2
- package/dist/{mcp-CTb-DbQH.js.map → mcp-DZgcvqQY.js.map} +1 -1
- package/dist/{server-FPXzFkg9.js → server-2tRe3sDu.js} +1798 -1713
- package/dist/server-2tRe3sDu.js.map +1 -0
- package/dist/{start-CbKg_0bY.js → start-CM-b3DRX.js} +4 -6
- package/dist/{start-CbKg_0bY.js.map → start-CM-b3DRX.js.map} +1 -1
- package/dist/token-BVXHiYEl.js +1875 -0
- package/dist/token-BVXHiYEl.js.map +1 -0
- package/dist/{tool-search-D3SN0jX-.js → tool-search-wA-fLduL.js} +1 -1
- package/dist/{tool-search-D3SN0jX-.js.map → tool-search-wA-fLduL.js.map} +1 -1
- package/package.json +2 -2
- package/dist/auth-BHa2OHXf.js +0 -45
- package/dist/auth-BHa2OHXf.js.map +0 -1
- package/dist/debug-C_TBkyUw.js.map +0 -1
- package/dist/paths-DC-mqCY3.js +0 -30
- package/dist/paths-DC-mqCY3.js.map +0 -1
- package/dist/proxy-DvlF9a-7.js.map +0 -1
- package/dist/server-FPXzFkg9.js.map +0 -1
- package/dist/token-Dj8XsAxn.js +0 -170
- package/dist/token-Dj8XsAxn.js.map +0 -1
- package/dist/utils-jHLgqAq2.js +0 -657
- package/dist/utils-jHLgqAq2.js.map +0 -1
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-
|
|
4
|
-
import { _ as setModelMappings, a as getConfig, c as getProviderConfig, d as isMessagesApiEnabled, f as isResponsesApiContextManagementModel, g as resolveMappedModel, i as getClaudeTokenMultiplier, l as getReasoningEffortForModel, m as isResponsesApiWebSocketEnabled, o as getExtraPromptForModel, p as isResponsesApiWebSearchEnabled, r as getAnthropicApiKey, s as getModelMappings, t as getProxyEnvDispatcher, u as getSmallModel } from "./proxy-DvlF9a-7.js";
|
|
1
|
+
import { A as compactSystemPromptStarts, B as createWebSocketUrl, C as prepareForCompact, D as compactAutoContinuePromptStarts, F as normalizeCodexResponsesEvent, H as state, I as generateTraceId, L as requestContext, N as createStandardizedCodexResponsesEventStream, O as compactMessageSections, P as forwardCodexResponses, R as resolveTraceId$1, S as copilotWebSocketHeaders, T as prepareMessageProxyHeaders, U as logCodexRateLimitsEvent, _ as getCopilotUsage, b as copilotBaseUrl, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as HTTPError, w as prepareInteractionHeaders, x as copilotHeaders, y as forwardError, z as createPooledWebSocketStream } from "./token-BVXHiYEl.js";
|
|
2
|
+
import { _ as setModelMappings, a as getModelMappings, c as getReasoningEffortForModel, d as isResponsesApiContextManagementModel, f as isResponsesApiWebSearchEnabled, g as resolveMappedModel, i as getExtraPromptForModel, l as getSmallModel, n as getClaudeTokenMultiplier, o as getProviderConfig, p as isResponsesApiWebSocketEnabled, r as getConfig, s as getRawProviderConfig, t as getAnthropicApiKey, u as isMessagesApiEnabled, y as PATHS } from "./config-ztdkLu9o.js";
|
|
3
|
+
import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-wA-fLduL.js";
|
|
5
4
|
import consola from "consola";
|
|
6
|
-
import fs from "node:fs/promises";
|
|
7
|
-
import path from "node:path";
|
|
8
5
|
import { createHash } from "node:crypto";
|
|
6
|
+
import fs, { readFileSync } from "node:fs";
|
|
7
|
+
import fs$1 from "node:fs/promises";
|
|
8
|
+
import path from "node:path";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { Hono } from "hono";
|
|
11
11
|
import { cors } from "hono/cors";
|
|
12
12
|
import { logger } from "hono/logger";
|
|
13
|
-
import fs$1, { readFileSync } from "node:fs";
|
|
14
13
|
import { streamSSE } from "hono/streaming";
|
|
15
14
|
import util from "node:util";
|
|
16
15
|
import { events } from "fetch-event-stream";
|
|
17
|
-
import { WebSocket } from "undici";
|
|
18
16
|
//#region src/lib/request-auth.ts
|
|
19
17
|
function normalizeApiKeys(apiKeys) {
|
|
20
18
|
if (!Array.isArray(apiKeys)) {
|
|
@@ -78,7 +76,7 @@ const traceIdMiddleware = async (c, next) => {
|
|
|
78
76
|
traceId,
|
|
79
77
|
startTime: Date.now(),
|
|
80
78
|
userAgent: c.req.header("user-agent") || "",
|
|
81
|
-
sessionAffinity: c.req.header("x-session-affinity"),
|
|
79
|
+
sessionAffinity: c.req.header("x-session-affinity") ?? c.req.header("x-client-request-id"),
|
|
82
80
|
parentSessionId: c.req.header("x-parent-session-id")
|
|
83
81
|
};
|
|
84
82
|
await requestContext.run(context, async () => {
|
|
@@ -154,22 +152,22 @@ let runtimeInitialized = false;
|
|
|
154
152
|
let flushInterval;
|
|
155
153
|
let cleanupInterval;
|
|
156
154
|
const ensureLogDirectory = () => {
|
|
157
|
-
if (!fs
|
|
155
|
+
if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
158
156
|
};
|
|
159
157
|
const cleanupOldLogs = () => {
|
|
160
|
-
if (!fs
|
|
158
|
+
if (!fs.existsSync(LOG_DIR)) return;
|
|
161
159
|
const now = Date.now();
|
|
162
|
-
for (const entry of fs
|
|
160
|
+
for (const entry of fs.readdirSync(LOG_DIR)) {
|
|
163
161
|
const filePath = path.join(LOG_DIR, entry);
|
|
164
162
|
let stats;
|
|
165
163
|
try {
|
|
166
|
-
stats = fs
|
|
164
|
+
stats = fs.statSync(filePath);
|
|
167
165
|
} catch {
|
|
168
166
|
continue;
|
|
169
167
|
}
|
|
170
168
|
if (!stats.isFile()) continue;
|
|
171
169
|
if (now - stats.mtimeMs > LOG_RETENTION_MS) try {
|
|
172
|
-
fs
|
|
170
|
+
fs.rmSync(filePath);
|
|
173
171
|
} catch {
|
|
174
172
|
continue;
|
|
175
173
|
}
|
|
@@ -228,7 +226,7 @@ const getLogStream = (filePath) => {
|
|
|
228
226
|
initializeLoggerRuntime();
|
|
229
227
|
let stream = logStreams.get(filePath);
|
|
230
228
|
if (!stream || stream.destroyed) {
|
|
231
|
-
stream = fs
|
|
229
|
+
stream = fs.createWriteStream(filePath, { flags: "a" });
|
|
232
230
|
logStreams.set(filePath, stream);
|
|
233
231
|
stream.on("error", (error) => {
|
|
234
232
|
console.warn("Log stream error", error);
|
|
@@ -373,7 +371,7 @@ async function openNodeDatabase(dbPath) {
|
|
|
373
371
|
}
|
|
374
372
|
async function openSqliteDatabase(dbPath) {
|
|
375
373
|
const dir = path.dirname(dbPath);
|
|
376
|
-
if (dbPath !== ":memory:" && dir !== ".") await fs.mkdir(dir, { recursive: true });
|
|
374
|
+
if (dbPath !== ":memory:" && dir !== ".") await fs$1.mkdir(dir, { recursive: true });
|
|
377
375
|
return isBunRuntime() ? openBunDatabase(dbPath) : openNodeDatabase(dbPath);
|
|
378
376
|
}
|
|
379
377
|
var SqliteDbStore = class {
|
|
@@ -987,7 +985,7 @@ const logCopilotQuotaSnapshots = (snapshots) => {
|
|
|
987
985
|
const logCopilotRateLimitUsage = (usage) => {
|
|
988
986
|
const d = new Date(usage.resetAt);
|
|
989
987
|
const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
|
|
990
|
-
consola.
|
|
988
|
+
consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
|
|
991
989
|
};
|
|
992
990
|
const isCopilotQuotaSnapshot = (value) => {
|
|
993
991
|
if (!value || typeof value !== "object") return false;
|
|
@@ -1026,11 +1024,11 @@ const createChatCompletions = async (payload, options) => {
|
|
|
1026
1024
|
};
|
|
1027
1025
|
//#endregion
|
|
1028
1026
|
//#region src/routes/chat-completions/handler.ts
|
|
1029
|
-
const logger$
|
|
1027
|
+
const logger$7 = createHandlerLogger("chat-completions-handler");
|
|
1030
1028
|
async function handleCompletion$1(c) {
|
|
1031
1029
|
await checkRateLimit(state);
|
|
1032
1030
|
let payload = await c.req.json();
|
|
1033
|
-
debugJsonTail(logger$
|
|
1031
|
+
debugJsonTail(logger$7, "Request payload:", {
|
|
1034
1032
|
value: payload,
|
|
1035
1033
|
tailLength: 400
|
|
1036
1034
|
});
|
|
@@ -1045,12 +1043,12 @@ async function handleCompletion$1(c) {
|
|
|
1045
1043
|
...payload,
|
|
1046
1044
|
max_tokens: selectedModel?.capabilities.limits.max_output_tokens
|
|
1047
1045
|
};
|
|
1048
|
-
debugJson(logger$
|
|
1046
|
+
debugJson(logger$7, "Set max_tokens to:", payload.max_tokens);
|
|
1049
1047
|
}
|
|
1050
1048
|
const requestId = generateRequestIdFromPayload(payload);
|
|
1051
|
-
logger$
|
|
1049
|
+
logger$7.debug("Generated request ID:", requestId);
|
|
1052
1050
|
const sessionId = getUUID(requestId);
|
|
1053
|
-
logger$
|
|
1051
|
+
logger$7.debug("Extracted session ID:", sessionId);
|
|
1054
1052
|
const recordUsage = createCopilotTokenUsageRecorder({
|
|
1055
1053
|
endpoint: "chat_completions",
|
|
1056
1054
|
fallbackSessionId: sessionId,
|
|
@@ -1061,15 +1059,15 @@ async function handleCompletion$1(c) {
|
|
|
1061
1059
|
sessionId
|
|
1062
1060
|
});
|
|
1063
1061
|
if (isNonStreaming$1(response)) {
|
|
1064
|
-
debugJson(logger$
|
|
1062
|
+
debugJson(logger$7, "Non-streaming response:", response);
|
|
1065
1063
|
recordUsage(normalizeOpenAIUsage(response.usage));
|
|
1066
1064
|
return c.json(response);
|
|
1067
1065
|
}
|
|
1068
|
-
logger$
|
|
1066
|
+
logger$7.debug("Streaming response");
|
|
1069
1067
|
return streamSSE(c, async (stream) => {
|
|
1070
1068
|
let usage = {};
|
|
1071
1069
|
for await (const chunk of response) {
|
|
1072
|
-
debugJson(logger$
|
|
1070
|
+
debugJson(logger$7, "Streaming chunk:", chunk);
|
|
1073
1071
|
const parsedChunk = parseChatCompletionChunk(chunk);
|
|
1074
1072
|
if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
|
|
1075
1073
|
await stream.writeSSE(chunk);
|
|
@@ -1402,6 +1400,31 @@ const getTokenCount = async (payload, model) => {
|
|
|
1402
1400
|
};
|
|
1403
1401
|
};
|
|
1404
1402
|
//#endregion
|
|
1403
|
+
//#region src/lib/provider-resolver.ts
|
|
1404
|
+
function isMissingCodexCredentialsError(error) {
|
|
1405
|
+
return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
|
|
1406
|
+
}
|
|
1407
|
+
async function resolveProviderConfig(providerName) {
|
|
1408
|
+
const normalizedProviderName = providerName.trim();
|
|
1409
|
+
if (!normalizedProviderName) return null;
|
|
1410
|
+
if (normalizedProviderName === "codex") {
|
|
1411
|
+
if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
|
|
1412
|
+
try {
|
|
1413
|
+
await setupCodexToken();
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
if (isMissingCodexCredentialsError(error)) return null;
|
|
1416
|
+
throw error;
|
|
1417
|
+
}
|
|
1418
|
+
const providerConfig = getProviderConfig(normalizedProviderName);
|
|
1419
|
+
if (!providerConfig) return null;
|
|
1420
|
+
return {
|
|
1421
|
+
...providerConfig,
|
|
1422
|
+
apiKey: state.codexAccessToken ?? providerConfig.apiKey
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
return getProviderConfig(normalizedProviderName);
|
|
1426
|
+
}
|
|
1427
|
+
//#endregion
|
|
1405
1428
|
//#region src/routes/messages/utils.ts
|
|
1406
1429
|
function mapOpenAIStopReasonToAnthropic(finishReason) {
|
|
1407
1430
|
if (finishReason === null) return null;
|
|
@@ -1727,7 +1750,7 @@ function getAnthropicToolUseBlocks(toolCalls) {
|
|
|
1727
1750
|
}
|
|
1728
1751
|
//#endregion
|
|
1729
1752
|
//#region src/routes/provider/messages/count-tokens-handler.ts
|
|
1730
|
-
const logger$
|
|
1753
|
+
const logger$6 = createHandlerLogger("provider-count-tokens-handler");
|
|
1731
1754
|
async function handleProviderCountTokens(c) {
|
|
1732
1755
|
const provider = c.req.param("provider");
|
|
1733
1756
|
return await handleProviderCountTokensForProvider(c, {
|
|
@@ -1738,18 +1761,18 @@ async function handleProviderCountTokens(c) {
|
|
|
1738
1761
|
async function handleProviderCountTokensForProvider(c, options) {
|
|
1739
1762
|
const { payload: anthropicPayload, provider } = options;
|
|
1740
1763
|
const modelId = anthropicPayload.model.trim();
|
|
1741
|
-
const providerConfig =
|
|
1764
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
1742
1765
|
if (!providerConfig) return c.json({ error: {
|
|
1743
1766
|
message: `Provider '${provider}' not found or disabled`,
|
|
1744
1767
|
type: "invalid_request_error"
|
|
1745
1768
|
} }, 404);
|
|
1746
1769
|
const modelConfig = providerConfig.models?.[modelId];
|
|
1747
|
-
const tokenCount = await getTokenCount(translateToOpenAI(anthropicPayload, providerConfig.type === "openai-compatible" ? {
|
|
1770
|
+
const tokenCount = await getTokenCount(translateToOpenAI(anthropicPayload, providerConfig.type === "openai-compatible" || providerConfig.type === "openai-responses" ? {
|
|
1748
1771
|
supportPdf: modelConfig?.supportPdf,
|
|
1749
1772
|
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
1750
1773
|
} : void 0), createFallbackModel(modelId));
|
|
1751
1774
|
const finalTokenCount = tokenCount.input + tokenCount.output;
|
|
1752
|
-
logger$
|
|
1775
|
+
logger$6.debug("provider.count_tokens.success", {
|
|
1753
1776
|
provider,
|
|
1754
1777
|
model: anthropicPayload.model,
|
|
1755
1778
|
input_tokens: finalTokenCount
|
|
@@ -2205,1847 +2228,1820 @@ function closeThinkingBlockIfOpen(state, events) {
|
|
|
2205
2228
|
}
|
|
2206
2229
|
}
|
|
2207
2230
|
//#endregion
|
|
2208
|
-
//#region src/services/
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2211
|
-
const STRIPPED_RESPONSE_HEADERS = [
|
|
2212
|
-
"connection",
|
|
2213
|
-
"content-encoding",
|
|
2214
|
-
"content-length",
|
|
2215
|
-
"keep-alive",
|
|
2216
|
-
"proxy-authenticate",
|
|
2217
|
-
"proxy-authorization",
|
|
2218
|
-
"te",
|
|
2219
|
-
"trailer",
|
|
2220
|
-
"transfer-encoding",
|
|
2221
|
-
"upgrade"
|
|
2222
|
-
];
|
|
2223
|
-
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
2224
|
-
const authHeaders = {};
|
|
2225
|
-
if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
2226
|
-
else authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
2231
|
+
//#region src/services/copilot/create-responses.ts
|
|
2232
|
+
const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
|
|
2233
|
+
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
2227
2234
|
const headers = {
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
...authHeaders
|
|
2235
|
+
...copilotHeaders(state, requestId, vision),
|
|
2236
|
+
"x-initiator": initiator
|
|
2231
2237
|
};
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
}
|
|
2236
|
-
if (
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2238
|
+
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
2239
|
+
prepareForCompact(headers, compactType);
|
|
2240
|
+
payload.service_tier = void 0;
|
|
2241
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
2242
|
+
if ((compactType === 1 ? "http" : transport) === "websocket") {
|
|
2243
|
+
const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
|
|
2244
|
+
requestId,
|
|
2245
|
+
subagentMarker
|
|
2246
|
+
}));
|
|
2247
|
+
if (payload.stream) return stream;
|
|
2248
|
+
return await consumeResponsesWebSocketStream(stream);
|
|
2240
2249
|
}
|
|
2241
|
-
return headers;
|
|
2242
|
-
}
|
|
2243
|
-
|
|
2244
|
-
const
|
|
2245
|
-
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
2246
|
-
return new Response(upstreamResponse.body, {
|
|
2247
|
-
headers,
|
|
2248
|
-
status: upstreamResponse.status,
|
|
2249
|
-
statusText: upstreamResponse.statusText
|
|
2250
|
-
});
|
|
2251
|
-
}
|
|
2252
|
-
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
2253
|
-
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
2254
|
-
method: "POST",
|
|
2255
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
2256
|
-
body: JSON.stringify(payload)
|
|
2257
|
-
});
|
|
2258
|
-
}
|
|
2259
|
-
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
2260
|
-
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
2250
|
+
return await createHttpResponses(payload, headers);
|
|
2251
|
+
};
|
|
2252
|
+
const createHttpResponses = async (payload, headers) => {
|
|
2253
|
+
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
|
2261
2254
|
method: "POST",
|
|
2262
|
-
headers
|
|
2255
|
+
headers,
|
|
2263
2256
|
body: JSON.stringify(payload)
|
|
2264
2257
|
});
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
2270
|
-
});
|
|
2271
|
-
}
|
|
2272
|
-
//#endregion
|
|
2273
|
-
//#region src/routes/provider/messages/handler.ts
|
|
2274
|
-
const logger$4 = createHandlerLogger("provider-messages-handler");
|
|
2275
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
|
|
2276
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
|
|
2277
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
|
|
2278
|
-
"system",
|
|
2279
|
-
"user",
|
|
2280
|
-
"assistant",
|
|
2281
|
-
"tool"
|
|
2282
|
-
]);
|
|
2283
|
-
async function handleProviderMessages(c) {
|
|
2284
|
-
const provider = c.req.param("provider");
|
|
2285
|
-
return await handleProviderMessagesForProvider(c, {
|
|
2286
|
-
payload: await c.req.json(),
|
|
2287
|
-
provider
|
|
2288
|
-
});
|
|
2289
|
-
}
|
|
2290
|
-
async function handleProviderMessagesForProvider(c, options) {
|
|
2291
|
-
const { payload, provider } = options;
|
|
2292
|
-
const providerConfig = getProviderConfig(provider);
|
|
2293
|
-
if (!providerConfig) return c.json({ error: {
|
|
2294
|
-
message: `Provider '${provider}' not found or disabled`,
|
|
2295
|
-
type: "invalid_request_error"
|
|
2296
|
-
} }, 404);
|
|
2297
|
-
try {
|
|
2298
|
-
const modelConfig = providerConfig.models?.[payload.model];
|
|
2299
|
-
applyModelDefaults(payload, modelConfig);
|
|
2300
|
-
debugJson(logger$4, "provider.messages.request", {
|
|
2301
|
-
payload,
|
|
2302
|
-
provider
|
|
2303
|
-
});
|
|
2304
|
-
if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
|
|
2305
|
-
modelConfig,
|
|
2306
|
-
payload,
|
|
2307
|
-
provider,
|
|
2308
|
-
providerConfig
|
|
2309
|
-
});
|
|
2310
|
-
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
2311
|
-
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
2312
|
-
if (!upstreamResponse.ok) {
|
|
2313
|
-
logger$4.error("Failed to create responses", upstreamResponse);
|
|
2314
|
-
throw new HTTPError("Failed to create responses", upstreamResponse);
|
|
2315
|
-
}
|
|
2316
|
-
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
2317
|
-
if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
|
|
2318
|
-
c,
|
|
2319
|
-
payload,
|
|
2320
|
-
provider,
|
|
2321
|
-
providerConfig,
|
|
2322
|
-
upstreamResponse
|
|
2323
|
-
});
|
|
2324
|
-
return respondProviderMessagesJson(c, {
|
|
2325
|
-
body: await upstreamResponse.json(),
|
|
2326
|
-
payload,
|
|
2327
|
-
provider,
|
|
2328
|
-
providerConfig
|
|
2329
|
-
});
|
|
2330
|
-
} catch (error) {
|
|
2331
|
-
logger$4.error("provider.messages.error", {
|
|
2332
|
-
provider,
|
|
2333
|
-
error
|
|
2334
|
-
});
|
|
2335
|
-
throw error;
|
|
2258
|
+
logCopilotRateLimits(response.headers);
|
|
2259
|
+
if (!response.ok) {
|
|
2260
|
+
consola.error("Failed to create responses", response);
|
|
2261
|
+
throw new HTTPError("Failed to create responses", response);
|
|
2336
2262
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
payload.temperature ??= modelConfig?.temperature;
|
|
2340
|
-
payload.top_p ??= modelConfig?.topP;
|
|
2341
|
-
payload.top_k ??= modelConfig?.topK;
|
|
2342
|
-
};
|
|
2343
|
-
const applyMissingExtraBody = (payload, options) => {
|
|
2344
|
-
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
2263
|
+
if (payload.stream) return events(response);
|
|
2264
|
+
return await response.json();
|
|
2345
2265
|
};
|
|
2346
|
-
const
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
|
|
2266
|
+
const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
|
|
2267
|
+
const initiator = getResponsesWebSocketInitiator(preparedHeaders);
|
|
2268
|
+
return {
|
|
2269
|
+
headers: copilotWebSocketHeaders(preparedHeaders),
|
|
2270
|
+
poolKey: buildResponsesWebSocketPoolKey(payload, options),
|
|
2271
|
+
payload: buildResponsesWebSocketPayload(payload, initiator),
|
|
2272
|
+
url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
|
|
2273
|
+
};
|
|
2350
2274
|
};
|
|
2351
|
-
const
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2275
|
+
const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
|
|
2276
|
+
const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
|
|
2277
|
+
const subagentKey = subagentMarker ? [
|
|
2278
|
+
subagentMarker.session_id,
|
|
2279
|
+
subagentMarker.agent_id,
|
|
2280
|
+
subagentMarker.agent_type
|
|
2281
|
+
].join(":") : "main";
|
|
2282
|
+
return [
|
|
2283
|
+
tokenFingerprint,
|
|
2284
|
+
payload.model,
|
|
2285
|
+
requestId,
|
|
2286
|
+
subagentKey
|
|
2287
|
+
].map(encodePoolKeyPart).join("|");
|
|
2358
2288
|
};
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2361
|
-
if (!extraBody || !Object.hasOwn(extraBody, "thinking_budget")) return;
|
|
2362
|
-
const rawPayload = payload;
|
|
2363
|
-
rawPayload.thinking_budget = extraBody.thinking_budget;
|
|
2289
|
+
const getResponsesWebSocketInitiator = (preparedHeaders) => {
|
|
2290
|
+
return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
|
|
2364
2291
|
};
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
upstreamResponse
|
|
2383
|
-
});
|
|
2384
|
-
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
2385
|
-
body: await upstreamResponse.json(),
|
|
2386
|
-
payload,
|
|
2387
|
-
provider
|
|
2388
|
-
});
|
|
2292
|
+
const createPooledResponsesWebSocketStream = (request) => createPooledWebSocketStream(request, {
|
|
2293
|
+
createChunk: createResponsesWebSocketStreamChunk,
|
|
2294
|
+
isTerminalChunk: isTerminalResponsesStreamChunk,
|
|
2295
|
+
openErrorMessage: "Failed to create responses websocket",
|
|
2296
|
+
streamErrorMessage: "Responses websocket stream error",
|
|
2297
|
+
terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
|
|
2298
|
+
});
|
|
2299
|
+
const buildResponsesWebSocketPayload = (payload, initiator) => {
|
|
2300
|
+
const websocketPayload = {
|
|
2301
|
+
...payload,
|
|
2302
|
+
type: "response.create",
|
|
2303
|
+
initiator
|
|
2304
|
+
};
|
|
2305
|
+
delete websocketPayload.stream;
|
|
2306
|
+
delete websocketPayload["background"];
|
|
2307
|
+
delete websocketPayload.service_tier;
|
|
2308
|
+
return websocketPayload;
|
|
2389
2309
|
};
|
|
2390
|
-
const
|
|
2391
|
-
|
|
2392
|
-
supportPdf: modelConfig?.supportPdf,
|
|
2393
|
-
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
2394
|
-
});
|
|
2395
|
-
applyOpenAICompatibleThinkingBudget(openAIPayload, payload);
|
|
2396
|
-
if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
|
|
2397
|
-
if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
|
|
2398
|
-
normalizeOpenAICompatibleReasoningContent(openAIPayload);
|
|
2399
|
-
applyOpenAICompatibleRequestOverrides(openAIPayload, {
|
|
2400
|
-
extraBody: modelConfig?.extraBody,
|
|
2401
|
-
source: payload
|
|
2402
|
-
});
|
|
2403
|
-
applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
2404
|
-
applyOpenAICompatibleExtraBodyThinkingBudget(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
2405
|
-
if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
|
|
2406
|
-
if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
|
|
2407
|
-
return openAIPayload;
|
|
2310
|
+
const buildResponsesWebSocketUrl = (baseUrl) => {
|
|
2311
|
+
return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
|
|
2408
2312
|
};
|
|
2409
|
-
const
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
|
|
2413
|
-
delete message.reasoning_text;
|
|
2414
|
-
delete message.reasoning_opaque;
|
|
2415
|
-
}
|
|
2313
|
+
const getHeaderValue = (headers, headerName) => {
|
|
2314
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
2315
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
2416
2316
|
};
|
|
2417
|
-
const
|
|
2418
|
-
|
|
2419
|
-
|
|
2317
|
+
const encodePoolKeyPart = (value) => encodeURIComponent(value);
|
|
2318
|
+
const createResponsesWebSocketStreamChunk = (data) => {
|
|
2319
|
+
if (data === "[DONE]") return { data };
|
|
2320
|
+
try {
|
|
2321
|
+
const parsed = JSON.parse(data);
|
|
2322
|
+
if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
|
|
2323
|
+
return {
|
|
2324
|
+
data: JSON.stringify(parsed),
|
|
2325
|
+
event: typeof parsed.type === "string" ? parsed.type : void 0,
|
|
2326
|
+
id: typeof parsed.id === "string" ? parsed.id : void 0
|
|
2327
|
+
};
|
|
2328
|
+
} catch {
|
|
2329
|
+
return { data };
|
|
2330
|
+
}
|
|
2420
2331
|
};
|
|
2421
|
-
const
|
|
2422
|
-
|
|
2423
|
-
|
|
2332
|
+
const isTerminalResponsesStreamChunk = (chunk) => {
|
|
2333
|
+
if (!chunk.data || chunk.data === "[DONE]") return false;
|
|
2334
|
+
try {
|
|
2335
|
+
const parsed = JSON.parse(chunk.data);
|
|
2336
|
+
return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
|
|
2337
|
+
} catch {
|
|
2338
|
+
return false;
|
|
2339
|
+
}
|
|
2424
2340
|
};
|
|
2425
|
-
const
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2341
|
+
const consumeResponsesWebSocketStream = async (stream) => {
|
|
2342
|
+
for await (const chunk of stream) {
|
|
2343
|
+
if (!chunk.data || chunk.data === "[DONE]") continue;
|
|
2344
|
+
const event = JSON.parse(chunk.data);
|
|
2345
|
+
if (event.type === "error") throw new Error(event.message);
|
|
2346
|
+
if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
|
|
2347
|
+
}
|
|
2348
|
+
throw new Error("Responses websocket ended without a terminal response");
|
|
2430
2349
|
};
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2350
|
+
//#endregion
|
|
2351
|
+
//#region src/routes/messages/responses-translation.ts
|
|
2352
|
+
const MESSAGE_TYPE = "message";
|
|
2353
|
+
const COMPACTION_SIGNATURE_PREFIX = "cm1#";
|
|
2354
|
+
const COMPACTION_SIGNATURE_SEPARATOR = "@";
|
|
2355
|
+
const THINKING_TEXT = "Thinking...";
|
|
2356
|
+
const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
|
|
2357
|
+
if (!basePromptCacheKey) return null;
|
|
2358
|
+
const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
|
|
2359
|
+
if (!normalizedSubagentAgentId) return basePromptCacheKey;
|
|
2360
|
+
return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
|
|
2436
2361
|
};
|
|
2437
|
-
const
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2362
|
+
const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId) => {
|
|
2363
|
+
const input = [];
|
|
2364
|
+
const applyPhase = shouldApplyPhase(payload.model);
|
|
2365
|
+
const toolSearchEnabled = shouldEnableResponsesToolSearch({
|
|
2366
|
+
model: payload.model,
|
|
2367
|
+
tools: payload.tools
|
|
2368
|
+
});
|
|
2369
|
+
const translationState = {
|
|
2370
|
+
originalTools: payload.tools ?? [],
|
|
2371
|
+
toolSearchEnabled,
|
|
2372
|
+
toolUseNameById: /* @__PURE__ */ new Map()
|
|
2373
|
+
};
|
|
2374
|
+
for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase, translationState));
|
|
2375
|
+
const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
|
|
2376
|
+
const translatedTools = convertAnthropicTools(payload.tools, toolSearchEnabled);
|
|
2377
|
+
const toolChoice = convertAnthropicToolChoice(payload.tool_choice, toolSearchEnabled);
|
|
2378
|
+
const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
|
|
2379
|
+
const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
|
|
2380
|
+
const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
|
|
2381
|
+
const responsesPayload = {
|
|
2382
|
+
model: payload.model,
|
|
2383
|
+
input,
|
|
2384
|
+
instructions: translateSystemPrompt(payload.system, payload.model),
|
|
2385
|
+
temperature: 1,
|
|
2386
|
+
top_p: payload.top_p ?? null,
|
|
2387
|
+
max_output_tokens: Math.max(payload.max_tokens, 12800),
|
|
2388
|
+
tools: translatedTools,
|
|
2389
|
+
tool_choice: toolChoice,
|
|
2390
|
+
metadata: payload.metadata ? { ...payload.metadata } : null,
|
|
2391
|
+
stream: payload.stream ?? null,
|
|
2392
|
+
store: false,
|
|
2393
|
+
parallel_tool_calls: true,
|
|
2394
|
+
reasoning: {
|
|
2395
|
+
effort: getReasoningEffortForModel(payload.model),
|
|
2396
|
+
summary: "detailed"
|
|
2397
|
+
},
|
|
2398
|
+
include: ["reasoning.encrypted_content"]
|
|
2399
|
+
};
|
|
2400
|
+
if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
|
|
2401
|
+
return responsesPayload;
|
|
2402
|
+
};
|
|
2403
|
+
const encodeCompactionCarrierSignature = (compaction) => {
|
|
2404
|
+
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
2405
|
+
};
|
|
2406
|
+
const decodeCompactionCarrierSignature = (signature) => {
|
|
2407
|
+
if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
|
|
2408
|
+
const raw = signature.slice(4);
|
|
2409
|
+
const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
|
|
2410
|
+
if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
|
|
2411
|
+
const encrypted_content = raw.slice(0, separatorIndex);
|
|
2412
|
+
const id = raw.slice(separatorIndex + 1);
|
|
2413
|
+
if (!encrypted_content) return;
|
|
2414
|
+
return {
|
|
2415
|
+
id,
|
|
2416
|
+
encrypted_content
|
|
2417
|
+
};
|
|
2446
2418
|
}
|
|
2447
|
-
if (!Array.isArray(message.content)) return;
|
|
2448
|
-
const lastPart = message.content.at(-1);
|
|
2449
|
-
if (!lastPart) return;
|
|
2450
|
-
setContextCacheControl(lastPart);
|
|
2451
2419
|
};
|
|
2452
|
-
const
|
|
2453
|
-
|
|
2420
|
+
const translateMessage = (message, model, applyPhase, state) => {
|
|
2421
|
+
if (message.role === "user") return translateUserMessage(message, state);
|
|
2422
|
+
return translateAssistantMessage(message, model, applyPhase, state);
|
|
2454
2423
|
};
|
|
2455
|
-
const
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
event: "ping",
|
|
2466
|
-
data: "{\"type\":\"ping\"}"
|
|
2467
|
-
});
|
|
2468
|
-
continue;
|
|
2469
|
-
}
|
|
2470
|
-
let data = chunk.data;
|
|
2471
|
-
if (!data) continue;
|
|
2472
|
-
if (chunk.data === "[DONE]") break;
|
|
2473
|
-
const parsed = parseProviderStreamEvent(data, providerConfig);
|
|
2474
|
-
if (parsed) {
|
|
2475
|
-
usage = mergeAnthropicUsage(usage, parsed.usage);
|
|
2476
|
-
data = parsed.data;
|
|
2477
|
-
}
|
|
2478
|
-
await stream.writeSSE({
|
|
2479
|
-
event: eventName,
|
|
2480
|
-
data
|
|
2481
|
-
});
|
|
2424
|
+
const translateUserMessage = (message, state) => {
|
|
2425
|
+
if (typeof message.content === "string") return [createMessage("user", message.content)];
|
|
2426
|
+
if (!Array.isArray(message.content)) return [];
|
|
2427
|
+
const items = [];
|
|
2428
|
+
const pendingContent = [];
|
|
2429
|
+
for (const block of message.content) {
|
|
2430
|
+
if (block.type === "tool_result") {
|
|
2431
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2432
|
+
items.push(createToolCallOutput(block, state));
|
|
2433
|
+
continue;
|
|
2482
2434
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2435
|
+
const converted = translateUserContentBlock(block);
|
|
2436
|
+
if (converted.length > 0) pendingContent.push(...converted);
|
|
2437
|
+
}
|
|
2438
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2439
|
+
return items;
|
|
2485
2440
|
};
|
|
2486
|
-
const
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2441
|
+
const translateAssistantMessage = (message, model, applyPhase, state) => {
|
|
2442
|
+
const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
|
|
2443
|
+
if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
|
|
2444
|
+
if (!Array.isArray(message.content)) return [];
|
|
2445
|
+
const items = [];
|
|
2446
|
+
const pendingContent = [];
|
|
2447
|
+
for (const block of message.content) {
|
|
2448
|
+
if (block.type === "tool_use") {
|
|
2449
|
+
state.toolUseNameById.set(block.id, block.name);
|
|
2450
|
+
flushPendingContent(pendingContent, items, {
|
|
2451
|
+
role: "assistant",
|
|
2452
|
+
phase: assistantPhase
|
|
2453
|
+
});
|
|
2454
|
+
items.push(createToolCall(block, state));
|
|
2455
|
+
continue;
|
|
2456
|
+
}
|
|
2457
|
+
if (block.type === "thinking" && block.signature) {
|
|
2458
|
+
const compactionContent = createCompactionContent(block);
|
|
2459
|
+
if (compactionContent) {
|
|
2460
|
+
flushPendingContent(pendingContent, items, {
|
|
2461
|
+
role: "assistant",
|
|
2462
|
+
phase: assistantPhase
|
|
2504
2463
|
});
|
|
2464
|
+
items.push(compactionContent);
|
|
2505
2465
|
continue;
|
|
2506
2466
|
}
|
|
2507
|
-
if (
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
|
|
2512
|
-
if (!parsed) continue;
|
|
2513
|
-
if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
|
|
2514
|
-
const events = translateChunkToAnthropicEvents(parsed, streamState);
|
|
2515
|
-
for (const event of events) {
|
|
2516
|
-
const eventData = JSON.stringify(event);
|
|
2517
|
-
debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
2518
|
-
await stream.writeSSE({
|
|
2519
|
-
event: event.type,
|
|
2520
|
-
data: eventData
|
|
2467
|
+
if (block.signature.includes("@")) {
|
|
2468
|
+
flushPendingContent(pendingContent, items, {
|
|
2469
|
+
role: "assistant",
|
|
2470
|
+
phase: assistantPhase
|
|
2521
2471
|
});
|
|
2472
|
+
items.push(createReasoningContent(block));
|
|
2473
|
+
continue;
|
|
2522
2474
|
}
|
|
2523
2475
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
2527
|
-
await stream.writeSSE({
|
|
2528
|
-
event: event.type,
|
|
2529
|
-
data: eventData
|
|
2530
|
-
});
|
|
2531
|
-
}
|
|
2532
|
-
recordUsage(usage);
|
|
2533
|
-
});
|
|
2534
|
-
};
|
|
2535
|
-
const parseOpenAICompatibleStreamChunk = (data) => {
|
|
2536
|
-
try {
|
|
2537
|
-
return JSON.parse(data);
|
|
2538
|
-
} catch (error) {
|
|
2539
|
-
logger$4.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
2540
|
-
data,
|
|
2541
|
-
error
|
|
2542
|
-
});
|
|
2543
|
-
return null;
|
|
2476
|
+
const converted = translateAssistantContentBlock(block);
|
|
2477
|
+
if (converted) pendingContent.push(converted);
|
|
2544
2478
|
}
|
|
2479
|
+
flushPendingContent(pendingContent, items, {
|
|
2480
|
+
role: "assistant",
|
|
2481
|
+
phase: assistantPhase
|
|
2482
|
+
});
|
|
2483
|
+
return items;
|
|
2545
2484
|
};
|
|
2546
|
-
const
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
data: JSON.stringify(parsed),
|
|
2553
|
-
model: parsed.message.model,
|
|
2554
|
-
usage: normalizeAnthropicUsage(parsed.message.usage)
|
|
2555
|
-
};
|
|
2556
|
-
}
|
|
2557
|
-
if (parsed.type === "message_delta") {
|
|
2558
|
-
adjustInputTokens(providerConfig, parsed.usage);
|
|
2559
|
-
return {
|
|
2560
|
-
data: JSON.stringify(parsed),
|
|
2561
|
-
usage: normalizeAnthropicUsage(parsed.usage)
|
|
2562
|
-
};
|
|
2563
|
-
}
|
|
2564
|
-
return {
|
|
2565
|
-
data: JSON.stringify(parsed),
|
|
2566
|
-
usage: {}
|
|
2567
|
-
};
|
|
2568
|
-
} catch (error) {
|
|
2569
|
-
logger$4.error("provider.messages.streaming.adjust_tokens_error", {
|
|
2570
|
-
error,
|
|
2571
|
-
originalData: data
|
|
2572
|
-
});
|
|
2573
|
-
return null;
|
|
2485
|
+
const translateUserContentBlock = (block) => {
|
|
2486
|
+
switch (block.type) {
|
|
2487
|
+
case "text": return [createTextContent(block.text)];
|
|
2488
|
+
case "image": return [createImageContent(block)];
|
|
2489
|
+
case "document": return [createFileContent(block)];
|
|
2490
|
+
default: return [];
|
|
2574
2491
|
}
|
|
2575
2492
|
};
|
|
2576
|
-
const
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
debugJson(logger$4, "provider.messages.no_stream result:", body);
|
|
2582
|
-
return c.json(body);
|
|
2493
|
+
const translateAssistantContentBlock = (block) => {
|
|
2494
|
+
switch (block.type) {
|
|
2495
|
+
case "text": return createOutPutTextContent(block.text);
|
|
2496
|
+
default: return;
|
|
2497
|
+
}
|
|
2583
2498
|
};
|
|
2584
|
-
const
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
return c.json(anthropicResponse);
|
|
2499
|
+
const flushPendingContent = (pendingContent, target, message) => {
|
|
2500
|
+
if (pendingContent.length === 0) return;
|
|
2501
|
+
const messageContent = [...pendingContent];
|
|
2502
|
+
target.push(createMessage(message.role, messageContent, message.phase));
|
|
2503
|
+
pendingContent.length = 0;
|
|
2590
2504
|
};
|
|
2591
|
-
const
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2505
|
+
const createMessage = (role, content, phase) => ({
|
|
2506
|
+
type: MESSAGE_TYPE,
|
|
2507
|
+
role,
|
|
2508
|
+
content,
|
|
2509
|
+
...role === "assistant" && phase ? { phase } : {}
|
|
2596
2510
|
});
|
|
2597
|
-
const
|
|
2598
|
-
if (!
|
|
2599
|
-
|
|
2600
|
-
|
|
2511
|
+
const resolveAssistantPhase = (_model, content, applyPhase) => {
|
|
2512
|
+
if (!applyPhase) return;
|
|
2513
|
+
if (typeof content === "string") return "final_answer";
|
|
2514
|
+
if (!Array.isArray(content)) return;
|
|
2515
|
+
if (!content.some((block) => block.type === "text")) return;
|
|
2516
|
+
return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
|
|
2601
2517
|
};
|
|
2602
|
-
const
|
|
2603
|
-
|
|
2604
|
-
isResponsesApiWebSocketEnabled
|
|
2518
|
+
const shouldApplyPhase = (_model) => {
|
|
2519
|
+
return true;
|
|
2605
2520
|
};
|
|
2606
|
-
const
|
|
2521
|
+
const createTextContent = (text) => ({
|
|
2522
|
+
type: "input_text",
|
|
2523
|
+
text
|
|
2524
|
+
});
|
|
2525
|
+
const createOutPutTextContent = (text) => ({
|
|
2526
|
+
type: "output_text",
|
|
2527
|
+
text
|
|
2528
|
+
});
|
|
2529
|
+
const createImageContent = (block) => ({
|
|
2530
|
+
type: "input_image",
|
|
2531
|
+
image_url: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
2532
|
+
detail: "auto"
|
|
2533
|
+
});
|
|
2534
|
+
const createFileContent = (block) => ({
|
|
2535
|
+
type: "input_file",
|
|
2536
|
+
file_data: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
2537
|
+
filename: block.title ?? "document.pdf"
|
|
2538
|
+
});
|
|
2539
|
+
const createReasoningContent = (block) => {
|
|
2540
|
+
const { encryptedContent, id } = parseReasoningSignature(block.signature);
|
|
2541
|
+
const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
|
|
2607
2542
|
return {
|
|
2608
|
-
|
|
2609
|
-
|
|
2543
|
+
id,
|
|
2544
|
+
type: "reasoning",
|
|
2545
|
+
summary: thinking ? [{
|
|
2546
|
+
type: "summary_text",
|
|
2547
|
+
text: thinking
|
|
2548
|
+
}] : [],
|
|
2549
|
+
encrypted_content: encryptedContent
|
|
2610
2550
|
};
|
|
2611
2551
|
};
|
|
2612
|
-
const
|
|
2613
|
-
const
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2552
|
+
const createCompactionContent = (block) => {
|
|
2553
|
+
const compaction = decodeCompactionCarrierSignature(block.signature);
|
|
2554
|
+
if (!compaction) return;
|
|
2555
|
+
return {
|
|
2556
|
+
id: compaction.id,
|
|
2557
|
+
type: "compaction",
|
|
2558
|
+
encrypted_content: compaction.encrypted_content
|
|
2559
|
+
};
|
|
2618
2560
|
};
|
|
2619
|
-
const
|
|
2620
|
-
const
|
|
2621
|
-
if (
|
|
2622
|
-
|
|
2623
|
-
|
|
2561
|
+
const parseReasoningSignature = (signature) => {
|
|
2562
|
+
const splitIndex = signature.lastIndexOf("@");
|
|
2563
|
+
if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
|
|
2564
|
+
encryptedContent: signature,
|
|
2565
|
+
id: ""
|
|
2566
|
+
};
|
|
2567
|
+
return {
|
|
2568
|
+
encryptedContent: signature.slice(0, splitIndex),
|
|
2569
|
+
id: signature.slice(splitIndex + 1)
|
|
2570
|
+
};
|
|
2624
2571
|
};
|
|
2625
|
-
const
|
|
2626
|
-
|
|
2572
|
+
const createFunctionToolCall = (block, state) => ({
|
|
2573
|
+
type: "function_call",
|
|
2574
|
+
call_id: block.id,
|
|
2575
|
+
name: block.name,
|
|
2576
|
+
arguments: JSON.stringify(block.input),
|
|
2577
|
+
status: "completed",
|
|
2578
|
+
...state.toolSearchEnabled && isDeferredToolName(block.name) ? { namespace: block.name } : {}
|
|
2579
|
+
});
|
|
2580
|
+
const createToolSearchCall = (block) => ({
|
|
2581
|
+
type: "tool_search_call",
|
|
2582
|
+
call_id: block.id,
|
|
2583
|
+
arguments: normalizeToolSearchBridgeArguments(block.input),
|
|
2584
|
+
execution: "client",
|
|
2585
|
+
status: "completed"
|
|
2586
|
+
});
|
|
2587
|
+
const createToolCall = (block, state) => {
|
|
2588
|
+
if (state.toolSearchEnabled && isBridgeToolSearchName(block.name)) return createToolSearchCall(block);
|
|
2589
|
+
return createFunctionToolCall(block, state);
|
|
2627
2590
|
};
|
|
2628
|
-
const
|
|
2629
|
-
|
|
2630
|
-
|
|
2591
|
+
const createFunctionCallOutput = (block) => ({
|
|
2592
|
+
type: "function_call_output",
|
|
2593
|
+
call_id: block.tool_use_id,
|
|
2594
|
+
output: convertToolResultContent(block.content),
|
|
2595
|
+
status: block.is_error ? "incomplete" : "completed"
|
|
2596
|
+
});
|
|
2597
|
+
const createToolCallOutput = (block, state) => {
|
|
2598
|
+
const toolUseName = state.toolUseNameById.get(block.tool_use_id);
|
|
2599
|
+
if (state.toolSearchEnabled && isBridgeToolSearchName(toolUseName ?? "")) return createToolSearchOutput(block, state.originalTools);
|
|
2600
|
+
return createFunctionCallOutput(block);
|
|
2631
2601
|
};
|
|
2632
|
-
const
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2602
|
+
const createToolSearchOutput = (block, originalTools) => {
|
|
2603
|
+
const referencedToolNames = resolveToolSearchReferencedToolNames(block.content, originalTools);
|
|
2604
|
+
return {
|
|
2605
|
+
type: "tool_search_output",
|
|
2606
|
+
call_id: block.tool_use_id,
|
|
2607
|
+
tools: referencedToolNames.map((toolName) => convertDeferredToolToNamespace(resolveDeferredTool(toolName, originalTools))),
|
|
2608
|
+
execution: "client",
|
|
2609
|
+
status: block.is_error ? "incomplete" : "completed"
|
|
2610
|
+
};
|
|
2640
2611
|
};
|
|
2641
|
-
const
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2612
|
+
const resolveToolSearchReferencedToolNames = (content, originalTools) => {
|
|
2613
|
+
const explicitReferences = extractToolReferenceNames(content);
|
|
2614
|
+
if (explicitReferences.length > 0) return uniqueToolNames(explicitReferences);
|
|
2615
|
+
const sentinel = extractMcpToolSearchSentinel(content);
|
|
2616
|
+
if (sentinel) return selectDeferredToolsByNames(sentinel.names, originalTools).map((tool) => tool.name);
|
|
2617
|
+
return [];
|
|
2646
2618
|
};
|
|
2647
|
-
const
|
|
2648
|
-
|
|
2619
|
+
const extractToolReferenceNames = (content) => {
|
|
2620
|
+
if (!Array.isArray(content)) return [];
|
|
2621
|
+
return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
|
|
2649
2622
|
};
|
|
2650
|
-
const
|
|
2651
|
-
|
|
2623
|
+
const extractMcpToolSearchSentinel = (content) => {
|
|
2624
|
+
if (typeof content === "string") return parseMcpToolSearchSentinel(content);
|
|
2625
|
+
for (const block of content) {
|
|
2626
|
+
if (block.type !== "text") continue;
|
|
2627
|
+
const sentinel = parseMcpToolSearchSentinel(block.text);
|
|
2628
|
+
if (sentinel) return sentinel;
|
|
2629
|
+
}
|
|
2630
|
+
return null;
|
|
2652
2631
|
};
|
|
2653
|
-
const
|
|
2654
|
-
const
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
return result;
|
|
2632
|
+
const resolveDeferredTool = (toolName, originalTools) => {
|
|
2633
|
+
const tool = originalTools.find((candidate) => candidate.name === toolName);
|
|
2634
|
+
if (tool && isDeferredToolName(tool.name)) return tool;
|
|
2635
|
+
throw createInvalidRequestError(`Tool reference '${toolName}' has no corresponding deferred tool definition`);
|
|
2658
2636
|
};
|
|
2659
|
-
const
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2637
|
+
const uniqueToolNames = (toolNames) => [...new Set(toolNames)];
|
|
2638
|
+
const createInvalidRequestError = (message) => new HTTPError(message, new Response(JSON.stringify({ error: {
|
|
2639
|
+
message,
|
|
2640
|
+
type: "invalid_request_error"
|
|
2641
|
+
} }), {
|
|
2642
|
+
status: 400,
|
|
2643
|
+
headers: { "content-type": "application/json" }
|
|
2644
|
+
}));
|
|
2645
|
+
const translateSystemPrompt = (system, model) => {
|
|
2646
|
+
if (!system) return null;
|
|
2647
|
+
const extraPrompt = getExtraPromptForModel(model);
|
|
2648
|
+
if (typeof system === "string") return system + extraPrompt;
|
|
2649
|
+
const text = system.map((block, index) => {
|
|
2650
|
+
if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
|
|
2651
|
+
return block.text;
|
|
2652
|
+
}).join(" ");
|
|
2653
|
+
return text.length > 0 ? text : null;
|
|
2667
2654
|
};
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
const
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
const
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
if (payload.stream) return stream;
|
|
2687
|
-
return await consumeResponsesWebSocketStream(stream);
|
|
2655
|
+
const convertAnthropicTools = (tools, toolSearchEnabled) => {
|
|
2656
|
+
if (!tools || tools.length === 0) return null;
|
|
2657
|
+
const converted = [];
|
|
2658
|
+
let addedToolSearch = false;
|
|
2659
|
+
const searchableToolNames = toolSearchEnabled ? listDeferredToolNames(tools) : [];
|
|
2660
|
+
for (const tool of tools) {
|
|
2661
|
+
if (isBridgeToolSearchName(tool.name)) {
|
|
2662
|
+
if (toolSearchEnabled && !addedToolSearch) {
|
|
2663
|
+
converted.push(createResponsesToolSearchDefinition(searchableToolNames));
|
|
2664
|
+
addedToolSearch = true;
|
|
2665
|
+
}
|
|
2666
|
+
continue;
|
|
2667
|
+
}
|
|
2668
|
+
if (toolSearchEnabled && isDeferredToolName(tool.name)) {
|
|
2669
|
+
converted.push(convertDeferredToolToNamespace(tool));
|
|
2670
|
+
continue;
|
|
2671
|
+
}
|
|
2672
|
+
converted.push(convertToolToFunction(tool));
|
|
2688
2673
|
}
|
|
2689
|
-
return
|
|
2674
|
+
return converted;
|
|
2690
2675
|
};
|
|
2691
|
-
const
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2676
|
+
const createResponsesToolSearchDefinition = (searchableToolNames) => ({
|
|
2677
|
+
type: "tool_search",
|
|
2678
|
+
execution: "client",
|
|
2679
|
+
description: "Load deferred tools by exact name before using them. Return only the searchable tool names you need for the next step.",
|
|
2680
|
+
parameters: {
|
|
2681
|
+
type: "object",
|
|
2682
|
+
properties: { names: {
|
|
2683
|
+
type: "array",
|
|
2684
|
+
description: "Exact deferred tool names to load.",
|
|
2685
|
+
items: {
|
|
2686
|
+
type: "string",
|
|
2687
|
+
enum: searchableToolNames
|
|
2688
|
+
},
|
|
2689
|
+
minItems: 1
|
|
2690
|
+
} },
|
|
2691
|
+
required: ["names"],
|
|
2692
|
+
additionalProperties: false
|
|
2693
|
+
}
|
|
2694
|
+
});
|
|
2695
|
+
const convertToolToFunction = (tool) => ({
|
|
2696
|
+
type: "function",
|
|
2697
|
+
name: tool.name,
|
|
2698
|
+
parameters: normalizeToolSchema(tool.input_schema),
|
|
2699
|
+
strict: false,
|
|
2700
|
+
...tool.description ? { description: tool.description } : {}
|
|
2701
|
+
});
|
|
2702
|
+
const convertDeferredToolToNamespace = (tool) => ({
|
|
2703
|
+
type: "namespace",
|
|
2704
|
+
name: tool.name,
|
|
2705
|
+
...tool.description ? { description: tool.description } : {},
|
|
2706
|
+
tools: [{
|
|
2707
|
+
type: "function",
|
|
2708
|
+
name: tool.name,
|
|
2709
|
+
parameters: normalizeToolSchema(tool.input_schema),
|
|
2710
|
+
strict: false,
|
|
2711
|
+
defer_loading: true,
|
|
2712
|
+
...tool.description ? { description: tool.description } : {}
|
|
2713
|
+
}]
|
|
2714
|
+
});
|
|
2715
|
+
const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
|
|
2716
|
+
if (!choice) return "auto";
|
|
2717
|
+
switch (choice.type) {
|
|
2718
|
+
case "auto": return "auto";
|
|
2719
|
+
case "any": return "required";
|
|
2720
|
+
case "tool":
|
|
2721
|
+
if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
|
|
2722
|
+
return choice.name ? {
|
|
2723
|
+
type: "function",
|
|
2724
|
+
name: choice.name
|
|
2725
|
+
} : "auto";
|
|
2726
|
+
case "none": return "none";
|
|
2727
|
+
default: return "auto";
|
|
2701
2728
|
}
|
|
2702
|
-
if (payload.stream) return events(response);
|
|
2703
|
-
return await response.json();
|
|
2704
2729
|
};
|
|
2705
|
-
const
|
|
2706
|
-
const
|
|
2730
|
+
const translateResponsesResultToAnthropic = (response, options) => {
|
|
2731
|
+
const contentBlocks = mapOutputToAnthropicContent(response.output, options);
|
|
2732
|
+
const usage = mapResponsesUsage(response);
|
|
2733
|
+
let anthropicContent = fallbackContentBlocks(response.output_text);
|
|
2734
|
+
if (contentBlocks.length > 0) anthropicContent = contentBlocks;
|
|
2735
|
+
const stopReason = mapResponsesStopReason(response, options);
|
|
2707
2736
|
return {
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2737
|
+
id: response.id,
|
|
2738
|
+
type: "message",
|
|
2739
|
+
role: "assistant",
|
|
2740
|
+
content: anthropicContent,
|
|
2741
|
+
model: response.model,
|
|
2742
|
+
stop_reason: stopReason,
|
|
2743
|
+
stop_sequence: null,
|
|
2744
|
+
usage
|
|
2711
2745
|
};
|
|
2712
2746
|
};
|
|
2713
|
-
const
|
|
2714
|
-
const
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
const
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2747
|
+
const mapOutputToAnthropicContent = (output, options) => {
|
|
2748
|
+
const contentBlocks = [];
|
|
2749
|
+
for (const item of output) switch (item.type) {
|
|
2750
|
+
case "reasoning": {
|
|
2751
|
+
const thinkingText = extractReasoningText(item);
|
|
2752
|
+
if (thinkingText.length > 0) contentBlocks.push({
|
|
2753
|
+
type: "thinking",
|
|
2754
|
+
thinking: thinkingText,
|
|
2755
|
+
signature: (item.encrypted_content ?? "") + "@" + item.id
|
|
2756
|
+
});
|
|
2757
|
+
break;
|
|
2758
|
+
}
|
|
2759
|
+
case "function_call": {
|
|
2760
|
+
const toolUseBlock = createToolUseContentBlock(item);
|
|
2761
|
+
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
2762
|
+
break;
|
|
2763
|
+
}
|
|
2764
|
+
case "tool_search_call": {
|
|
2765
|
+
const toolUseBlock = createToolSearchUseContentBlock(item, options?.toolSearchName);
|
|
2766
|
+
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
2767
|
+
break;
|
|
2768
|
+
}
|
|
2769
|
+
case "tool_search_output": break;
|
|
2770
|
+
case "message": {
|
|
2771
|
+
const combinedText = combineMessageTextContent(item.content);
|
|
2772
|
+
if (combinedText.length > 0) contentBlocks.push({
|
|
2773
|
+
type: "text",
|
|
2774
|
+
text: combinedText
|
|
2775
|
+
});
|
|
2776
|
+
break;
|
|
2777
|
+
}
|
|
2778
|
+
case "compaction": {
|
|
2779
|
+
const compactionBlock = createCompactionThinkingBlock(item);
|
|
2780
|
+
if (compactionBlock) contentBlocks.push(compactionBlock);
|
|
2781
|
+
break;
|
|
2782
|
+
}
|
|
2783
|
+
default: {
|
|
2784
|
+
const combinedText = combineMessageTextContent(item.content);
|
|
2785
|
+
if (combinedText.length > 0) contentBlocks.push({
|
|
2786
|
+
type: "text",
|
|
2787
|
+
text: combinedText
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
return contentBlocks;
|
|
2741
2792
|
};
|
|
2742
|
-
const
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2793
|
+
const combineMessageTextContent = (content) => {
|
|
2794
|
+
if (!Array.isArray(content)) return "";
|
|
2795
|
+
let aggregated = "";
|
|
2796
|
+
for (const block of content) {
|
|
2797
|
+
if (isResponseOutputText(block)) {
|
|
2798
|
+
aggregated += block.text;
|
|
2799
|
+
continue;
|
|
2800
|
+
}
|
|
2801
|
+
if (isResponseOutputRefusal(block)) {
|
|
2802
|
+
aggregated += block.refusal;
|
|
2803
|
+
continue;
|
|
2804
|
+
}
|
|
2805
|
+
if (typeof block.text === "string") {
|
|
2806
|
+
aggregated += block.text;
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
if (typeof block.reasoning === "string") {
|
|
2810
|
+
aggregated += block.reasoning;
|
|
2811
|
+
continue;
|
|
2760
2812
|
}
|
|
2761
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2762
|
-
throw new Error("Responses websocket ended without a terminal response");
|
|
2763
|
-
} catch (error) {
|
|
2764
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2765
|
-
throw toError(error);
|
|
2766
|
-
} finally {
|
|
2767
|
-
release();
|
|
2768
2813
|
}
|
|
2814
|
+
return aggregated;
|
|
2769
2815
|
};
|
|
2770
|
-
const
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2816
|
+
const extractReasoningText = (item) => {
|
|
2817
|
+
const segments = [];
|
|
2818
|
+
const collectFromBlocks = (blocks) => {
|
|
2819
|
+
if (!Array.isArray(blocks)) return;
|
|
2820
|
+
for (const block of blocks) if (typeof block.text === "string") {
|
|
2821
|
+
segments.push(block.text);
|
|
2822
|
+
continue;
|
|
2823
|
+
}
|
|
2774
2824
|
};
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
const entry = createResponsesWebSocketEntry(request);
|
|
2784
|
-
responsesWebSocketPool.set(request.poolKey, entry);
|
|
2825
|
+
if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
|
|
2826
|
+
collectFromBlocks(item.summary);
|
|
2827
|
+
return segments.join("").trim();
|
|
2828
|
+
};
|
|
2829
|
+
const createToolUseContentBlock = (call) => {
|
|
2830
|
+
const toolId = call.call_id;
|
|
2831
|
+
const toolName = resolveToolUseName(call);
|
|
2832
|
+
if (!toolName || !toolId) return null;
|
|
2785
2833
|
return {
|
|
2786
|
-
|
|
2787
|
-
|
|
2834
|
+
type: "tool_use",
|
|
2835
|
+
id: toolId,
|
|
2836
|
+
name: toolName,
|
|
2837
|
+
input: parseFunctionCallArguments(call.arguments)
|
|
2788
2838
|
};
|
|
2789
2839
|
};
|
|
2790
|
-
const
|
|
2791
|
-
const
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
})
|
|
2840
|
+
const createToolSearchUseContentBlock = (call, toolSearchName = BRIDGE_TOOL_SEARCH_NAME) => {
|
|
2841
|
+
const toolId = call.call_id;
|
|
2842
|
+
if (!toolId) return null;
|
|
2843
|
+
return {
|
|
2844
|
+
type: "tool_use",
|
|
2845
|
+
id: toolId,
|
|
2846
|
+
name: toolSearchName,
|
|
2847
|
+
input: parseToolSearchArguments(call.arguments)
|
|
2799
2848
|
};
|
|
2800
|
-
entry.websocketPromise.then((websocket) => {
|
|
2801
|
-
websocket.addEventListener("close", () => {
|
|
2802
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2803
|
-
});
|
|
2804
|
-
websocket.addEventListener("error", () => {
|
|
2805
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2806
|
-
});
|
|
2807
|
-
}).catch(() => {
|
|
2808
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2809
|
-
});
|
|
2810
|
-
return entry;
|
|
2811
2849
|
};
|
|
2812
|
-
const
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
entry.requestCount += 1;
|
|
2816
|
-
let released = false;
|
|
2817
|
-
return () => {
|
|
2818
|
-
if (released) return;
|
|
2819
|
-
released = true;
|
|
2820
|
-
entry.requestCount -= 1;
|
|
2821
|
-
decrementResponsesWebSocketActiveRequestCount(poolKey);
|
|
2822
|
-
if (entry.closed || entry.requestCount > 0) return;
|
|
2823
|
-
if (pooled && responsesWebSocketPool.get(poolKey) === entry) {
|
|
2824
|
-
scheduleResponsesWebSocketIdleClose(poolKey, entry);
|
|
2825
|
-
return;
|
|
2826
|
-
}
|
|
2827
|
-
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
2828
|
-
};
|
|
2850
|
+
const resolveToolUseName = (call) => {
|
|
2851
|
+
if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
|
|
2852
|
+
return call.name;
|
|
2829
2853
|
};
|
|
2830
|
-
const
|
|
2831
|
-
if (
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
};
|
|
2840
|
-
const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
|
|
2841
|
-
clearResponsesWebSocketIdleTimer(entry);
|
|
2842
|
-
entry.idleTimer = setTimeout(() => {
|
|
2843
|
-
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
2844
|
-
}, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
|
|
2845
|
-
unrefTimer(entry.idleTimer);
|
|
2846
|
-
};
|
|
2847
|
-
const clearResponsesWebSocketIdleTimer = (entry) => {
|
|
2848
|
-
if (entry.idleTimer) {
|
|
2849
|
-
clearTimeout(entry.idleTimer);
|
|
2850
|
-
entry.idleTimer = null;
|
|
2851
|
-
}
|
|
2852
|
-
};
|
|
2853
|
-
const getResponsesWebSocketActiveRequestCount = (poolKey) => responsesWebSocketActiveRequests.get(poolKey) ?? 0;
|
|
2854
|
-
const incrementResponsesWebSocketActiveRequestCount = (poolKey) => {
|
|
2855
|
-
responsesWebSocketActiveRequests.set(poolKey, getResponsesWebSocketActiveRequestCount(poolKey) + 1);
|
|
2856
|
-
};
|
|
2857
|
-
const decrementResponsesWebSocketActiveRequestCount = (poolKey) => {
|
|
2858
|
-
const nextCount = getResponsesWebSocketActiveRequestCount(poolKey) - 1;
|
|
2859
|
-
if (nextCount <= 0) {
|
|
2860
|
-
responsesWebSocketActiveRequests.delete(poolKey);
|
|
2861
|
-
return;
|
|
2862
|
-
}
|
|
2863
|
-
responsesWebSocketActiveRequests.set(poolKey, nextCount);
|
|
2864
|
-
};
|
|
2865
|
-
const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
|
|
2866
|
-
if (responsesWebSocketPool.get(poolKey) === entry) responsesWebSocketPool.delete(poolKey);
|
|
2867
|
-
if (entry.closed) return;
|
|
2868
|
-
entry.closed = true;
|
|
2869
|
-
clearResponsesWebSocketIdleTimer(entry);
|
|
2870
|
-
entry.websocketPromise.then(closeResponsesWebSocket).catch(() => {});
|
|
2871
|
-
};
|
|
2872
|
-
const unrefTimer = (timer) => {
|
|
2873
|
-
if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
|
|
2874
|
-
};
|
|
2875
|
-
const createResponsesWebSocketError = (message, event) => {
|
|
2876
|
-
const reason = event?.error ?? event?.message;
|
|
2877
|
-
if (reason === void 0 || reason === "") return new Error(message);
|
|
2878
|
-
const cause = toError(reason);
|
|
2879
|
-
return new Error(`${message}: ${cause.message}`, { cause });
|
|
2880
|
-
};
|
|
2881
|
-
const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
|
|
2882
|
-
const dispatcher = getProxyEnvDispatcher();
|
|
2883
|
-
const websocket = new WebSocket(url, dispatcher ? {
|
|
2884
|
-
dispatcher,
|
|
2885
|
-
headers
|
|
2886
|
-
} : { headers });
|
|
2887
|
-
const cleanup = () => {
|
|
2888
|
-
websocket.removeEventListener("open", onOpen);
|
|
2889
|
-
websocket.removeEventListener("error", onError);
|
|
2890
|
-
};
|
|
2891
|
-
const onOpen = () => {
|
|
2892
|
-
cleanup();
|
|
2893
|
-
resolve(websocket);
|
|
2894
|
-
};
|
|
2895
|
-
const onError = (event) => {
|
|
2896
|
-
cleanup();
|
|
2897
|
-
reject(createResponsesWebSocketError("Failed to create responses websocket", event));
|
|
2898
|
-
};
|
|
2899
|
-
websocket.addEventListener("open", onOpen);
|
|
2900
|
-
websocket.addEventListener("error", onError);
|
|
2901
|
-
});
|
|
2902
|
-
const createWebSocketMessageStream = async function* (websocket) {
|
|
2903
|
-
const queue = [];
|
|
2904
|
-
let closed = false;
|
|
2905
|
-
let error = null;
|
|
2906
|
-
let notify = null;
|
|
2907
|
-
const wake = () => {
|
|
2908
|
-
notify?.();
|
|
2909
|
-
notify = null;
|
|
2910
|
-
};
|
|
2911
|
-
const onMessage = (event) => {
|
|
2912
|
-
queue.push(normalizeWebSocketMessageData(event.data));
|
|
2913
|
-
wake();
|
|
2914
|
-
};
|
|
2915
|
-
const onClose = () => {
|
|
2916
|
-
closed = true;
|
|
2917
|
-
wake();
|
|
2918
|
-
};
|
|
2919
|
-
const onError = (event) => {
|
|
2920
|
-
error = createResponsesWebSocketError("Responses websocket stream error", event);
|
|
2921
|
-
wake();
|
|
2854
|
+
const createCompactionThinkingBlock = (item) => {
|
|
2855
|
+
if (!item.id || !item.encrypted_content) return null;
|
|
2856
|
+
return {
|
|
2857
|
+
type: "thinking",
|
|
2858
|
+
thinking: THINKING_TEXT,
|
|
2859
|
+
signature: encodeCompactionCarrierSignature({
|
|
2860
|
+
id: item.id,
|
|
2861
|
+
encrypted_content: item.encrypted_content
|
|
2862
|
+
})
|
|
2922
2863
|
};
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2864
|
+
};
|
|
2865
|
+
const parseFunctionCallArguments = (rawArguments) => {
|
|
2866
|
+
if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
|
|
2926
2867
|
try {
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
await new Promise((resolve) => {
|
|
2936
|
-
notify = resolve;
|
|
2937
|
-
});
|
|
2938
|
-
}
|
|
2939
|
-
} finally {
|
|
2940
|
-
websocket.removeEventListener("message", onMessage);
|
|
2941
|
-
websocket.removeEventListener("close", onClose);
|
|
2942
|
-
websocket.removeEventListener("error", onError);
|
|
2868
|
+
const parsed = JSON.parse(rawArguments);
|
|
2869
|
+
if (Array.isArray(parsed)) return { arguments: parsed };
|
|
2870
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
2871
|
+
} catch (error) {
|
|
2872
|
+
consola.warn("Failed to parse function call arguments", {
|
|
2873
|
+
error,
|
|
2874
|
+
rawArguments
|
|
2875
|
+
});
|
|
2943
2876
|
}
|
|
2877
|
+
return { raw_arguments: rawArguments };
|
|
2944
2878
|
};
|
|
2945
|
-
const
|
|
2946
|
-
|
|
2947
|
-
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
2948
|
-
if (ArrayBuffer.isView(data)) {
|
|
2949
|
-
const view = data;
|
|
2950
|
-
return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
|
|
2951
|
-
}
|
|
2952
|
-
if (isTextReadable(data)) return await data.text();
|
|
2953
|
-
return String(data);
|
|
2879
|
+
const parseToolSearchArguments = (argumentsValue) => {
|
|
2880
|
+
return formatToolSearchBridgeArguments(argumentsValue);
|
|
2954
2881
|
};
|
|
2955
|
-
const
|
|
2956
|
-
if (!
|
|
2957
|
-
return
|
|
2882
|
+
const fallbackContentBlocks = (outputText) => {
|
|
2883
|
+
if (!outputText) return [];
|
|
2884
|
+
return [{
|
|
2885
|
+
type: "text",
|
|
2886
|
+
text: outputText
|
|
2887
|
+
}];
|
|
2958
2888
|
};
|
|
2959
|
-
const
|
|
2960
|
-
|
|
2961
|
-
|
|
2889
|
+
const mapResponsesStopReason = (response, options) => {
|
|
2890
|
+
const { status, incomplete_details: incompleteDetails } = response;
|
|
2891
|
+
if (status === "completed") {
|
|
2892
|
+
if (response.output.length === 0) return options?.hasToolCall ? "tool_use" : "end_turn";
|
|
2893
|
+
if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
|
|
2894
|
+
return "end_turn";
|
|
2895
|
+
}
|
|
2896
|
+
if (status === "incomplete") {
|
|
2897
|
+
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
2898
|
+
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
2899
|
+
}
|
|
2900
|
+
return null;
|
|
2962
2901
|
};
|
|
2963
|
-
const
|
|
2964
|
-
const
|
|
2965
|
-
|
|
2902
|
+
const mapResponsesUsage = (response) => {
|
|
2903
|
+
const inputTokens = response.usage?.input_tokens ?? 0;
|
|
2904
|
+
const outputTokens = response.usage?.output_tokens ?? 0;
|
|
2905
|
+
return {
|
|
2906
|
+
input_tokens: inputTokens - (response.usage?.input_tokens_details?.cached_tokens ?? 0),
|
|
2907
|
+
output_tokens: outputTokens,
|
|
2908
|
+
...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
|
|
2909
|
+
};
|
|
2966
2910
|
};
|
|
2967
|
-
const
|
|
2968
|
-
const
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2911
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
2912
|
+
const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
|
|
2913
|
+
const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
|
|
2914
|
+
const convertToolResultContent = (content) => {
|
|
2915
|
+
if (typeof content === "string") return content;
|
|
2916
|
+
if (Array.isArray(content)) {
|
|
2917
|
+
const result = [];
|
|
2918
|
+
for (const block of content) switch (block.type) {
|
|
2919
|
+
case "text":
|
|
2920
|
+
result.push(createTextContent(block.text));
|
|
2921
|
+
break;
|
|
2922
|
+
case "image":
|
|
2923
|
+
result.push(createImageContent(block));
|
|
2924
|
+
break;
|
|
2925
|
+
case "document":
|
|
2926
|
+
result.push(createFileContent(block));
|
|
2927
|
+
break;
|
|
2928
|
+
case "tool_reference":
|
|
2929
|
+
result.push(createTextContent(`Tool ${block.tool_name} loaded`));
|
|
2930
|
+
break;
|
|
2931
|
+
default: break;
|
|
2932
|
+
}
|
|
2933
|
+
return result;
|
|
2980
2934
|
}
|
|
2935
|
+
return "";
|
|
2981
2936
|
};
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2937
|
+
//#endregion
|
|
2938
|
+
//#region src/routes/messages/responses-stream-translation.ts
|
|
2939
|
+
const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
|
|
2940
|
+
var FunctionCallArgumentsValidationError = class extends Error {
|
|
2941
|
+
constructor(message) {
|
|
2942
|
+
super(message);
|
|
2943
|
+
this.name = "FunctionCallArgumentsValidationError";
|
|
2989
2944
|
}
|
|
2990
2945
|
};
|
|
2991
|
-
const
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2946
|
+
const updateWhitespaceRunState = (previousCount, chunk) => {
|
|
2947
|
+
let count = previousCount;
|
|
2948
|
+
for (const char of chunk) {
|
|
2949
|
+
if (char === "\r" || char === "\n" || char === " ") {
|
|
2950
|
+
count += 1;
|
|
2951
|
+
if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
|
|
2952
|
+
nextCount: count,
|
|
2953
|
+
exceeded: true
|
|
2954
|
+
};
|
|
2955
|
+
continue;
|
|
2956
|
+
}
|
|
2957
|
+
if (char !== " ") count = 0;
|
|
2997
2958
|
}
|
|
2998
|
-
|
|
2959
|
+
return {
|
|
2960
|
+
nextCount: count,
|
|
2961
|
+
exceeded: false
|
|
2962
|
+
};
|
|
2999
2963
|
};
|
|
3000
|
-
const
|
|
3001
|
-
|
|
2964
|
+
const createResponsesStreamState = (options) => ({
|
|
2965
|
+
messageStartSent: false,
|
|
2966
|
+
messageCompleted: false,
|
|
2967
|
+
nextContentBlockIndex: 0,
|
|
2968
|
+
blockIndexByKey: /* @__PURE__ */ new Map(),
|
|
2969
|
+
openBlocks: /* @__PURE__ */ new Set(),
|
|
2970
|
+
blockHasDelta: /* @__PURE__ */ new Set(),
|
|
2971
|
+
functionCallStateByOutputIndex: /* @__PURE__ */ new Map(),
|
|
2972
|
+
toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search",
|
|
2973
|
+
hasToolCall: false
|
|
2974
|
+
});
|
|
2975
|
+
const translateResponsesStreamEvent = (rawEvent, state) => {
|
|
2976
|
+
switch (rawEvent.type) {
|
|
2977
|
+
case "response.created": return handleResponseCreated(rawEvent, state);
|
|
2978
|
+
case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state);
|
|
2979
|
+
case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state);
|
|
2980
|
+
case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state);
|
|
2981
|
+
case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state);
|
|
2982
|
+
case "response.output_text.done": return handleOutputTextDone(rawEvent, state);
|
|
2983
|
+
case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state);
|
|
2984
|
+
case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state);
|
|
2985
|
+
case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state);
|
|
2986
|
+
case "response.completed":
|
|
2987
|
+
case "response.incomplete": return handleResponseCompleted(rawEvent, state);
|
|
2988
|
+
case "response.failed": return handleResponseFailed(rawEvent, state);
|
|
2989
|
+
case "error": return handleErrorEvent(rawEvent, state);
|
|
2990
|
+
default: return [];
|
|
2991
|
+
}
|
|
3002
2992
|
};
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
const MESSAGE_TYPE = "message";
|
|
3006
|
-
const COMPACTION_SIGNATURE_PREFIX = "cm1#";
|
|
3007
|
-
const COMPACTION_SIGNATURE_SEPARATOR = "@";
|
|
3008
|
-
const THINKING_TEXT = "Thinking...";
|
|
3009
|
-
const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
|
|
3010
|
-
if (!basePromptCacheKey) return null;
|
|
3011
|
-
const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
|
|
3012
|
-
if (!normalizedSubagentAgentId) return basePromptCacheKey;
|
|
3013
|
-
return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
|
|
2993
|
+
const handleResponseCreated = (rawEvent, state) => {
|
|
2994
|
+
return messageStart(state, rawEvent.response);
|
|
3014
2995
|
};
|
|
3015
|
-
const
|
|
3016
|
-
const
|
|
3017
|
-
const
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
2996
|
+
const handleOutputItemAdded$1 = (rawEvent, state) => {
|
|
2997
|
+
const events = new Array();
|
|
2998
|
+
const functionCallDetails = extractFunctionCallDetails(rawEvent, state);
|
|
2999
|
+
if (!functionCallDetails) return events;
|
|
3000
|
+
const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
|
|
3001
|
+
const blockIndex = openFunctionCallBlock(state, {
|
|
3002
|
+
outputIndex,
|
|
3003
|
+
toolCallId,
|
|
3004
|
+
name,
|
|
3005
|
+
events
|
|
3021
3006
|
});
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
|
|
3033
|
-
const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
|
|
3034
|
-
const responsesPayload = {
|
|
3035
|
-
model: payload.model,
|
|
3036
|
-
input,
|
|
3037
|
-
instructions: translateSystemPrompt(payload.system, payload.model),
|
|
3038
|
-
temperature: 1,
|
|
3039
|
-
top_p: payload.top_p ?? null,
|
|
3040
|
-
max_output_tokens: Math.max(payload.max_tokens, 12800),
|
|
3041
|
-
tools: translatedTools,
|
|
3042
|
-
tool_choice: toolChoice,
|
|
3043
|
-
metadata: payload.metadata ? { ...payload.metadata } : null,
|
|
3044
|
-
stream: payload.stream ?? null,
|
|
3045
|
-
store: false,
|
|
3046
|
-
parallel_tool_calls: true,
|
|
3047
|
-
reasoning: {
|
|
3048
|
-
effort: getReasoningEffortForModel(payload.model),
|
|
3049
|
-
summary: "detailed"
|
|
3050
|
-
},
|
|
3051
|
-
include: ["reasoning.encrypted_content"]
|
|
3052
|
-
};
|
|
3053
|
-
if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
|
|
3054
|
-
return responsesPayload;
|
|
3055
|
-
};
|
|
3056
|
-
const encodeCompactionCarrierSignature = (compaction) => {
|
|
3057
|
-
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
3058
|
-
};
|
|
3059
|
-
const decodeCompactionCarrierSignature = (signature) => {
|
|
3060
|
-
if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
|
|
3061
|
-
const raw = signature.slice(4);
|
|
3062
|
-
const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
|
|
3063
|
-
if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
|
|
3064
|
-
const encrypted_content = raw.slice(0, separatorIndex);
|
|
3065
|
-
const id = raw.slice(separatorIndex + 1);
|
|
3066
|
-
if (!encrypted_content) return;
|
|
3067
|
-
return {
|
|
3068
|
-
id,
|
|
3069
|
-
encrypted_content
|
|
3070
|
-
};
|
|
3007
|
+
if (initialArguments !== void 0 && initialArguments.length > 0) {
|
|
3008
|
+
events.push({
|
|
3009
|
+
type: "content_block_delta",
|
|
3010
|
+
index: blockIndex,
|
|
3011
|
+
delta: {
|
|
3012
|
+
type: "input_json_delta",
|
|
3013
|
+
partial_json: initialArguments
|
|
3014
|
+
}
|
|
3015
|
+
});
|
|
3016
|
+
state.blockHasDelta.add(blockIndex);
|
|
3071
3017
|
}
|
|
3018
|
+
return events;
|
|
3072
3019
|
};
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
const
|
|
3078
|
-
if (
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
}
|
|
3094
|
-
const translateAssistantMessage = (message, model, applyPhase, state) => {
|
|
3095
|
-
const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
|
|
3096
|
-
if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
|
|
3097
|
-
if (!Array.isArray(message.content)) return [];
|
|
3098
|
-
const items = [];
|
|
3099
|
-
const pendingContent = [];
|
|
3100
|
-
for (const block of message.content) {
|
|
3101
|
-
if (block.type === "tool_use") {
|
|
3102
|
-
state.toolUseNameById.set(block.id, block.name);
|
|
3103
|
-
flushPendingContent(pendingContent, items, {
|
|
3104
|
-
role: "assistant",
|
|
3105
|
-
phase: assistantPhase
|
|
3020
|
+
const handleOutputItemDone$1 = (rawEvent, state) => {
|
|
3021
|
+
const events = new Array();
|
|
3022
|
+
const item = rawEvent.item;
|
|
3023
|
+
const itemType = item.type;
|
|
3024
|
+
const outputIndex = rawEvent.output_index;
|
|
3025
|
+
if (itemType === "tool_search_call") {
|
|
3026
|
+
const blockIndex = openFunctionCallBlock(state, {
|
|
3027
|
+
outputIndex,
|
|
3028
|
+
toolCallId: item.call_id,
|
|
3029
|
+
name: state.toolSearchName,
|
|
3030
|
+
events
|
|
3031
|
+
});
|
|
3032
|
+
const finalArguments = stringifyToolSearchArguments(item.arguments);
|
|
3033
|
+
if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
|
|
3034
|
+
events.push({
|
|
3035
|
+
type: "content_block_delta",
|
|
3036
|
+
index: blockIndex,
|
|
3037
|
+
delta: {
|
|
3038
|
+
type: "input_json_delta",
|
|
3039
|
+
partial_json: finalArguments
|
|
3040
|
+
}
|
|
3106
3041
|
});
|
|
3107
|
-
|
|
3108
|
-
continue;
|
|
3042
|
+
state.blockHasDelta.add(blockIndex);
|
|
3109
3043
|
}
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3044
|
+
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3045
|
+
return events;
|
|
3046
|
+
}
|
|
3047
|
+
if (itemType === "compaction") {
|
|
3048
|
+
if (!item.id || !item.encrypted_content) return events;
|
|
3049
|
+
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3050
|
+
if (!state.blockHasDelta.has(blockIndex)) events.push({
|
|
3051
|
+
type: "content_block_delta",
|
|
3052
|
+
index: blockIndex,
|
|
3053
|
+
delta: {
|
|
3054
|
+
type: "thinking_delta",
|
|
3055
|
+
thinking: THINKING_TEXT
|
|
3119
3056
|
}
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3057
|
+
});
|
|
3058
|
+
events.push({
|
|
3059
|
+
type: "content_block_delta",
|
|
3060
|
+
index: blockIndex,
|
|
3061
|
+
delta: {
|
|
3062
|
+
type: "signature_delta",
|
|
3063
|
+
signature: encodeCompactionCarrierSignature({
|
|
3064
|
+
id: item.id,
|
|
3065
|
+
encrypted_content: item.encrypted_content
|
|
3066
|
+
})
|
|
3127
3067
|
}
|
|
3128
|
-
}
|
|
3129
|
-
|
|
3130
|
-
|
|
3068
|
+
});
|
|
3069
|
+
state.blockHasDelta.add(blockIndex);
|
|
3070
|
+
return events;
|
|
3131
3071
|
}
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3072
|
+
if (itemType !== "reasoning") return events;
|
|
3073
|
+
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3074
|
+
const signature = (item.encrypted_content ?? "") + "@" + item.id;
|
|
3075
|
+
if (signature) {
|
|
3076
|
+
if (!item.summary || item.summary.length === 0) events.push({
|
|
3077
|
+
type: "content_block_delta",
|
|
3078
|
+
index: blockIndex,
|
|
3079
|
+
delta: {
|
|
3080
|
+
type: "thinking_delta",
|
|
3081
|
+
thinking: THINKING_TEXT
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
events.push({
|
|
3085
|
+
type: "content_block_delta",
|
|
3086
|
+
index: blockIndex,
|
|
3087
|
+
delta: {
|
|
3088
|
+
type: "signature_delta",
|
|
3089
|
+
signature
|
|
3090
|
+
}
|
|
3091
|
+
});
|
|
3092
|
+
state.blockHasDelta.add(blockIndex);
|
|
3144
3093
|
}
|
|
3094
|
+
return events;
|
|
3145
3095
|
};
|
|
3146
|
-
const
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3096
|
+
const handleFunctionCallArgumentsDelta = (rawEvent, state) => {
|
|
3097
|
+
const events = new Array();
|
|
3098
|
+
const outputIndex = rawEvent.output_index;
|
|
3099
|
+
const deltaText = rawEvent.delta;
|
|
3100
|
+
if (!deltaText) return events;
|
|
3101
|
+
const blockIndex = openFunctionCallBlock(state, {
|
|
3102
|
+
outputIndex,
|
|
3103
|
+
events
|
|
3104
|
+
});
|
|
3105
|
+
const functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
|
|
3106
|
+
if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state, events);
|
|
3107
|
+
const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
|
|
3108
|
+
if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state, events);
|
|
3109
|
+
functionCallState.consecutiveWhitespaceCount = nextCount;
|
|
3110
|
+
events.push({
|
|
3111
|
+
type: "content_block_delta",
|
|
3112
|
+
index: blockIndex,
|
|
3113
|
+
delta: {
|
|
3114
|
+
type: "input_json_delta",
|
|
3115
|
+
partial_json: deltaText
|
|
3116
|
+
}
|
|
3117
|
+
});
|
|
3118
|
+
state.blockHasDelta.add(blockIndex);
|
|
3119
|
+
return events;
|
|
3120
|
+
};
|
|
3121
|
+
const handleFunctionCallArgumentsDone = (rawEvent, state) => {
|
|
3122
|
+
const events = new Array();
|
|
3123
|
+
const outputIndex = rawEvent.output_index;
|
|
3124
|
+
const blockIndex = openFunctionCallBlock(state, {
|
|
3125
|
+
outputIndex,
|
|
3126
|
+
events
|
|
3127
|
+
});
|
|
3128
|
+
const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
|
|
3129
|
+
if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
|
|
3130
|
+
events.push({
|
|
3131
|
+
type: "content_block_delta",
|
|
3132
|
+
index: blockIndex,
|
|
3133
|
+
delta: {
|
|
3134
|
+
type: "input_json_delta",
|
|
3135
|
+
partial_json: finalArguments
|
|
3136
|
+
}
|
|
3137
|
+
});
|
|
3138
|
+
state.blockHasDelta.add(blockIndex);
|
|
3150
3139
|
}
|
|
3140
|
+
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3141
|
+
return events;
|
|
3151
3142
|
};
|
|
3152
|
-
const
|
|
3153
|
-
|
|
3154
|
-
const
|
|
3155
|
-
|
|
3156
|
-
|
|
3143
|
+
const handleOutputTextDelta = (rawEvent, state) => {
|
|
3144
|
+
const events = new Array();
|
|
3145
|
+
const outputIndex = rawEvent.output_index;
|
|
3146
|
+
const contentIndex = rawEvent.content_index;
|
|
3147
|
+
const deltaText = rawEvent.delta;
|
|
3148
|
+
if (!deltaText) return events;
|
|
3149
|
+
const blockIndex = openTextBlockIfNeeded(state, {
|
|
3150
|
+
outputIndex,
|
|
3151
|
+
contentIndex,
|
|
3152
|
+
events
|
|
3153
|
+
});
|
|
3154
|
+
events.push({
|
|
3155
|
+
type: "content_block_delta",
|
|
3156
|
+
index: blockIndex,
|
|
3157
|
+
delta: {
|
|
3158
|
+
type: "text_delta",
|
|
3159
|
+
text: deltaText
|
|
3160
|
+
}
|
|
3161
|
+
});
|
|
3162
|
+
state.blockHasDelta.add(blockIndex);
|
|
3163
|
+
return events;
|
|
3157
3164
|
};
|
|
3158
|
-
const
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3165
|
+
const handleReasoningSummaryTextDelta = (rawEvent, state) => {
|
|
3166
|
+
const outputIndex = rawEvent.output_index;
|
|
3167
|
+
const deltaText = rawEvent.delta;
|
|
3168
|
+
const events = new Array();
|
|
3169
|
+
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3170
|
+
events.push({
|
|
3171
|
+
type: "content_block_delta",
|
|
3172
|
+
index: blockIndex,
|
|
3173
|
+
delta: {
|
|
3174
|
+
type: "thinking_delta",
|
|
3175
|
+
thinking: deltaText
|
|
3176
|
+
}
|
|
3177
|
+
});
|
|
3178
|
+
state.blockHasDelta.add(blockIndex);
|
|
3179
|
+
return events;
|
|
3170
3180
|
};
|
|
3171
|
-
const
|
|
3172
|
-
|
|
3181
|
+
const handleReasoningSummaryTextDone = (rawEvent, state) => {
|
|
3182
|
+
const outputIndex = rawEvent.output_index;
|
|
3183
|
+
const text = rawEvent.text;
|
|
3184
|
+
const events = new Array();
|
|
3185
|
+
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3186
|
+
if (text && !state.blockHasDelta.has(blockIndex)) events.push({
|
|
3187
|
+
type: "content_block_delta",
|
|
3188
|
+
index: blockIndex,
|
|
3189
|
+
delta: {
|
|
3190
|
+
type: "thinking_delta",
|
|
3191
|
+
thinking: text
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
3194
|
+
return events;
|
|
3173
3195
|
};
|
|
3174
|
-
const
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
const
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
});
|
|
3192
|
-
|
|
3193
|
-
const { encryptedContent, id } = parseReasoningSignature(block.signature);
|
|
3194
|
-
const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
|
|
3195
|
-
return {
|
|
3196
|
-
id,
|
|
3197
|
-
type: "reasoning",
|
|
3198
|
-
summary: thinking ? [{
|
|
3199
|
-
type: "summary_text",
|
|
3200
|
-
text: thinking
|
|
3201
|
-
}] : [],
|
|
3202
|
-
encrypted_content: encryptedContent
|
|
3203
|
-
};
|
|
3204
|
-
};
|
|
3205
|
-
const createCompactionContent = (block) => {
|
|
3206
|
-
const compaction = decodeCompactionCarrierSignature(block.signature);
|
|
3207
|
-
if (!compaction) return;
|
|
3208
|
-
return {
|
|
3209
|
-
id: compaction.id,
|
|
3210
|
-
type: "compaction",
|
|
3211
|
-
encrypted_content: compaction.encrypted_content
|
|
3212
|
-
};
|
|
3213
|
-
};
|
|
3214
|
-
const parseReasoningSignature = (signature) => {
|
|
3215
|
-
const splitIndex = signature.lastIndexOf("@");
|
|
3216
|
-
if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
|
|
3217
|
-
encryptedContent: signature,
|
|
3218
|
-
id: ""
|
|
3219
|
-
};
|
|
3220
|
-
return {
|
|
3221
|
-
encryptedContent: signature.slice(0, splitIndex),
|
|
3222
|
-
id: signature.slice(splitIndex + 1)
|
|
3223
|
-
};
|
|
3196
|
+
const handleOutputTextDone = (rawEvent, state) => {
|
|
3197
|
+
const events = new Array();
|
|
3198
|
+
const outputIndex = rawEvent.output_index;
|
|
3199
|
+
const contentIndex = rawEvent.content_index;
|
|
3200
|
+
const text = rawEvent.text;
|
|
3201
|
+
const blockIndex = openTextBlockIfNeeded(state, {
|
|
3202
|
+
outputIndex,
|
|
3203
|
+
contentIndex,
|
|
3204
|
+
events
|
|
3205
|
+
});
|
|
3206
|
+
if (text && !state.blockHasDelta.has(blockIndex)) events.push({
|
|
3207
|
+
type: "content_block_delta",
|
|
3208
|
+
index: blockIndex,
|
|
3209
|
+
delta: {
|
|
3210
|
+
type: "text_delta",
|
|
3211
|
+
text
|
|
3212
|
+
}
|
|
3213
|
+
});
|
|
3214
|
+
return events;
|
|
3224
3215
|
};
|
|
3225
|
-
const
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
});
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
return
|
|
3216
|
+
const handleResponseCompleted = (rawEvent, state) => {
|
|
3217
|
+
const response = rawEvent.response;
|
|
3218
|
+
const events = new Array();
|
|
3219
|
+
closeAllOpenBlocks(state, events);
|
|
3220
|
+
const anthropic = translateResponsesResultToAnthropic(response, {
|
|
3221
|
+
hasToolCall: state.hasToolCall,
|
|
3222
|
+
toolSearchName: state.toolSearchName
|
|
3223
|
+
});
|
|
3224
|
+
events.push({
|
|
3225
|
+
type: "message_delta",
|
|
3226
|
+
delta: {
|
|
3227
|
+
stop_reason: anthropic.stop_reason,
|
|
3228
|
+
stop_sequence: anthropic.stop_sequence
|
|
3229
|
+
},
|
|
3230
|
+
usage: anthropic.usage
|
|
3231
|
+
}, { type: "message_stop" });
|
|
3232
|
+
state.messageCompleted = true;
|
|
3233
|
+
return events;
|
|
3243
3234
|
};
|
|
3244
|
-
const
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
if (state.toolSearchEnabled && isBridgeToolSearchName(toolUseName ?? "")) return createToolSearchOutput(block, state.originalTools);
|
|
3253
|
-
return createFunctionCallOutput(block);
|
|
3235
|
+
const handleResponseFailed = (rawEvent, state) => {
|
|
3236
|
+
const response = rawEvent.response;
|
|
3237
|
+
const events = new Array();
|
|
3238
|
+
closeAllOpenBlocks(state, events);
|
|
3239
|
+
const message = response.error?.message ?? "The response failed due to an unknown error.";
|
|
3240
|
+
events.push(buildErrorEvent(message));
|
|
3241
|
+
state.messageCompleted = true;
|
|
3242
|
+
return events;
|
|
3254
3243
|
};
|
|
3255
|
-
const
|
|
3256
|
-
const
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
call_id: block.tool_use_id,
|
|
3260
|
-
tools: referencedToolNames.map((toolName) => convertDeferredToolToNamespace(resolveDeferredTool(toolName, originalTools))),
|
|
3261
|
-
execution: "client",
|
|
3262
|
-
status: block.is_error ? "incomplete" : "completed"
|
|
3263
|
-
};
|
|
3244
|
+
const handleErrorEvent = (rawEvent, state) => {
|
|
3245
|
+
const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
|
|
3246
|
+
state.messageCompleted = true;
|
|
3247
|
+
return [buildErrorEvent(message)];
|
|
3264
3248
|
};
|
|
3265
|
-
const
|
|
3266
|
-
const
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
return
|
|
3249
|
+
const handleFunctionCallArgumentsValidationError = (error, state, events = []) => {
|
|
3250
|
+
const reason = error.message;
|
|
3251
|
+
closeAllOpenBlocks(state, events);
|
|
3252
|
+
state.messageCompleted = true;
|
|
3253
|
+
events.push(buildErrorEvent(reason));
|
|
3254
|
+
return events;
|
|
3271
3255
|
};
|
|
3272
|
-
const
|
|
3273
|
-
|
|
3274
|
-
|
|
3256
|
+
const messageStart = (state, response) => {
|
|
3257
|
+
state.messageStartSent = true;
|
|
3258
|
+
const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
|
|
3259
|
+
const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
|
|
3260
|
+
return [{
|
|
3261
|
+
type: "message_start",
|
|
3262
|
+
message: {
|
|
3263
|
+
id: response.id,
|
|
3264
|
+
type: "message",
|
|
3265
|
+
role: "assistant",
|
|
3266
|
+
content: [],
|
|
3267
|
+
model: response.model,
|
|
3268
|
+
stop_reason: null,
|
|
3269
|
+
stop_sequence: null,
|
|
3270
|
+
usage: {
|
|
3271
|
+
input_tokens: inputTokens,
|
|
3272
|
+
output_tokens: 0,
|
|
3273
|
+
cache_read_input_tokens: inputCachedTokens ?? 0
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
}];
|
|
3275
3277
|
};
|
|
3276
|
-
const
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3278
|
+
const openTextBlockIfNeeded = (state, params) => {
|
|
3279
|
+
const { outputIndex, contentIndex, events } = params;
|
|
3280
|
+
const key = getBlockKey(outputIndex, contentIndex);
|
|
3281
|
+
let blockIndex = state.blockIndexByKey.get(key);
|
|
3282
|
+
if (blockIndex === void 0) {
|
|
3283
|
+
blockIndex = state.nextContentBlockIndex;
|
|
3284
|
+
state.nextContentBlockIndex += 1;
|
|
3285
|
+
state.blockIndexByKey.set(key, blockIndex);
|
|
3282
3286
|
}
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
const createInvalidRequestError = (message) => new HTTPError(message, new Response(JSON.stringify({ error: {
|
|
3292
|
-
message,
|
|
3293
|
-
type: "invalid_request_error"
|
|
3294
|
-
} }), {
|
|
3295
|
-
status: 400,
|
|
3296
|
-
headers: { "content-type": "application/json" }
|
|
3297
|
-
}));
|
|
3298
|
-
const translateSystemPrompt = (system, model) => {
|
|
3299
|
-
if (!system) return null;
|
|
3300
|
-
const extraPrompt = getExtraPromptForModel(model);
|
|
3301
|
-
if (typeof system === "string") return system + extraPrompt;
|
|
3302
|
-
const text = system.map((block, index) => {
|
|
3303
|
-
if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
|
|
3304
|
-
return block.text;
|
|
3305
|
-
}).join(" ");
|
|
3306
|
-
return text.length > 0 ? text : null;
|
|
3307
|
-
};
|
|
3308
|
-
const convertAnthropicTools = (tools, toolSearchEnabled) => {
|
|
3309
|
-
if (!tools || tools.length === 0) return null;
|
|
3310
|
-
const converted = [];
|
|
3311
|
-
let addedToolSearch = false;
|
|
3312
|
-
const searchableToolNames = toolSearchEnabled ? listDeferredToolNames(tools) : [];
|
|
3313
|
-
for (const tool of tools) {
|
|
3314
|
-
if (isBridgeToolSearchName(tool.name)) {
|
|
3315
|
-
if (toolSearchEnabled && !addedToolSearch) {
|
|
3316
|
-
converted.push(createResponsesToolSearchDefinition(searchableToolNames));
|
|
3317
|
-
addedToolSearch = true;
|
|
3287
|
+
if (!state.openBlocks.has(blockIndex)) {
|
|
3288
|
+
closeOpenBlocks(state, events);
|
|
3289
|
+
events.push({
|
|
3290
|
+
type: "content_block_start",
|
|
3291
|
+
index: blockIndex,
|
|
3292
|
+
content_block: {
|
|
3293
|
+
type: "text",
|
|
3294
|
+
text: ""
|
|
3318
3295
|
}
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
if (toolSearchEnabled && isDeferredToolName(tool.name)) {
|
|
3322
|
-
converted.push(convertDeferredToolToNamespace(tool));
|
|
3323
|
-
continue;
|
|
3324
|
-
}
|
|
3325
|
-
converted.push(convertToolToFunction(tool));
|
|
3296
|
+
});
|
|
3297
|
+
state.openBlocks.add(blockIndex);
|
|
3326
3298
|
}
|
|
3327
|
-
return
|
|
3299
|
+
return blockIndex;
|
|
3328
3300
|
};
|
|
3329
|
-
const
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
type: "array",
|
|
3337
|
-
description: "Exact deferred tool names to load.",
|
|
3338
|
-
items: {
|
|
3339
|
-
type: "string",
|
|
3340
|
-
enum: searchableToolNames
|
|
3341
|
-
},
|
|
3342
|
-
minItems: 1
|
|
3343
|
-
} },
|
|
3344
|
-
required: ["names"],
|
|
3345
|
-
additionalProperties: false
|
|
3301
|
+
const openThinkingBlockIfNeeded = (state, outputIndex, events) => {
|
|
3302
|
+
const key = getBlockKey(outputIndex, 0);
|
|
3303
|
+
let blockIndex = state.blockIndexByKey.get(key);
|
|
3304
|
+
if (blockIndex === void 0) {
|
|
3305
|
+
blockIndex = state.nextContentBlockIndex;
|
|
3306
|
+
state.nextContentBlockIndex += 1;
|
|
3307
|
+
state.blockIndexByKey.set(key, blockIndex);
|
|
3346
3308
|
}
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
...tool.description ? { description: tool.description } : {},
|
|
3359
|
-
tools: [{
|
|
3360
|
-
type: "function",
|
|
3361
|
-
name: tool.name,
|
|
3362
|
-
parameters: normalizeToolSchema(tool.input_schema),
|
|
3363
|
-
strict: false,
|
|
3364
|
-
defer_loading: true,
|
|
3365
|
-
...tool.description ? { description: tool.description } : {}
|
|
3366
|
-
}]
|
|
3367
|
-
});
|
|
3368
|
-
const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
|
|
3369
|
-
if (!choice) return "auto";
|
|
3370
|
-
switch (choice.type) {
|
|
3371
|
-
case "auto": return "auto";
|
|
3372
|
-
case "any": return "required";
|
|
3373
|
-
case "tool":
|
|
3374
|
-
if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
|
|
3375
|
-
return choice.name ? {
|
|
3376
|
-
type: "function",
|
|
3377
|
-
name: choice.name
|
|
3378
|
-
} : "auto";
|
|
3379
|
-
case "none": return "none";
|
|
3380
|
-
default: return "auto";
|
|
3309
|
+
if (!state.openBlocks.has(blockIndex)) {
|
|
3310
|
+
closeOpenBlocks(state, events);
|
|
3311
|
+
events.push({
|
|
3312
|
+
type: "content_block_start",
|
|
3313
|
+
index: blockIndex,
|
|
3314
|
+
content_block: {
|
|
3315
|
+
type: "thinking",
|
|
3316
|
+
thinking: ""
|
|
3317
|
+
}
|
|
3318
|
+
});
|
|
3319
|
+
state.openBlocks.add(blockIndex);
|
|
3381
3320
|
}
|
|
3321
|
+
return blockIndex;
|
|
3382
3322
|
};
|
|
3383
|
-
const
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
type: "message",
|
|
3392
|
-
role: "assistant",
|
|
3393
|
-
content: anthropicContent,
|
|
3394
|
-
model: response.model,
|
|
3395
|
-
stop_reason: stopReason,
|
|
3396
|
-
stop_sequence: null,
|
|
3397
|
-
usage
|
|
3398
|
-
};
|
|
3323
|
+
const closeBlockIfOpen = (state, blockIndex, events) => {
|
|
3324
|
+
if (!state.openBlocks.has(blockIndex)) return;
|
|
3325
|
+
events.push({
|
|
3326
|
+
type: "content_block_stop",
|
|
3327
|
+
index: blockIndex
|
|
3328
|
+
});
|
|
3329
|
+
state.openBlocks.delete(blockIndex);
|
|
3330
|
+
state.blockHasDelta.delete(blockIndex);
|
|
3399
3331
|
};
|
|
3400
|
-
const
|
|
3401
|
-
const
|
|
3402
|
-
for (const item of output) switch (item.type) {
|
|
3403
|
-
case "reasoning": {
|
|
3404
|
-
const thinkingText = extractReasoningText(item);
|
|
3405
|
-
if (thinkingText.length > 0) contentBlocks.push({
|
|
3406
|
-
type: "thinking",
|
|
3407
|
-
thinking: thinkingText,
|
|
3408
|
-
signature: (item.encrypted_content ?? "") + "@" + item.id
|
|
3409
|
-
});
|
|
3410
|
-
break;
|
|
3411
|
-
}
|
|
3412
|
-
case "function_call": {
|
|
3413
|
-
const toolUseBlock = createToolUseContentBlock(item);
|
|
3414
|
-
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
3415
|
-
break;
|
|
3416
|
-
}
|
|
3417
|
-
case "tool_search_call": {
|
|
3418
|
-
const toolUseBlock = createToolSearchUseContentBlock(item, options?.toolSearchName);
|
|
3419
|
-
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
3420
|
-
break;
|
|
3421
|
-
}
|
|
3422
|
-
case "tool_search_output": break;
|
|
3423
|
-
case "message": {
|
|
3424
|
-
const combinedText = combineMessageTextContent(item.content);
|
|
3425
|
-
if (combinedText.length > 0) contentBlocks.push({
|
|
3426
|
-
type: "text",
|
|
3427
|
-
text: combinedText
|
|
3428
|
-
});
|
|
3429
|
-
break;
|
|
3430
|
-
}
|
|
3431
|
-
case "compaction": {
|
|
3432
|
-
const compactionBlock = createCompactionThinkingBlock(item);
|
|
3433
|
-
if (compactionBlock) contentBlocks.push(compactionBlock);
|
|
3434
|
-
break;
|
|
3435
|
-
}
|
|
3436
|
-
default: {
|
|
3437
|
-
const combinedText = combineMessageTextContent(item.content);
|
|
3438
|
-
if (combinedText.length > 0) contentBlocks.push({
|
|
3439
|
-
type: "text",
|
|
3440
|
-
text: combinedText
|
|
3441
|
-
});
|
|
3442
|
-
}
|
|
3443
|
-
}
|
|
3444
|
-
return contentBlocks;
|
|
3332
|
+
const closeOpenBlocks = (state, events) => {
|
|
3333
|
+
for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
|
|
3445
3334
|
};
|
|
3446
|
-
const
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
for (const block of content) {
|
|
3450
|
-
if (isResponseOutputText(block)) {
|
|
3451
|
-
aggregated += block.text;
|
|
3452
|
-
continue;
|
|
3453
|
-
}
|
|
3454
|
-
if (isResponseOutputRefusal(block)) {
|
|
3455
|
-
aggregated += block.refusal;
|
|
3456
|
-
continue;
|
|
3457
|
-
}
|
|
3458
|
-
if (typeof block.text === "string") {
|
|
3459
|
-
aggregated += block.text;
|
|
3460
|
-
continue;
|
|
3461
|
-
}
|
|
3462
|
-
if (typeof block.reasoning === "string") {
|
|
3463
|
-
aggregated += block.reasoning;
|
|
3464
|
-
continue;
|
|
3465
|
-
}
|
|
3466
|
-
}
|
|
3467
|
-
return aggregated;
|
|
3335
|
+
const closeAllOpenBlocks = (state, events) => {
|
|
3336
|
+
closeOpenBlocks(state, events);
|
|
3337
|
+
state.functionCallStateByOutputIndex.clear();
|
|
3468
3338
|
};
|
|
3469
|
-
const
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3339
|
+
const buildErrorEvent = (message) => ({
|
|
3340
|
+
type: "error",
|
|
3341
|
+
error: {
|
|
3342
|
+
type: "api_error",
|
|
3343
|
+
message
|
|
3344
|
+
}
|
|
3345
|
+
});
|
|
3346
|
+
const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
|
|
3347
|
+
const openFunctionCallBlock = (state, params) => {
|
|
3348
|
+
const { outputIndex, toolCallId, name, events } = params;
|
|
3349
|
+
state.hasToolCall = true;
|
|
3350
|
+
let functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
|
|
3351
|
+
if (!functionCallState) {
|
|
3352
|
+
const blockIndex = state.nextContentBlockIndex;
|
|
3353
|
+
state.nextContentBlockIndex += 1;
|
|
3354
|
+
functionCallState = {
|
|
3355
|
+
blockIndex,
|
|
3356
|
+
toolCallId: toolCallId ?? `tool_call_${blockIndex}`,
|
|
3357
|
+
name: name ?? "function",
|
|
3358
|
+
consecutiveWhitespaceCount: 0
|
|
3359
|
+
};
|
|
3360
|
+
state.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
|
|
3361
|
+
}
|
|
3362
|
+
const { blockIndex } = functionCallState;
|
|
3363
|
+
if (!state.openBlocks.has(blockIndex)) {
|
|
3364
|
+
closeOpenBlocks(state, events);
|
|
3365
|
+
events.push({
|
|
3366
|
+
type: "content_block_start",
|
|
3367
|
+
index: blockIndex,
|
|
3368
|
+
content_block: {
|
|
3369
|
+
type: "tool_use",
|
|
3370
|
+
id: functionCallState.toolCallId,
|
|
3371
|
+
name: functionCallState.name,
|
|
3372
|
+
input: {}
|
|
3373
|
+
}
|
|
3374
|
+
});
|
|
3375
|
+
state.openBlocks.add(blockIndex);
|
|
3376
|
+
}
|
|
3377
|
+
return blockIndex;
|
|
3481
3378
|
};
|
|
3482
|
-
const
|
|
3483
|
-
const
|
|
3484
|
-
const
|
|
3485
|
-
if (
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
input: parseFunctionCallArguments(call.arguments)
|
|
3379
|
+
const extractFunctionCallDetails = (rawEvent, state) => {
|
|
3380
|
+
const item = rawEvent.item;
|
|
3381
|
+
const itemType = item.type;
|
|
3382
|
+
if (itemType === "tool_search_call") return {
|
|
3383
|
+
outputIndex: rawEvent.output_index,
|
|
3384
|
+
toolCallId: item.call_id,
|
|
3385
|
+
name: state.toolSearchName,
|
|
3386
|
+
initialArguments: ""
|
|
3491
3387
|
};
|
|
3492
|
-
|
|
3493
|
-
const createToolSearchUseContentBlock = (call, toolSearchName = BRIDGE_TOOL_SEARCH_NAME) => {
|
|
3494
|
-
const toolId = call.call_id;
|
|
3495
|
-
if (!toolId) return null;
|
|
3388
|
+
if (itemType !== "function_call") return;
|
|
3496
3389
|
return {
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
name:
|
|
3500
|
-
|
|
3390
|
+
outputIndex: rawEvent.output_index,
|
|
3391
|
+
toolCallId: item.call_id,
|
|
3392
|
+
name: resolveToolUseName(item),
|
|
3393
|
+
initialArguments: item.arguments
|
|
3501
3394
|
};
|
|
3502
3395
|
};
|
|
3503
|
-
const
|
|
3504
|
-
|
|
3505
|
-
|
|
3396
|
+
const stringifyToolSearchArguments = (argumentsValue) => {
|
|
3397
|
+
try {
|
|
3398
|
+
return JSON.stringify(formatToolSearchBridgeArguments(argumentsValue));
|
|
3399
|
+
} catch {
|
|
3400
|
+
return;
|
|
3401
|
+
}
|
|
3506
3402
|
};
|
|
3507
|
-
const
|
|
3508
|
-
|
|
3403
|
+
const responsesUtilsDependencies = {
|
|
3404
|
+
isResponsesApiContextManagementModel,
|
|
3405
|
+
isResponsesApiWebSocketEnabled
|
|
3406
|
+
};
|
|
3407
|
+
const getResponsesRequestOptions = (payload) => {
|
|
3509
3408
|
return {
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
signature: encodeCompactionCarrierSignature({
|
|
3513
|
-
id: item.id,
|
|
3514
|
-
encrypted_content: item.encrypted_content
|
|
3515
|
-
})
|
|
3409
|
+
vision: hasVisionInput(payload),
|
|
3410
|
+
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
|
3516
3411
|
};
|
|
3517
3412
|
};
|
|
3518
|
-
const
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
} catch (error) {
|
|
3525
|
-
consola.warn("Failed to parse function call arguments", {
|
|
3526
|
-
error,
|
|
3527
|
-
rawArguments
|
|
3528
|
-
});
|
|
3529
|
-
}
|
|
3530
|
-
return { raw_arguments: rawArguments };
|
|
3413
|
+
const getResponsesTransportForModel = (selectedModel, options = {}) => {
|
|
3414
|
+
const supportedEndpoints = selectedModel?.supported_endpoints ?? [];
|
|
3415
|
+
const useWebSocket = responsesUtilsDependencies.isResponsesApiWebSocketEnabled();
|
|
3416
|
+
if (options.compactType !== 1 && useWebSocket && supportedEndpoints.includes("ws:/responses")) return "websocket";
|
|
3417
|
+
if (supportedEndpoints.includes("/responses")) return "http";
|
|
3418
|
+
return null;
|
|
3531
3419
|
};
|
|
3532
|
-
const
|
|
3533
|
-
|
|
3420
|
+
const hasAgentInitiator = (payload) => {
|
|
3421
|
+
const lastItem = getPayloadItems(payload).at(-1);
|
|
3422
|
+
if (!lastItem) return false;
|
|
3423
|
+
if (!("role" in lastItem) || !lastItem.role) return true;
|
|
3424
|
+
return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
|
|
3534
3425
|
};
|
|
3535
|
-
const
|
|
3536
|
-
|
|
3537
|
-
return [{
|
|
3538
|
-
type: "text",
|
|
3539
|
-
text: outputText
|
|
3540
|
-
}];
|
|
3426
|
+
const hasVisionInput = (payload) => {
|
|
3427
|
+
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
3541
3428
|
};
|
|
3542
|
-
const
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
|
|
3546
|
-
return "end_turn";
|
|
3547
|
-
}
|
|
3548
|
-
if (status === "incomplete") {
|
|
3549
|
-
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
3550
|
-
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
3551
|
-
}
|
|
3552
|
-
return null;
|
|
3429
|
+
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
3430
|
+
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
|
|
3431
|
+
return 5e4;
|
|
3553
3432
|
};
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3433
|
+
const createCompactionContextManagement = (compactThreshold) => [{
|
|
3434
|
+
type: "compaction",
|
|
3435
|
+
compact_threshold: compactThreshold
|
|
3436
|
+
}];
|
|
3437
|
+
const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
|
|
3438
|
+
if (payload.context_management !== void 0) return;
|
|
3439
|
+
if (!responsesUtilsDependencies.isResponsesApiContextManagementModel(payload.model)) return;
|
|
3440
|
+
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
|
|
3562
3441
|
};
|
|
3563
|
-
const
|
|
3564
|
-
|
|
3565
|
-
const
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
if (Array.isArray(content)) {
|
|
3569
|
-
const result = [];
|
|
3570
|
-
for (const block of content) switch (block.type) {
|
|
3571
|
-
case "text":
|
|
3572
|
-
result.push(createTextContent(block.text));
|
|
3573
|
-
break;
|
|
3574
|
-
case "image":
|
|
3575
|
-
result.push(createImageContent(block));
|
|
3576
|
-
break;
|
|
3577
|
-
case "document":
|
|
3578
|
-
result.push(createFileContent(block));
|
|
3579
|
-
break;
|
|
3580
|
-
case "tool_reference":
|
|
3581
|
-
result.push(createTextContent(`Tool ${block.tool_name} loaded`));
|
|
3582
|
-
break;
|
|
3583
|
-
default: break;
|
|
3584
|
-
}
|
|
3585
|
-
return result;
|
|
3586
|
-
}
|
|
3587
|
-
return "";
|
|
3442
|
+
const compactInputByLatestCompaction = (payload) => {
|
|
3443
|
+
if (!Array.isArray(payload.input) || payload.input.length === 0) return;
|
|
3444
|
+
const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
|
|
3445
|
+
if (latestCompactionMessageIndex === void 0) return;
|
|
3446
|
+
payload.input = payload.input.slice(latestCompactionMessageIndex);
|
|
3588
3447
|
};
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
|
|
3592
|
-
var FunctionCallArgumentsValidationError = class extends Error {
|
|
3593
|
-
constructor(message) {
|
|
3594
|
-
super(message);
|
|
3595
|
-
this.name = "FunctionCallArgumentsValidationError";
|
|
3596
|
-
}
|
|
3448
|
+
const getLatestCompactionMessageIndex = (input) => {
|
|
3449
|
+
for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
|
|
3597
3450
|
};
|
|
3598
|
-
const
|
|
3599
|
-
|
|
3600
|
-
for (const char of chunk) {
|
|
3601
|
-
if (char === "\r" || char === "\n" || char === " ") {
|
|
3602
|
-
count += 1;
|
|
3603
|
-
if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
|
|
3604
|
-
nextCount: count,
|
|
3605
|
-
exceeded: true
|
|
3606
|
-
};
|
|
3607
|
-
continue;
|
|
3608
|
-
}
|
|
3609
|
-
if (char !== " ") count = 0;
|
|
3610
|
-
}
|
|
3611
|
-
return {
|
|
3612
|
-
nextCount: count,
|
|
3613
|
-
exceeded: false
|
|
3614
|
-
};
|
|
3451
|
+
const isCompactionInputItem = (value) => {
|
|
3452
|
+
return "type" in value && typeof value.type === "string" && value.type === "compaction";
|
|
3615
3453
|
};
|
|
3616
|
-
const
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
openBlocks: /* @__PURE__ */ new Set(),
|
|
3622
|
-
blockHasDelta: /* @__PURE__ */ new Set(),
|
|
3623
|
-
functionCallStateByOutputIndex: /* @__PURE__ */ new Map(),
|
|
3624
|
-
toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search"
|
|
3625
|
-
});
|
|
3626
|
-
const translateResponsesStreamEvent = (rawEvent, state) => {
|
|
3627
|
-
switch (rawEvent.type) {
|
|
3628
|
-
case "response.created": return handleResponseCreated(rawEvent, state);
|
|
3629
|
-
case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state);
|
|
3630
|
-
case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state);
|
|
3631
|
-
case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state);
|
|
3632
|
-
case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state);
|
|
3633
|
-
case "response.output_text.done": return handleOutputTextDone(rawEvent, state);
|
|
3634
|
-
case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state);
|
|
3635
|
-
case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state);
|
|
3636
|
-
case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state);
|
|
3637
|
-
case "response.completed":
|
|
3638
|
-
case "response.incomplete": return handleResponseCompleted(rawEvent, state);
|
|
3639
|
-
case "response.failed": return handleResponseFailed(rawEvent, state);
|
|
3640
|
-
case "error": return handleErrorEvent(rawEvent, state);
|
|
3641
|
-
default: return [];
|
|
3642
|
-
}
|
|
3454
|
+
const getPayloadItems = (payload) => {
|
|
3455
|
+
const result = [];
|
|
3456
|
+
const { input } = payload;
|
|
3457
|
+
if (Array.isArray(input)) result.push(...input);
|
|
3458
|
+
return result;
|
|
3643
3459
|
};
|
|
3644
|
-
const
|
|
3645
|
-
|
|
3460
|
+
const containsVisionContent = (value) => {
|
|
3461
|
+
if (!value) return false;
|
|
3462
|
+
if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
|
|
3463
|
+
if (typeof value !== "object") return false;
|
|
3464
|
+
const record = value;
|
|
3465
|
+
if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
|
|
3466
|
+
if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
|
|
3467
|
+
return false;
|
|
3646
3468
|
};
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
name
|
|
3656
|
-
|
|
3469
|
+
//#endregion
|
|
3470
|
+
//#region src/services/codex/get-models.ts
|
|
3471
|
+
const CODEX_MODELS = [
|
|
3472
|
+
{
|
|
3473
|
+
contextWindow: 272e3,
|
|
3474
|
+
id: "gpt-5.3-codex-spark",
|
|
3475
|
+
input: ["text"],
|
|
3476
|
+
maxTokens: 128e3,
|
|
3477
|
+
name: "GPT-5.3 Codex Spark"
|
|
3478
|
+
},
|
|
3479
|
+
{
|
|
3480
|
+
contextWindow: 1e6,
|
|
3481
|
+
id: "gpt-5.4",
|
|
3482
|
+
input: ["text", "image"],
|
|
3483
|
+
maxTokens: 128e3,
|
|
3484
|
+
name: "GPT-5.4"
|
|
3485
|
+
},
|
|
3486
|
+
{
|
|
3487
|
+
contextWindow: 1e6,
|
|
3488
|
+
id: "gpt-5.4-mini",
|
|
3489
|
+
input: ["text", "image"],
|
|
3490
|
+
maxTokens: 128e3,
|
|
3491
|
+
name: "GPT-5.4 mini"
|
|
3492
|
+
},
|
|
3493
|
+
{
|
|
3494
|
+
contextWindow: 272e3,
|
|
3495
|
+
id: "gpt-5.5",
|
|
3496
|
+
input: ["text", "image"],
|
|
3497
|
+
maxTokens: 128e3,
|
|
3498
|
+
name: "GPT-5.5"
|
|
3499
|
+
}
|
|
3500
|
+
];
|
|
3501
|
+
function normalizeCodexModel(model) {
|
|
3502
|
+
const supportsVision = model.input.includes("image");
|
|
3503
|
+
return {
|
|
3504
|
+
capabilities: {
|
|
3505
|
+
family: "gpt",
|
|
3506
|
+
limits: {
|
|
3507
|
+
max_context_window_tokens: model.contextWindow,
|
|
3508
|
+
max_output_tokens: model.maxTokens,
|
|
3509
|
+
max_prompt_tokens: model.contextWindow
|
|
3510
|
+
},
|
|
3511
|
+
object: "model_capabilities",
|
|
3512
|
+
supports: {
|
|
3513
|
+
adaptive_thinking: true,
|
|
3514
|
+
parallel_tool_calls: true,
|
|
3515
|
+
reasoning_effort: [
|
|
3516
|
+
"minimal",
|
|
3517
|
+
"low",
|
|
3518
|
+
"medium",
|
|
3519
|
+
"high",
|
|
3520
|
+
"xhigh"
|
|
3521
|
+
],
|
|
3522
|
+
streaming: true,
|
|
3523
|
+
tool_calls: true,
|
|
3524
|
+
vision: supportsVision
|
|
3525
|
+
},
|
|
3526
|
+
tokenizer: "o200k_base",
|
|
3527
|
+
type: "chat"
|
|
3528
|
+
},
|
|
3529
|
+
id: model.id,
|
|
3530
|
+
model_picker_enabled: true,
|
|
3531
|
+
name: model.name,
|
|
3532
|
+
object: "model",
|
|
3533
|
+
preview: false,
|
|
3534
|
+
supported_endpoints: ["/v1/messages", "/v1/responses"],
|
|
3535
|
+
vendor: "openai",
|
|
3536
|
+
version: "chatgpt-codex"
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
function getModels() {
|
|
3540
|
+
return {
|
|
3541
|
+
object: "list",
|
|
3542
|
+
data: CODEX_MODELS.map((model) => normalizeCodexModel(model))
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
//#endregion
|
|
3546
|
+
//#region src/services/providers/provider-proxy.ts
|
|
3547
|
+
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
3548
|
+
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
3549
|
+
const STRIPPED_RESPONSE_HEADERS = [
|
|
3550
|
+
"connection",
|
|
3551
|
+
"content-encoding",
|
|
3552
|
+
"content-length",
|
|
3553
|
+
"keep-alive",
|
|
3554
|
+
"proxy-authenticate",
|
|
3555
|
+
"proxy-authorization",
|
|
3556
|
+
"te",
|
|
3557
|
+
"trailer",
|
|
3558
|
+
"transfer-encoding",
|
|
3559
|
+
"upgrade"
|
|
3560
|
+
];
|
|
3561
|
+
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
3562
|
+
const authHeaders = {};
|
|
3563
|
+
if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
3564
|
+
else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
3565
|
+
const headers = {
|
|
3566
|
+
"content-type": "application/json",
|
|
3567
|
+
accept: "application/json",
|
|
3568
|
+
...authHeaders
|
|
3569
|
+
};
|
|
3570
|
+
for (const headerName of SHARED_FORWARDABLE_HEADERS) {
|
|
3571
|
+
const headerValue = requestHeaders.get(headerName);
|
|
3572
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
3573
|
+
}
|
|
3574
|
+
if (providerConfig.type !== "anthropic") return headers;
|
|
3575
|
+
for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
|
|
3576
|
+
const headerValue = requestHeaders.get(headerName);
|
|
3577
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
3578
|
+
}
|
|
3579
|
+
return headers;
|
|
3580
|
+
}
|
|
3581
|
+
function createProviderProxyResponse(upstreamResponse, body) {
|
|
3582
|
+
const headers = new Headers(upstreamResponse.headers);
|
|
3583
|
+
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
3584
|
+
return new Response(body ?? upstreamResponse.body, {
|
|
3585
|
+
headers,
|
|
3586
|
+
status: upstreamResponse.status,
|
|
3587
|
+
statusText: upstreamResponse.statusText
|
|
3657
3588
|
});
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3589
|
+
}
|
|
3590
|
+
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
3591
|
+
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
3592
|
+
method: "POST",
|
|
3593
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3594
|
+
body: JSON.stringify(payload)
|
|
3595
|
+
});
|
|
3596
|
+
}
|
|
3597
|
+
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
3598
|
+
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
3599
|
+
method: "POST",
|
|
3600
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3601
|
+
body: JSON.stringify(payload)
|
|
3602
|
+
});
|
|
3603
|
+
}
|
|
3604
|
+
async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
|
|
3605
|
+
return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
|
|
3606
|
+
method: "POST",
|
|
3607
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3608
|
+
body: JSON.stringify(payload)
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
3612
|
+
return await fetch(`${providerConfig.baseUrl}/v1/models`, {
|
|
3613
|
+
method: "GET",
|
|
3614
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
3615
|
+
});
|
|
3616
|
+
}
|
|
3617
|
+
//#endregion
|
|
3618
|
+
//#region src/routes/provider/messages/handler.ts
|
|
3619
|
+
const logger$5 = createHandlerLogger("provider-messages-handler");
|
|
3620
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
|
|
3621
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
|
|
3622
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
|
|
3623
|
+
"system",
|
|
3624
|
+
"user",
|
|
3625
|
+
"assistant",
|
|
3626
|
+
"tool"
|
|
3627
|
+
]);
|
|
3628
|
+
async function handleProviderMessages(c) {
|
|
3629
|
+
const provider = c.req.param("provider");
|
|
3630
|
+
return await handleProviderMessagesForProvider(c, {
|
|
3631
|
+
payload: await c.req.json(),
|
|
3632
|
+
provider
|
|
3633
|
+
});
|
|
3634
|
+
}
|
|
3635
|
+
async function handleProviderMessagesForProvider(c, options) {
|
|
3636
|
+
const { payload, provider } = options;
|
|
3637
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
3638
|
+
if (!providerConfig) return c.json({ error: {
|
|
3639
|
+
message: `Provider '${provider}' not found or disabled`,
|
|
3640
|
+
type: "invalid_request_error"
|
|
3641
|
+
} }, 404);
|
|
3642
|
+
try {
|
|
3643
|
+
const modelConfig = providerConfig.models?.[payload.model];
|
|
3644
|
+
applyModelDefaults(payload, modelConfig);
|
|
3645
|
+
debugJson(logger$5, "provider.messages.request", {
|
|
3646
|
+
payload,
|
|
3647
|
+
provider
|
|
3666
3648
|
});
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
const events = new Array();
|
|
3673
|
-
const item = rawEvent.item;
|
|
3674
|
-
const itemType = item.type;
|
|
3675
|
-
const outputIndex = rawEvent.output_index;
|
|
3676
|
-
if (itemType === "tool_search_call") {
|
|
3677
|
-
const blockIndex = openFunctionCallBlock(state, {
|
|
3678
|
-
outputIndex,
|
|
3679
|
-
toolCallId: item.call_id,
|
|
3680
|
-
name: state.toolSearchName,
|
|
3681
|
-
events
|
|
3649
|
+
if (providerConfig.type === "openai-responses") return await handleOpenAIResponsesProviderMessages(c, {
|
|
3650
|
+
modelConfig,
|
|
3651
|
+
payload,
|
|
3652
|
+
provider,
|
|
3653
|
+
providerConfig
|
|
3682
3654
|
});
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
delta: {
|
|
3689
|
-
type: "input_json_delta",
|
|
3690
|
-
partial_json: finalArguments
|
|
3691
|
-
}
|
|
3692
|
-
});
|
|
3693
|
-
state.blockHasDelta.add(blockIndex);
|
|
3694
|
-
}
|
|
3695
|
-
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3696
|
-
return events;
|
|
3697
|
-
}
|
|
3698
|
-
if (itemType === "compaction") {
|
|
3699
|
-
if (!item.id || !item.encrypted_content) return events;
|
|
3700
|
-
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3701
|
-
if (!state.blockHasDelta.has(blockIndex)) events.push({
|
|
3702
|
-
type: "content_block_delta",
|
|
3703
|
-
index: blockIndex,
|
|
3704
|
-
delta: {
|
|
3705
|
-
type: "thinking_delta",
|
|
3706
|
-
thinking: THINKING_TEXT
|
|
3707
|
-
}
|
|
3655
|
+
if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
|
|
3656
|
+
modelConfig,
|
|
3657
|
+
payload,
|
|
3658
|
+
provider,
|
|
3659
|
+
providerConfig
|
|
3708
3660
|
});
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3661
|
+
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
3662
|
+
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
3663
|
+
if (!upstreamResponse.ok) {
|
|
3664
|
+
logger$5.error("Failed to create responses", upstreamResponse);
|
|
3665
|
+
throw new HTTPError("Failed to create responses", upstreamResponse);
|
|
3666
|
+
}
|
|
3667
|
+
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
3668
|
+
if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
|
|
3669
|
+
c,
|
|
3670
|
+
payload,
|
|
3671
|
+
provider,
|
|
3672
|
+
providerConfig,
|
|
3673
|
+
upstreamResponse
|
|
3719
3674
|
});
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
const signature = (item.encrypted_content ?? "") + "@" + item.id;
|
|
3726
|
-
if (signature) {
|
|
3727
|
-
if (!item.summary || item.summary.length === 0) events.push({
|
|
3728
|
-
type: "content_block_delta",
|
|
3729
|
-
index: blockIndex,
|
|
3730
|
-
delta: {
|
|
3731
|
-
type: "thinking_delta",
|
|
3732
|
-
thinking: THINKING_TEXT
|
|
3733
|
-
}
|
|
3675
|
+
return respondProviderMessagesJson(c, {
|
|
3676
|
+
body: await upstreamResponse.json(),
|
|
3677
|
+
payload,
|
|
3678
|
+
provider,
|
|
3679
|
+
providerConfig
|
|
3734
3680
|
});
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
type: "signature_delta",
|
|
3740
|
-
signature
|
|
3741
|
-
}
|
|
3681
|
+
} catch (error) {
|
|
3682
|
+
logger$5.error("provider.messages.error", {
|
|
3683
|
+
provider,
|
|
3684
|
+
error
|
|
3742
3685
|
});
|
|
3743
|
-
|
|
3686
|
+
throw error;
|
|
3744
3687
|
}
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
const
|
|
3748
|
-
const
|
|
3749
|
-
const
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3688
|
+
}
|
|
3689
|
+
const handleOpenAIResponsesProviderMessages = async (c, options) => {
|
|
3690
|
+
const { payload, provider, providerConfig } = options;
|
|
3691
|
+
const selectedModel = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
|
|
3692
|
+
const responsesPayload = translateAnthropicMessagesToResponsesPayload(payload);
|
|
3693
|
+
applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
|
|
3694
|
+
compactInputByLatestCompaction(responsesPayload);
|
|
3695
|
+
debugJson(logger$5, "provider.messages.responses.request", {
|
|
3696
|
+
payload: responsesPayload,
|
|
3697
|
+
provider
|
|
3755
3698
|
});
|
|
3756
|
-
const
|
|
3757
|
-
if (!
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
}
|
|
3699
|
+
const upstreamResponse = providerConfig.name === "codex" ? await forwardCodexResponses(responsesPayload, c.req.raw.headers, providerConfig.baseUrl) : await forwardProviderResponses(providerConfig, responsesPayload, c.req.raw.headers);
|
|
3700
|
+
if (!upstreamResponse.ok) {
|
|
3701
|
+
logger$5.error("Failed to create provider responses", upstreamResponse);
|
|
3702
|
+
throw new HTTPError("Failed to create provider responses", upstreamResponse);
|
|
3703
|
+
}
|
|
3704
|
+
if (responsesPayload.stream) return streamResponsesProviderMessages({
|
|
3705
|
+
c,
|
|
3706
|
+
payload,
|
|
3707
|
+
provider,
|
|
3708
|
+
providerConfig,
|
|
3709
|
+
upstreamResponse
|
|
3768
3710
|
});
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
const outputIndex = rawEvent.output_index;
|
|
3775
|
-
const blockIndex = openFunctionCallBlock(state, {
|
|
3776
|
-
outputIndex,
|
|
3777
|
-
events
|
|
3711
|
+
return respondResponsesProviderMessagesJson(c, {
|
|
3712
|
+
body: await upstreamResponse.json(),
|
|
3713
|
+
payload,
|
|
3714
|
+
provider,
|
|
3715
|
+
providerConfig
|
|
3778
3716
|
});
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3717
|
+
};
|
|
3718
|
+
const applyModelDefaults = (payload, modelConfig) => {
|
|
3719
|
+
payload.temperature ??= modelConfig?.temperature;
|
|
3720
|
+
payload.top_p ??= modelConfig?.topP;
|
|
3721
|
+
payload.top_k ??= modelConfig?.topK;
|
|
3722
|
+
};
|
|
3723
|
+
const applyMissingExtraBody = (payload, options) => {
|
|
3724
|
+
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
3725
|
+
};
|
|
3726
|
+
const getRequestThinkingBudget = (payload) => {
|
|
3727
|
+
const budget = payload.thinking?.budget_tokens;
|
|
3728
|
+
if (typeof budget !== "number" || !Number.isFinite(budget)) return;
|
|
3729
|
+
return budget;
|
|
3730
|
+
};
|
|
3731
|
+
const applyOpenAICompatibleThinkingBudget = (payload, source) => {
|
|
3732
|
+
const thinkingBudget = getRequestThinkingBudget(source);
|
|
3733
|
+
if (thinkingBudget !== void 0) {
|
|
3734
|
+
payload.thinking_budget = thinkingBudget;
|
|
3735
|
+
return;
|
|
3790
3736
|
}
|
|
3791
|
-
|
|
3792
|
-
|
|
3737
|
+
if (payload.thinking_budget === void 0) delete payload.thinking_budget;
|
|
3738
|
+
};
|
|
3739
|
+
const applyOpenAICompatibleExtraBodyThinkingBudget = (payload, options) => {
|
|
3740
|
+
const { extraBody } = options;
|
|
3741
|
+
if (!extraBody || !Object.hasOwn(extraBody, "thinking_budget")) return;
|
|
3742
|
+
const rawPayload = payload;
|
|
3743
|
+
rawPayload.thinking_budget = extraBody.thinking_budget;
|
|
3793
3744
|
};
|
|
3794
|
-
const
|
|
3795
|
-
const
|
|
3796
|
-
const
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
const blockIndex = openTextBlockIfNeeded(state, {
|
|
3801
|
-
outputIndex,
|
|
3802
|
-
contentIndex,
|
|
3803
|
-
events
|
|
3804
|
-
});
|
|
3805
|
-
events.push({
|
|
3806
|
-
type: "content_block_delta",
|
|
3807
|
-
index: blockIndex,
|
|
3808
|
-
delta: {
|
|
3809
|
-
type: "text_delta",
|
|
3810
|
-
text: deltaText
|
|
3811
|
-
}
|
|
3745
|
+
const handleOpenAICompatibleProviderMessages = async (c, options) => {
|
|
3746
|
+
const { modelConfig, payload, provider, providerConfig } = options;
|
|
3747
|
+
const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
|
|
3748
|
+
debugJson(logger$5, "provider.messages.openai_compatible.request", {
|
|
3749
|
+
payload: openAIPayload,
|
|
3750
|
+
provider
|
|
3812
3751
|
});
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
const
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
delta: {
|
|
3825
|
-
type: "thinking_delta",
|
|
3826
|
-
thinking: deltaText
|
|
3827
|
-
}
|
|
3752
|
+
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
|
|
3753
|
+
if (!upstreamResponse.ok) {
|
|
3754
|
+
logger$5.error("Failed to create openai-compatible responses", upstreamResponse);
|
|
3755
|
+
throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
|
|
3756
|
+
}
|
|
3757
|
+
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
3758
|
+
if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
|
|
3759
|
+
c,
|
|
3760
|
+
payload,
|
|
3761
|
+
provider,
|
|
3762
|
+
upstreamResponse
|
|
3828
3763
|
});
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
const outputIndex = rawEvent.output_index;
|
|
3834
|
-
const text = rawEvent.text;
|
|
3835
|
-
const events = new Array();
|
|
3836
|
-
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3837
|
-
if (text && !state.blockHasDelta.has(blockIndex)) events.push({
|
|
3838
|
-
type: "content_block_delta",
|
|
3839
|
-
index: blockIndex,
|
|
3840
|
-
delta: {
|
|
3841
|
-
type: "thinking_delta",
|
|
3842
|
-
thinking: text
|
|
3843
|
-
}
|
|
3764
|
+
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
3765
|
+
body: await upstreamResponse.json(),
|
|
3766
|
+
payload,
|
|
3767
|
+
provider
|
|
3844
3768
|
});
|
|
3845
|
-
return events;
|
|
3846
3769
|
};
|
|
3847
|
-
const
|
|
3848
|
-
const
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
const text = rawEvent.text;
|
|
3852
|
-
const blockIndex = openTextBlockIfNeeded(state, {
|
|
3853
|
-
outputIndex,
|
|
3854
|
-
contentIndex,
|
|
3855
|
-
events
|
|
3770
|
+
const createOpenAICompatiblePayload = (payload, modelConfig) => {
|
|
3771
|
+
const openAIPayload = translateToOpenAI(payload, {
|
|
3772
|
+
supportPdf: modelConfig?.supportPdf,
|
|
3773
|
+
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
3856
3774
|
});
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3775
|
+
applyOpenAICompatibleThinkingBudget(openAIPayload, payload);
|
|
3776
|
+
if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
|
|
3777
|
+
if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
|
|
3778
|
+
normalizeOpenAICompatibleReasoningContent(openAIPayload);
|
|
3779
|
+
applyOpenAICompatibleRequestOverrides(openAIPayload, {
|
|
3780
|
+
extraBody: modelConfig?.extraBody,
|
|
3781
|
+
source: payload
|
|
3864
3782
|
});
|
|
3865
|
-
|
|
3783
|
+
applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
3784
|
+
applyOpenAICompatibleExtraBodyThinkingBudget(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
3785
|
+
if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
|
|
3786
|
+
if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
|
|
3787
|
+
return openAIPayload;
|
|
3866
3788
|
};
|
|
3867
|
-
const
|
|
3868
|
-
const
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
delta: {
|
|
3875
|
-
stop_reason: anthropic.stop_reason,
|
|
3876
|
-
stop_sequence: anthropic.stop_sequence
|
|
3877
|
-
},
|
|
3878
|
-
usage: anthropic.usage
|
|
3879
|
-
}, { type: "message_stop" });
|
|
3880
|
-
state.messageCompleted = true;
|
|
3881
|
-
return events;
|
|
3789
|
+
const normalizeOpenAICompatibleReasoningContent = (payload) => {
|
|
3790
|
+
for (const message of payload.messages) {
|
|
3791
|
+
if (message.role !== "assistant") continue;
|
|
3792
|
+
if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
|
|
3793
|
+
delete message.reasoning_text;
|
|
3794
|
+
delete message.reasoning_opaque;
|
|
3795
|
+
}
|
|
3882
3796
|
};
|
|
3883
|
-
const
|
|
3884
|
-
const
|
|
3885
|
-
const
|
|
3886
|
-
closeAllOpenBlocks(state, events);
|
|
3887
|
-
const message = response.error?.message ?? "The response failed due to an unknown error.";
|
|
3888
|
-
events.push(buildErrorEvent(message));
|
|
3889
|
-
state.messageCompleted = true;
|
|
3890
|
-
return events;
|
|
3797
|
+
const applyOpenAICompatibleRequestOverrides = (payload, options) => {
|
|
3798
|
+
const allowedKeys = new Set(Object.keys(options.extraBody ?? {}));
|
|
3799
|
+
for (const key of allowedKeys) if (Object.hasOwn(options.source, key)) payload[key] = options.source[key];
|
|
3891
3800
|
};
|
|
3892
|
-
const
|
|
3893
|
-
const
|
|
3894
|
-
|
|
3895
|
-
return [buildErrorEvent(message)];
|
|
3801
|
+
const applyOpenAICompatibleContextCache = (payload) => {
|
|
3802
|
+
const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
|
|
3803
|
+
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
3896
3804
|
};
|
|
3897
|
-
const
|
|
3898
|
-
const
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
return events;
|
|
3805
|
+
const selectContextCacheMessageIndexes = (messages) => {
|
|
3806
|
+
const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
|
|
3807
|
+
const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
|
|
3808
|
+
const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-2);
|
|
3809
|
+
return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
|
|
3903
3810
|
};
|
|
3904
|
-
const
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
return
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3811
|
+
const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
|
|
3812
|
+
const isContextCacheMarkerEligible = (message) => {
|
|
3813
|
+
if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
|
|
3814
|
+
if (typeof message.content === "string") return message.content.length > 0;
|
|
3815
|
+
return Array.isArray(message.content) && message.content.length > 0;
|
|
3816
|
+
};
|
|
3817
|
+
const applyContextCacheControl = (message) => {
|
|
3818
|
+
if (!message) return;
|
|
3819
|
+
if (typeof message.content === "string") {
|
|
3820
|
+
message.content = [{
|
|
3821
|
+
type: "text",
|
|
3822
|
+
text: message.content,
|
|
3823
|
+
cache_control: { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL }
|
|
3824
|
+
}];
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
if (!Array.isArray(message.content)) return;
|
|
3828
|
+
const lastPart = message.content.at(-1);
|
|
3829
|
+
if (!lastPart) return;
|
|
3830
|
+
setContextCacheControl(lastPart);
|
|
3831
|
+
};
|
|
3832
|
+
const setContextCacheControl = (part) => {
|
|
3833
|
+
part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
|
|
3834
|
+
};
|
|
3835
|
+
const streamProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
|
|
3836
|
+
logger$5.debug("provider.messages.streaming");
|
|
3837
|
+
const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
|
|
3838
|
+
return streamSSE(c, async (stream) => {
|
|
3839
|
+
let usage = {};
|
|
3840
|
+
for await (const chunk of events(upstreamResponse)) {
|
|
3841
|
+
logger$5.debug("provider.messages.raw_stream_event:", chunk.data);
|
|
3842
|
+
const eventName = chunk.event;
|
|
3843
|
+
if (eventName === "ping") {
|
|
3844
|
+
await stream.writeSSE({
|
|
3845
|
+
event: "ping",
|
|
3846
|
+
data: "{\"type\":\"ping\"}"
|
|
3847
|
+
});
|
|
3848
|
+
continue;
|
|
3849
|
+
}
|
|
3850
|
+
let data = chunk.data;
|
|
3851
|
+
if (!data) continue;
|
|
3852
|
+
if (chunk.data === "[DONE]") break;
|
|
3853
|
+
const parsed = parseProviderStreamEvent(data, providerConfig);
|
|
3854
|
+
if (parsed) {
|
|
3855
|
+
usage = mergeAnthropicUsage(usage, parsed.usage);
|
|
3856
|
+
data = parsed.data;
|
|
3857
|
+
}
|
|
3858
|
+
await stream.writeSSE({
|
|
3859
|
+
event: eventName,
|
|
3860
|
+
data
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
recordUsage(usage);
|
|
3864
|
+
});
|
|
3865
|
+
};
|
|
3866
|
+
const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
|
|
3867
|
+
logger$5.debug("provider.messages.openai_compatible.streaming");
|
|
3868
|
+
const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
|
|
3869
|
+
return streamSSE(c, async (stream) => {
|
|
3870
|
+
let usage = {};
|
|
3871
|
+
const streamState = {
|
|
3872
|
+
messageStartSent: false,
|
|
3873
|
+
contentBlockIndex: 0,
|
|
3874
|
+
contentBlockOpen: false,
|
|
3875
|
+
toolCalls: {},
|
|
3876
|
+
thinkingBlockOpen: false
|
|
3877
|
+
};
|
|
3878
|
+
for await (const chunk of events(upstreamResponse)) {
|
|
3879
|
+
logger$5.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
|
|
3880
|
+
if (chunk.event === "ping") {
|
|
3881
|
+
await stream.writeSSE({
|
|
3882
|
+
event: "ping",
|
|
3883
|
+
data: "{\"type\":\"ping\"}"
|
|
3884
|
+
});
|
|
3885
|
+
continue;
|
|
3886
|
+
}
|
|
3887
|
+
if (!chunk.data || chunk.data === "[DONE]") {
|
|
3888
|
+
if (chunk.data === "[DONE]") break;
|
|
3889
|
+
continue;
|
|
3890
|
+
}
|
|
3891
|
+
const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
|
|
3892
|
+
if (!parsed) continue;
|
|
3893
|
+
if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
|
|
3894
|
+
const events = translateChunkToAnthropicEvents(parsed, streamState);
|
|
3895
|
+
for (const event of events) {
|
|
3896
|
+
const eventData = JSON.stringify(event);
|
|
3897
|
+
debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
3898
|
+
await stream.writeSSE({
|
|
3899
|
+
event: event.type,
|
|
3900
|
+
data: eventData
|
|
3901
|
+
});
|
|
3922
3902
|
}
|
|
3923
3903
|
}
|
|
3924
|
-
|
|
3904
|
+
for (const event of flushPendingAnthropicStreamEvents(streamState)) {
|
|
3905
|
+
const eventData = JSON.stringify(event);
|
|
3906
|
+
debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
3907
|
+
await stream.writeSSE({
|
|
3908
|
+
event: event.type,
|
|
3909
|
+
data: eventData
|
|
3910
|
+
});
|
|
3911
|
+
}
|
|
3912
|
+
recordUsage(usage);
|
|
3913
|
+
});
|
|
3925
3914
|
};
|
|
3926
|
-
const
|
|
3927
|
-
|
|
3928
|
-
const
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3915
|
+
const streamResponsesProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
|
|
3916
|
+
logger$5.debug("provider.messages.responses.streaming", { provider });
|
|
3917
|
+
const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
|
|
3918
|
+
return streamSSE(c, async (stream) => {
|
|
3919
|
+
let usage = {};
|
|
3920
|
+
const streamState = createResponsesStreamState({ toolSearchName: resolveBridgeToolSearchName(payload.tools) });
|
|
3921
|
+
for await (const chunk of events(upstreamResponse)) {
|
|
3922
|
+
logger$5.debug("provider.messages.responses.raw_stream_event:", chunk.data);
|
|
3923
|
+
if (chunk.event === "ping") {
|
|
3924
|
+
await stream.writeSSE({
|
|
3925
|
+
event: "ping",
|
|
3926
|
+
data: "{\"type\":\"ping\"}"
|
|
3927
|
+
});
|
|
3928
|
+
continue;
|
|
3929
|
+
}
|
|
3930
|
+
if (!chunk.data || chunk.data === "[DONE]") {
|
|
3931
|
+
if (chunk.data === "[DONE]") break;
|
|
3932
|
+
continue;
|
|
3933
|
+
}
|
|
3934
|
+
const parsed = parseResponsesProviderStreamChunk(chunk.data, providerConfig);
|
|
3935
|
+
if (!parsed) continue;
|
|
3936
|
+
if (parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete") usage = normalizeResponsesUsage(parsed.response.usage);
|
|
3937
|
+
const events = translateResponsesStreamEvent(parsed, streamState);
|
|
3938
|
+
for (const event of events) {
|
|
3939
|
+
const eventData = JSON.stringify(event);
|
|
3940
|
+
debugLazy(logger$5, () => ["provider.messages.responses.translated_event:", eventData]);
|
|
3941
|
+
await stream.writeSSE({
|
|
3942
|
+
event: event.type,
|
|
3943
|
+
data: eventData
|
|
3944
|
+
});
|
|
3943
3945
|
}
|
|
3946
|
+
}
|
|
3947
|
+
if (!streamState.messageCompleted) {
|
|
3948
|
+
const errorEvent = buildErrorEvent(`${provider} stream ended without a completion event`);
|
|
3949
|
+
await stream.writeSSE({
|
|
3950
|
+
event: errorEvent.type,
|
|
3951
|
+
data: JSON.stringify(errorEvent)
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
recordUsage(usage);
|
|
3955
|
+
});
|
|
3956
|
+
};
|
|
3957
|
+
const parseOpenAICompatibleStreamChunk = (data) => {
|
|
3958
|
+
try {
|
|
3959
|
+
return JSON.parse(data);
|
|
3960
|
+
} catch (error) {
|
|
3961
|
+
logger$5.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
3962
|
+
data,
|
|
3963
|
+
error
|
|
3944
3964
|
});
|
|
3945
|
-
|
|
3965
|
+
return null;
|
|
3946
3966
|
}
|
|
3947
|
-
return blockIndex;
|
|
3948
3967
|
};
|
|
3949
|
-
const
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
events.push({
|
|
3960
|
-
type: "content_block_start",
|
|
3961
|
-
index: blockIndex,
|
|
3962
|
-
content_block: {
|
|
3963
|
-
type: "thinking",
|
|
3964
|
-
thinking: ""
|
|
3965
|
-
}
|
|
3968
|
+
const parseResponsesProviderStreamChunk = (data, providerConfig) => {
|
|
3969
|
+
try {
|
|
3970
|
+
const parsed = JSON.parse(data);
|
|
3971
|
+
if (providerConfig.name === "codex") logCodexRateLimitsEvent(parsed);
|
|
3972
|
+
return providerConfig.name === "codex" ? normalizeCodexResponsesEvent(parsed) : parsed;
|
|
3973
|
+
} catch (error) {
|
|
3974
|
+
logger$5.error("provider.messages.responses.parse_chunk_error", {
|
|
3975
|
+
provider: providerConfig.name,
|
|
3976
|
+
data,
|
|
3977
|
+
error
|
|
3966
3978
|
});
|
|
3967
|
-
|
|
3979
|
+
return null;
|
|
3968
3980
|
}
|
|
3969
|
-
return blockIndex;
|
|
3970
|
-
};
|
|
3971
|
-
const closeBlockIfOpen = (state, blockIndex, events) => {
|
|
3972
|
-
if (!state.openBlocks.has(blockIndex)) return;
|
|
3973
|
-
events.push({
|
|
3974
|
-
type: "content_block_stop",
|
|
3975
|
-
index: blockIndex
|
|
3976
|
-
});
|
|
3977
|
-
state.openBlocks.delete(blockIndex);
|
|
3978
|
-
state.blockHasDelta.delete(blockIndex);
|
|
3979
|
-
};
|
|
3980
|
-
const closeOpenBlocks = (state, events) => {
|
|
3981
|
-
for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
|
|
3982
|
-
};
|
|
3983
|
-
const closeAllOpenBlocks = (state, events) => {
|
|
3984
|
-
closeOpenBlocks(state, events);
|
|
3985
|
-
state.functionCallStateByOutputIndex.clear();
|
|
3986
3981
|
};
|
|
3987
|
-
const
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
type
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
3982
|
+
const parseProviderStreamEvent = (data, providerConfig) => {
|
|
3983
|
+
try {
|
|
3984
|
+
const parsed = JSON.parse(data);
|
|
3985
|
+
if (parsed.type === "message_start") {
|
|
3986
|
+
adjustInputTokens(providerConfig, parsed.message.usage);
|
|
3987
|
+
return {
|
|
3988
|
+
data: JSON.stringify(parsed),
|
|
3989
|
+
model: parsed.message.model,
|
|
3990
|
+
usage: normalizeAnthropicUsage(parsed.message.usage)
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
if (parsed.type === "message_delta") {
|
|
3994
|
+
adjustInputTokens(providerConfig, parsed.usage);
|
|
3995
|
+
return {
|
|
3996
|
+
data: JSON.stringify(parsed),
|
|
3997
|
+
usage: normalizeAnthropicUsage(parsed.usage)
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
return {
|
|
4001
|
+
data: JSON.stringify(parsed),
|
|
4002
|
+
usage: {}
|
|
4006
4003
|
};
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
closeOpenBlocks(state, events);
|
|
4012
|
-
events.push({
|
|
4013
|
-
type: "content_block_start",
|
|
4014
|
-
index: blockIndex,
|
|
4015
|
-
content_block: {
|
|
4016
|
-
type: "tool_use",
|
|
4017
|
-
id: functionCallState.toolCallId,
|
|
4018
|
-
name: functionCallState.name,
|
|
4019
|
-
input: {}
|
|
4020
|
-
}
|
|
4004
|
+
} catch (error) {
|
|
4005
|
+
logger$5.error("provider.messages.streaming.adjust_tokens_error", {
|
|
4006
|
+
error,
|
|
4007
|
+
originalData: data
|
|
4021
4008
|
});
|
|
4022
|
-
|
|
4009
|
+
return null;
|
|
4023
4010
|
}
|
|
4024
|
-
return blockIndex;
|
|
4025
4011
|
};
|
|
4026
|
-
const
|
|
4027
|
-
const
|
|
4028
|
-
const
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
initialArguments: ""
|
|
4034
|
-
};
|
|
4035
|
-
if (itemType !== "function_call") return;
|
|
4036
|
-
return {
|
|
4037
|
-
outputIndex: rawEvent.output_index,
|
|
4038
|
-
toolCallId: item.call_id,
|
|
4039
|
-
name: resolveToolUseName(item),
|
|
4040
|
-
initialArguments: item.arguments
|
|
4041
|
-
};
|
|
4012
|
+
const respondProviderMessagesJson = (c, options) => {
|
|
4013
|
+
const { body, payload, provider, providerConfig } = options;
|
|
4014
|
+
const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
|
|
4015
|
+
adjustInputTokens(providerConfig, body.usage);
|
|
4016
|
+
recordUsage(normalizeAnthropicUsage(body.usage));
|
|
4017
|
+
debugJson(logger$5, "provider.messages.no_stream result:", body);
|
|
4018
|
+
return c.json(body);
|
|
4042
4019
|
};
|
|
4043
|
-
const
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4020
|
+
const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
|
|
4021
|
+
const { body, payload, provider } = options;
|
|
4022
|
+
createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
|
|
4023
|
+
const anthropicResponse = translateToAnthropic(body);
|
|
4024
|
+
debugJson(logger$5, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
|
|
4025
|
+
return c.json(anthropicResponse);
|
|
4026
|
+
};
|
|
4027
|
+
const respondResponsesProviderMessagesJson = (c, options) => {
|
|
4028
|
+
const { body, payload, provider, providerConfig } = options;
|
|
4029
|
+
createProviderMessagesUsageRecorder(payload, provider)(normalizeResponsesUsage(body.usage));
|
|
4030
|
+
const anthropicResponse = translateResponsesResultToAnthropic(body, { toolSearchName: resolveBridgeToolSearchName(payload.tools) });
|
|
4031
|
+
debugJson(logger$5, "provider.messages.responses.no_stream result:", anthropicResponse);
|
|
4032
|
+
if (providerConfig.name === "codex") logger$5.debug("provider.messages.codex.no_stream.result");
|
|
4033
|
+
return c.json(anthropicResponse);
|
|
4034
|
+
};
|
|
4035
|
+
const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
|
|
4036
|
+
endpoint: "provider_messages",
|
|
4037
|
+
model: payload.model,
|
|
4038
|
+
providerName: provider,
|
|
4039
|
+
sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
|
|
4040
|
+
});
|
|
4041
|
+
const adjustInputTokens = (providerConfig, usage) => {
|
|
4042
|
+
if (!providerConfig.adjustInputTokens || !usage) return;
|
|
4043
|
+
usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
|
|
4044
|
+
debugJson(logger$5, "provider.messages.adjusted_usage:", usage);
|
|
4049
4045
|
};
|
|
4050
4046
|
//#endregion
|
|
4051
4047
|
//#region src/services/copilot/create-messages.ts
|
|
@@ -4632,7 +4628,7 @@ const parseSubagentMarkerFromSystemReminder = (text) => {
|
|
|
4632
4628
|
};
|
|
4633
4629
|
//#endregion
|
|
4634
4630
|
//#region src/routes/messages/handler.ts
|
|
4635
|
-
const logger$
|
|
4631
|
+
const logger$4 = createHandlerLogger("messages-handler");
|
|
4636
4632
|
const messagesFlowHandlers = {
|
|
4637
4633
|
handleWithChatCompletions,
|
|
4638
4634
|
handleWithMessagesApi,
|
|
@@ -4642,7 +4638,7 @@ async function handleCompletion(c) {
|
|
|
4642
4638
|
const anthropicPayload = await c.req.json();
|
|
4643
4639
|
const requestedModel = anthropicPayload.model;
|
|
4644
4640
|
anthropicPayload.model = resolveMappedModel(anthropicPayload.model);
|
|
4645
|
-
if (anthropicPayload.model !== requestedModel) logger$
|
|
4641
|
+
if (anthropicPayload.model !== requestedModel) logger$4.debug(`Resolved model mapping: ${requestedModel} -> ${anthropicPayload.model}`);
|
|
4646
4642
|
const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
|
|
4647
4643
|
if (providerModelAlias) {
|
|
4648
4644
|
anthropicPayload.model = providerModelAlias.model;
|
|
@@ -4652,24 +4648,24 @@ async function handleCompletion(c) {
|
|
|
4652
4648
|
});
|
|
4653
4649
|
}
|
|
4654
4650
|
await checkRateLimit(state);
|
|
4655
|
-
debugJson(logger$
|
|
4651
|
+
debugJson(logger$4, "Anthropic request payload:", anthropicPayload);
|
|
4656
4652
|
sanitizeIdeTools(anthropicPayload);
|
|
4657
4653
|
const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
|
|
4658
|
-
if (subagentMarker) debugJson(logger$
|
|
4654
|
+
if (subagentMarker) debugJson(logger$4, "Detected Subagent marker:", subagentMarker);
|
|
4659
4655
|
const sessionId = getRootSessionId(anthropicPayload, c);
|
|
4660
|
-
logger$
|
|
4656
|
+
logger$4.debug("Extracted session ID:", sessionId);
|
|
4661
4657
|
const compactType = getCompactType(anthropicPayload);
|
|
4662
4658
|
const anthropicBeta = c.req.header("anthropic-beta");
|
|
4663
|
-
logger$
|
|
4659
|
+
logger$4.debug("Anthropic Beta header:", anthropicBeta);
|
|
4664
4660
|
const noTools = !anthropicPayload.tools || anthropicPayload.tools.length === 0;
|
|
4665
4661
|
if (anthropicBeta && noTools && compactType === 0) anthropicPayload.model = getSmallModel();
|
|
4666
|
-
if (compactType) logger$
|
|
4662
|
+
if (compactType) logger$4.debug("Compact request type:", compactType);
|
|
4667
4663
|
const lastMessageCacheControl = getLastMessageContentCacheControl(anthropicPayload.messages.at(-1));
|
|
4668
4664
|
stripToolReferenceTurnBoundary(anthropicPayload);
|
|
4669
4665
|
mergeToolResultForClaude(anthropicPayload, { skipLastMessage: compactType === 1 });
|
|
4670
4666
|
applyLastMessageCacheControl(anthropicPayload, lastMessageCacheControl);
|
|
4671
4667
|
const requestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
|
|
4672
|
-
logger$
|
|
4668
|
+
logger$4.debug("Generated request ID:", requestId);
|
|
4673
4669
|
if (state.manualApprove) await awaitApproval();
|
|
4674
4670
|
const selectedModel = findEndpointModel(anthropicPayload.model);
|
|
4675
4671
|
anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
|
|
@@ -4680,7 +4676,7 @@ async function handleCompletion(c) {
|
|
|
4680
4676
|
requestId,
|
|
4681
4677
|
sessionId,
|
|
4682
4678
|
compactType,
|
|
4683
|
-
logger: logger$
|
|
4679
|
+
logger: logger$4
|
|
4684
4680
|
});
|
|
4685
4681
|
if (shouldUseResponsesApi(selectedModel, compactType)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
|
|
4686
4682
|
subagentMarker,
|
|
@@ -4688,14 +4684,14 @@ async function handleCompletion(c) {
|
|
|
4688
4684
|
requestId,
|
|
4689
4685
|
sessionId,
|
|
4690
4686
|
compactType,
|
|
4691
|
-
logger: logger$
|
|
4687
|
+
logger: logger$4
|
|
4692
4688
|
});
|
|
4693
4689
|
return await messagesFlowHandlers.handleWithChatCompletions(c, anthropicPayload, {
|
|
4694
4690
|
subagentMarker,
|
|
4695
4691
|
requestId,
|
|
4696
4692
|
sessionId,
|
|
4697
4693
|
compactType,
|
|
4698
|
-
logger: logger$
|
|
4694
|
+
logger: logger$4
|
|
4699
4695
|
});
|
|
4700
4696
|
}
|
|
4701
4697
|
const MESSAGES_ENDPOINT = "/v1/messages";
|
|
@@ -4770,24 +4766,32 @@ providerMessageRoutes.post("/count_tokens", async (c) => {
|
|
|
4770
4766
|
});
|
|
4771
4767
|
//#endregion
|
|
4772
4768
|
//#region src/routes/provider/models/route.ts
|
|
4773
|
-
const logger$
|
|
4769
|
+
const logger$3 = createHandlerLogger("provider-models-handler");
|
|
4774
4770
|
const providerModelRoutes = new Hono();
|
|
4775
4771
|
providerModelRoutes.get("/", async (c) => {
|
|
4776
4772
|
const provider = c.req.param("provider") ?? "";
|
|
4777
4773
|
try {
|
|
4778
|
-
const providerConfig =
|
|
4774
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
4779
4775
|
if (!providerConfig) return c.json({ error: {
|
|
4780
4776
|
message: `Provider '${provider}' not found or disabled`,
|
|
4781
4777
|
type: "invalid_request_error"
|
|
4782
4778
|
} }, 404);
|
|
4779
|
+
if (providerConfig.name === "codex") {
|
|
4780
|
+
const models = getModels();
|
|
4781
|
+
return c.json({
|
|
4782
|
+
object: "list",
|
|
4783
|
+
data: models.data,
|
|
4784
|
+
has_more: false
|
|
4785
|
+
});
|
|
4786
|
+
}
|
|
4783
4787
|
const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers);
|
|
4784
|
-
logger$
|
|
4788
|
+
logger$3.debug("provider.models.response", {
|
|
4785
4789
|
provider,
|
|
4786
4790
|
statusCode: upstreamResponse.status
|
|
4787
4791
|
});
|
|
4788
4792
|
return createProviderProxyResponse(upstreamResponse);
|
|
4789
4793
|
} catch (error) {
|
|
4790
|
-
logger$
|
|
4794
|
+
logger$3.error("provider.models.error", {
|
|
4791
4795
|
provider,
|
|
4792
4796
|
error
|
|
4793
4797
|
});
|
|
@@ -4795,6 +4799,79 @@ providerModelRoutes.get("/", async (c) => {
|
|
|
4795
4799
|
}
|
|
4796
4800
|
});
|
|
4797
4801
|
//#endregion
|
|
4802
|
+
//#region src/routes/provider/responses/handler.ts
|
|
4803
|
+
const logger$2 = createHandlerLogger("provider-responses-handler");
|
|
4804
|
+
async function handleProviderResponsesForProvider(c, options) {
|
|
4805
|
+
const { payload, provider } = options;
|
|
4806
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
4807
|
+
if (providerConfig?.type !== "openai-responses") return c.json({ error: {
|
|
4808
|
+
message: `Provider '${provider}' does not support the /v1/responses endpoint`,
|
|
4809
|
+
type: "invalid_request_error"
|
|
4810
|
+
} }, 400);
|
|
4811
|
+
applyResponsesApiContextManagement(payload, (providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0)?.capabilities.limits.max_prompt_tokens);
|
|
4812
|
+
const upstreamResponse = providerConfig.name === "codex" ? await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl) : await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
|
|
4813
|
+
if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${provider} responses`, upstreamResponse);
|
|
4814
|
+
const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
|
|
4815
|
+
if (payload.stream) recordProviderResponsesStreamUsage(upstreamResponse.clone(), {
|
|
4816
|
+
normalizeCodex: providerConfig.name === "codex",
|
|
4817
|
+
provider,
|
|
4818
|
+
recordUsage
|
|
4819
|
+
}).catch((error) => {
|
|
4820
|
+
logger$2.warn("provider.responses.usage_stream_error", {
|
|
4821
|
+
provider,
|
|
4822
|
+
error: getErrorMessage(error)
|
|
4823
|
+
});
|
|
4824
|
+
});
|
|
4825
|
+
else recordUsage(normalizeResponsesUsage((await upstreamResponse.clone().json()).usage));
|
|
4826
|
+
if (providerConfig.name === "codex" && payload.stream) return createProviderProxyResponse(upstreamResponse, createStandardizedCodexResponsesEventStream(getResponsesEvents(upstreamResponse)));
|
|
4827
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
4828
|
+
}
|
|
4829
|
+
const getErrorMessage = (error) => {
|
|
4830
|
+
if (error instanceof Error && error.message) return error.message;
|
|
4831
|
+
return String(error);
|
|
4832
|
+
};
|
|
4833
|
+
const createProviderResponsesUsageRecorder = (payload, provider) => {
|
|
4834
|
+
const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
|
|
4835
|
+
return createProviderTokenUsageRecorder({
|
|
4836
|
+
endpoint: "responses",
|
|
4837
|
+
model: payload.model,
|
|
4838
|
+
providerName: provider,
|
|
4839
|
+
sessionId: sessionAffinity ?? ""
|
|
4840
|
+
});
|
|
4841
|
+
};
|
|
4842
|
+
const recordProviderResponsesStreamUsage = async (upstreamResponse, options) => {
|
|
4843
|
+
let usage = {};
|
|
4844
|
+
try {
|
|
4845
|
+
for await (const chunk of getResponsesEvents(upstreamResponse)) {
|
|
4846
|
+
debugJson(logger$2, "Responses stream chunk:", chunk);
|
|
4847
|
+
if (!chunk.data || chunk.data === "[DONE]") continue;
|
|
4848
|
+
const parsed = parseProviderResponsesStreamEvent(chunk.data, {
|
|
4849
|
+
normalizeCodex: options.normalizeCodex,
|
|
4850
|
+
provider: options.provider
|
|
4851
|
+
});
|
|
4852
|
+
if (parsed?.type === "response.completed" || parsed?.type === "response.failed" || parsed?.type === "response.incomplete") usage = normalizeResponsesUsage(parsed.response.usage);
|
|
4853
|
+
}
|
|
4854
|
+
} finally {
|
|
4855
|
+
options.recordUsage(usage);
|
|
4856
|
+
}
|
|
4857
|
+
};
|
|
4858
|
+
const parseProviderResponsesStreamEvent = (data, options) => {
|
|
4859
|
+
try {
|
|
4860
|
+
const parsed = JSON.parse(data);
|
|
4861
|
+
return options.normalizeCodex ? normalizeCodexResponsesEvent(parsed) : parsed;
|
|
4862
|
+
} catch (error) {
|
|
4863
|
+
logger$2.error("provider.responses.parse_chunk_error", {
|
|
4864
|
+
provider: options.provider,
|
|
4865
|
+
data,
|
|
4866
|
+
error
|
|
4867
|
+
});
|
|
4868
|
+
return null;
|
|
4869
|
+
}
|
|
4870
|
+
};
|
|
4871
|
+
const getResponsesEvents = (response) => {
|
|
4872
|
+
return events(response);
|
|
4873
|
+
};
|
|
4874
|
+
//#endregion
|
|
4798
4875
|
//#region src/routes/responses/stream-id-sync.ts
|
|
4799
4876
|
const createStreamIdTracker = () => ({ outputItems: /* @__PURE__ */ new Map() });
|
|
4800
4877
|
const fixStreamIds = (data, event, tracker) => {
|
|
@@ -4839,9 +4916,17 @@ const responsesHandlerDependencies = {
|
|
|
4839
4916
|
isResponsesApiWebSearchEnabled
|
|
4840
4917
|
};
|
|
4841
4918
|
const handleResponses = async (c) => {
|
|
4842
|
-
await responsesHandlerDependencies.checkRateLimit(state);
|
|
4843
4919
|
const payload = await c.req.json();
|
|
4844
4920
|
debugJson(logger$1, "Responses request payload:", payload);
|
|
4921
|
+
const providerModelAlias = parseProviderModelAlias(payload.model);
|
|
4922
|
+
if (providerModelAlias) {
|
|
4923
|
+
payload.model = providerModelAlias.model;
|
|
4924
|
+
return await handleProviderResponsesForProvider(c, {
|
|
4925
|
+
payload,
|
|
4926
|
+
provider: providerModelAlias.provider
|
|
4927
|
+
});
|
|
4928
|
+
}
|
|
4929
|
+
await responsesHandlerDependencies.checkRateLimit(state);
|
|
4845
4930
|
const requestId = generateRequestIdFromPayload({ messages: payload.input });
|
|
4846
4931
|
logger$1.debug("Generated request ID:", requestId);
|
|
4847
4932
|
const sessionId = getUUID(requestId);
|
|
@@ -5040,4 +5125,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
5040
5125
|
//#endregion
|
|
5041
5126
|
export { server };
|
|
5042
5127
|
|
|
5043
|
-
//# sourceMappingURL=server-
|
|
5128
|
+
//# sourceMappingURL=server-2tRe3sDu.js.map
|