@jeffreycao/copilot-api 1.10.9 → 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 +16 -13
- package/README.zh-CN.md +16 -13
- 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-FPXzFkg9.js → server-D5O9IzAY.js} +1773 -1692
- package/dist/server-D5O9IzAY.js.map +1 -0
- package/dist/{start-CbKg_0bY.js → start-BhPxHgu8.js} +4 -6
- package/dist/{start-CbKg_0bY.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-FPXzFkg9.js.map +0 -1
- package/dist/token-Dj8XsAxn.js +0 -170
- package/dist/token-Dj8XsAxn.js.map +0 -1
- package/dist/utils-jHLgqAq2.js +0 -657
- package/dist/utils-jHLgqAq2.js.map +0 -1
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-
|
|
4
|
-
import { _ as setModelMappings, a as getConfig, c as getProviderConfig, d as isMessagesApiEnabled, f as isResponsesApiContextManagementModel, g as resolveMappedModel, i as getClaudeTokenMultiplier, l as getReasoningEffortForModel, m as isResponsesApiWebSocketEnabled, o as getExtraPromptForModel, p as isResponsesApiWebSearchEnabled, r as getAnthropicApiKey, s as getModelMappings, t as getProxyEnvDispatcher, u as getSmallModel } from "./proxy-DvlF9a-7.js";
|
|
1
|
+
import { A as compactSystemPromptStarts, B as createWebSocketUrl, C as prepareForCompact, D as compactAutoContinuePromptStarts, F as normalizeCodexResponsesEvent, H as state, I as generateTraceId, L as requestContext, N as createStandardizedCodexResponsesEventStream, O as compactMessageSections, P as forwardCodexResponses, R as resolveTraceId$1, S as copilotWebSocketHeaders, T as prepareMessageProxyHeaders, U as logCodexRateLimitsEvent, _ as getCopilotUsage, b as copilotBaseUrl, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as HTTPError, w as prepareInteractionHeaders, x as copilotHeaders, y as forwardError, z as createPooledWebSocketStream } from "./token-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,1847 +2228,1820 @@ function closeThinkingBlockIfOpen(state, events) {
|
|
|
2205
2228
|
}
|
|
2206
2229
|
}
|
|
2207
2230
|
//#endregion
|
|
2208
|
-
//#region src/services/
|
|
2209
|
-
const
|
|
2210
|
-
|
|
2211
|
-
const STRIPPED_RESPONSE_HEADERS = [
|
|
2212
|
-
"connection",
|
|
2213
|
-
"content-encoding",
|
|
2214
|
-
"content-length",
|
|
2215
|
-
"keep-alive",
|
|
2216
|
-
"proxy-authenticate",
|
|
2217
|
-
"proxy-authorization",
|
|
2218
|
-
"te",
|
|
2219
|
-
"trailer",
|
|
2220
|
-
"transfer-encoding",
|
|
2221
|
-
"upgrade"
|
|
2222
|
-
];
|
|
2223
|
-
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
2224
|
-
const authHeaders = {};
|
|
2225
|
-
if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
2226
|
-
else authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
2231
|
+
//#region src/services/copilot/create-responses.ts
|
|
2232
|
+
const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
|
|
2233
|
+
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
2227
2234
|
const headers = {
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
...authHeaders
|
|
2235
|
+
...copilotHeaders(state, requestId, vision),
|
|
2236
|
+
"x-initiator": initiator
|
|
2231
2237
|
};
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
}
|
|
2236
|
-
if (
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2238
|
+
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
2239
|
+
prepareForCompact(headers, compactType);
|
|
2240
|
+
payload.service_tier = void 0;
|
|
2241
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
2242
|
+
if ((compactType === 1 ? "http" : transport) === "websocket") {
|
|
2243
|
+
const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
|
|
2244
|
+
requestId,
|
|
2245
|
+
subagentMarker
|
|
2246
|
+
}));
|
|
2247
|
+
if (payload.stream) return stream;
|
|
2248
|
+
return await consumeResponsesWebSocketStream(stream);
|
|
2240
2249
|
}
|
|
2241
|
-
return headers;
|
|
2242
|
-
}
|
|
2243
|
-
|
|
2244
|
-
const
|
|
2245
|
-
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
2246
|
-
return new Response(upstreamResponse.body, {
|
|
2247
|
-
headers,
|
|
2248
|
-
status: upstreamResponse.status,
|
|
2249
|
-
statusText: upstreamResponse.statusText
|
|
2250
|
-
});
|
|
2251
|
-
}
|
|
2252
|
-
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
2253
|
-
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
2254
|
-
method: "POST",
|
|
2255
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
2256
|
-
body: JSON.stringify(payload)
|
|
2257
|
-
});
|
|
2258
|
-
}
|
|
2259
|
-
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
2260
|
-
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
2250
|
+
return await createHttpResponses(payload, headers);
|
|
2251
|
+
};
|
|
2252
|
+
const createHttpResponses = async (payload, headers) => {
|
|
2253
|
+
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
|
2261
2254
|
method: "POST",
|
|
2262
|
-
headers
|
|
2255
|
+
headers,
|
|
2263
2256
|
body: JSON.stringify(payload)
|
|
2264
2257
|
});
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
2270
|
-
});
|
|
2271
|
-
}
|
|
2272
|
-
//#endregion
|
|
2273
|
-
//#region src/routes/provider/messages/handler.ts
|
|
2274
|
-
const logger$4 = createHandlerLogger("provider-messages-handler");
|
|
2275
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
|
|
2276
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
|
|
2277
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
|
|
2278
|
-
"system",
|
|
2279
|
-
"user",
|
|
2280
|
-
"assistant",
|
|
2281
|
-
"tool"
|
|
2282
|
-
]);
|
|
2283
|
-
async function handleProviderMessages(c) {
|
|
2284
|
-
const provider = c.req.param("provider");
|
|
2285
|
-
return await handleProviderMessagesForProvider(c, {
|
|
2286
|
-
payload: await c.req.json(),
|
|
2287
|
-
provider
|
|
2288
|
-
});
|
|
2289
|
-
}
|
|
2290
|
-
async function handleProviderMessagesForProvider(c, options) {
|
|
2291
|
-
const { payload, provider } = options;
|
|
2292
|
-
const providerConfig = getProviderConfig(provider);
|
|
2293
|
-
if (!providerConfig) return c.json({ error: {
|
|
2294
|
-
message: `Provider '${provider}' not found or disabled`,
|
|
2295
|
-
type: "invalid_request_error"
|
|
2296
|
-
} }, 404);
|
|
2297
|
-
try {
|
|
2298
|
-
const modelConfig = providerConfig.models?.[payload.model];
|
|
2299
|
-
applyModelDefaults(payload, modelConfig);
|
|
2300
|
-
debugJson(logger$4, "provider.messages.request", {
|
|
2301
|
-
payload,
|
|
2302
|
-
provider
|
|
2303
|
-
});
|
|
2304
|
-
if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
|
|
2305
|
-
modelConfig,
|
|
2306
|
-
payload,
|
|
2307
|
-
provider,
|
|
2308
|
-
providerConfig
|
|
2309
|
-
});
|
|
2310
|
-
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
2311
|
-
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
2312
|
-
if (!upstreamResponse.ok) {
|
|
2313
|
-
logger$4.error("Failed to create responses", upstreamResponse);
|
|
2314
|
-
throw new HTTPError("Failed to create responses", upstreamResponse);
|
|
2315
|
-
}
|
|
2316
|
-
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
2317
|
-
if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
|
|
2318
|
-
c,
|
|
2319
|
-
payload,
|
|
2320
|
-
provider,
|
|
2321
|
-
providerConfig,
|
|
2322
|
-
upstreamResponse
|
|
2323
|
-
});
|
|
2324
|
-
return respondProviderMessagesJson(c, {
|
|
2325
|
-
body: await upstreamResponse.json(),
|
|
2326
|
-
payload,
|
|
2327
|
-
provider,
|
|
2328
|
-
providerConfig
|
|
2329
|
-
});
|
|
2330
|
-
} catch (error) {
|
|
2331
|
-
logger$4.error("provider.messages.error", {
|
|
2332
|
-
provider,
|
|
2333
|
-
error
|
|
2334
|
-
});
|
|
2335
|
-
throw error;
|
|
2258
|
+
logCopilotRateLimits(response.headers);
|
|
2259
|
+
if (!response.ok) {
|
|
2260
|
+
consola.error("Failed to create responses", response);
|
|
2261
|
+
throw new HTTPError("Failed to create responses", response);
|
|
2336
2262
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
payload.temperature ??= modelConfig?.temperature;
|
|
2340
|
-
payload.top_p ??= modelConfig?.topP;
|
|
2341
|
-
payload.top_k ??= modelConfig?.topK;
|
|
2263
|
+
if (payload.stream) return events(response);
|
|
2264
|
+
return await response.json();
|
|
2342
2265
|
};
|
|
2343
|
-
const
|
|
2344
|
-
|
|
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
|
+
};
|
|
2345
2274
|
};
|
|
2346
|
-
const
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
|
|
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("|");
|
|
2350
2288
|
};
|
|
2351
|
-
const
|
|
2352
|
-
|
|
2353
|
-
if (thinkingBudget !== void 0) {
|
|
2354
|
-
payload.thinking_budget = thinkingBudget;
|
|
2355
|
-
return;
|
|
2356
|
-
}
|
|
2357
|
-
if (payload.thinking_budget === void 0) delete payload.thinking_budget;
|
|
2289
|
+
const getResponsesWebSocketInitiator = (preparedHeaders) => {
|
|
2290
|
+
return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
|
|
2358
2291
|
};
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
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;
|
|
2364
2309
|
};
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
|
|
2368
|
-
debugJson(logger$4, "provider.messages.openai_compatible.request", {
|
|
2369
|
-
payload: openAIPayload,
|
|
2370
|
-
provider
|
|
2371
|
-
});
|
|
2372
|
-
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
|
|
2373
|
-
if (!upstreamResponse.ok) {
|
|
2374
|
-
logger$4.error("Failed to create openai-compatible responses", upstreamResponse);
|
|
2375
|
-
throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
|
|
2376
|
-
}
|
|
2377
|
-
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
2378
|
-
if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
|
|
2379
|
-
c,
|
|
2380
|
-
payload,
|
|
2381
|
-
provider,
|
|
2382
|
-
upstreamResponse
|
|
2383
|
-
});
|
|
2384
|
-
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
2385
|
-
body: await upstreamResponse.json(),
|
|
2386
|
-
payload,
|
|
2387
|
-
provider
|
|
2388
|
-
});
|
|
2310
|
+
const buildResponsesWebSocketUrl = (baseUrl) => {
|
|
2311
|
+
return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
|
|
2389
2312
|
};
|
|
2390
|
-
const
|
|
2391
|
-
const
|
|
2392
|
-
|
|
2393
|
-
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
2394
|
-
});
|
|
2395
|
-
applyOpenAICompatibleThinkingBudget(openAIPayload, payload);
|
|
2396
|
-
if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
|
|
2397
|
-
if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
|
|
2398
|
-
normalizeOpenAICompatibleReasoningContent(openAIPayload);
|
|
2399
|
-
applyOpenAICompatibleRequestOverrides(openAIPayload, {
|
|
2400
|
-
extraBody: modelConfig?.extraBody,
|
|
2401
|
-
source: payload
|
|
2402
|
-
});
|
|
2403
|
-
applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
2404
|
-
applyOpenAICompatibleExtraBodyThinkingBudget(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
2405
|
-
if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
|
|
2406
|
-
if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
|
|
2407
|
-
return openAIPayload;
|
|
2313
|
+
const getHeaderValue = (headers, headerName) => {
|
|
2314
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
2315
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
2408
2316
|
};
|
|
2409
|
-
const
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
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 };
|
|
2415
2330
|
}
|
|
2416
2331
|
};
|
|
2417
|
-
const
|
|
2418
|
-
|
|
2419
|
-
|
|
2332
|
+
const isTerminalResponsesStreamChunk = (chunk) => {
|
|
2333
|
+
if (!chunk.data || chunk.data === "[DONE]") return false;
|
|
2334
|
+
try {
|
|
2335
|
+
const parsed = JSON.parse(chunk.data);
|
|
2336
|
+
return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
|
|
2337
|
+
} catch {
|
|
2338
|
+
return false;
|
|
2339
|
+
}
|
|
2420
2340
|
};
|
|
2421
|
-
const
|
|
2422
|
-
const
|
|
2423
|
-
|
|
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");
|
|
2424
2349
|
};
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
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}`;
|
|
2430
2361
|
};
|
|
2431
|
-
const
|
|
2432
|
-
const
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2362
|
+
const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId) => {
|
|
2363
|
+
const input = [];
|
|
2364
|
+
const applyPhase = shouldApplyPhase(payload.model);
|
|
2365
|
+
const toolSearchEnabled = shouldEnableResponsesToolSearch({
|
|
2366
|
+
model: payload.model,
|
|
2367
|
+
tools: payload.tools
|
|
2368
|
+
});
|
|
2369
|
+
const translationState = {
|
|
2370
|
+
originalTools: payload.tools ?? [],
|
|
2371
|
+
toolSearchEnabled,
|
|
2372
|
+
toolUseNameById: /* @__PURE__ */ new Map()
|
|
2373
|
+
};
|
|
2374
|
+
for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase, translationState));
|
|
2375
|
+
const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
|
|
2376
|
+
const translatedTools = convertAnthropicTools(payload.tools, toolSearchEnabled);
|
|
2377
|
+
const toolChoice = convertAnthropicToolChoice(payload.tool_choice, toolSearchEnabled);
|
|
2378
|
+
const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
|
|
2379
|
+
const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
|
|
2380
|
+
const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
|
|
2381
|
+
const responsesPayload = {
|
|
2382
|
+
model: payload.model,
|
|
2383
|
+
input,
|
|
2384
|
+
instructions: translateSystemPrompt(payload.system, payload.model),
|
|
2385
|
+
temperature: 1,
|
|
2386
|
+
top_p: payload.top_p ?? null,
|
|
2387
|
+
max_output_tokens: Math.max(payload.max_tokens, 12800),
|
|
2388
|
+
tools: translatedTools,
|
|
2389
|
+
tool_choice: toolChoice,
|
|
2390
|
+
metadata: payload.metadata ? { ...payload.metadata } : null,
|
|
2391
|
+
stream: payload.stream ?? null,
|
|
2392
|
+
store: false,
|
|
2393
|
+
parallel_tool_calls: true,
|
|
2394
|
+
reasoning: {
|
|
2395
|
+
effort: getReasoningEffortForModel(payload.model),
|
|
2396
|
+
summary: "detailed"
|
|
2397
|
+
},
|
|
2398
|
+
include: ["reasoning.encrypted_content"]
|
|
2399
|
+
};
|
|
2400
|
+
if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
|
|
2401
|
+
return responsesPayload;
|
|
2436
2402
|
};
|
|
2437
|
-
const
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2403
|
+
const encodeCompactionCarrierSignature = (compaction) => {
|
|
2404
|
+
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
2405
|
+
};
|
|
2406
|
+
const decodeCompactionCarrierSignature = (signature) => {
|
|
2407
|
+
if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
|
|
2408
|
+
const raw = signature.slice(4);
|
|
2409
|
+
const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
|
|
2410
|
+
if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
|
|
2411
|
+
const encrypted_content = raw.slice(0, separatorIndex);
|
|
2412
|
+
const id = raw.slice(separatorIndex + 1);
|
|
2413
|
+
if (!encrypted_content) return;
|
|
2414
|
+
return {
|
|
2415
|
+
id,
|
|
2416
|
+
encrypted_content
|
|
2417
|
+
};
|
|
2446
2418
|
}
|
|
2447
|
-
if (!Array.isArray(message.content)) return;
|
|
2448
|
-
const lastPart = message.content.at(-1);
|
|
2449
|
-
if (!lastPart) return;
|
|
2450
|
-
setContextCacheControl(lastPart);
|
|
2451
2419
|
};
|
|
2452
|
-
const
|
|
2453
|
-
|
|
2420
|
+
const translateMessage = (message, model, applyPhase, state) => {
|
|
2421
|
+
if (message.role === "user") return translateUserMessage(message, state);
|
|
2422
|
+
return translateAssistantMessage(message, model, applyPhase, state);
|
|
2454
2423
|
};
|
|
2455
|
-
const
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
event: "ping",
|
|
2466
|
-
data: "{\"type\":\"ping\"}"
|
|
2467
|
-
});
|
|
2468
|
-
continue;
|
|
2469
|
-
}
|
|
2470
|
-
let data = chunk.data;
|
|
2471
|
-
if (!data) continue;
|
|
2472
|
-
if (chunk.data === "[DONE]") break;
|
|
2473
|
-
const parsed = parseProviderStreamEvent(data, providerConfig);
|
|
2474
|
-
if (parsed) {
|
|
2475
|
-
usage = mergeAnthropicUsage(usage, parsed.usage);
|
|
2476
|
-
data = parsed.data;
|
|
2477
|
-
}
|
|
2478
|
-
await stream.writeSSE({
|
|
2479
|
-
event: eventName,
|
|
2480
|
-
data
|
|
2481
|
-
});
|
|
2424
|
+
const translateUserMessage = (message, state) => {
|
|
2425
|
+
if (typeof message.content === "string") return [createMessage("user", message.content)];
|
|
2426
|
+
if (!Array.isArray(message.content)) return [];
|
|
2427
|
+
const items = [];
|
|
2428
|
+
const pendingContent = [];
|
|
2429
|
+
for (const block of message.content) {
|
|
2430
|
+
if (block.type === "tool_result") {
|
|
2431
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2432
|
+
items.push(createToolCallOutput(block, state));
|
|
2433
|
+
continue;
|
|
2482
2434
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2435
|
+
const converted = translateUserContentBlock(block);
|
|
2436
|
+
if (converted.length > 0) pendingContent.push(...converted);
|
|
2437
|
+
}
|
|
2438
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2439
|
+
return items;
|
|
2485
2440
|
};
|
|
2486
|
-
const
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2441
|
+
const translateAssistantMessage = (message, model, applyPhase, state) => {
|
|
2442
|
+
const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
|
|
2443
|
+
if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
|
|
2444
|
+
if (!Array.isArray(message.content)) return [];
|
|
2445
|
+
const items = [];
|
|
2446
|
+
const pendingContent = [];
|
|
2447
|
+
for (const block of message.content) {
|
|
2448
|
+
if (block.type === "tool_use") {
|
|
2449
|
+
state.toolUseNameById.set(block.id, block.name);
|
|
2450
|
+
flushPendingContent(pendingContent, items, {
|
|
2451
|
+
role: "assistant",
|
|
2452
|
+
phase: assistantPhase
|
|
2453
|
+
});
|
|
2454
|
+
items.push(createToolCall(block, state));
|
|
2455
|
+
continue;
|
|
2456
|
+
}
|
|
2457
|
+
if (block.type === "thinking" && block.signature) {
|
|
2458
|
+
const compactionContent = createCompactionContent(block);
|
|
2459
|
+
if (compactionContent) {
|
|
2460
|
+
flushPendingContent(pendingContent, items, {
|
|
2461
|
+
role: "assistant",
|
|
2462
|
+
phase: assistantPhase
|
|
2504
2463
|
});
|
|
2464
|
+
items.push(compactionContent);
|
|
2505
2465
|
continue;
|
|
2506
2466
|
}
|
|
2507
|
-
if (
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
|
|
2512
|
-
if (!parsed) continue;
|
|
2513
|
-
if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
|
|
2514
|
-
const events = translateChunkToAnthropicEvents(parsed, streamState);
|
|
2515
|
-
for (const event of events) {
|
|
2516
|
-
const eventData = JSON.stringify(event);
|
|
2517
|
-
debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
2518
|
-
await stream.writeSSE({
|
|
2519
|
-
event: event.type,
|
|
2520
|
-
data: eventData
|
|
2467
|
+
if (block.signature.includes("@")) {
|
|
2468
|
+
flushPendingContent(pendingContent, items, {
|
|
2469
|
+
role: "assistant",
|
|
2470
|
+
phase: assistantPhase
|
|
2521
2471
|
});
|
|
2472
|
+
items.push(createReasoningContent(block));
|
|
2473
|
+
continue;
|
|
2522
2474
|
}
|
|
2523
2475
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
});
|
|
2531
|
-
}
|
|
2532
|
-
recordUsage(usage);
|
|
2476
|
+
const converted = translateAssistantContentBlock(block);
|
|
2477
|
+
if (converted) pendingContent.push(converted);
|
|
2478
|
+
}
|
|
2479
|
+
flushPendingContent(pendingContent, items, {
|
|
2480
|
+
role: "assistant",
|
|
2481
|
+
phase: assistantPhase
|
|
2533
2482
|
});
|
|
2483
|
+
return items;
|
|
2534
2484
|
};
|
|
2535
|
-
const
|
|
2536
|
-
|
|
2537
|
-
return
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
error
|
|
2542
|
-
});
|
|
2543
|
-
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 [];
|
|
2544
2491
|
}
|
|
2545
2492
|
};
|
|
2546
|
-
const
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
adjustInputTokens(providerConfig, parsed.message.usage);
|
|
2551
|
-
return {
|
|
2552
|
-
data: JSON.stringify(parsed),
|
|
2553
|
-
model: parsed.message.model,
|
|
2554
|
-
usage: normalizeAnthropicUsage(parsed.message.usage)
|
|
2555
|
-
};
|
|
2556
|
-
}
|
|
2557
|
-
if (parsed.type === "message_delta") {
|
|
2558
|
-
adjustInputTokens(providerConfig, parsed.usage);
|
|
2559
|
-
return {
|
|
2560
|
-
data: JSON.stringify(parsed),
|
|
2561
|
-
usage: normalizeAnthropicUsage(parsed.usage)
|
|
2562
|
-
};
|
|
2563
|
-
}
|
|
2564
|
-
return {
|
|
2565
|
-
data: JSON.stringify(parsed),
|
|
2566
|
-
usage: {}
|
|
2567
|
-
};
|
|
2568
|
-
} catch (error) {
|
|
2569
|
-
logger$4.error("provider.messages.streaming.adjust_tokens_error", {
|
|
2570
|
-
error,
|
|
2571
|
-
originalData: data
|
|
2572
|
-
});
|
|
2573
|
-
return null;
|
|
2493
|
+
const translateAssistantContentBlock = (block) => {
|
|
2494
|
+
switch (block.type) {
|
|
2495
|
+
case "text": return createOutPutTextContent(block.text);
|
|
2496
|
+
default: return;
|
|
2574
2497
|
}
|
|
2575
2498
|
};
|
|
2576
|
-
const
|
|
2577
|
-
|
|
2578
|
-
const
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
debugJson(logger$4, "provider.messages.no_stream result:", body);
|
|
2582
|
-
return c.json(body);
|
|
2583
|
-
};
|
|
2584
|
-
const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
|
|
2585
|
-
const { body, payload, provider } = options;
|
|
2586
|
-
createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
|
|
2587
|
-
const anthropicResponse = translateToAnthropic(body);
|
|
2588
|
-
debugJson(logger$4, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
|
|
2589
|
-
return c.json(anthropicResponse);
|
|
2499
|
+
const flushPendingContent = (pendingContent, target, message) => {
|
|
2500
|
+
if (pendingContent.length === 0) return;
|
|
2501
|
+
const messageContent = [...pendingContent];
|
|
2502
|
+
target.push(createMessage(message.role, messageContent, message.phase));
|
|
2503
|
+
pendingContent.length = 0;
|
|
2590
2504
|
};
|
|
2591
|
-
const
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2505
|
+
const createMessage = (role, content, phase) => ({
|
|
2506
|
+
type: MESSAGE_TYPE,
|
|
2507
|
+
role,
|
|
2508
|
+
content,
|
|
2509
|
+
...role === "assistant" && phase ? { phase } : {}
|
|
2596
2510
|
});
|
|
2597
|
-
const
|
|
2598
|
-
if (!
|
|
2599
|
-
|
|
2600
|
-
|
|
2511
|
+
const resolveAssistantPhase = (_model, content, applyPhase) => {
|
|
2512
|
+
if (!applyPhase) return;
|
|
2513
|
+
if (typeof content === "string") return "final_answer";
|
|
2514
|
+
if (!Array.isArray(content)) return;
|
|
2515
|
+
if (!content.some((block) => block.type === "text")) return;
|
|
2516
|
+
return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
|
|
2601
2517
|
};
|
|
2602
|
-
const
|
|
2603
|
-
|
|
2604
|
-
isResponsesApiWebSocketEnabled
|
|
2518
|
+
const shouldApplyPhase = (_model) => {
|
|
2519
|
+
return true;
|
|
2605
2520
|
};
|
|
2606
|
-
const
|
|
2521
|
+
const createTextContent = (text) => ({
|
|
2522
|
+
type: "input_text",
|
|
2523
|
+
text
|
|
2524
|
+
});
|
|
2525
|
+
const createOutPutTextContent = (text) => ({
|
|
2526
|
+
type: "output_text",
|
|
2527
|
+
text
|
|
2528
|
+
});
|
|
2529
|
+
const createImageContent = (block) => ({
|
|
2530
|
+
type: "input_image",
|
|
2531
|
+
image_url: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
2532
|
+
detail: "auto"
|
|
2533
|
+
});
|
|
2534
|
+
const createFileContent = (block) => ({
|
|
2535
|
+
type: "input_file",
|
|
2536
|
+
file_data: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
2537
|
+
filename: block.title ?? "document.pdf"
|
|
2538
|
+
});
|
|
2539
|
+
const createReasoningContent = (block) => {
|
|
2540
|
+
const { encryptedContent, id } = parseReasoningSignature(block.signature);
|
|
2541
|
+
const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
|
|
2607
2542
|
return {
|
|
2608
|
-
|
|
2609
|
-
|
|
2543
|
+
id,
|
|
2544
|
+
type: "reasoning",
|
|
2545
|
+
summary: thinking ? [{
|
|
2546
|
+
type: "summary_text",
|
|
2547
|
+
text: thinking
|
|
2548
|
+
}] : [],
|
|
2549
|
+
encrypted_content: encryptedContent
|
|
2610
2550
|
};
|
|
2611
2551
|
};
|
|
2612
|
-
const
|
|
2613
|
-
const
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
const lastItem = getPayloadItems(payload).at(-1);
|
|
2621
|
-
if (!lastItem) return false;
|
|
2622
|
-
if (!("role" in lastItem) || !lastItem.role) return true;
|
|
2623
|
-
return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
|
|
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
|
+
};
|
|
2624
2560
|
};
|
|
2625
|
-
const
|
|
2626
|
-
|
|
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
|
+
};
|
|
2627
2571
|
};
|
|
2628
|
-
const
|
|
2629
|
-
|
|
2630
|
-
|
|
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);
|
|
2631
2590
|
};
|
|
2632
|
-
const
|
|
2633
|
-
type: "
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
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);
|
|
2640
2601
|
};
|
|
2641
|
-
const
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
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
|
+
};
|
|
2646
2611
|
};
|
|
2647
|
-
const
|
|
2648
|
-
|
|
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 [];
|
|
2649
2618
|
};
|
|
2650
|
-
const
|
|
2651
|
-
|
|
2619
|
+
const extractToolReferenceNames = (content) => {
|
|
2620
|
+
if (!Array.isArray(content)) return [];
|
|
2621
|
+
return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
|
|
2652
2622
|
};
|
|
2653
|
-
const
|
|
2654
|
-
|
|
2655
|
-
const
|
|
2656
|
-
|
|
2657
|
-
|
|
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;
|
|
2658
2631
|
};
|
|
2659
|
-
const
|
|
2660
|
-
|
|
2661
|
-
if (
|
|
2662
|
-
|
|
2663
|
-
const record = value;
|
|
2664
|
-
if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
|
|
2665
|
-
if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
|
|
2666
|
-
return false;
|
|
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`);
|
|
2667
2636
|
};
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
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;
|
|
2654
|
+
};
|
|
2655
|
+
const convertAnthropicTools = (tools, toolSearchEnabled) => {
|
|
2656
|
+
if (!tools || tools.length === 0) return null;
|
|
2657
|
+
const converted = [];
|
|
2658
|
+
let addedToolSearch = false;
|
|
2659
|
+
const searchableToolNames = toolSearchEnabled ? listDeferredToolNames(tools) : [];
|
|
2660
|
+
for (const tool of tools) {
|
|
2661
|
+
if (isBridgeToolSearchName(tool.name)) {
|
|
2662
|
+
if (toolSearchEnabled && !addedToolSearch) {
|
|
2663
|
+
converted.push(createResponsesToolSearchDefinition(searchableToolNames));
|
|
2664
|
+
addedToolSearch = true;
|
|
2665
|
+
}
|
|
2666
|
+
continue;
|
|
2667
|
+
}
|
|
2668
|
+
if (toolSearchEnabled && isDeferredToolName(tool.name)) {
|
|
2669
|
+
converted.push(convertDeferredToolToNamespace(tool));
|
|
2670
|
+
continue;
|
|
2671
|
+
}
|
|
2672
|
+
converted.push(convertToolToFunction(tool));
|
|
2688
2673
|
}
|
|
2689
|
-
return
|
|
2674
|
+
return converted;
|
|
2690
2675
|
};
|
|
2691
|
-
const
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2676
|
+
const createResponsesToolSearchDefinition = (searchableToolNames) => ({
|
|
2677
|
+
type: "tool_search",
|
|
2678
|
+
execution: "client",
|
|
2679
|
+
description: "Load deferred tools by exact name before using them. Return only the searchable tool names you need for the next step.",
|
|
2680
|
+
parameters: {
|
|
2681
|
+
type: "object",
|
|
2682
|
+
properties: { names: {
|
|
2683
|
+
type: "array",
|
|
2684
|
+
description: "Exact deferred tool names to load.",
|
|
2685
|
+
items: {
|
|
2686
|
+
type: "string",
|
|
2687
|
+
enum: searchableToolNames
|
|
2688
|
+
},
|
|
2689
|
+
minItems: 1
|
|
2690
|
+
} },
|
|
2691
|
+
required: ["names"],
|
|
2692
|
+
additionalProperties: false
|
|
2693
|
+
}
|
|
2694
|
+
});
|
|
2695
|
+
const convertToolToFunction = (tool) => ({
|
|
2696
|
+
type: "function",
|
|
2697
|
+
name: tool.name,
|
|
2698
|
+
parameters: normalizeToolSchema(tool.input_schema),
|
|
2699
|
+
strict: false,
|
|
2700
|
+
...tool.description ? { description: tool.description } : {}
|
|
2701
|
+
});
|
|
2702
|
+
const convertDeferredToolToNamespace = (tool) => ({
|
|
2703
|
+
type: "namespace",
|
|
2704
|
+
name: tool.name,
|
|
2705
|
+
...tool.description ? { description: tool.description } : {},
|
|
2706
|
+
tools: [{
|
|
2707
|
+
type: "function",
|
|
2708
|
+
name: tool.name,
|
|
2709
|
+
parameters: normalizeToolSchema(tool.input_schema),
|
|
2710
|
+
strict: false,
|
|
2711
|
+
defer_loading: true,
|
|
2712
|
+
...tool.description ? { description: tool.description } : {}
|
|
2713
|
+
}]
|
|
2714
|
+
});
|
|
2715
|
+
const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
|
|
2716
|
+
if (!choice) return "auto";
|
|
2717
|
+
switch (choice.type) {
|
|
2718
|
+
case "auto": return "auto";
|
|
2719
|
+
case "any": return "required";
|
|
2720
|
+
case "tool":
|
|
2721
|
+
if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
|
|
2722
|
+
return choice.name ? {
|
|
2723
|
+
type: "function",
|
|
2724
|
+
name: choice.name
|
|
2725
|
+
} : "auto";
|
|
2726
|
+
case "none": return "none";
|
|
2727
|
+
default: return "auto";
|
|
2701
2728
|
}
|
|
2702
|
-
if (payload.stream) return events(response);
|
|
2703
|
-
return await response.json();
|
|
2704
2729
|
};
|
|
2705
|
-
const
|
|
2706
|
-
const
|
|
2730
|
+
const translateResponsesResultToAnthropic = (response, options) => {
|
|
2731
|
+
const contentBlocks = mapOutputToAnthropicContent(response.output, options);
|
|
2732
|
+
const usage = mapResponsesUsage(response);
|
|
2733
|
+
let anthropicContent = fallbackContentBlocks(response.output_text);
|
|
2734
|
+
if (contentBlocks.length > 0) anthropicContent = contentBlocks;
|
|
2735
|
+
const stopReason = mapResponsesStopReason(response, options);
|
|
2707
2736
|
return {
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2737
|
+
id: response.id,
|
|
2738
|
+
type: "message",
|
|
2739
|
+
role: "assistant",
|
|
2740
|
+
content: anthropicContent,
|
|
2741
|
+
model: response.model,
|
|
2742
|
+
stop_reason: stopReason,
|
|
2743
|
+
stop_sequence: null,
|
|
2744
|
+
usage
|
|
2711
2745
|
};
|
|
2712
2746
|
};
|
|
2713
|
-
const
|
|
2714
|
-
const
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
const
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2747
|
+
const mapOutputToAnthropicContent = (output, options) => {
|
|
2748
|
+
const contentBlocks = [];
|
|
2749
|
+
for (const item of output) switch (item.type) {
|
|
2750
|
+
case "reasoning": {
|
|
2751
|
+
const thinkingText = extractReasoningText(item);
|
|
2752
|
+
if (thinkingText.length > 0) contentBlocks.push({
|
|
2753
|
+
type: "thinking",
|
|
2754
|
+
thinking: thinkingText,
|
|
2755
|
+
signature: (item.encrypted_content ?? "") + "@" + item.id
|
|
2756
|
+
});
|
|
2757
|
+
break;
|
|
2758
|
+
}
|
|
2759
|
+
case "function_call": {
|
|
2760
|
+
const toolUseBlock = createToolUseContentBlock(item);
|
|
2761
|
+
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
2762
|
+
break;
|
|
2763
|
+
}
|
|
2764
|
+
case "tool_search_call": {
|
|
2765
|
+
const toolUseBlock = createToolSearchUseContentBlock(item, options?.toolSearchName);
|
|
2766
|
+
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
2767
|
+
break;
|
|
2768
|
+
}
|
|
2769
|
+
case "tool_search_output": break;
|
|
2770
|
+
case "message": {
|
|
2771
|
+
const combinedText = combineMessageTextContent(item.content);
|
|
2772
|
+
if (combinedText.length > 0) contentBlocks.push({
|
|
2773
|
+
type: "text",
|
|
2774
|
+
text: combinedText
|
|
2775
|
+
});
|
|
2776
|
+
break;
|
|
2777
|
+
}
|
|
2778
|
+
case "compaction": {
|
|
2779
|
+
const compactionBlock = createCompactionThinkingBlock(item);
|
|
2780
|
+
if (compactionBlock) contentBlocks.push(compactionBlock);
|
|
2781
|
+
break;
|
|
2782
|
+
}
|
|
2783
|
+
default: {
|
|
2784
|
+
const combinedText = combineMessageTextContent(item.content);
|
|
2785
|
+
if (combinedText.length > 0) contentBlocks.push({
|
|
2786
|
+
type: "text",
|
|
2787
|
+
text: combinedText
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
return contentBlocks;
|
|
2741
2792
|
};
|
|
2742
|
-
const
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2793
|
+
const combineMessageTextContent = (content) => {
|
|
2794
|
+
if (!Array.isArray(content)) return "";
|
|
2795
|
+
let aggregated = "";
|
|
2796
|
+
for (const block of content) {
|
|
2797
|
+
if (isResponseOutputText(block)) {
|
|
2798
|
+
aggregated += block.text;
|
|
2799
|
+
continue;
|
|
2800
|
+
}
|
|
2801
|
+
if (isResponseOutputRefusal(block)) {
|
|
2802
|
+
aggregated += block.refusal;
|
|
2803
|
+
continue;
|
|
2804
|
+
}
|
|
2805
|
+
if (typeof block.text === "string") {
|
|
2806
|
+
aggregated += block.text;
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
if (typeof block.reasoning === "string") {
|
|
2810
|
+
aggregated += block.reasoning;
|
|
2811
|
+
continue;
|
|
2760
2812
|
}
|
|
2761
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2762
|
-
throw new Error("Responses websocket ended without a terminal response");
|
|
2763
|
-
} catch (error) {
|
|
2764
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2765
|
-
throw toError(error);
|
|
2766
|
-
} finally {
|
|
2767
|
-
release();
|
|
2768
2813
|
}
|
|
2814
|
+
return aggregated;
|
|
2769
2815
|
};
|
|
2770
|
-
const
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2816
|
+
const extractReasoningText = (item) => {
|
|
2817
|
+
const segments = [];
|
|
2818
|
+
const collectFromBlocks = (blocks) => {
|
|
2819
|
+
if (!Array.isArray(blocks)) return;
|
|
2820
|
+
for (const block of blocks) if (typeof block.text === "string") {
|
|
2821
|
+
segments.push(block.text);
|
|
2822
|
+
continue;
|
|
2823
|
+
}
|
|
2774
2824
|
};
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
const entry = createResponsesWebSocketEntry(request);
|
|
2784
|
-
responsesWebSocketPool.set(request.poolKey, entry);
|
|
2825
|
+
if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
|
|
2826
|
+
collectFromBlocks(item.summary);
|
|
2827
|
+
return segments.join("").trim();
|
|
2828
|
+
};
|
|
2829
|
+
const createToolUseContentBlock = (call) => {
|
|
2830
|
+
const toolId = call.call_id;
|
|
2831
|
+
const toolName = resolveToolUseName(call);
|
|
2832
|
+
if (!toolName || !toolId) return null;
|
|
2785
2833
|
return {
|
|
2786
|
-
|
|
2787
|
-
|
|
2834
|
+
type: "tool_use",
|
|
2835
|
+
id: toolId,
|
|
2836
|
+
name: toolName,
|
|
2837
|
+
input: parseFunctionCallArguments(call.arguments)
|
|
2788
2838
|
};
|
|
2789
2839
|
};
|
|
2790
|
-
const
|
|
2791
|
-
const
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
})
|
|
2840
|
+
const createToolSearchUseContentBlock = (call, toolSearchName = BRIDGE_TOOL_SEARCH_NAME) => {
|
|
2841
|
+
const toolId = call.call_id;
|
|
2842
|
+
if (!toolId) return null;
|
|
2843
|
+
return {
|
|
2844
|
+
type: "tool_use",
|
|
2845
|
+
id: toolId,
|
|
2846
|
+
name: toolSearchName,
|
|
2847
|
+
input: parseToolSearchArguments(call.arguments)
|
|
2799
2848
|
};
|
|
2800
|
-
entry.websocketPromise.then((websocket) => {
|
|
2801
|
-
websocket.addEventListener("close", () => {
|
|
2802
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2803
|
-
});
|
|
2804
|
-
websocket.addEventListener("error", () => {
|
|
2805
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2806
|
-
});
|
|
2807
|
-
}).catch(() => {
|
|
2808
|
-
removeResponsesWebSocketPoolEntry(request.poolKey, entry);
|
|
2809
|
-
});
|
|
2810
|
-
return entry;
|
|
2811
2849
|
};
|
|
2812
|
-
const
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
entry.requestCount += 1;
|
|
2816
|
-
let released = false;
|
|
2817
|
-
return () => {
|
|
2818
|
-
if (released) return;
|
|
2819
|
-
released = true;
|
|
2820
|
-
entry.requestCount -= 1;
|
|
2821
|
-
decrementResponsesWebSocketActiveRequestCount(poolKey);
|
|
2822
|
-
if (entry.closed || entry.requestCount > 0) return;
|
|
2823
|
-
if (pooled && responsesWebSocketPool.get(poolKey) === entry) {
|
|
2824
|
-
scheduleResponsesWebSocketIdleClose(poolKey, entry);
|
|
2825
|
-
return;
|
|
2826
|
-
}
|
|
2827
|
-
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
2828
|
-
};
|
|
2850
|
+
const resolveToolUseName = (call) => {
|
|
2851
|
+
if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
|
|
2852
|
+
return call.name;
|
|
2829
2853
|
};
|
|
2830
|
-
const
|
|
2831
|
-
if (
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
};
|
|
2840
|
-
const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
|
|
2841
|
-
clearResponsesWebSocketIdleTimer(entry);
|
|
2842
|
-
entry.idleTimer = setTimeout(() => {
|
|
2843
|
-
removeResponsesWebSocketPoolEntry(poolKey, entry);
|
|
2844
|
-
}, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
|
|
2845
|
-
unrefTimer(entry.idleTimer);
|
|
2846
|
-
};
|
|
2847
|
-
const clearResponsesWebSocketIdleTimer = (entry) => {
|
|
2848
|
-
if (entry.idleTimer) {
|
|
2849
|
-
clearTimeout(entry.idleTimer);
|
|
2850
|
-
entry.idleTimer = null;
|
|
2851
|
-
}
|
|
2852
|
-
};
|
|
2853
|
-
const getResponsesWebSocketActiveRequestCount = (poolKey) => responsesWebSocketActiveRequests.get(poolKey) ?? 0;
|
|
2854
|
-
const incrementResponsesWebSocketActiveRequestCount = (poolKey) => {
|
|
2855
|
-
responsesWebSocketActiveRequests.set(poolKey, getResponsesWebSocketActiveRequestCount(poolKey) + 1);
|
|
2856
|
-
};
|
|
2857
|
-
const decrementResponsesWebSocketActiveRequestCount = (poolKey) => {
|
|
2858
|
-
const nextCount = getResponsesWebSocketActiveRequestCount(poolKey) - 1;
|
|
2859
|
-
if (nextCount <= 0) {
|
|
2860
|
-
responsesWebSocketActiveRequests.delete(poolKey);
|
|
2861
|
-
return;
|
|
2862
|
-
}
|
|
2863
|
-
responsesWebSocketActiveRequests.set(poolKey, nextCount);
|
|
2864
|
-
};
|
|
2865
|
-
const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
|
|
2866
|
-
if (responsesWebSocketPool.get(poolKey) === entry) responsesWebSocketPool.delete(poolKey);
|
|
2867
|
-
if (entry.closed) return;
|
|
2868
|
-
entry.closed = true;
|
|
2869
|
-
clearResponsesWebSocketIdleTimer(entry);
|
|
2870
|
-
entry.websocketPromise.then(closeResponsesWebSocket).catch(() => {});
|
|
2871
|
-
};
|
|
2872
|
-
const unrefTimer = (timer) => {
|
|
2873
|
-
if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
|
|
2874
|
-
};
|
|
2875
|
-
const createResponsesWebSocketError = (message, event) => {
|
|
2876
|
-
const reason = event?.error ?? event?.message;
|
|
2877
|
-
if (reason === void 0 || reason === "") return new Error(message);
|
|
2878
|
-
const cause = toError(reason);
|
|
2879
|
-
return new Error(`${message}: ${cause.message}`, { cause });
|
|
2880
|
-
};
|
|
2881
|
-
const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
|
|
2882
|
-
const dispatcher = getProxyEnvDispatcher();
|
|
2883
|
-
const websocket = new WebSocket(url, dispatcher ? {
|
|
2884
|
-
dispatcher,
|
|
2885
|
-
headers
|
|
2886
|
-
} : { headers });
|
|
2887
|
-
const cleanup = () => {
|
|
2888
|
-
websocket.removeEventListener("open", onOpen);
|
|
2889
|
-
websocket.removeEventListener("error", onError);
|
|
2890
|
-
};
|
|
2891
|
-
const onOpen = () => {
|
|
2892
|
-
cleanup();
|
|
2893
|
-
resolve(websocket);
|
|
2894
|
-
};
|
|
2895
|
-
const onError = (event) => {
|
|
2896
|
-
cleanup();
|
|
2897
|
-
reject(createResponsesWebSocketError("Failed to create responses websocket", event));
|
|
2898
|
-
};
|
|
2899
|
-
websocket.addEventListener("open", onOpen);
|
|
2900
|
-
websocket.addEventListener("error", onError);
|
|
2901
|
-
});
|
|
2902
|
-
const createWebSocketMessageStream = async function* (websocket) {
|
|
2903
|
-
const queue = [];
|
|
2904
|
-
let closed = false;
|
|
2905
|
-
let error = null;
|
|
2906
|
-
let notify = null;
|
|
2907
|
-
const wake = () => {
|
|
2908
|
-
notify?.();
|
|
2909
|
-
notify = null;
|
|
2910
|
-
};
|
|
2911
|
-
const onMessage = (event) => {
|
|
2912
|
-
queue.push(normalizeWebSocketMessageData(event.data));
|
|
2913
|
-
wake();
|
|
2914
|
-
};
|
|
2915
|
-
const onClose = () => {
|
|
2916
|
-
closed = true;
|
|
2917
|
-
wake();
|
|
2918
|
-
};
|
|
2919
|
-
const onError = (event) => {
|
|
2920
|
-
error = createResponsesWebSocketError("Responses websocket stream error", event);
|
|
2921
|
-
wake();
|
|
2854
|
+
const createCompactionThinkingBlock = (item) => {
|
|
2855
|
+
if (!item.id || !item.encrypted_content) return null;
|
|
2856
|
+
return {
|
|
2857
|
+
type: "thinking",
|
|
2858
|
+
thinking: THINKING_TEXT,
|
|
2859
|
+
signature: encodeCompactionCarrierSignature({
|
|
2860
|
+
id: item.id,
|
|
2861
|
+
encrypted_content: item.encrypted_content
|
|
2862
|
+
})
|
|
2922
2863
|
};
|
|
2923
|
-
websocket.addEventListener("message", onMessage);
|
|
2924
|
-
websocket.addEventListener("close", onClose);
|
|
2925
|
-
websocket.addEventListener("error", onError);
|
|
2926
|
-
try {
|
|
2927
|
-
while (true) {
|
|
2928
|
-
const item = queue.shift();
|
|
2929
|
-
if (item) {
|
|
2930
|
-
yield await item;
|
|
2931
|
-
continue;
|
|
2932
|
-
}
|
|
2933
|
-
if (error) throw toError(error);
|
|
2934
|
-
if (closed) break;
|
|
2935
|
-
await new Promise((resolve) => {
|
|
2936
|
-
notify = resolve;
|
|
2937
|
-
});
|
|
2938
|
-
}
|
|
2939
|
-
} finally {
|
|
2940
|
-
websocket.removeEventListener("message", onMessage);
|
|
2941
|
-
websocket.removeEventListener("close", onClose);
|
|
2942
|
-
websocket.removeEventListener("error", onError);
|
|
2943
|
-
}
|
|
2944
2864
|
};
|
|
2945
|
-
const
|
|
2946
|
-
if (typeof
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2865
|
+
const parseFunctionCallArguments = (rawArguments) => {
|
|
2866
|
+
if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
|
|
2867
|
+
try {
|
|
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
|
+
});
|
|
2951
2876
|
}
|
|
2952
|
-
|
|
2953
|
-
return String(data);
|
|
2954
|
-
};
|
|
2955
|
-
const isTextReadable = (value) => {
|
|
2956
|
-
if (!value || typeof value !== "object" || !("text" in value)) return false;
|
|
2957
|
-
return typeof value.text === "function";
|
|
2958
|
-
};
|
|
2959
|
-
const toError = (value) => {
|
|
2960
|
-
if (value instanceof Error) return value;
|
|
2961
|
-
return new Error(String(value));
|
|
2877
|
+
return { raw_arguments: rawArguments };
|
|
2962
2878
|
};
|
|
2963
|
-
const
|
|
2964
|
-
|
|
2965
|
-
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
2879
|
+
const parseToolSearchArguments = (argumentsValue) => {
|
|
2880
|
+
return formatToolSearchBridgeArguments(argumentsValue);
|
|
2966
2881
|
};
|
|
2967
|
-
const
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
return {
|
|
2974
|
-
data: JSON.stringify(parsed),
|
|
2975
|
-
event: typeof parsed.type === "string" ? parsed.type : void 0,
|
|
2976
|
-
id: typeof parsed.id === "string" ? parsed.id : void 0
|
|
2977
|
-
};
|
|
2978
|
-
} catch {
|
|
2979
|
-
return { data };
|
|
2980
|
-
}
|
|
2882
|
+
const fallbackContentBlocks = (outputText) => {
|
|
2883
|
+
if (!outputText) return [];
|
|
2884
|
+
return [{
|
|
2885
|
+
type: "text",
|
|
2886
|
+
text: outputText
|
|
2887
|
+
}];
|
|
2981
2888
|
};
|
|
2982
|
-
const
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
return false;
|
|
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";
|
|
2989
2895
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
if (!chunk.data || chunk.data === "[DONE]") continue;
|
|
2994
|
-
const event = JSON.parse(chunk.data);
|
|
2995
|
-
if (event.type === "error") throw new Error(event.message);
|
|
2996
|
-
if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
|
|
2896
|
+
if (status === "incomplete") {
|
|
2897
|
+
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
2898
|
+
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
2997
2899
|
}
|
|
2998
|
-
|
|
2999
|
-
};
|
|
3000
|
-
const closeResponsesWebSocket = (websocket) => {
|
|
3001
|
-
if (websocket.readyState === WebSocket.CONNECTING || websocket.readyState === WebSocket.OPEN) websocket.close();
|
|
3002
|
-
};
|
|
3003
|
-
//#endregion
|
|
3004
|
-
//#region src/routes/messages/responses-translation.ts
|
|
3005
|
-
const MESSAGE_TYPE = "message";
|
|
3006
|
-
const COMPACTION_SIGNATURE_PREFIX = "cm1#";
|
|
3007
|
-
const COMPACTION_SIGNATURE_SEPARATOR = "@";
|
|
3008
|
-
const THINKING_TEXT = "Thinking...";
|
|
3009
|
-
const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
|
|
3010
|
-
if (!basePromptCacheKey) return null;
|
|
3011
|
-
const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
|
|
3012
|
-
if (!normalizedSubagentAgentId) return basePromptCacheKey;
|
|
3013
|
-
return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
|
|
2900
|
+
return null;
|
|
3014
2901
|
};
|
|
3015
|
-
const
|
|
3016
|
-
const
|
|
3017
|
-
const
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
const translationState = {
|
|
3023
|
-
originalTools: payload.tools ?? [],
|
|
3024
|
-
toolSearchEnabled,
|
|
3025
|
-
toolUseNameById: /* @__PURE__ */ new Map()
|
|
3026
|
-
};
|
|
3027
|
-
for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase, translationState));
|
|
3028
|
-
const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
|
|
3029
|
-
const translatedTools = convertAnthropicTools(payload.tools, toolSearchEnabled);
|
|
3030
|
-
const toolChoice = convertAnthropicToolChoice(payload.tool_choice, toolSearchEnabled);
|
|
3031
|
-
const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
|
|
3032
|
-
const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
|
|
3033
|
-
const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
|
|
3034
|
-
const responsesPayload = {
|
|
3035
|
-
model: payload.model,
|
|
3036
|
-
input,
|
|
3037
|
-
instructions: translateSystemPrompt(payload.system, payload.model),
|
|
3038
|
-
temperature: 1,
|
|
3039
|
-
top_p: payload.top_p ?? null,
|
|
3040
|
-
max_output_tokens: Math.max(payload.max_tokens, 12800),
|
|
3041
|
-
tools: translatedTools,
|
|
3042
|
-
tool_choice: toolChoice,
|
|
3043
|
-
metadata: payload.metadata ? { ...payload.metadata } : null,
|
|
3044
|
-
stream: payload.stream ?? null,
|
|
3045
|
-
store: false,
|
|
3046
|
-
parallel_tool_calls: true,
|
|
3047
|
-
reasoning: {
|
|
3048
|
-
effort: getReasoningEffortForModel(payload.model),
|
|
3049
|
-
summary: "detailed"
|
|
3050
|
-
},
|
|
3051
|
-
include: ["reasoning.encrypted_content"]
|
|
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 }
|
|
3052
2909
|
};
|
|
3053
|
-
if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
|
|
3054
|
-
return responsesPayload;
|
|
3055
|
-
};
|
|
3056
|
-
const encodeCompactionCarrierSignature = (compaction) => {
|
|
3057
|
-
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
3058
2910
|
};
|
|
3059
|
-
const
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
const
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
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;
|
|
3071
2934
|
}
|
|
2935
|
+
return "";
|
|
3072
2936
|
};
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
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";
|
|
2944
|
+
}
|
|
3076
2945
|
};
|
|
3077
|
-
const
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
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
|
+
};
|
|
3086
2955
|
continue;
|
|
3087
2956
|
}
|
|
3088
|
-
|
|
3089
|
-
if (converted.length > 0) pendingContent.push(...converted);
|
|
2957
|
+
if (char !== " ") count = 0;
|
|
3090
2958
|
}
|
|
3091
|
-
|
|
3092
|
-
|
|
2959
|
+
return {
|
|
2960
|
+
nextCount: count,
|
|
2961
|
+
exceeded: false
|
|
2962
|
+
};
|
|
3093
2963
|
};
|
|
3094
|
-
const
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
if (block.signature.includes("@")) {
|
|
3121
|
-
flushPendingContent(pendingContent, items, {
|
|
3122
|
-
role: "assistant",
|
|
3123
|
-
phase: assistantPhase
|
|
3124
|
-
});
|
|
3125
|
-
items.push(createReasoningContent(block));
|
|
3126
|
-
continue;
|
|
3127
|
-
}
|
|
3128
|
-
}
|
|
3129
|
-
const converted = translateAssistantContentBlock(block);
|
|
3130
|
-
if (converted) pendingContent.push(converted);
|
|
3131
|
-
}
|
|
3132
|
-
flushPendingContent(pendingContent, items, {
|
|
3133
|
-
role: "assistant",
|
|
3134
|
-
phase: assistantPhase
|
|
3135
|
-
});
|
|
3136
|
-
return items;
|
|
3137
|
-
};
|
|
3138
|
-
const translateUserContentBlock = (block) => {
|
|
3139
|
-
switch (block.type) {
|
|
3140
|
-
case "text": return [createTextContent(block.text)];
|
|
3141
|
-
case "image": return [createImageContent(block)];
|
|
3142
|
-
case "document": return [createFileContent(block)];
|
|
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);
|
|
3143
2990
|
default: return [];
|
|
3144
2991
|
}
|
|
3145
2992
|
};
|
|
3146
|
-
const
|
|
3147
|
-
|
|
3148
|
-
case "text": return createOutPutTextContent(block.text);
|
|
3149
|
-
default: return;
|
|
3150
|
-
}
|
|
3151
|
-
};
|
|
3152
|
-
const flushPendingContent = (pendingContent, target, message) => {
|
|
3153
|
-
if (pendingContent.length === 0) return;
|
|
3154
|
-
const messageContent = [...pendingContent];
|
|
3155
|
-
target.push(createMessage(message.role, messageContent, message.phase));
|
|
3156
|
-
pendingContent.length = 0;
|
|
3157
|
-
};
|
|
3158
|
-
const createMessage = (role, content, phase) => ({
|
|
3159
|
-
type: MESSAGE_TYPE,
|
|
3160
|
-
role,
|
|
3161
|
-
content,
|
|
3162
|
-
...role === "assistant" && phase ? { phase } : {}
|
|
3163
|
-
});
|
|
3164
|
-
const resolveAssistantPhase = (_model, content, applyPhase) => {
|
|
3165
|
-
if (!applyPhase) return;
|
|
3166
|
-
if (typeof content === "string") return "final_answer";
|
|
3167
|
-
if (!Array.isArray(content)) return;
|
|
3168
|
-
if (!content.some((block) => block.type === "text")) return;
|
|
3169
|
-
return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
|
|
3170
|
-
};
|
|
3171
|
-
const shouldApplyPhase = (_model) => {
|
|
3172
|
-
return true;
|
|
3173
|
-
};
|
|
3174
|
-
const createTextContent = (text) => ({
|
|
3175
|
-
type: "input_text",
|
|
3176
|
-
text
|
|
3177
|
-
});
|
|
3178
|
-
const createOutPutTextContent = (text) => ({
|
|
3179
|
-
type: "output_text",
|
|
3180
|
-
text
|
|
3181
|
-
});
|
|
3182
|
-
const createImageContent = (block) => ({
|
|
3183
|
-
type: "input_image",
|
|
3184
|
-
image_url: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
3185
|
-
detail: "auto"
|
|
3186
|
-
});
|
|
3187
|
-
const createFileContent = (block) => ({
|
|
3188
|
-
type: "input_file",
|
|
3189
|
-
file_data: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
3190
|
-
filename: block.title ?? "document.pdf"
|
|
3191
|
-
});
|
|
3192
|
-
const createReasoningContent = (block) => {
|
|
3193
|
-
const { encryptedContent, id } = parseReasoningSignature(block.signature);
|
|
3194
|
-
const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
|
|
3195
|
-
return {
|
|
3196
|
-
id,
|
|
3197
|
-
type: "reasoning",
|
|
3198
|
-
summary: thinking ? [{
|
|
3199
|
-
type: "summary_text",
|
|
3200
|
-
text: thinking
|
|
3201
|
-
}] : [],
|
|
3202
|
-
encrypted_content: encryptedContent
|
|
3203
|
-
};
|
|
3204
|
-
};
|
|
3205
|
-
const createCompactionContent = (block) => {
|
|
3206
|
-
const compaction = decodeCompactionCarrierSignature(block.signature);
|
|
3207
|
-
if (!compaction) return;
|
|
3208
|
-
return {
|
|
3209
|
-
id: compaction.id,
|
|
3210
|
-
type: "compaction",
|
|
3211
|
-
encrypted_content: compaction.encrypted_content
|
|
3212
|
-
};
|
|
3213
|
-
};
|
|
3214
|
-
const parseReasoningSignature = (signature) => {
|
|
3215
|
-
const splitIndex = signature.lastIndexOf("@");
|
|
3216
|
-
if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
|
|
3217
|
-
encryptedContent: signature,
|
|
3218
|
-
id: ""
|
|
3219
|
-
};
|
|
3220
|
-
return {
|
|
3221
|
-
encryptedContent: signature.slice(0, splitIndex),
|
|
3222
|
-
id: signature.slice(splitIndex + 1)
|
|
3223
|
-
};
|
|
3224
|
-
};
|
|
3225
|
-
const createFunctionToolCall = (block, state) => ({
|
|
3226
|
-
type: "function_call",
|
|
3227
|
-
call_id: block.id,
|
|
3228
|
-
name: block.name,
|
|
3229
|
-
arguments: JSON.stringify(block.input),
|
|
3230
|
-
status: "completed",
|
|
3231
|
-
...state.toolSearchEnabled && isDeferredToolName(block.name) ? { namespace: block.name } : {}
|
|
3232
|
-
});
|
|
3233
|
-
const createToolSearchCall = (block) => ({
|
|
3234
|
-
type: "tool_search_call",
|
|
3235
|
-
call_id: block.id,
|
|
3236
|
-
arguments: normalizeToolSearchBridgeArguments(block.input),
|
|
3237
|
-
execution: "client",
|
|
3238
|
-
status: "completed"
|
|
3239
|
-
});
|
|
3240
|
-
const createToolCall = (block, state) => {
|
|
3241
|
-
if (state.toolSearchEnabled && isBridgeToolSearchName(block.name)) return createToolSearchCall(block);
|
|
3242
|
-
return createFunctionToolCall(block, state);
|
|
3243
|
-
};
|
|
3244
|
-
const createFunctionCallOutput = (block) => ({
|
|
3245
|
-
type: "function_call_output",
|
|
3246
|
-
call_id: block.tool_use_id,
|
|
3247
|
-
output: convertToolResultContent(block.content),
|
|
3248
|
-
status: block.is_error ? "incomplete" : "completed"
|
|
3249
|
-
});
|
|
3250
|
-
const createToolCallOutput = (block, state) => {
|
|
3251
|
-
const toolUseName = state.toolUseNameById.get(block.tool_use_id);
|
|
3252
|
-
if (state.toolSearchEnabled && isBridgeToolSearchName(toolUseName ?? "")) return createToolSearchOutput(block, state.originalTools);
|
|
3253
|
-
return createFunctionCallOutput(block);
|
|
3254
|
-
};
|
|
3255
|
-
const createToolSearchOutput = (block, originalTools) => {
|
|
3256
|
-
const referencedToolNames = resolveToolSearchReferencedToolNames(block.content, originalTools);
|
|
3257
|
-
return {
|
|
3258
|
-
type: "tool_search_output",
|
|
3259
|
-
call_id: block.tool_use_id,
|
|
3260
|
-
tools: referencedToolNames.map((toolName) => convertDeferredToolToNamespace(resolveDeferredTool(toolName, originalTools))),
|
|
3261
|
-
execution: "client",
|
|
3262
|
-
status: block.is_error ? "incomplete" : "completed"
|
|
3263
|
-
};
|
|
3264
|
-
};
|
|
3265
|
-
const resolveToolSearchReferencedToolNames = (content, originalTools) => {
|
|
3266
|
-
const explicitReferences = extractToolReferenceNames(content);
|
|
3267
|
-
if (explicitReferences.length > 0) return uniqueToolNames(explicitReferences);
|
|
3268
|
-
const sentinel = extractMcpToolSearchSentinel(content);
|
|
3269
|
-
if (sentinel) return selectDeferredToolsByNames(sentinel.names, originalTools).map((tool) => tool.name);
|
|
3270
|
-
return [];
|
|
3271
|
-
};
|
|
3272
|
-
const extractToolReferenceNames = (content) => {
|
|
3273
|
-
if (!Array.isArray(content)) return [];
|
|
3274
|
-
return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
|
|
2993
|
+
const handleResponseCreated = (rawEvent, state) => {
|
|
2994
|
+
return messageStart(state, rawEvent.response);
|
|
3275
2995
|
};
|
|
3276
|
-
const
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
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
|
|
3006
|
+
});
|
|
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);
|
|
3282
3017
|
}
|
|
3283
|
-
return
|
|
3284
|
-
};
|
|
3285
|
-
const resolveDeferredTool = (toolName, originalTools) => {
|
|
3286
|
-
const tool = originalTools.find((candidate) => candidate.name === toolName);
|
|
3287
|
-
if (tool && isDeferredToolName(tool.name)) return tool;
|
|
3288
|
-
throw createInvalidRequestError(`Tool reference '${toolName}' has no corresponding deferred tool definition`);
|
|
3289
|
-
};
|
|
3290
|
-
const uniqueToolNames = (toolNames) => [...new Set(toolNames)];
|
|
3291
|
-
const createInvalidRequestError = (message) => new HTTPError(message, new Response(JSON.stringify({ error: {
|
|
3292
|
-
message,
|
|
3293
|
-
type: "invalid_request_error"
|
|
3294
|
-
} }), {
|
|
3295
|
-
status: 400,
|
|
3296
|
-
headers: { "content-type": "application/json" }
|
|
3297
|
-
}));
|
|
3298
|
-
const translateSystemPrompt = (system, model) => {
|
|
3299
|
-
if (!system) return null;
|
|
3300
|
-
const extraPrompt = getExtraPromptForModel(model);
|
|
3301
|
-
if (typeof system === "string") return system + extraPrompt;
|
|
3302
|
-
const text = system.map((block, index) => {
|
|
3303
|
-
if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
|
|
3304
|
-
return block.text;
|
|
3305
|
-
}).join(" ");
|
|
3306
|
-
return text.length > 0 ? text : null;
|
|
3018
|
+
return events;
|
|
3307
3019
|
};
|
|
3308
|
-
const
|
|
3309
|
-
|
|
3310
|
-
const
|
|
3311
|
-
|
|
3312
|
-
const
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
if (
|
|
3322
|
-
|
|
3323
|
-
|
|
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
|
+
}
|
|
3041
|
+
});
|
|
3042
|
+
state.blockHasDelta.add(blockIndex);
|
|
3324
3043
|
}
|
|
3325
|
-
|
|
3044
|
+
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3045
|
+
return events;
|
|
3326
3046
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
const
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
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
|
|
3056
|
+
}
|
|
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
|
+
})
|
|
3067
|
+
}
|
|
3068
|
+
});
|
|
3069
|
+
state.blockHasDelta.add(blockIndex);
|
|
3070
|
+
return events;
|
|
3346
3071
|
}
|
|
3347
|
-
|
|
3348
|
-
const
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
|
|
3369
|
-
if (!choice) return "auto";
|
|
3370
|
-
switch (choice.type) {
|
|
3371
|
-
case "auto": return "auto";
|
|
3372
|
-
case "any": return "required";
|
|
3373
|
-
case "tool":
|
|
3374
|
-
if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
|
|
3375
|
-
return choice.name ? {
|
|
3376
|
-
type: "function",
|
|
3377
|
-
name: choice.name
|
|
3378
|
-
} : "auto";
|
|
3379
|
-
case "none": return "none";
|
|
3380
|
-
default: return "auto";
|
|
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);
|
|
3381
3093
|
}
|
|
3094
|
+
return events;
|
|
3382
3095
|
};
|
|
3383
|
-
const
|
|
3384
|
-
const
|
|
3385
|
-
const
|
|
3386
|
-
|
|
3387
|
-
if (
|
|
3388
|
-
const
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
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;
|
|
3399
3120
|
};
|
|
3400
|
-
const
|
|
3401
|
-
const
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
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);
|
|
3139
|
+
}
|
|
3140
|
+
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3141
|
+
return events;
|
|
3142
|
+
};
|
|
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
|
|
3411
3160
|
}
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3161
|
+
});
|
|
3162
|
+
state.blockHasDelta.add(blockIndex);
|
|
3163
|
+
return events;
|
|
3164
|
+
};
|
|
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
|
|
3416
3176
|
}
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3177
|
+
});
|
|
3178
|
+
state.blockHasDelta.add(blockIndex);
|
|
3179
|
+
return events;
|
|
3180
|
+
};
|
|
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
|
|
3421
3192
|
}
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3193
|
+
});
|
|
3194
|
+
return events;
|
|
3195
|
+
};
|
|
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
|
|
3430
3212
|
}
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3213
|
+
});
|
|
3214
|
+
return events;
|
|
3215
|
+
};
|
|
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;
|
|
3234
|
+
};
|
|
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;
|
|
3243
|
+
};
|
|
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)];
|
|
3248
|
+
};
|
|
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;
|
|
3255
|
+
};
|
|
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
|
+
}
|
|
3435
3275
|
}
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3276
|
+
}];
|
|
3277
|
+
};
|
|
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);
|
|
3286
|
+
}
|
|
3287
|
+
if (!state.openBlocks.has(blockIndex)) {
|
|
3288
|
+
closeOpenBlocks(state, events);
|
|
3289
|
+
events.push({
|
|
3290
|
+
type: "content_block_start",
|
|
3291
|
+
index: blockIndex,
|
|
3292
|
+
content_block: {
|
|
3439
3293
|
type: "text",
|
|
3440
|
-
text:
|
|
3441
|
-
}
|
|
3442
|
-
}
|
|
3294
|
+
text: ""
|
|
3295
|
+
}
|
|
3296
|
+
});
|
|
3297
|
+
state.openBlocks.add(blockIndex);
|
|
3443
3298
|
}
|
|
3444
|
-
return
|
|
3299
|
+
return blockIndex;
|
|
3445
3300
|
};
|
|
3446
|
-
const
|
|
3447
|
-
|
|
3448
|
-
let
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
}
|
|
3454
|
-
if (isResponseOutputRefusal(block)) {
|
|
3455
|
-
aggregated += block.refusal;
|
|
3456
|
-
continue;
|
|
3457
|
-
}
|
|
3458
|
-
if (typeof block.text === "string") {
|
|
3459
|
-
aggregated += block.text;
|
|
3460
|
-
continue;
|
|
3461
|
-
}
|
|
3462
|
-
if (typeof block.reasoning === "string") {
|
|
3463
|
-
aggregated += block.reasoning;
|
|
3464
|
-
continue;
|
|
3465
|
-
}
|
|
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);
|
|
3466
3308
|
}
|
|
3467
|
-
|
|
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);
|
|
3320
|
+
}
|
|
3321
|
+
return blockIndex;
|
|
3468
3322
|
};
|
|
3469
|
-
const
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
};
|
|
3478
|
-
if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
|
|
3479
|
-
collectFromBlocks(item.summary);
|
|
3480
|
-
return segments.join("").trim();
|
|
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);
|
|
3481
3331
|
};
|
|
3482
|
-
const
|
|
3483
|
-
const
|
|
3484
|
-
const toolName = resolveToolUseName(call);
|
|
3485
|
-
if (!toolName || !toolId) return null;
|
|
3486
|
-
return {
|
|
3487
|
-
type: "tool_use",
|
|
3488
|
-
id: toolId,
|
|
3489
|
-
name: toolName,
|
|
3490
|
-
input: parseFunctionCallArguments(call.arguments)
|
|
3491
|
-
};
|
|
3332
|
+
const closeOpenBlocks = (state, events) => {
|
|
3333
|
+
for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
|
|
3492
3334
|
};
|
|
3493
|
-
const
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3335
|
+
const closeAllOpenBlocks = (state, events) => {
|
|
3336
|
+
closeOpenBlocks(state, events);
|
|
3337
|
+
state.functionCallStateByOutputIndex.clear();
|
|
3338
|
+
};
|
|
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;
|
|
3378
|
+
};
|
|
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: ""
|
|
3501
3387
|
};
|
|
3502
|
-
|
|
3503
|
-
const resolveToolUseName = (call) => {
|
|
3504
|
-
if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
|
|
3505
|
-
return call.name;
|
|
3506
|
-
};
|
|
3507
|
-
const createCompactionThinkingBlock = (item) => {
|
|
3508
|
-
if (!item.id || !item.encrypted_content) return null;
|
|
3388
|
+
if (itemType !== "function_call") return;
|
|
3509
3389
|
return {
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
encrypted_content: item.encrypted_content
|
|
3515
|
-
})
|
|
3390
|
+
outputIndex: rawEvent.output_index,
|
|
3391
|
+
toolCallId: item.call_id,
|
|
3392
|
+
name: resolveToolUseName(item),
|
|
3393
|
+
initialArguments: item.arguments
|
|
3516
3394
|
};
|
|
3517
3395
|
};
|
|
3518
|
-
const
|
|
3519
|
-
if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
|
|
3396
|
+
const stringifyToolSearchArguments = (argumentsValue) => {
|
|
3520
3397
|
try {
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
} catch (error) {
|
|
3525
|
-
consola.warn("Failed to parse function call arguments", {
|
|
3526
|
-
error,
|
|
3527
|
-
rawArguments
|
|
3528
|
-
});
|
|
3398
|
+
return JSON.stringify(formatToolSearchBridgeArguments(argumentsValue));
|
|
3399
|
+
} catch {
|
|
3400
|
+
return;
|
|
3529
3401
|
}
|
|
3530
|
-
return { raw_arguments: rawArguments };
|
|
3531
3402
|
};
|
|
3532
|
-
const
|
|
3533
|
-
|
|
3403
|
+
const responsesUtilsDependencies = {
|
|
3404
|
+
isResponsesApiContextManagementModel,
|
|
3405
|
+
isResponsesApiWebSocketEnabled
|
|
3534
3406
|
};
|
|
3535
|
-
const
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
}];
|
|
3407
|
+
const getResponsesRequestOptions = (payload) => {
|
|
3408
|
+
return {
|
|
3409
|
+
vision: hasVisionInput(payload),
|
|
3410
|
+
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
|
3411
|
+
};
|
|
3541
3412
|
};
|
|
3542
|
-
const
|
|
3543
|
-
const
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
}
|
|
3548
|
-
if (status === "incomplete") {
|
|
3549
|
-
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
3550
|
-
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
3551
|
-
}
|
|
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";
|
|
3552
3418
|
return null;
|
|
3553
3419
|
};
|
|
3554
|
-
const
|
|
3555
|
-
const
|
|
3556
|
-
|
|
3557
|
-
return
|
|
3558
|
-
|
|
3559
|
-
output_tokens: outputTokens,
|
|
3560
|
-
...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
|
|
3561
|
-
};
|
|
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";
|
|
3562
3425
|
};
|
|
3563
|
-
const
|
|
3564
|
-
|
|
3565
|
-
const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
|
|
3566
|
-
const convertToolResultContent = (content) => {
|
|
3567
|
-
if (typeof content === "string") return content;
|
|
3568
|
-
if (Array.isArray(content)) {
|
|
3569
|
-
const result = [];
|
|
3570
|
-
for (const block of content) switch (block.type) {
|
|
3571
|
-
case "text":
|
|
3572
|
-
result.push(createTextContent(block.text));
|
|
3573
|
-
break;
|
|
3574
|
-
case "image":
|
|
3575
|
-
result.push(createImageContent(block));
|
|
3576
|
-
break;
|
|
3577
|
-
case "document":
|
|
3578
|
-
result.push(createFileContent(block));
|
|
3579
|
-
break;
|
|
3580
|
-
case "tool_reference":
|
|
3581
|
-
result.push(createTextContent(`Tool ${block.tool_name} loaded`));
|
|
3582
|
-
break;
|
|
3583
|
-
default: break;
|
|
3584
|
-
}
|
|
3585
|
-
return result;
|
|
3586
|
-
}
|
|
3587
|
-
return "";
|
|
3426
|
+
const hasVisionInput = (payload) => {
|
|
3427
|
+
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
3588
3428
|
};
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
var FunctionCallArgumentsValidationError = class extends Error {
|
|
3593
|
-
constructor(message) {
|
|
3594
|
-
super(message);
|
|
3595
|
-
this.name = "FunctionCallArgumentsValidationError";
|
|
3596
|
-
}
|
|
3429
|
+
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
3430
|
+
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
|
|
3431
|
+
return 5e4;
|
|
3597
3432
|
};
|
|
3598
|
-
const
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
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));
|
|
3441
|
+
};
|
|
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);
|
|
3447
|
+
};
|
|
3448
|
+
const getLatestCompactionMessageIndex = (input) => {
|
|
3449
|
+
for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
|
|
3450
|
+
};
|
|
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"
|
|
3610
3499
|
}
|
|
3500
|
+
];
|
|
3501
|
+
function normalizeCodexModel(model) {
|
|
3502
|
+
const supportsVision = model.input.includes("image");
|
|
3611
3503
|
return {
|
|
3612
|
-
|
|
3613
|
-
|
|
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"
|
|
3614
3537
|
};
|
|
3615
|
-
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
const
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
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;
|
|
3642
3573
|
}
|
|
3643
|
-
|
|
3644
|
-
const
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
const
|
|
3652
|
-
const
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
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
|
|
3588
|
+
});
|
|
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)
|
|
3657
3595
|
});
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
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
|
|
3666
3648
|
});
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
const events = new Array();
|
|
3673
|
-
const item = rawEvent.item;
|
|
3674
|
-
const itemType = item.type;
|
|
3675
|
-
const outputIndex = rawEvent.output_index;
|
|
3676
|
-
if (itemType === "tool_search_call") {
|
|
3677
|
-
const blockIndex = openFunctionCallBlock(state, {
|
|
3678
|
-
outputIndex,
|
|
3679
|
-
toolCallId: item.call_id,
|
|
3680
|
-
name: state.toolSearchName,
|
|
3681
|
-
events
|
|
3649
|
+
if (providerConfig.type === "openai-responses") return await handleOpenAIResponsesProviderMessages(c, {
|
|
3650
|
+
modelConfig,
|
|
3651
|
+
payload,
|
|
3652
|
+
provider,
|
|
3653
|
+
providerConfig
|
|
3682
3654
|
});
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
delta: {
|
|
3689
|
-
type: "input_json_delta",
|
|
3690
|
-
partial_json: finalArguments
|
|
3691
|
-
}
|
|
3692
|
-
});
|
|
3693
|
-
state.blockHasDelta.add(blockIndex);
|
|
3694
|
-
}
|
|
3695
|
-
state.functionCallStateByOutputIndex.delete(outputIndex);
|
|
3696
|
-
return events;
|
|
3697
|
-
}
|
|
3698
|
-
if (itemType === "compaction") {
|
|
3699
|
-
if (!item.id || !item.encrypted_content) return events;
|
|
3700
|
-
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3701
|
-
if (!state.blockHasDelta.has(blockIndex)) events.push({
|
|
3702
|
-
type: "content_block_delta",
|
|
3703
|
-
index: blockIndex,
|
|
3704
|
-
delta: {
|
|
3705
|
-
type: "thinking_delta",
|
|
3706
|
-
thinking: THINKING_TEXT
|
|
3707
|
-
}
|
|
3655
|
+
if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
|
|
3656
|
+
modelConfig,
|
|
3657
|
+
payload,
|
|
3658
|
+
provider,
|
|
3659
|
+
providerConfig
|
|
3708
3660
|
});
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3661
|
+
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
3662
|
+
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
3663
|
+
if (!upstreamResponse.ok) {
|
|
3664
|
+
logger$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
|
|
3719
3674
|
});
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
const signature = (item.encrypted_content ?? "") + "@" + item.id;
|
|
3726
|
-
if (signature) {
|
|
3727
|
-
if (!item.summary || item.summary.length === 0) events.push({
|
|
3728
|
-
type: "content_block_delta",
|
|
3729
|
-
index: blockIndex,
|
|
3730
|
-
delta: {
|
|
3731
|
-
type: "thinking_delta",
|
|
3732
|
-
thinking: THINKING_TEXT
|
|
3733
|
-
}
|
|
3675
|
+
return respondProviderMessagesJson(c, {
|
|
3676
|
+
body: await upstreamResponse.json(),
|
|
3677
|
+
payload,
|
|
3678
|
+
provider,
|
|
3679
|
+
providerConfig
|
|
3734
3680
|
});
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
type: "signature_delta",
|
|
3740
|
-
signature
|
|
3741
|
-
}
|
|
3681
|
+
} catch (error) {
|
|
3682
|
+
logger$4.error("provider.messages.error", {
|
|
3683
|
+
provider,
|
|
3684
|
+
error
|
|
3742
3685
|
});
|
|
3743
|
-
|
|
3686
|
+
throw error;
|
|
3744
3687
|
}
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
const
|
|
3748
|
-
const
|
|
3749
|
-
const
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3688
|
+
}
|
|
3689
|
+
const handleOpenAIResponsesProviderMessages = async (c, options) => {
|
|
3690
|
+
const { payload, provider, providerConfig } = options;
|
|
3691
|
+
const selectedModel = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
|
|
3692
|
+
const responsesPayload = translateAnthropicMessagesToResponsesPayload(payload);
|
|
3693
|
+
applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
|
|
3694
|
+
compactInputByLatestCompaction(responsesPayload);
|
|
3695
|
+
debugJson(logger$4, "provider.messages.responses.request", {
|
|
3696
|
+
payload: responsesPayload,
|
|
3697
|
+
provider
|
|
3755
3698
|
});
|
|
3756
|
-
const
|
|
3757
|
-
if (!
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3699
|
+
const upstreamResponse = providerConfig.name === "codex" ? await forwardCodexResponses(responsesPayload, c.req.raw.headers, providerConfig.baseUrl) : await forwardProviderResponses(providerConfig, responsesPayload, c.req.raw.headers);
|
|
3700
|
+
if (!upstreamResponse.ok) {
|
|
3701
|
+
logger$4.error("Failed to create provider responses", upstreamResponse);
|
|
3702
|
+
throw new HTTPError("Failed to create provider responses", upstreamResponse);
|
|
3703
|
+
}
|
|
3704
|
+
if (responsesPayload.stream) return streamResponsesProviderMessages({
|
|
3705
|
+
c,
|
|
3706
|
+
payload,
|
|
3707
|
+
provider,
|
|
3708
|
+
providerConfig,
|
|
3709
|
+
upstreamResponse
|
|
3710
|
+
});
|
|
3711
|
+
return respondResponsesProviderMessagesJson(c, {
|
|
3712
|
+
body: await upstreamResponse.json(),
|
|
3713
|
+
payload,
|
|
3714
|
+
provider,
|
|
3715
|
+
providerConfig
|
|
3768
3716
|
});
|
|
3769
|
-
state.blockHasDelta.add(blockIndex);
|
|
3770
|
-
return events;
|
|
3771
3717
|
};
|
|
3772
|
-
const
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3718
|
+
const applyModelDefaults = (payload, modelConfig) => {
|
|
3719
|
+
payload.temperature ??= modelConfig?.temperature;
|
|
3720
|
+
payload.top_p ??= modelConfig?.topP;
|
|
3721
|
+
payload.top_k ??= modelConfig?.topK;
|
|
3722
|
+
};
|
|
3723
|
+
const applyMissingExtraBody = (payload, options) => {
|
|
3724
|
+
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
3725
|
+
};
|
|
3726
|
+
const getRequestThinkingBudget = (payload) => {
|
|
3727
|
+
const budget = payload.thinking?.budget_tokens;
|
|
3728
|
+
if (typeof budget !== "number" || !Number.isFinite(budget)) return;
|
|
3729
|
+
return budget;
|
|
3730
|
+
};
|
|
3731
|
+
const applyOpenAICompatibleThinkingBudget = (payload, source) => {
|
|
3732
|
+
const thinkingBudget = getRequestThinkingBudget(source);
|
|
3733
|
+
if (thinkingBudget !== void 0) {
|
|
3734
|
+
payload.thinking_budget = thinkingBudget;
|
|
3735
|
+
return;
|
|
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
|
|
3778
3751
|
});
|
|
3779
|
-
const
|
|
3780
|
-
if (!
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
index: blockIndex,
|
|
3784
|
-
delta: {
|
|
3785
|
-
type: "input_json_delta",
|
|
3786
|
-
partial_json: finalArguments
|
|
3787
|
-
}
|
|
3788
|
-
});
|
|
3789
|
-
state.blockHasDelta.add(blockIndex);
|
|
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);
|
|
3790
3756
|
}
|
|
3791
|
-
|
|
3792
|
-
return
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
const contentIndex = rawEvent.content_index;
|
|
3798
|
-
const deltaText = rawEvent.delta;
|
|
3799
|
-
if (!deltaText) return events;
|
|
3800
|
-
const blockIndex = openTextBlockIfNeeded(state, {
|
|
3801
|
-
outputIndex,
|
|
3802
|
-
contentIndex,
|
|
3803
|
-
events
|
|
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
|
|
3804
3763
|
});
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
type: "text_delta",
|
|
3810
|
-
text: deltaText
|
|
3811
|
-
}
|
|
3764
|
+
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
3765
|
+
body: await upstreamResponse.json(),
|
|
3766
|
+
payload,
|
|
3767
|
+
provider
|
|
3812
3768
|
});
|
|
3813
|
-
state.blockHasDelta.add(blockIndex);
|
|
3814
|
-
return events;
|
|
3815
3769
|
};
|
|
3816
|
-
const
|
|
3817
|
-
const
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3821
|
-
events.push({
|
|
3822
|
-
type: "content_block_delta",
|
|
3823
|
-
index: blockIndex,
|
|
3824
|
-
delta: {
|
|
3825
|
-
type: "thinking_delta",
|
|
3826
|
-
thinking: deltaText
|
|
3827
|
-
}
|
|
3770
|
+
const createOpenAICompatiblePayload = (payload, modelConfig) => {
|
|
3771
|
+
const openAIPayload = translateToOpenAI(payload, {
|
|
3772
|
+
supportPdf: modelConfig?.supportPdf,
|
|
3773
|
+
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
3828
3774
|
});
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
};
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
|
|
3837
|
-
if (text && !state.blockHasDelta.has(blockIndex)) events.push({
|
|
3838
|
-
type: "content_block_delta",
|
|
3839
|
-
index: blockIndex,
|
|
3840
|
-
delta: {
|
|
3841
|
-
type: "thinking_delta",
|
|
3842
|
-
thinking: text
|
|
3843
|
-
}
|
|
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
|
|
3844
3782
|
});
|
|
3845
|
-
|
|
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;
|
|
3846
3788
|
};
|
|
3847
|
-
const
|
|
3848
|
-
const
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
contentIndex,
|
|
3855
|
-
events
|
|
3856
|
-
});
|
|
3857
|
-
if (text && !state.blockHasDelta.has(blockIndex)) events.push({
|
|
3858
|
-
type: "content_block_delta",
|
|
3859
|
-
index: blockIndex,
|
|
3860
|
-
delta: {
|
|
3861
|
-
type: "text_delta",
|
|
3862
|
-
text
|
|
3863
|
-
}
|
|
3864
|
-
});
|
|
3865
|
-
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
|
+
}
|
|
3866
3796
|
};
|
|
3867
|
-
const
|
|
3868
|
-
const
|
|
3869
|
-
const
|
|
3870
|
-
closeAllOpenBlocks(state, events);
|
|
3871
|
-
const anthropic = translateResponsesResultToAnthropic(response);
|
|
3872
|
-
events.push({
|
|
3873
|
-
type: "message_delta",
|
|
3874
|
-
delta: {
|
|
3875
|
-
stop_reason: anthropic.stop_reason,
|
|
3876
|
-
stop_sequence: anthropic.stop_sequence
|
|
3877
|
-
},
|
|
3878
|
-
usage: anthropic.usage
|
|
3879
|
-
}, { type: "message_stop" });
|
|
3880
|
-
state.messageCompleted = true;
|
|
3881
|
-
return events;
|
|
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];
|
|
3882
3800
|
};
|
|
3883
|
-
const
|
|
3884
|
-
const
|
|
3885
|
-
const
|
|
3886
|
-
closeAllOpenBlocks(state, events);
|
|
3887
|
-
const message = response.error?.message ?? "The response failed due to an unknown error.";
|
|
3888
|
-
events.push(buildErrorEvent(message));
|
|
3889
|
-
state.messageCompleted = true;
|
|
3890
|
-
return events;
|
|
3801
|
+
const applyOpenAICompatibleContextCache = (payload) => {
|
|
3802
|
+
const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
|
|
3803
|
+
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
3891
3804
|
};
|
|
3892
|
-
const
|
|
3893
|
-
const
|
|
3894
|
-
|
|
3895
|
-
|
|
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);
|
|
3896
3810
|
};
|
|
3897
|
-
const
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
return events;
|
|
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;
|
|
3903
3816
|
};
|
|
3904
|
-
const
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
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
|
+
});
|
|
3922
3902
|
}
|
|
3923
3903
|
}
|
|
3924
|
-
|
|
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
|
+
});
|
|
3925
3914
|
};
|
|
3926
|
-
const
|
|
3927
|
-
|
|
3928
|
-
const
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3915
|
+
const streamResponsesProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
|
|
3916
|
+
logger$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
|
+
});
|
|
3943
3945
|
}
|
|
3946
|
+
}
|
|
3947
|
+
if (!streamState.messageCompleted) {
|
|
3948
|
+
const errorEvent = buildErrorEvent(`${provider} stream ended without a completion event`);
|
|
3949
|
+
await stream.writeSSE({
|
|
3950
|
+
event: errorEvent.type,
|
|
3951
|
+
data: JSON.stringify(errorEvent)
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
recordUsage(usage);
|
|
3955
|
+
});
|
|
3956
|
+
};
|
|
3957
|
+
const parseOpenAICompatibleStreamChunk = (data) => {
|
|
3958
|
+
try {
|
|
3959
|
+
return JSON.parse(data);
|
|
3960
|
+
} catch (error) {
|
|
3961
|
+
logger$4.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
3962
|
+
data,
|
|
3963
|
+
error
|
|
3944
3964
|
});
|
|
3945
|
-
|
|
3965
|
+
return null;
|
|
3946
3966
|
}
|
|
3947
|
-
return blockIndex;
|
|
3948
3967
|
};
|
|
3949
|
-
const
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
events.push({
|
|
3960
|
-
type: "content_block_start",
|
|
3961
|
-
index: blockIndex,
|
|
3962
|
-
content_block: {
|
|
3963
|
-
type: "thinking",
|
|
3964
|
-
thinking: ""
|
|
3965
|
-
}
|
|
3968
|
+
const parseResponsesProviderStreamChunk = (data, providerConfig) => {
|
|
3969
|
+
try {
|
|
3970
|
+
const parsed = JSON.parse(data);
|
|
3971
|
+
if (providerConfig.name === "codex") logCodexRateLimitsEvent(parsed);
|
|
3972
|
+
return providerConfig.name === "codex" ? normalizeCodexResponsesEvent(parsed) : parsed;
|
|
3973
|
+
} catch (error) {
|
|
3974
|
+
logger$4.error("provider.messages.responses.parse_chunk_error", {
|
|
3975
|
+
provider: providerConfig.name,
|
|
3976
|
+
data,
|
|
3977
|
+
error
|
|
3966
3978
|
});
|
|
3967
|
-
|
|
3979
|
+
return null;
|
|
3968
3980
|
}
|
|
3969
|
-
return blockIndex;
|
|
3970
|
-
};
|
|
3971
|
-
const closeBlockIfOpen = (state, blockIndex, events) => {
|
|
3972
|
-
if (!state.openBlocks.has(blockIndex)) return;
|
|
3973
|
-
events.push({
|
|
3974
|
-
type: "content_block_stop",
|
|
3975
|
-
index: blockIndex
|
|
3976
|
-
});
|
|
3977
|
-
state.openBlocks.delete(blockIndex);
|
|
3978
|
-
state.blockHasDelta.delete(blockIndex);
|
|
3979
|
-
};
|
|
3980
|
-
const closeOpenBlocks = (state, events) => {
|
|
3981
|
-
for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
|
|
3982
|
-
};
|
|
3983
|
-
const closeAllOpenBlocks = (state, events) => {
|
|
3984
|
-
closeOpenBlocks(state, events);
|
|
3985
|
-
state.functionCallStateByOutputIndex.clear();
|
|
3986
3981
|
};
|
|
3987
|
-
const
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
type
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
3982
|
+
const parseProviderStreamEvent = (data, providerConfig) => {
|
|
3983
|
+
try {
|
|
3984
|
+
const parsed = JSON.parse(data);
|
|
3985
|
+
if (parsed.type === "message_start") {
|
|
3986
|
+
adjustInputTokens(providerConfig, parsed.message.usage);
|
|
3987
|
+
return {
|
|
3988
|
+
data: JSON.stringify(parsed),
|
|
3989
|
+
model: parsed.message.model,
|
|
3990
|
+
usage: normalizeAnthropicUsage(parsed.message.usage)
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
if (parsed.type === "message_delta") {
|
|
3994
|
+
adjustInputTokens(providerConfig, parsed.usage);
|
|
3995
|
+
return {
|
|
3996
|
+
data: JSON.stringify(parsed),
|
|
3997
|
+
usage: normalizeAnthropicUsage(parsed.usage)
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
return {
|
|
4001
|
+
data: JSON.stringify(parsed),
|
|
4002
|
+
usage: {}
|
|
4006
4003
|
};
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
closeOpenBlocks(state, events);
|
|
4012
|
-
events.push({
|
|
4013
|
-
type: "content_block_start",
|
|
4014
|
-
index: blockIndex,
|
|
4015
|
-
content_block: {
|
|
4016
|
-
type: "tool_use",
|
|
4017
|
-
id: functionCallState.toolCallId,
|
|
4018
|
-
name: functionCallState.name,
|
|
4019
|
-
input: {}
|
|
4020
|
-
}
|
|
4004
|
+
} catch (error) {
|
|
4005
|
+
logger$4.error("provider.messages.streaming.adjust_tokens_error", {
|
|
4006
|
+
error,
|
|
4007
|
+
originalData: data
|
|
4021
4008
|
});
|
|
4022
|
-
|
|
4009
|
+
return null;
|
|
4023
4010
|
}
|
|
4024
|
-
return blockIndex;
|
|
4025
4011
|
};
|
|
4026
|
-
const
|
|
4027
|
-
const
|
|
4028
|
-
const
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
initialArguments: ""
|
|
4034
|
-
};
|
|
4035
|
-
if (itemType !== "function_call") return;
|
|
4036
|
-
return {
|
|
4037
|
-
outputIndex: rawEvent.output_index,
|
|
4038
|
-
toolCallId: item.call_id,
|
|
4039
|
-
name: resolveToolUseName(item),
|
|
4040
|
-
initialArguments: item.arguments
|
|
4041
|
-
};
|
|
4012
|
+
const respondProviderMessagesJson = (c, options) => {
|
|
4013
|
+
const { body, payload, provider, providerConfig } = options;
|
|
4014
|
+
const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
|
|
4015
|
+
adjustInputTokens(providerConfig, body.usage);
|
|
4016
|
+
recordUsage(normalizeAnthropicUsage(body.usage));
|
|
4017
|
+
debugJson(logger$4, "provider.messages.no_stream result:", body);
|
|
4018
|
+
return c.json(body);
|
|
4042
4019
|
};
|
|
4043
|
-
const
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4020
|
+
const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
|
|
4021
|
+
const { body, payload, provider } = options;
|
|
4022
|
+
createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
|
|
4023
|
+
const anthropicResponse = translateToAnthropic(body);
|
|
4024
|
+
debugJson(logger$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);
|
|
4049
4045
|
};
|
|
4050
4046
|
//#endregion
|
|
4051
4047
|
//#region src/services/copilot/create-messages.ts
|
|
@@ -4775,11 +4771,19 @@ const providerModelRoutes = new Hono();
|
|
|
4775
4771
|
providerModelRoutes.get("/", async (c) => {
|
|
4776
4772
|
const provider = c.req.param("provider") ?? "";
|
|
4777
4773
|
try {
|
|
4778
|
-
const providerConfig =
|
|
4774
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
4779
4775
|
if (!providerConfig) return c.json({ error: {
|
|
4780
4776
|
message: `Provider '${provider}' not found or disabled`,
|
|
4781
4777
|
type: "invalid_request_error"
|
|
4782
4778
|
} }, 404);
|
|
4779
|
+
if (providerConfig.name === "codex") {
|
|
4780
|
+
const models = getModels();
|
|
4781
|
+
return c.json({
|
|
4782
|
+
object: "list",
|
|
4783
|
+
data: models.data,
|
|
4784
|
+
has_more: false
|
|
4785
|
+
});
|
|
4786
|
+
}
|
|
4783
4787
|
const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers);
|
|
4784
4788
|
logger$2.debug("provider.models.response", {
|
|
4785
4789
|
provider,
|
|
@@ -4839,9 +4843,17 @@ const responsesHandlerDependencies = {
|
|
|
4839
4843
|
isResponsesApiWebSearchEnabled
|
|
4840
4844
|
};
|
|
4841
4845
|
const handleResponses = async (c) => {
|
|
4842
|
-
await responsesHandlerDependencies.checkRateLimit(state);
|
|
4843
4846
|
const payload = await c.req.json();
|
|
4844
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);
|
|
4845
4857
|
const requestId = generateRequestIdFromPayload({ messages: payload.input });
|
|
4846
4858
|
logger$1.debug("Generated request ID:", requestId);
|
|
4847
4859
|
const sessionId = getUUID(requestId);
|
|
@@ -4897,6 +4909,30 @@ const handleResponses = async (c) => {
|
|
|
4897
4909
|
recordUsage(normalizeResponsesUsage(response.usage));
|
|
4898
4910
|
return c.json(response);
|
|
4899
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
|
+
};
|
|
4900
4936
|
const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
4901
4937
|
const isStreamingRequested = (payload) => Boolean(payload.stream);
|
|
4902
4938
|
const parseResponsesStreamEvent = (chunk) => {
|
|
@@ -4908,6 +4944,51 @@ const parseResponsesStreamEvent = (chunk) => {
|
|
|
4908
4944
|
return null;
|
|
4909
4945
|
}
|
|
4910
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
|
+
};
|
|
4911
4992
|
const removeWebSearchTool = (payload) => {
|
|
4912
4993
|
if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
|
|
4913
4994
|
payload.tools = payload.tools.filter((t) => {
|
|
@@ -5040,4 +5121,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
5040
5121
|
//#endregion
|
|
5041
5122
|
export { server };
|
|
5042
5123
|
|
|
5043
|
-
//# sourceMappingURL=server-
|
|
5124
|
+
//# sourceMappingURL=server-D5O9IzAY.js.map
|