@jeffreycao/copilot-api 1.10.8 → 1.10.10
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 +17 -14
- package/README.zh-CN.md +17 -14
- package/dist/auth-CeZtnQE_.js +116 -0
- package/dist/auth-CeZtnQE_.js.map +1 -0
- package/dist/{check-usage-BdXGp1Wr.js → check-usage-CTShKCvR.js} +3 -4
- package/dist/{check-usage-BdXGp1Wr.js.map → check-usage-CTShKCvR.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-BRFLAT4z.js → server-D5O9IzAY.js} +1770 -1668
- package/dist/server-D5O9IzAY.js.map +1 -0
- package/dist/{start-Bzfnxzlh.js → start-BhPxHgu8.js} +4 -6
- package/dist/{start-Bzfnxzlh.js.map → start-BhPxHgu8.js.map} +1 -1
- package/dist/token-1SfgxCRm.js +1875 -0
- package/dist/token-1SfgxCRm.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-BRFLAT4z.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-1SfgxCRm.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 {
|
|
@@ -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;
|
|
@@ -1738,13 +1761,13 @@ 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));
|
|
@@ -2205,1826 +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
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
if (!upstreamResponse.ok) {
|
|
2355
|
-
logger$4.error("Failed to create openai-compatible responses", upstreamResponse);
|
|
2356
|
-
throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
|
|
2357
|
-
}
|
|
2358
|
-
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
2359
|
-
if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
|
|
2360
|
-
c,
|
|
2361
|
-
payload,
|
|
2362
|
-
provider,
|
|
2363
|
-
upstreamResponse
|
|
2364
|
-
});
|
|
2365
|
-
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
2366
|
-
body: await upstreamResponse.json(),
|
|
2367
|
-
payload,
|
|
2368
|
-
provider
|
|
2369
|
-
});
|
|
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
|
+
};
|
|
2370
2274
|
};
|
|
2371
|
-
const
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
|
|
2385
|
-
if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
|
|
2386
|
-
return openAIPayload;
|
|
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("|");
|
|
2387
2288
|
};
|
|
2388
|
-
const
|
|
2389
|
-
|
|
2390
|
-
if (message.role !== "assistant") continue;
|
|
2391
|
-
if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
|
|
2392
|
-
delete message.reasoning_text;
|
|
2393
|
-
delete message.reasoning_opaque;
|
|
2394
|
-
}
|
|
2289
|
+
const getResponsesWebSocketInitiator = (preparedHeaders) => {
|
|
2290
|
+
return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
|
|
2395
2291
|
};
|
|
2396
|
-
const
|
|
2397
|
-
|
|
2398
|
-
|
|
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;
|
|
2399
2309
|
};
|
|
2400
|
-
const
|
|
2401
|
-
|
|
2402
|
-
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
2310
|
+
const buildResponsesWebSocketUrl = (baseUrl) => {
|
|
2311
|
+
return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
|
|
2403
2312
|
};
|
|
2404
|
-
const
|
|
2405
|
-
const
|
|
2406
|
-
|
|
2407
|
-
const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-2);
|
|
2408
|
-
return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
|
|
2313
|
+
const getHeaderValue = (headers, headerName) => {
|
|
2314
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
2315
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
2409
2316
|
};
|
|
2410
|
-
const
|
|
2411
|
-
const
|
|
2412
|
-
if (
|
|
2413
|
-
|
|
2414
|
-
|
|
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
|
+
}
|
|
2415
2331
|
};
|
|
2416
|
-
const
|
|
2417
|
-
if (!
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
}];
|
|
2424
|
-
return;
|
|
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;
|
|
2425
2339
|
}
|
|
2426
|
-
if (!Array.isArray(message.content)) return;
|
|
2427
|
-
const lastPart = message.content.at(-1);
|
|
2428
|
-
if (!lastPart) return;
|
|
2429
|
-
setContextCacheControl(lastPart);
|
|
2430
2340
|
};
|
|
2431
|
-
const
|
|
2432
|
-
|
|
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");
|
|
2433
2349
|
};
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
const parsed = parseProviderStreamEvent(data, providerConfig);
|
|
2453
|
-
if (parsed) {
|
|
2454
|
-
usage = mergeAnthropicUsage(usage, parsed.usage);
|
|
2455
|
-
data = parsed.data;
|
|
2456
|
-
}
|
|
2457
|
-
await stream.writeSSE({
|
|
2458
|
-
event: eventName,
|
|
2459
|
-
data
|
|
2460
|
-
});
|
|
2461
|
-
}
|
|
2462
|
-
recordUsage(usage);
|
|
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}`;
|
|
2361
|
+
};
|
|
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
|
|
2463
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;
|
|
2464
2402
|
};
|
|
2465
|
-
const
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
const
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
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
|
|
2476
2417
|
};
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
const translateMessage = (message, model, applyPhase, state) => {
|
|
2421
|
+
if (message.role === "user") return translateUserMessage(message, state);
|
|
2422
|
+
return translateAssistantMessage(message, model, applyPhase, state);
|
|
2423
|
+
};
|
|
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;
|
|
2434
|
+
}
|
|
2435
|
+
const converted = translateUserContentBlock(block);
|
|
2436
|
+
if (converted.length > 0) pendingContent.push(...converted);
|
|
2437
|
+
}
|
|
2438
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2439
|
+
return items;
|
|
2440
|
+
};
|
|
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
|
|
2483
2463
|
});
|
|
2464
|
+
items.push(compactionContent);
|
|
2484
2465
|
continue;
|
|
2485
2466
|
}
|
|
2486
|
-
if (
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
|
|
2491
|
-
if (!parsed) continue;
|
|
2492
|
-
if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
|
|
2493
|
-
const events = translateChunkToAnthropicEvents(parsed, streamState);
|
|
2494
|
-
for (const event of events) {
|
|
2495
|
-
const eventData = JSON.stringify(event);
|
|
2496
|
-
debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
2497
|
-
await stream.writeSSE({
|
|
2498
|
-
event: event.type,
|
|
2499
|
-
data: eventData
|
|
2467
|
+
if (block.signature.includes("@")) {
|
|
2468
|
+
flushPendingContent(pendingContent, items, {
|
|
2469
|
+
role: "assistant",
|
|
2470
|
+
phase: assistantPhase
|
|
2500
2471
|
});
|
|
2472
|
+
items.push(createReasoningContent(block));
|
|
2473
|
+
continue;
|
|
2501
2474
|
}
|
|
2502
2475
|
}
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
});
|
|
2510
|
-
}
|
|
2511
|
-
recordUsage(usage);
|
|
2476
|
+
const converted = translateAssistantContentBlock(block);
|
|
2477
|
+
if (converted) pendingContent.push(converted);
|
|
2478
|
+
}
|
|
2479
|
+
flushPendingContent(pendingContent, items, {
|
|
2480
|
+
role: "assistant",
|
|
2481
|
+
phase: assistantPhase
|
|
2512
2482
|
});
|
|
2483
|
+
return items;
|
|
2513
2484
|
};
|
|
2514
|
-
const
|
|
2515
|
-
|
|
2516
|
-
return
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
error
|
|
2521
|
-
});
|
|
2522
|
-
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 [];
|
|
2523
2491
|
}
|
|
2524
2492
|
};
|
|
2525
|
-
const
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
adjustInputTokens(providerConfig, parsed.message.usage);
|
|
2530
|
-
return {
|
|
2531
|
-
data: JSON.stringify(parsed),
|
|
2532
|
-
model: parsed.message.model,
|
|
2533
|
-
usage: normalizeAnthropicUsage(parsed.message.usage)
|
|
2534
|
-
};
|
|
2535
|
-
}
|
|
2536
|
-
if (parsed.type === "message_delta") {
|
|
2537
|
-
adjustInputTokens(providerConfig, parsed.usage);
|
|
2538
|
-
return {
|
|
2539
|
-
data: JSON.stringify(parsed),
|
|
2540
|
-
usage: normalizeAnthropicUsage(parsed.usage)
|
|
2541
|
-
};
|
|
2542
|
-
}
|
|
2543
|
-
return {
|
|
2544
|
-
data: JSON.stringify(parsed),
|
|
2545
|
-
usage: {}
|
|
2546
|
-
};
|
|
2547
|
-
} catch (error) {
|
|
2548
|
-
logger$4.error("provider.messages.streaming.adjust_tokens_error", {
|
|
2549
|
-
error,
|
|
2550
|
-
originalData: data
|
|
2551
|
-
});
|
|
2552
|
-
return null;
|
|
2493
|
+
const translateAssistantContentBlock = (block) => {
|
|
2494
|
+
switch (block.type) {
|
|
2495
|
+
case "text": return createOutPutTextContent(block.text);
|
|
2496
|
+
default: return;
|
|
2553
2497
|
}
|
|
2554
2498
|
};
|
|
2555
|
-
const
|
|
2556
|
-
|
|
2557
|
-
const
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
debugJson(logger$4, "provider.messages.no_stream result:", body);
|
|
2561
|
-
return c.json(body);
|
|
2562
|
-
};
|
|
2563
|
-
const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
|
|
2564
|
-
const { body, payload, provider } = options;
|
|
2565
|
-
createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
|
|
2566
|
-
const anthropicResponse = translateToAnthropic(body);
|
|
2567
|
-
debugJson(logger$4, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
|
|
2568
|
-
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;
|
|
2569
2504
|
};
|
|
2570
|
-
const
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2505
|
+
const createMessage = (role, content, phase) => ({
|
|
2506
|
+
type: MESSAGE_TYPE,
|
|
2507
|
+
role,
|
|
2508
|
+
content,
|
|
2509
|
+
...role === "assistant" && phase ? { phase } : {}
|
|
2575
2510
|
});
|
|
2576
|
-
const
|
|
2577
|
-
if (!
|
|
2578
|
-
|
|
2579
|
-
|
|
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";
|
|
2580
2517
|
};
|
|
2581
|
-
const
|
|
2582
|
-
|
|
2583
|
-
isResponsesApiWebSocketEnabled
|
|
2518
|
+
const shouldApplyPhase = (_model) => {
|
|
2519
|
+
return true;
|
|
2584
2520
|
};
|
|
2585
|
-
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;
|
|
2586
2542
|
return {
|
|
2587
|
-
|
|
2588
|
-
|
|
2543
|
+
id,
|
|
2544
|
+
type: "reasoning",
|
|
2545
|
+
summary: thinking ? [{
|
|
2546
|
+
type: "summary_text",
|
|
2547
|
+
text: thinking
|
|
2548
|
+
}] : [],
|
|
2549
|
+
encrypted_content: encryptedContent
|
|
2589
2550
|
};
|
|
2590
2551
|
};
|
|
2591
|
-
const
|
|
2592
|
-
const
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
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
|
+
};
|
|
2597
2560
|
};
|
|
2598
|
-
const
|
|
2599
|
-
const
|
|
2600
|
-
if (
|
|
2601
|
-
|
|
2602
|
-
|
|
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
|
+
};
|
|
2603
2571
|
};
|
|
2604
|
-
const
|
|
2605
|
-
|
|
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);
|
|
2606
2590
|
};
|
|
2607
|
-
const
|
|
2608
|
-
|
|
2609
|
-
|
|
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);
|
|
2610
2601
|
};
|
|
2611
|
-
const
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
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
|
+
};
|
|
2619
2611
|
};
|
|
2620
|
-
const
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
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 [];
|
|
2625
2618
|
};
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2619
|
+
const extractToolReferenceNames = (content) => {
|
|
2620
|
+
if (!Array.isArray(content)) return [];
|
|
2621
|
+
return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
|
|
2628
2622
|
};
|
|
2629
|
-
const
|
|
2630
|
-
|
|
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;
|
|
2631
2631
|
};
|
|
2632
|
-
const
|
|
2633
|
-
const
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
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`);
|
|
2637
2636
|
};
|
|
2638
|
-
const
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
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;
|
|
2646
2654
|
};
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
const
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
const
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
if (payload.stream) return stream;
|
|
2666
|
-
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));
|
|
2667
2673
|
}
|
|
2668
|
-
return
|
|
2674
|
+
return converted;
|
|
2669
2675
|
};
|
|
2670
|
-
const
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
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";
|
|
2680
2728
|
}
|
|
2681
|
-
if (payload.stream) return events(response);
|
|
2682
|
-
return await response.json();
|
|
2683
2729
|
};
|
|
2684
|
-
const
|
|
2685
|
-
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);
|
|
2686
2736
|
return {
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
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
|
|
2690
2745
|
};
|
|
2691
2746
|
};
|
|
2692
|
-
const
|
|
2693
|
-
const
|
|
2694
|
-
const
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
}
|
|
2709
|
-
|
|
2710
|
-
const
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
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;
|
|
2720
2792
|
};
|
|
2721
|
-
const
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
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;
|
|
2739
2812
|
}
|
|
2740
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2741
|
-
throw new Error("Responses websocket ended without a terminal response");
|
|
2742
|
-
} catch (error) {
|
|
2743
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2744
|
-
throw toError(error);
|
|
2745
|
-
} finally {
|
|
2746
|
-
release();
|
|
2747
2813
|
}
|
|
2814
|
+
return aggregated;
|
|
2748
2815
|
};
|
|
2749
|
-
const
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
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
|
+
}
|
|
2753
2824
|
};
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
const entry = createResponsesWebSocketEntry(request);
|
|
2763
|
-
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;
|
|
2764
2833
|
return {
|
|
2765
|
-
|
|
2766
|
-
|
|
2834
|
+
type: "tool_use",
|
|
2835
|
+
id: toolId,
|
|
2836
|
+
name: toolName,
|
|
2837
|
+
input: parseFunctionCallArguments(call.arguments)
|
|
2767
2838
|
};
|
|
2768
2839
|
};
|
|
2769
|
-
const
|
|
2770
|
-
const
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
})
|
|
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)
|
|
2778
2848
|
};
|
|
2779
|
-
entry.websocketPromise.then((websocket) => {
|
|
2780
|
-
websocket.addEventListener("close", () => {
|
|
2781
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2782
|
-
});
|
|
2783
|
-
websocket.addEventListener("error", () => {
|
|
2784
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2785
|
-
});
|
|
2786
|
-
}).catch(() => {
|
|
2787
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2788
|
-
});
|
|
2789
|
-
return entry;
|
|
2790
2849
|
};
|
|
2791
|
-
const
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
entry.requestCount += 1;
|
|
2795
|
-
let released = false;
|
|
2796
|
-
return () => {
|
|
2797
|
-
if (released) return;
|
|
2798
|
-
released = true;
|
|
2799
|
-
entry.requestCount -= 1;
|
|
2800
|
-
decrementResponsesWebSocketActiveRequestCount(poolKey);
|
|
2801
|
-
if (entry.closed || entry.requestCount > 0) return;
|
|
2802
|
-
if (pooled && responsesWebSocketPool.get(poolKey) === entry) {
|
|
2803
|
-
scheduleResponsesWebSocketIdleClose(poolKey, entry);
|
|
2804
|
-
return;
|
|
2805
|
-
}
|
|
2806
|
-
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
2807
|
-
};
|
|
2850
|
+
const resolveToolUseName = (call) => {
|
|
2851
|
+
if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
|
|
2852
|
+
return call.name;
|
|
2808
2853
|
};
|
|
2809
|
-
const
|
|
2810
|
-
if (
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
};
|
|
2819
|
-
const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
|
|
2820
|
-
clearResponsesWebSocketIdleTimer(entry);
|
|
2821
|
-
entry.idleTimer = setTimeout(() => {
|
|
2822
|
-
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
2823
|
-
}, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
|
|
2824
|
-
unrefTimer(entry.idleTimer);
|
|
2825
|
-
};
|
|
2826
|
-
const clearResponsesWebSocketIdleTimer = (entry) => {
|
|
2827
|
-
if (entry.idleTimer) {
|
|
2828
|
-
clearTimeout(entry.idleTimer);
|
|
2829
|
-
entry.idleTimer = null;
|
|
2830
|
-
}
|
|
2831
|
-
};
|
|
2832
|
-
const getResponsesWebSocketActiveRequestCount = (poolKey) => responsesWebSocketActiveRequests.get(poolKey) ?? 0;
|
|
2833
|
-
const incrementResponsesWebSocketActiveRequestCount = (poolKey) => {
|
|
2834
|
-
responsesWebSocketActiveRequests.set(poolKey, getResponsesWebSocketActiveRequestCount(poolKey) + 1);
|
|
2835
|
-
};
|
|
2836
|
-
const decrementResponsesWebSocketActiveRequestCount = (poolKey) => {
|
|
2837
|
-
const nextCount = getResponsesWebSocketActiveRequestCount(poolKey) - 1;
|
|
2838
|
-
if (nextCount <= 0) {
|
|
2839
|
-
responsesWebSocketActiveRequests.delete(poolKey);
|
|
2840
|
-
return;
|
|
2841
|
-
}
|
|
2842
|
-
responsesWebSocketActiveRequests.set(poolKey, nextCount);
|
|
2843
|
-
};
|
|
2844
|
-
const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
|
|
2845
|
-
if (responsesWebSocketPool.get(poolKey) === entry) responsesWebSocketPool.delete(poolKey);
|
|
2846
|
-
if (entry.closed) return;
|
|
2847
|
-
entry.closed = true;
|
|
2848
|
-
clearResponsesWebSocketIdleTimer(entry);
|
|
2849
|
-
entry.websocketPromise.then(closeResponsesWebSocket).catch(() => {});
|
|
2850
|
-
};
|
|
2851
|
-
const unrefTimer = (timer) => {
|
|
2852
|
-
if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
|
|
2853
|
-
};
|
|
2854
|
-
const createResponsesWebSocketError = (message, event) => {
|
|
2855
|
-
const reason = event?.error ?? event?.message;
|
|
2856
|
-
if (reason === void 0 || reason === "") return new Error(message);
|
|
2857
|
-
const cause = toError(reason);
|
|
2858
|
-
return new Error(`${message}: ${cause.message}`, { cause });
|
|
2859
|
-
};
|
|
2860
|
-
const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
|
|
2861
|
-
const dispatcher = getProxyEnvDispatcher();
|
|
2862
|
-
const websocket = new WebSocket(url, dispatcher ? {
|
|
2863
|
-
dispatcher,
|
|
2864
|
-
headers
|
|
2865
|
-
} : { headers });
|
|
2866
|
-
const cleanup = () => {
|
|
2867
|
-
websocket.removeEventListener("open", onOpen);
|
|
2868
|
-
websocket.removeEventListener("error", onError);
|
|
2869
|
-
};
|
|
2870
|
-
const onOpen = () => {
|
|
2871
|
-
cleanup();
|
|
2872
|
-
resolve(websocket);
|
|
2873
|
-
};
|
|
2874
|
-
const onError = (event) => {
|
|
2875
|
-
cleanup();
|
|
2876
|
-
reject(createResponsesWebSocketError("Failed to create responses websocket", event));
|
|
2877
|
-
};
|
|
2878
|
-
websocket.addEventListener("open", onOpen);
|
|
2879
|
-
websocket.addEventListener("error", onError);
|
|
2880
|
-
});
|
|
2881
|
-
const createWebSocketMessageStream = async function* (websocket) {
|
|
2882
|
-
const queue = [];
|
|
2883
|
-
let closed = false;
|
|
2884
|
-
let error = null;
|
|
2885
|
-
let notify = null;
|
|
2886
|
-
const wake = () => {
|
|
2887
|
-
notify?.();
|
|
2888
|
-
notify = null;
|
|
2889
|
-
};
|
|
2890
|
-
const onMessage = (event) => {
|
|
2891
|
-
queue.push(normalizeWebSocketMessageData(event.data));
|
|
2892
|
-
wake();
|
|
2893
|
-
};
|
|
2894
|
-
const onClose = () => {
|
|
2895
|
-
closed = true;
|
|
2896
|
-
wake();
|
|
2897
|
-
};
|
|
2898
|
-
const onError = (event) => {
|
|
2899
|
-
error = createResponsesWebSocketError("Responses websocket stream error", event);
|
|
2900
|
-
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
|
+
})
|
|
2901
2863
|
};
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2864
|
+
};
|
|
2865
|
+
const parseFunctionCallArguments = (rawArguments) => {
|
|
2866
|
+
if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
|
|
2905
2867
|
try {
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
await new Promise((resolve) => {
|
|
2915
|
-
notify = resolve;
|
|
2916
|
-
});
|
|
2917
|
-
}
|
|
2918
|
-
} finally {
|
|
2919
|
-
websocket.removeEventListener("message", onMessage);
|
|
2920
|
-
websocket.removeEventListener("close", onClose);
|
|
2921
|
-
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
|
+
});
|
|
2922
2876
|
}
|
|
2877
|
+
return { raw_arguments: rawArguments };
|
|
2923
2878
|
};
|
|
2924
|
-
const
|
|
2925
|
-
|
|
2926
|
-
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
2927
|
-
if (ArrayBuffer.isView(data)) {
|
|
2928
|
-
const view = data;
|
|
2929
|
-
return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
|
|
2930
|
-
}
|
|
2931
|
-
if (isTextReadable(data)) return await data.text();
|
|
2932
|
-
return String(data);
|
|
2879
|
+
const parseToolSearchArguments = (argumentsValue) => {
|
|
2880
|
+
return formatToolSearchBridgeArguments(argumentsValue);
|
|
2933
2881
|
};
|
|
2934
|
-
const
|
|
2935
|
-
if (!
|
|
2936
|
-
return
|
|
2882
|
+
const fallbackContentBlocks = (outputText) => {
|
|
2883
|
+
if (!outputText) return [];
|
|
2884
|
+
return [{
|
|
2885
|
+
type: "text",
|
|
2886
|
+
text: outputText
|
|
2887
|
+
}];
|
|
2937
2888
|
};
|
|
2938
|
-
const
|
|
2939
|
-
|
|
2940
|
-
|
|
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;
|
|
2941
2901
|
};
|
|
2942
|
-
const
|
|
2943
|
-
const
|
|
2944
|
-
|
|
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
|
+
};
|
|
2945
2910
|
};
|
|
2946
|
-
const
|
|
2947
|
-
const
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
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;
|
|
2959
2934
|
}
|
|
2935
|
+
return "";
|
|
2960
2936
|
};
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
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";
|
|
2968
2944
|
}
|
|
2969
2945
|
};
|
|
2970
|
-
const
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
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;
|
|
2976
2958
|
}
|
|
2977
|
-
|
|
2959
|
+
return {
|
|
2960
|
+
nextCount: count,
|
|
2961
|
+
exceeded: false
|
|
2962
|
+
};
|
|
2978
2963
|
};
|
|
2979
|
-
const
|
|
2980
|
-
|
|
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
|
+
}
|
|
2981
2992
|
};
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
const MESSAGE_TYPE = "message";
|
|
2985
|
-
const COMPACTION_SIGNATURE_PREFIX = "cm1#";
|
|
2986
|
-
const COMPACTION_SIGNATURE_SEPARATOR = "@";
|
|
2987
|
-
const THINKING_TEXT = "Thinking...";
|
|
2988
|
-
const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
|
|
2989
|
-
if (!basePromptCacheKey) return null;
|
|
2990
|
-
const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
|
|
2991
|
-
if (!normalizedSubagentAgentId) return basePromptCacheKey;
|
|
2992
|
-
return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
|
|
2993
|
+
const handleResponseCreated = (rawEvent, state) => {
|
|
2994
|
+
return messageStart(state, rawEvent.response);
|
|
2993
2995
|
};
|
|
2994
|
-
const
|
|
2995
|
-
const
|
|
2996
|
-
const
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
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
|
|
3000
3006
|
});
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
|
|
3012
|
-
const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
|
|
3013
|
-
const responsesPayload = {
|
|
3014
|
-
model: payload.model,
|
|
3015
|
-
input,
|
|
3016
|
-
instructions: translateSystemPrompt(payload.system, payload.model),
|
|
3017
|
-
temperature: 1,
|
|
3018
|
-
top_p: payload.top_p ?? null,
|
|
3019
|
-
max_output_tokens: Math.max(payload.max_tokens, 12800),
|
|
3020
|
-
tools: translatedTools,
|
|
3021
|
-
tool_choice: toolChoice,
|
|
3022
|
-
metadata: payload.metadata ? { ...payload.metadata } : null,
|
|
3023
|
-
stream: payload.stream ?? null,
|
|
3024
|
-
store: false,
|
|
3025
|
-
parallel_tool_calls: true,
|
|
3026
|
-
reasoning: {
|
|
3027
|
-
effort: getReasoningEffortForModel(payload.model),
|
|
3028
|
-
summary: "detailed"
|
|
3029
|
-
},
|
|
3030
|
-
include: ["reasoning.encrypted_content"]
|
|
3031
|
-
};
|
|
3032
|
-
if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
|
|
3033
|
-
return responsesPayload;
|
|
3034
|
-
};
|
|
3035
|
-
const encodeCompactionCarrierSignature = (compaction) => {
|
|
3036
|
-
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
3037
|
-
};
|
|
3038
|
-
const decodeCompactionCarrierSignature = (signature) => {
|
|
3039
|
-
if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
|
|
3040
|
-
const raw = signature.slice(4);
|
|
3041
|
-
const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
|
|
3042
|
-
if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
|
|
3043
|
-
const encrypted_content = raw.slice(0, separatorIndex);
|
|
3044
|
-
const id = raw.slice(separatorIndex + 1);
|
|
3045
|
-
if (!encrypted_content) return;
|
|
3046
|
-
return {
|
|
3047
|
-
id,
|
|
3048
|
-
encrypted_content
|
|
3049
|
-
};
|
|
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);
|
|
3050
3017
|
}
|
|
3018
|
+
return events;
|
|
3051
3019
|
};
|
|
3052
|
-
const
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
const
|
|
3057
|
-
if (
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
}
|
|
3073
|
-
const translateAssistantMessage = (message, model, applyPhase, state) => {
|
|
3074
|
-
const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
|
|
3075
|
-
if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
|
|
3076
|
-
if (!Array.isArray(message.content)) return [];
|
|
3077
|
-
const items = [];
|
|
3078
|
-
const pendingContent = [];
|
|
3079
|
-
for (const block of message.content) {
|
|
3080
|
-
if (block.type === "tool_use") {
|
|
3081
|
-
state.toolUseNameById.set(block.id, block.name);
|
|
3082
|
-
flushPendingContent(pendingContent, items, {
|
|
3083
|
-
role: "assistant",
|
|
3084
|
-
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
|
+
}
|
|
3085
3041
|
});
|
|
3086
|
-
|
|
3087
|
-
continue;
|
|
3042
|
+
state.blockHasDelta.add(blockIndex);
|
|
3088
3043
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
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
|
|
3098
3056
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
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
|
+
})
|
|
3106
3067
|
}
|
|
3107
|
-
}
|
|
3108
|
-
|
|
3109
|
-
|
|
3068
|
+
});
|
|
3069
|
+
state.blockHasDelta.add(blockIndex);
|
|
3070
|
+
return events;
|
|
3110
3071
|
}
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
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);
|
|
3123
3093
|
}
|
|
3094
|
+
return events;
|
|
3124
3095
|
};
|
|
3125
|
-
const
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
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);
|
|
3129
3139
|
}
|
|
3140
|
+
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3141
|
+
return events;
|
|
3130
3142
|
};
|
|
3131
|
-
const
|
|
3132
|
-
|
|
3133
|
-
const
|
|
3134
|
-
|
|
3135
|
-
|
|
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;
|
|
3136
3164
|
};
|
|
3137
|
-
const
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
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;
|
|
3149
3180
|
};
|
|
3150
|
-
const
|
|
3151
|
-
|
|
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;
|
|
3152
3195
|
};
|
|
3153
|
-
const
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
const
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
});
|
|
3171
|
-
|
|
3172
|
-
const { encryptedContent, id } = parseReasoningSignature(block.signature);
|
|
3173
|
-
const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
|
|
3174
|
-
return {
|
|
3175
|
-
id,
|
|
3176
|
-
type: "reasoning",
|
|
3177
|
-
summary: thinking ? [{
|
|
3178
|
-
type: "summary_text",
|
|
3179
|
-
text: thinking
|
|
3180
|
-
}] : [],
|
|
3181
|
-
encrypted_content: encryptedContent
|
|
3182
|
-
};
|
|
3183
|
-
};
|
|
3184
|
-
const createCompactionContent = (block) => {
|
|
3185
|
-
const compaction = decodeCompactionCarrierSignature(block.signature);
|
|
3186
|
-
if (!compaction) return;
|
|
3187
|
-
return {
|
|
3188
|
-
id: compaction.id,
|
|
3189
|
-
type: "compaction",
|
|
3190
|
-
encrypted_content: compaction.encrypted_content
|
|
3191
|
-
};
|
|
3192
|
-
};
|
|
3193
|
-
const parseReasoningSignature = (signature) => {
|
|
3194
|
-
const splitIndex = signature.lastIndexOf("@");
|
|
3195
|
-
if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
|
|
3196
|
-
encryptedContent: signature,
|
|
3197
|
-
id: ""
|
|
3198
|
-
};
|
|
3199
|
-
return {
|
|
3200
|
-
encryptedContent: signature.slice(0, splitIndex),
|
|
3201
|
-
id: signature.slice(splitIndex + 1)
|
|
3202
|
-
};
|
|
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;
|
|
3203
3215
|
};
|
|
3204
|
-
const
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
});
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
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;
|
|
3222
3234
|
};
|
|
3223
|
-
const
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
if (state.toolSearchEnabled && isBridgeToolSearchName(toolUseName ?? "")) return createToolSearchOutput(block, state.originalTools);
|
|
3232
|
-
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;
|
|
3233
3243
|
};
|
|
3234
|
-
const
|
|
3235
|
-
const
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
call_id: block.tool_use_id,
|
|
3239
|
-
tools: referencedToolNames.map((toolName) => convertDeferredToolToNamespace(resolveDeferredTool(toolName, originalTools))),
|
|
3240
|
-
execution: "client",
|
|
3241
|
-
status: block.is_error ? "incomplete" : "completed"
|
|
3242
|
-
};
|
|
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)];
|
|
3243
3248
|
};
|
|
3244
|
-
const
|
|
3245
|
-
const
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
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;
|
|
3250
3255
|
};
|
|
3251
|
-
const
|
|
3252
|
-
|
|
3253
|
-
|
|
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
|
+
}];
|
|
3254
3277
|
};
|
|
3255
|
-
const
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
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);
|
|
3261
3286
|
}
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
const createInvalidRequestError = (message) => new HTTPError(message, new Response(JSON.stringify({ error: {
|
|
3271
|
-
message,
|
|
3272
|
-
type: "invalid_request_error"
|
|
3273
|
-
} }), {
|
|
3274
|
-
status: 400,
|
|
3275
|
-
headers: { "content-type": "application/json" }
|
|
3276
|
-
}));
|
|
3277
|
-
const translateSystemPrompt = (system, model) => {
|
|
3278
|
-
if (!system) return null;
|
|
3279
|
-
const extraPrompt = getExtraPromptForModel(model);
|
|
3280
|
-
if (typeof system === "string") return system + extraPrompt;
|
|
3281
|
-
const text = system.map((block, index) => {
|
|
3282
|
-
if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
|
|
3283
|
-
return block.text;
|
|
3284
|
-
}).join(" ");
|
|
3285
|
-
return text.length > 0 ? text : null;
|
|
3286
|
-
};
|
|
3287
|
-
const convertAnthropicTools = (tools, toolSearchEnabled) => {
|
|
3288
|
-
if (!tools || tools.length === 0) return null;
|
|
3289
|
-
const converted = [];
|
|
3290
|
-
let addedToolSearch = false;
|
|
3291
|
-
const searchableToolNames = toolSearchEnabled ? listDeferredToolNames(tools) : [];
|
|
3292
|
-
for (const tool of tools) {
|
|
3293
|
-
if (isBridgeToolSearchName(tool.name)) {
|
|
3294
|
-
if (toolSearchEnabled && !addedToolSearch) {
|
|
3295
|
-
converted.push(createResponsesToolSearchDefinition(searchableToolNames));
|
|
3296
|
-
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: ""
|
|
3297
3295
|
}
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
if (toolSearchEnabled && isDeferredToolName(tool.name)) {
|
|
3301
|
-
converted.push(convertDeferredToolToNamespace(tool));
|
|
3302
|
-
continue;
|
|
3303
|
-
}
|
|
3304
|
-
converted.push(convertToolToFunction(tool));
|
|
3296
|
+
});
|
|
3297
|
+
state.openBlocks.add(blockIndex);
|
|
3305
3298
|
}
|
|
3306
|
-
return
|
|
3299
|
+
return blockIndex;
|
|
3307
3300
|
};
|
|
3308
|
-
const
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
type: "array",
|
|
3316
|
-
description: "Exact deferred tool names to load.",
|
|
3317
|
-
items: {
|
|
3318
|
-
type: "string",
|
|
3319
|
-
enum: searchableToolNames
|
|
3320
|
-
},
|
|
3321
|
-
minItems: 1
|
|
3322
|
-
} },
|
|
3323
|
-
required: ["names"],
|
|
3324
|
-
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);
|
|
3325
3308
|
}
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
...tool.description ? { description: tool.description } : {},
|
|
3338
|
-
tools: [{
|
|
3339
|
-
type: "function",
|
|
3340
|
-
name: tool.name,
|
|
3341
|
-
parameters: normalizeToolSchema(tool.input_schema),
|
|
3342
|
-
strict: false,
|
|
3343
|
-
defer_loading: true,
|
|
3344
|
-
...tool.description ? { description: tool.description } : {}
|
|
3345
|
-
}]
|
|
3346
|
-
});
|
|
3347
|
-
const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
|
|
3348
|
-
if (!choice) return "auto";
|
|
3349
|
-
switch (choice.type) {
|
|
3350
|
-
case "auto": return "auto";
|
|
3351
|
-
case "any": return "required";
|
|
3352
|
-
case "tool":
|
|
3353
|
-
if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
|
|
3354
|
-
return choice.name ? {
|
|
3355
|
-
type: "function",
|
|
3356
|
-
name: choice.name
|
|
3357
|
-
} : "auto";
|
|
3358
|
-
case "none": return "none";
|
|
3359
|
-
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);
|
|
3360
3320
|
}
|
|
3321
|
+
return blockIndex;
|
|
3361
3322
|
};
|
|
3362
|
-
const
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
type: "message",
|
|
3371
|
-
role: "assistant",
|
|
3372
|
-
content: anthropicContent,
|
|
3373
|
-
model: response.model,
|
|
3374
|
-
stop_reason: stopReason,
|
|
3375
|
-
stop_sequence: null,
|
|
3376
|
-
usage
|
|
3377
|
-
};
|
|
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);
|
|
3378
3331
|
};
|
|
3379
|
-
const
|
|
3380
|
-
const
|
|
3381
|
-
for (const item of output) switch (item.type) {
|
|
3382
|
-
case "reasoning": {
|
|
3383
|
-
const thinkingText = extractReasoningText(item);
|
|
3384
|
-
if (thinkingText.length > 0) contentBlocks.push({
|
|
3385
|
-
type: "thinking",
|
|
3386
|
-
thinking: thinkingText,
|
|
3387
|
-
signature: (item.encrypted_content ?? "") + "@" + item.id
|
|
3388
|
-
});
|
|
3389
|
-
break;
|
|
3390
|
-
}
|
|
3391
|
-
case "function_call": {
|
|
3392
|
-
const toolUseBlock = createToolUseContentBlock(item);
|
|
3393
|
-
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
3394
|
-
break;
|
|
3395
|
-
}
|
|
3396
|
-
case "tool_search_call": {
|
|
3397
|
-
const toolUseBlock = createToolSearchUseContentBlock(item, options?.toolSearchName);
|
|
3398
|
-
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
3399
|
-
break;
|
|
3400
|
-
}
|
|
3401
|
-
case "tool_search_output": break;
|
|
3402
|
-
case "message": {
|
|
3403
|
-
const combinedText = combineMessageTextContent(item.content);
|
|
3404
|
-
if (combinedText.length > 0) contentBlocks.push({
|
|
3405
|
-
type: "text",
|
|
3406
|
-
text: combinedText
|
|
3407
|
-
});
|
|
3408
|
-
break;
|
|
3409
|
-
}
|
|
3410
|
-
case "compaction": {
|
|
3411
|
-
const compactionBlock = createCompactionThinkingBlock(item);
|
|
3412
|
-
if (compactionBlock) contentBlocks.push(compactionBlock);
|
|
3413
|
-
break;
|
|
3414
|
-
}
|
|
3415
|
-
default: {
|
|
3416
|
-
const combinedText = combineMessageTextContent(item.content);
|
|
3417
|
-
if (combinedText.length > 0) contentBlocks.push({
|
|
3418
|
-
type: "text",
|
|
3419
|
-
text: combinedText
|
|
3420
|
-
});
|
|
3421
|
-
}
|
|
3422
|
-
}
|
|
3423
|
-
return contentBlocks;
|
|
3332
|
+
const closeOpenBlocks = (state, events) => {
|
|
3333
|
+
for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
|
|
3424
3334
|
};
|
|
3425
|
-
const
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
for (const block of content) {
|
|
3429
|
-
if (isResponseOutputText(block)) {
|
|
3430
|
-
aggregated += block.text;
|
|
3431
|
-
continue;
|
|
3432
|
-
}
|
|
3433
|
-
if (isResponseOutputRefusal(block)) {
|
|
3434
|
-
aggregated += block.refusal;
|
|
3435
|
-
continue;
|
|
3436
|
-
}
|
|
3437
|
-
if (typeof block.text === "string") {
|
|
3438
|
-
aggregated += block.text;
|
|
3439
|
-
continue;
|
|
3440
|
-
}
|
|
3441
|
-
if (typeof block.reasoning === "string") {
|
|
3442
|
-
aggregated += block.reasoning;
|
|
3443
|
-
continue;
|
|
3444
|
-
}
|
|
3445
|
-
}
|
|
3446
|
-
return aggregated;
|
|
3335
|
+
const closeAllOpenBlocks = (state, events) => {
|
|
3336
|
+
closeOpenBlocks(state, events);
|
|
3337
|
+
state.functionCallStateByOutputIndex.clear();
|
|
3447
3338
|
};
|
|
3448
|
-
const
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
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;
|
|
3460
3378
|
};
|
|
3461
|
-
const
|
|
3462
|
-
const
|
|
3463
|
-
const
|
|
3464
|
-
if (
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
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: ""
|
|
3470
3387
|
};
|
|
3471
|
-
|
|
3472
|
-
const createToolSearchUseContentBlock = (call, toolSearchName = BRIDGE_TOOL_SEARCH_NAME) => {
|
|
3473
|
-
const toolId = call.call_id;
|
|
3474
|
-
if (!toolId) return null;
|
|
3388
|
+
if (itemType !== "function_call") return;
|
|
3475
3389
|
return {
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
name:
|
|
3479
|
-
|
|
3390
|
+
outputIndex: rawEvent.output_index,
|
|
3391
|
+
toolCallId: item.call_id,
|
|
3392
|
+
name: resolveToolUseName(item),
|
|
3393
|
+
initialArguments: item.arguments
|
|
3480
3394
|
};
|
|
3481
3395
|
};
|
|
3482
|
-
const
|
|
3483
|
-
|
|
3484
|
-
|
|
3396
|
+
const stringifyToolSearchArguments = (argumentsValue) => {
|
|
3397
|
+
try {
|
|
3398
|
+
return JSON.stringify(formatToolSearchBridgeArguments(argumentsValue));
|
|
3399
|
+
} catch {
|
|
3400
|
+
return;
|
|
3401
|
+
}
|
|
3485
3402
|
};
|
|
3486
|
-
const
|
|
3487
|
-
|
|
3403
|
+
const responsesUtilsDependencies = {
|
|
3404
|
+
isResponsesApiContextManagementModel,
|
|
3405
|
+
isResponsesApiWebSocketEnabled
|
|
3406
|
+
};
|
|
3407
|
+
const getResponsesRequestOptions = (payload) => {
|
|
3488
3408
|
return {
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
signature: encodeCompactionCarrierSignature({
|
|
3492
|
-
id: item.id,
|
|
3493
|
-
encrypted_content: item.encrypted_content
|
|
3494
|
-
})
|
|
3409
|
+
vision: hasVisionInput(payload),
|
|
3410
|
+
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
|
3495
3411
|
};
|
|
3496
3412
|
};
|
|
3497
|
-
const
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
} catch (error) {
|
|
3504
|
-
consola.warn("Failed to parse function call arguments", {
|
|
3505
|
-
error,
|
|
3506
|
-
rawArguments
|
|
3507
|
-
});
|
|
3508
|
-
}
|
|
3509
|
-
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;
|
|
3510
3419
|
};
|
|
3511
|
-
const
|
|
3512
|
-
|
|
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";
|
|
3513
3425
|
};
|
|
3514
|
-
const
|
|
3515
|
-
|
|
3516
|
-
return [{
|
|
3517
|
-
type: "text",
|
|
3518
|
-
text: outputText
|
|
3519
|
-
}];
|
|
3426
|
+
const hasVisionInput = (payload) => {
|
|
3427
|
+
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
3520
3428
|
};
|
|
3521
|
-
const
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
|
|
3525
|
-
return "end_turn";
|
|
3526
|
-
}
|
|
3527
|
-
if (status === "incomplete") {
|
|
3528
|
-
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
3529
|
-
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
3530
|
-
}
|
|
3531
|
-
return null;
|
|
3429
|
+
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
3430
|
+
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
|
|
3431
|
+
return 5e4;
|
|
3532
3432
|
};
|
|
3533
|
-
const
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
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));
|
|
3541
3441
|
};
|
|
3542
|
-
const
|
|
3543
|
-
|
|
3544
|
-
const
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
if (Array.isArray(content)) {
|
|
3548
|
-
const result = [];
|
|
3549
|
-
for (const block of content) switch (block.type) {
|
|
3550
|
-
case "text":
|
|
3551
|
-
result.push(createTextContent(block.text));
|
|
3552
|
-
break;
|
|
3553
|
-
case "image":
|
|
3554
|
-
result.push(createImageContent(block));
|
|
3555
|
-
break;
|
|
3556
|
-
case "document":
|
|
3557
|
-
result.push(createFileContent(block));
|
|
3558
|
-
break;
|
|
3559
|
-
case "tool_reference":
|
|
3560
|
-
result.push(createTextContent(`Tool ${block.tool_name} loaded`));
|
|
3561
|
-
break;
|
|
3562
|
-
default: break;
|
|
3563
|
-
}
|
|
3564
|
-
return result;
|
|
3565
|
-
}
|
|
3566
|
-
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);
|
|
3567
3447
|
};
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
|
|
3571
|
-
var FunctionCallArgumentsValidationError = class extends Error {
|
|
3572
|
-
constructor(message) {
|
|
3573
|
-
super(message);
|
|
3574
|
-
this.name = "FunctionCallArgumentsValidationError";
|
|
3575
|
-
}
|
|
3448
|
+
const getLatestCompactionMessageIndex = (input) => {
|
|
3449
|
+
for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
|
|
3576
3450
|
};
|
|
3577
|
-
const
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3451
|
+
const isCompactionInputItem = (value) => {
|
|
3452
|
+
return "type" in value && typeof value.type === "string" && value.type === "compaction";
|
|
3453
|
+
};
|
|
3454
|
+
const getPayloadItems = (payload) => {
|
|
3455
|
+
const result = [];
|
|
3456
|
+
const { input } = payload;
|
|
3457
|
+
if (Array.isArray(input)) result.push(...input);
|
|
3458
|
+
return result;
|
|
3459
|
+
};
|
|
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;
|
|
3468
|
+
};
|
|
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"
|
|
3589
3499
|
}
|
|
3500
|
+
];
|
|
3501
|
+
function normalizeCodexModel(model) {
|
|
3502
|
+
const supportsVision = model.input.includes("image");
|
|
3590
3503
|
return {
|
|
3591
|
-
|
|
3592
|
-
|
|
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"
|
|
3593
3537
|
};
|
|
3594
|
-
}
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
const
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
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;
|
|
3621
3573
|
}
|
|
3622
|
-
|
|
3623
|
-
const
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
const
|
|
3631
|
-
const
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
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
|
|
3636
3588
|
});
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
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$4 = 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$4, "provider.messages.request", {
|
|
3646
|
+
payload,
|
|
3647
|
+
provider
|
|
3645
3648
|
});
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
const events = new Array();
|
|
3652
|
-
const item = rawEvent.item;
|
|
3653
|
-
const itemType = item.type;
|
|
3654
|
-
const outputIndex = rawEvent.output_index;
|
|
3655
|
-
if (itemType === "tool_search_call") {
|
|
3656
|
-
const blockIndex = openFunctionCallBlock(state, {
|
|
3657
|
-
outputIndex,
|
|
3658
|
-
toolCallId: item.call_id,
|
|
3659
|
-
name: state.toolSearchName,
|
|
3660
|
-
events
|
|
3649
|
+
if (providerConfig.type === "openai-responses") return await handleOpenAIResponsesProviderMessages(c, {
|
|
3650
|
+
modelConfig,
|
|
3651
|
+
payload,
|
|
3652
|
+
provider,
|
|
3653
|
+
providerConfig
|
|
3661
3654
|
});
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
delta: {
|
|
3668
|
-
type: "input_json_delta",
|
|
3669
|
-
partial_json: finalArguments
|
|
3670
|
-
}
|
|
3671
|
-
});
|
|
3672
|
-
state.blockHasDelta.add(blockIndex);
|
|
3673
|
-
}
|
|
3674
|
-
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3675
|
-
return events;
|
|
3676
|
-
}
|
|
3677
|
-
if (itemType === "compaction") {
|
|
3678
|
-
if (!item.id || !item.encrypted_content) return events;
|
|
3679
|
-
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3680
|
-
if (!state.blockHasDelta.has(blockIndex)) events.push({
|
|
3681
|
-
type: "content_block_delta",
|
|
3682
|
-
index: blockIndex,
|
|
3683
|
-
delta: {
|
|
3684
|
-
type: "thinking_delta",
|
|
3685
|
-
thinking: THINKING_TEXT
|
|
3686
|
-
}
|
|
3655
|
+
if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
|
|
3656
|
+
modelConfig,
|
|
3657
|
+
payload,
|
|
3658
|
+
provider,
|
|
3659
|
+
providerConfig
|
|
3687
3660
|
});
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3661
|
+
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
3662
|
+
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
3663
|
+
if (!upstreamResponse.ok) {
|
|
3664
|
+
logger$4.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
|
|
3698
3674
|
});
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
const signature = (item.encrypted_content ?? "") + "@" + item.id;
|
|
3705
|
-
if (signature) {
|
|
3706
|
-
if (!item.summary || item.summary.length === 0) events.push({
|
|
3707
|
-
type: "content_block_delta",
|
|
3708
|
-
index: blockIndex,
|
|
3709
|
-
delta: {
|
|
3710
|
-
type: "thinking_delta",
|
|
3711
|
-
thinking: THINKING_TEXT
|
|
3712
|
-
}
|
|
3675
|
+
return respondProviderMessagesJson(c, {
|
|
3676
|
+
body: await upstreamResponse.json(),
|
|
3677
|
+
payload,
|
|
3678
|
+
provider,
|
|
3679
|
+
providerConfig
|
|
3713
3680
|
});
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
type: "signature_delta",
|
|
3719
|
-
signature
|
|
3720
|
-
}
|
|
3681
|
+
} catch (error) {
|
|
3682
|
+
logger$4.error("provider.messages.error", {
|
|
3683
|
+
provider,
|
|
3684
|
+
error
|
|
3721
3685
|
});
|
|
3722
|
-
|
|
3686
|
+
throw error;
|
|
3723
3687
|
}
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
const
|
|
3727
|
-
const
|
|
3728
|
-
const
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
});
|
|
3735
|
-
const functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
|
|
3736
|
-
if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state, events);
|
|
3737
|
-
const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
|
|
3738
|
-
if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state, events);
|
|
3739
|
-
functionCallState.consecutiveWhitespaceCount = nextCount;
|
|
3740
|
-
events.push({
|
|
3741
|
-
type: "content_block_delta",
|
|
3742
|
-
index: blockIndex,
|
|
3743
|
-
delta: {
|
|
3744
|
-
type: "input_json_delta",
|
|
3745
|
-
partial_json: deltaText
|
|
3746
|
-
}
|
|
3747
|
-
});
|
|
3748
|
-
state.blockHasDelta.add(blockIndex);
|
|
3749
|
-
return events;
|
|
3750
|
-
};
|
|
3751
|
-
const handleFunctionCallArgumentsDone = (rawEvent, state) => {
|
|
3752
|
-
const events = new Array();
|
|
3753
|
-
const outputIndex = rawEvent.output_index;
|
|
3754
|
-
const blockIndex = openFunctionCallBlock(state, {
|
|
3755
|
-
outputIndex,
|
|
3756
|
-
events
|
|
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$4, "provider.messages.responses.request", {
|
|
3696
|
+
payload: responsesPayload,
|
|
3697
|
+
provider
|
|
3757
3698
|
});
|
|
3758
|
-
const
|
|
3759
|
-
if (!
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
index: blockIndex,
|
|
3763
|
-
delta: {
|
|
3764
|
-
type: "input_json_delta",
|
|
3765
|
-
partial_json: finalArguments
|
|
3766
|
-
}
|
|
3767
|
-
});
|
|
3768
|
-
state.blockHasDelta.add(blockIndex);
|
|
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$4.error("Failed to create provider responses", upstreamResponse);
|
|
3702
|
+
throw new HTTPError("Failed to create provider responses", upstreamResponse);
|
|
3769
3703
|
}
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
const contentIndex = rawEvent.content_index;
|
|
3777
|
-
const deltaText = rawEvent.delta;
|
|
3778
|
-
if (!deltaText) return events;
|
|
3779
|
-
const blockIndex = openTextBlockIfNeeded(state, {
|
|
3780
|
-
outputIndex,
|
|
3781
|
-
contentIndex,
|
|
3782
|
-
events
|
|
3704
|
+
if (responsesPayload.stream) return streamResponsesProviderMessages({
|
|
3705
|
+
c,
|
|
3706
|
+
payload,
|
|
3707
|
+
provider,
|
|
3708
|
+
providerConfig,
|
|
3709
|
+
upstreamResponse
|
|
3783
3710
|
});
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
text: deltaText
|
|
3790
|
-
}
|
|
3711
|
+
return respondResponsesProviderMessagesJson(c, {
|
|
3712
|
+
body: await upstreamResponse.json(),
|
|
3713
|
+
payload,
|
|
3714
|
+
provider,
|
|
3715
|
+
providerConfig
|
|
3791
3716
|
});
|
|
3792
|
-
state.blockHasDelta.add(blockIndex);
|
|
3793
|
-
return events;
|
|
3794
3717
|
};
|
|
3795
|
-
const
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3800
|
-
events.push({
|
|
3801
|
-
type: "content_block_delta",
|
|
3802
|
-
index: blockIndex,
|
|
3803
|
-
delta: {
|
|
3804
|
-
type: "thinking_delta",
|
|
3805
|
-
thinking: deltaText
|
|
3806
|
-
}
|
|
3807
|
-
});
|
|
3808
|
-
state.blockHasDelta.add(blockIndex);
|
|
3809
|
-
return events;
|
|
3718
|
+
const applyModelDefaults = (payload, modelConfig) => {
|
|
3719
|
+
payload.temperature ??= modelConfig?.temperature;
|
|
3720
|
+
payload.top_p ??= modelConfig?.topP;
|
|
3721
|
+
payload.top_k ??= modelConfig?.topK;
|
|
3810
3722
|
};
|
|
3811
|
-
const
|
|
3812
|
-
const
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
const
|
|
3816
|
-
if (
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
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;
|
|
3736
|
+
}
|
|
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;
|
|
3744
|
+
};
|
|
3745
|
+
const handleOpenAICompatibleProviderMessages = async (c, options) => {
|
|
3746
|
+
const { modelConfig, payload, provider, providerConfig } = options;
|
|
3747
|
+
const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
|
|
3748
|
+
debugJson(logger$4, "provider.messages.openai_compatible.request", {
|
|
3749
|
+
payload: openAIPayload,
|
|
3750
|
+
provider
|
|
3751
|
+
});
|
|
3752
|
+
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
|
|
3753
|
+
if (!upstreamResponse.ok) {
|
|
3754
|
+
logger$4.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
|
|
3763
|
+
});
|
|
3764
|
+
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
3765
|
+
body: await upstreamResponse.json(),
|
|
3766
|
+
payload,
|
|
3767
|
+
provider
|
|
3823
3768
|
});
|
|
3824
|
-
return events;
|
|
3825
3769
|
};
|
|
3826
|
-
const
|
|
3827
|
-
const
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
const text = rawEvent.text;
|
|
3831
|
-
const blockIndex = openTextBlockIfNeeded(state, {
|
|
3832
|
-
outputIndex,
|
|
3833
|
-
contentIndex,
|
|
3834
|
-
events
|
|
3770
|
+
const createOpenAICompatiblePayload = (payload, modelConfig) => {
|
|
3771
|
+
const openAIPayload = translateToOpenAI(payload, {
|
|
3772
|
+
supportPdf: modelConfig?.supportPdf,
|
|
3773
|
+
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
3835
3774
|
});
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
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
|
|
3843
3782
|
});
|
|
3844
|
-
|
|
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;
|
|
3845
3788
|
};
|
|
3846
|
-
const
|
|
3847
|
-
const
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
delta: {
|
|
3854
|
-
stop_reason: anthropic.stop_reason,
|
|
3855
|
-
stop_sequence: anthropic.stop_sequence
|
|
3856
|
-
},
|
|
3857
|
-
usage: anthropic.usage
|
|
3858
|
-
}, { type: "message_stop" });
|
|
3859
|
-
state.messageCompleted = true;
|
|
3860
|
-
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
|
+
}
|
|
3861
3796
|
};
|
|
3862
|
-
const
|
|
3863
|
-
const
|
|
3864
|
-
const
|
|
3865
|
-
closeAllOpenBlocks(state, events);
|
|
3866
|
-
const message = response.error?.message ?? "The response failed due to an unknown error.";
|
|
3867
|
-
events.push(buildErrorEvent(message));
|
|
3868
|
-
state.messageCompleted = true;
|
|
3869
|
-
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];
|
|
3870
3800
|
};
|
|
3871
|
-
const
|
|
3872
|
-
const
|
|
3873
|
-
|
|
3874
|
-
return [buildErrorEvent(message)];
|
|
3801
|
+
const applyOpenAICompatibleContextCache = (payload) => {
|
|
3802
|
+
const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
|
|
3803
|
+
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
3875
3804
|
};
|
|
3876
|
-
const
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
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);
|
|
3882
3810
|
};
|
|
3883
|
-
const
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
return
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
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$4.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$4.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$4.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$4.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$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
3898
|
+
await stream.writeSSE({
|
|
3899
|
+
event: event.type,
|
|
3900
|
+
data: eventData
|
|
3901
|
+
});
|
|
3901
3902
|
}
|
|
3902
3903
|
}
|
|
3903
|
-
|
|
3904
|
+
for (const event of flushPendingAnthropicStreamEvents(streamState)) {
|
|
3905
|
+
const eventData = JSON.stringify(event);
|
|
3906
|
+
debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
3907
|
+
await stream.writeSSE({
|
|
3908
|
+
event: event.type,
|
|
3909
|
+
data: eventData
|
|
3910
|
+
});
|
|
3911
|
+
}
|
|
3912
|
+
recordUsage(usage);
|
|
3913
|
+
});
|
|
3904
3914
|
};
|
|
3905
|
-
const
|
|
3906
|
-
|
|
3907
|
-
const
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3915
|
+
const streamResponsesProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
|
|
3916
|
+
logger$4.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$4.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$4, () => ["provider.messages.responses.translated_event:", eventData]);
|
|
3941
|
+
await stream.writeSSE({
|
|
3942
|
+
event: event.type,
|
|
3943
|
+
data: eventData
|
|
3944
|
+
});
|
|
3922
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$4.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
3962
|
+
data,
|
|
3963
|
+
error
|
|
3923
3964
|
});
|
|
3924
|
-
|
|
3965
|
+
return null;
|
|
3925
3966
|
}
|
|
3926
|
-
return blockIndex;
|
|
3927
3967
|
};
|
|
3928
|
-
const
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
events.push({
|
|
3939
|
-
type: "content_block_start",
|
|
3940
|
-
index: blockIndex,
|
|
3941
|
-
content_block: {
|
|
3942
|
-
type: "thinking",
|
|
3943
|
-
thinking: ""
|
|
3944
|
-
}
|
|
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$4.error("provider.messages.responses.parse_chunk_error", {
|
|
3975
|
+
provider: providerConfig.name,
|
|
3976
|
+
data,
|
|
3977
|
+
error
|
|
3945
3978
|
});
|
|
3946
|
-
|
|
3979
|
+
return null;
|
|
3947
3980
|
}
|
|
3948
|
-
return blockIndex;
|
|
3949
|
-
};
|
|
3950
|
-
const closeBlockIfOpen = (state, blockIndex, events) => {
|
|
3951
|
-
if (!state.openBlocks.has(blockIndex)) return;
|
|
3952
|
-
events.push({
|
|
3953
|
-
type: "content_block_stop",
|
|
3954
|
-
index: blockIndex
|
|
3955
|
-
});
|
|
3956
|
-
state.openBlocks.delete(blockIndex);
|
|
3957
|
-
state.blockHasDelta.delete(blockIndex);
|
|
3958
|
-
};
|
|
3959
|
-
const closeOpenBlocks = (state, events) => {
|
|
3960
|
-
for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
|
|
3961
|
-
};
|
|
3962
|
-
const closeAllOpenBlocks = (state, events) => {
|
|
3963
|
-
closeOpenBlocks(state, events);
|
|
3964
|
-
state.functionCallStateByOutputIndex.clear();
|
|
3965
3981
|
};
|
|
3966
|
-
const
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
type
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
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: {}
|
|
3985
4003
|
};
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
closeOpenBlocks(state, events);
|
|
3991
|
-
events.push({
|
|
3992
|
-
type: "content_block_start",
|
|
3993
|
-
index: blockIndex,
|
|
3994
|
-
content_block: {
|
|
3995
|
-
type: "tool_use",
|
|
3996
|
-
id: functionCallState.toolCallId,
|
|
3997
|
-
name: functionCallState.name,
|
|
3998
|
-
input: {}
|
|
3999
|
-
}
|
|
4004
|
+
} catch (error) {
|
|
4005
|
+
logger$4.error("provider.messages.streaming.adjust_tokens_error", {
|
|
4006
|
+
error,
|
|
4007
|
+
originalData: data
|
|
4000
4008
|
});
|
|
4001
|
-
|
|
4009
|
+
return null;
|
|
4002
4010
|
}
|
|
4003
|
-
return blockIndex;
|
|
4004
4011
|
};
|
|
4005
|
-
const
|
|
4006
|
-
const
|
|
4007
|
-
const
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
initialArguments: ""
|
|
4013
|
-
};
|
|
4014
|
-
if (itemType !== "function_call") return;
|
|
4015
|
-
return {
|
|
4016
|
-
outputIndex: rawEvent.output_index,
|
|
4017
|
-
toolCallId: item.call_id,
|
|
4018
|
-
name: resolveToolUseName(item),
|
|
4019
|
-
initialArguments: item.arguments
|
|
4020
|
-
};
|
|
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$4, "provider.messages.no_stream result:", body);
|
|
4018
|
+
return c.json(body);
|
|
4021
4019
|
};
|
|
4022
|
-
const
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
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$4, "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$4, "provider.messages.responses.no_stream result:", anthropicResponse);
|
|
4032
|
+
if (providerConfig.name === "codex") logger$4.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$4, "provider.messages.adjusted_usage:", usage);
|
|
4028
4045
|
};
|
|
4029
4046
|
//#endregion
|
|
4030
4047
|
//#region src/services/copilot/create-messages.ts
|
|
@@ -4754,11 +4771,19 @@ const providerModelRoutes = new Hono();
|
|
|
4754
4771
|
providerModelRoutes.get("/", async (c) => {
|
|
4755
4772
|
const provider = c.req.param("provider") ?? "";
|
|
4756
4773
|
try {
|
|
4757
|
-
const providerConfig =
|
|
4774
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
4758
4775
|
if (!providerConfig) return c.json({ error: {
|
|
4759
4776
|
message: `Provider '${provider}' not found or disabled`,
|
|
4760
4777
|
type: "invalid_request_error"
|
|
4761
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
|
+
}
|
|
4762
4787
|
const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers);
|
|
4763
4788
|
logger$2.debug("provider.models.response", {
|
|
4764
4789
|
provider,
|
|
@@ -4818,9 +4843,17 @@ const responsesHandlerDependencies = {
|
|
|
4818
4843
|
isResponsesApiWebSearchEnabled
|
|
4819
4844
|
};
|
|
4820
4845
|
const handleResponses = async (c) => {
|
|
4821
|
-
await responsesHandlerDependencies.checkRateLimit(state);
|
|
4822
4846
|
const payload = await c.req.json();
|
|
4823
4847
|
debugJson(logger$1, "Responses request payload:", payload);
|
|
4848
|
+
const providerModelAlias = parseProviderModelAlias(payload.model);
|
|
4849
|
+
if (providerModelAlias) {
|
|
4850
|
+
payload.model = providerModelAlias.model;
|
|
4851
|
+
return await handleProviderResponsesForProvider(c, {
|
|
4852
|
+
payload,
|
|
4853
|
+
provider: providerModelAlias.provider
|
|
4854
|
+
});
|
|
4855
|
+
}
|
|
4856
|
+
await responsesHandlerDependencies.checkRateLimit(state);
|
|
4824
4857
|
const requestId = generateRequestIdFromPayload({ messages: payload.input });
|
|
4825
4858
|
logger$1.debug("Generated request ID:", requestId);
|
|
4826
4859
|
const sessionId = getUUID(requestId);
|
|
@@ -4876,6 +4909,30 @@ const handleResponses = async (c) => {
|
|
|
4876
4909
|
recordUsage(normalizeResponsesUsage(response.usage));
|
|
4877
4910
|
return c.json(response);
|
|
4878
4911
|
};
|
|
4912
|
+
const handleProviderResponsesForProvider = async (c, options) => {
|
|
4913
|
+
const { payload, provider } = options;
|
|
4914
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
4915
|
+
if (providerConfig?.type !== "openai-responses") return c.json({ error: {
|
|
4916
|
+
message: `Provider '${provider}' does not support the /v1/responses endpoint`,
|
|
4917
|
+
type: "invalid_request_error"
|
|
4918
|
+
} }, 400);
|
|
4919
|
+
const upstreamResponse = providerConfig.name === "codex" ? await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl) : await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
|
|
4920
|
+
if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${provider} responses`, upstreamResponse);
|
|
4921
|
+
const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
|
|
4922
|
+
if (payload.stream) recordProviderResponsesStreamUsage(upstreamResponse.clone(), {
|
|
4923
|
+
normalizeCodex: providerConfig.name === "codex",
|
|
4924
|
+
provider,
|
|
4925
|
+
recordUsage
|
|
4926
|
+
}).catch((error) => {
|
|
4927
|
+
logger$1.warn("provider.responses.usage_stream_error", {
|
|
4928
|
+
provider,
|
|
4929
|
+
error: getErrorMessage(error)
|
|
4930
|
+
});
|
|
4931
|
+
});
|
|
4932
|
+
else recordUsage(normalizeResponsesUsage((await upstreamResponse.clone().json()).usage));
|
|
4933
|
+
if (providerConfig.name === "codex" && payload.stream) return createProviderProxyResponse(upstreamResponse, createStandardizedCodexResponsesEventStream(getResponsesEvents(upstreamResponse)));
|
|
4934
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
4935
|
+
};
|
|
4879
4936
|
const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
4880
4937
|
const isStreamingRequested = (payload) => Boolean(payload.stream);
|
|
4881
4938
|
const parseResponsesStreamEvent = (chunk) => {
|
|
@@ -4887,6 +4944,51 @@ const parseResponsesStreamEvent = (chunk) => {
|
|
|
4887
4944
|
return null;
|
|
4888
4945
|
}
|
|
4889
4946
|
};
|
|
4947
|
+
const getErrorMessage = (error) => {
|
|
4948
|
+
if (error instanceof Error && error.message) return error.message;
|
|
4949
|
+
return String(error);
|
|
4950
|
+
};
|
|
4951
|
+
const createProviderResponsesUsageRecorder = (payload, provider) => {
|
|
4952
|
+
const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
|
|
4953
|
+
return createProviderTokenUsageRecorder({
|
|
4954
|
+
endpoint: "responses",
|
|
4955
|
+
model: payload.model,
|
|
4956
|
+
providerName: provider,
|
|
4957
|
+
sessionId: sessionAffinity ?? ""
|
|
4958
|
+
});
|
|
4959
|
+
};
|
|
4960
|
+
const recordProviderResponsesStreamUsage = async (upstreamResponse, options) => {
|
|
4961
|
+
let usage = {};
|
|
4962
|
+
try {
|
|
4963
|
+
for await (const chunk of getResponsesEvents(upstreamResponse)) {
|
|
4964
|
+
debugJson(logger$1, "Responses stream chunk:", chunk);
|
|
4965
|
+
if (!chunk.data || chunk.data === "[DONE]") continue;
|
|
4966
|
+
const parsed = parseProviderResponsesStreamEvent(chunk.data, {
|
|
4967
|
+
normalizeCodex: options.normalizeCodex,
|
|
4968
|
+
provider: options.provider
|
|
4969
|
+
});
|
|
4970
|
+
if (parsed?.type === "response.completed" || parsed?.type === "response.failed" || parsed?.type === "response.incomplete") usage = normalizeResponsesUsage(parsed.response.usage);
|
|
4971
|
+
}
|
|
4972
|
+
} finally {
|
|
4973
|
+
options.recordUsage(usage);
|
|
4974
|
+
}
|
|
4975
|
+
};
|
|
4976
|
+
const parseProviderResponsesStreamEvent = (data, options) => {
|
|
4977
|
+
try {
|
|
4978
|
+
const parsed = JSON.parse(data);
|
|
4979
|
+
return options.normalizeCodex ? normalizeCodexResponsesEvent(parsed) : parsed;
|
|
4980
|
+
} catch (error) {
|
|
4981
|
+
logger$1.error("provider.responses.parse_chunk_error", {
|
|
4982
|
+
provider: options.provider,
|
|
4983
|
+
data,
|
|
4984
|
+
error
|
|
4985
|
+
});
|
|
4986
|
+
return null;
|
|
4987
|
+
}
|
|
4988
|
+
};
|
|
4989
|
+
const getResponsesEvents = (response) => {
|
|
4990
|
+
return events(response);
|
|
4991
|
+
};
|
|
4890
4992
|
const removeWebSearchTool = (payload) => {
|
|
4891
4993
|
if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
|
|
4892
4994
|
payload.tools = payload.tools.filter((t) => {
|
|
@@ -5019,4 +5121,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
5019
5121
|
//#endregion
|
|
5020
5122
|
export { server };
|
|
5021
5123
|
|
|
5022
|
-
//# sourceMappingURL=server-
|
|
5124
|
+
//# sourceMappingURL=server-D5O9IzAY.js.map
|