@jeffreycao/copilot-api 1.14.23 → 1.15.2
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 +7 -5
- package/README.zh-CN.md +7 -5
- package/dist/auth-CRonPTZQ.js +2 -0
- package/dist/{auth-Dx98-OPn.js → auth-CskgMMwz.js} +11 -4
- package/dist/auth-CskgMMwz.js.map +1 -0
- package/dist/{config-NTX23mAe.js → config-B3hsNlmB.js} +6 -2
- package/dist/config-B3hsNlmB.js.map +1 -0
- package/dist/{debug-6pwRAzyw.js → debug-CVd8Dq56.js} +2 -2
- package/dist/{debug-6pwRAzyw.js.map → debug-CVd8Dq56.js.map} +1 -1
- package/dist/main.js +3 -3
- package/dist/{models-D-Rwtv0H.js → models-_FSj4UOe.js} +2 -2
- package/dist/{models-D-Rwtv0H.js.map → models-_FSj4UOe.js.map} +1 -1
- package/dist/{server-D8An7dpg.js → server-DWY7PmxG.js} +1254 -549
- package/dist/server-DWY7PmxG.js.map +1 -0
- package/dist/{start-C1hWSQR4.js → start-YxEMb8V2.js} +6 -6
- package/dist/{start-C1hWSQR4.js.map → start-YxEMb8V2.js.map} +1 -1
- package/dist/{token-BjW645tq.js → token-DArgO8vi.js} +2 -2
- package/dist/{token-BjW645tq.js.map → token-DArgO8vi.js.map} +1 -1
- package/package.json +4 -3
- package/dist/auth-CitK8MfX.js +0 -2
- package/dist/auth-Dx98-OPn.js.map +0 -1
- package/dist/config-NTX23mAe.js.map +0 -1
- package/dist/server-D8An7dpg.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { D as
|
|
2
|
-
import { B as HTTPError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as requestContext, I as resolveTraceId$1, L as createPooledWebSocketStream, M as buildCodexRequestHeaders, N as forwardCodexResponses, O as compactSystemPromptStarts, P as generateTraceId, R as createWebSocketUrl, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, V as forwardError, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as getCopilotUsage, h as parseUserIdMetadata, j as CODEX_API_BASE_URL, m as isNullish, p as getUUID, r as setupCodexToken, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as state } from "./token-
|
|
1
|
+
import { A as PATHS, D as resolveProviderAuthType, E as resolveMappedModel, O as setModelMappings, S as listEnabledProviders, T as resolveEffectiveProviderType, _ as isGpt56OrAbove, a as getConfig, b as isResponsesApiWebSocketEnabled, c as getModelMappings, d as getRawProviderConfig, f as getReasoningEffortForModel, g as isContextManagementEnabledForResponses, h as isContextManagementEnabledForMessages, i as getClaudeTokenMultiplier, l as getModelResponsesApiCompactThreshold$1, m as isAlphaSearchCodexPriorityEnabled, n as getAnthropicApiKey, o as getExtraPromptForModel, p as getSmallModel, r as getClaudeAutoModel, s as getMessageApiWebSearchModel, u as getProviderConfig, v as isMessagesApiEnabled, y as isResponsesApiWebSearchEnabled } from "./config-B3hsNlmB.js";
|
|
2
|
+
import { B as HTTPError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as requestContext, I as resolveTraceId$1, L as createPooledWebSocketStream, M as buildCodexRequestHeaders, N as forwardCodexResponses, O as compactSystemPromptStarts, P as generateTraceId, R as createWebSocketUrl, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, V as forwardError, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as getCopilotUsage, h as parseUserIdMetadata, j as CODEX_API_BASE_URL, m as isNullish, p as getUUID, r as setupCodexToken, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as state } from "./token-DArgO8vi.js";
|
|
3
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-Ds1vbmGG.js";
|
|
4
|
-
import { i as toClientModelId, r as normalizeSdkModelId, t as findEndpointModel } from "./models-
|
|
4
|
+
import { i as toClientModelId, r as normalizeSdkModelId, t as findEndpointModel } from "./models-_FSj4UOe.js";
|
|
5
5
|
import consola from "consola";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import fs, { readFileSync } from "node:fs";
|
|
@@ -193,22 +193,26 @@ function registerProcessCleanup(handler) {
|
|
|
193
193
|
//#region src/lib/logger.ts
|
|
194
194
|
const LOG_RETENTION_MS = 10080 * 60 * 1e3;
|
|
195
195
|
const CLEANUP_INTERVAL_MS = 1440 * 60 * 1e3;
|
|
196
|
-
const
|
|
196
|
+
const LOG_DIR_ENV = "COPILOT_API_LOG_DIR";
|
|
197
|
+
const DEFAULT_LOG_DIR = path.join(PATHS.APP_DIR, "logs");
|
|
197
198
|
const FLUSH_INTERVAL_MS = 1e3;
|
|
198
199
|
const MAX_BUFFER_SIZE = 100;
|
|
200
|
+
const getLogDir = () => process.env[LOG_DIR_ENV]?.trim() || DEFAULT_LOG_DIR;
|
|
199
201
|
const logStreams = /* @__PURE__ */ new Map();
|
|
200
202
|
const logBuffers = /* @__PURE__ */ new Map();
|
|
201
203
|
let runtimeInitialized = false;
|
|
202
204
|
let flushInterval;
|
|
203
205
|
let cleanupInterval;
|
|
204
206
|
const ensureLogDirectory = () => {
|
|
205
|
-
|
|
207
|
+
const logDir = getLogDir();
|
|
208
|
+
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
|
206
209
|
};
|
|
207
210
|
const cleanupOldLogs = () => {
|
|
208
|
-
|
|
211
|
+
const logDir = getLogDir();
|
|
212
|
+
if (!fs.existsSync(logDir)) return;
|
|
209
213
|
const now = Date.now();
|
|
210
|
-
for (const entry of fs.readdirSync(
|
|
211
|
-
const filePath = path.join(
|
|
214
|
+
for (const entry of fs.readdirSync(logDir)) {
|
|
215
|
+
const filePath = path.join(logDir, entry);
|
|
212
216
|
let stats;
|
|
213
217
|
try {
|
|
214
218
|
stats = fs.statSync(filePath);
|
|
@@ -247,7 +251,13 @@ const flushBuffer = (filePath) => {
|
|
|
247
251
|
const flushAllBuffers = () => {
|
|
248
252
|
for (const filePath of logBuffers.keys()) flushBuffer(filePath);
|
|
249
253
|
};
|
|
250
|
-
const
|
|
254
|
+
const flushAndCloseLogStreams = () => {
|
|
255
|
+
flushAllBuffers();
|
|
256
|
+
for (const stream of logStreams.values()) stream.end();
|
|
257
|
+
logStreams.clear();
|
|
258
|
+
logBuffers.clear();
|
|
259
|
+
};
|
|
260
|
+
const shutdownLoggerRuntime = () => {
|
|
251
261
|
if (flushInterval) {
|
|
252
262
|
clearInterval(flushInterval);
|
|
253
263
|
flushInterval = void 0;
|
|
@@ -256,10 +266,8 @@ const cleanup = () => {
|
|
|
256
266
|
clearInterval(cleanupInterval);
|
|
257
267
|
cleanupInterval = void 0;
|
|
258
268
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
logStreams.clear();
|
|
262
|
-
logBuffers.clear();
|
|
269
|
+
flushAndCloseLogStreams();
|
|
270
|
+
runtimeInitialized = false;
|
|
263
271
|
};
|
|
264
272
|
const initializeLoggerRuntime = () => {
|
|
265
273
|
if (runtimeInitialized) return;
|
|
@@ -270,7 +278,7 @@ const initializeLoggerRuntime = () => {
|
|
|
270
278
|
maybeUnref(flushInterval);
|
|
271
279
|
cleanupInterval = setInterval(cleanupOldLogs, CLEANUP_INTERVAL_MS);
|
|
272
280
|
maybeUnref(cleanupInterval);
|
|
273
|
-
registerProcessCleanup(
|
|
281
|
+
registerProcessCleanup(shutdownLoggerRuntime);
|
|
274
282
|
};
|
|
275
283
|
const getLogStream = (filePath) => {
|
|
276
284
|
initializeLoggerRuntime();
|
|
@@ -319,7 +327,7 @@ const createHandlerLogger = (name) => {
|
|
|
319
327
|
const date = logObj.date;
|
|
320
328
|
const dateKey = date.toLocaleDateString("sv-SE");
|
|
321
329
|
const timestamp = date.toLocaleString("sv-SE", { hour12: false });
|
|
322
|
-
const filePath = path.join(
|
|
330
|
+
const filePath = path.join(getLogDir(), `${sanitizedName}-${dateKey}.log`);
|
|
323
331
|
const message = formatArgs(logObj.args);
|
|
324
332
|
const traceIdStr = traceId ? ` [${traceId}]` : "";
|
|
325
333
|
appendLine(filePath, `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${traceIdStr}${message ? ` ${message}` : ""}`);
|
|
@@ -327,203 +335,6 @@ const createHandlerLogger = (name) => {
|
|
|
327
335
|
return instance;
|
|
328
336
|
};
|
|
329
337
|
//#endregion
|
|
330
|
-
//#region src/lib/provider-resolver.ts
|
|
331
|
-
function isMissingCodexCredentialsError(error) {
|
|
332
|
-
return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
|
|
333
|
-
}
|
|
334
|
-
async function resolveProviderConfig(providerName) {
|
|
335
|
-
const normalizedProviderName = providerName.trim();
|
|
336
|
-
if (!normalizedProviderName) return null;
|
|
337
|
-
if (normalizedProviderName === "codex") {
|
|
338
|
-
if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
|
|
339
|
-
try {
|
|
340
|
-
await setupCodexToken();
|
|
341
|
-
} catch (error) {
|
|
342
|
-
if (isMissingCodexCredentialsError(error)) return null;
|
|
343
|
-
throw error;
|
|
344
|
-
}
|
|
345
|
-
const providerConfig = getProviderConfig(normalizedProviderName);
|
|
346
|
-
if (!providerConfig) return null;
|
|
347
|
-
return {
|
|
348
|
-
...providerConfig,
|
|
349
|
-
apiKey: state.codexAccessToken ?? providerConfig.apiKey
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
return getProviderConfig(normalizedProviderName);
|
|
353
|
-
}
|
|
354
|
-
//#endregion
|
|
355
|
-
//#region src/services/codex/alpha-search.ts
|
|
356
|
-
const CODEX_ALPHA_SEARCH_URL = `${CODEX_API_BASE_URL}/codex/alpha/search`;
|
|
357
|
-
function resolveCodexAlphaSearchUrl(requestUrl) {
|
|
358
|
-
const upstreamUrl = new URL(CODEX_ALPHA_SEARCH_URL);
|
|
359
|
-
upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
|
|
360
|
-
return upstreamUrl.toString();
|
|
361
|
-
}
|
|
362
|
-
async function forwardCodexAlphaSearch(request) {
|
|
363
|
-
const headers = buildCodexRequestHeaders(request.headers);
|
|
364
|
-
if (!headers.has("accept")) headers.set("accept", "application/json");
|
|
365
|
-
const body = await request.arrayBuffer();
|
|
366
|
-
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
367
|
-
return await fetch(resolveCodexAlphaSearchUrl(request.url), {
|
|
368
|
-
method: "POST",
|
|
369
|
-
headers,
|
|
370
|
-
body
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
//#endregion
|
|
374
|
-
//#region src/services/providers/provider-proxy.ts
|
|
375
|
-
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
376
|
-
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
377
|
-
const STRIPPED_RESPONSE_HEADERS = [
|
|
378
|
-
"connection",
|
|
379
|
-
"content-encoding",
|
|
380
|
-
"content-length",
|
|
381
|
-
"keep-alive",
|
|
382
|
-
"proxy-authenticate",
|
|
383
|
-
"proxy-authorization",
|
|
384
|
-
"te",
|
|
385
|
-
"trailer",
|
|
386
|
-
"transfer-encoding",
|
|
387
|
-
"upgrade"
|
|
388
|
-
];
|
|
389
|
-
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
390
|
-
const authHeaders = {};
|
|
391
|
-
if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
392
|
-
else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
393
|
-
const headers = {
|
|
394
|
-
"content-type": "application/json",
|
|
395
|
-
accept: "application/json",
|
|
396
|
-
...authHeaders
|
|
397
|
-
};
|
|
398
|
-
for (const headerName of SHARED_FORWARDABLE_HEADERS) {
|
|
399
|
-
const headerValue = requestHeaders.get(headerName);
|
|
400
|
-
if (headerValue) headers[headerName] = headerValue;
|
|
401
|
-
}
|
|
402
|
-
if (providerConfig.type !== "anthropic") return headers;
|
|
403
|
-
for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
|
|
404
|
-
const headerValue = requestHeaders.get(headerName);
|
|
405
|
-
if (headerValue) headers[headerName] = headerValue;
|
|
406
|
-
}
|
|
407
|
-
return headers;
|
|
408
|
-
}
|
|
409
|
-
function createProviderProxyResponse(upstreamResponse, body) {
|
|
410
|
-
const headers = new Headers(upstreamResponse.headers);
|
|
411
|
-
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
412
|
-
return new Response(body ?? upstreamResponse.body, {
|
|
413
|
-
headers,
|
|
414
|
-
status: upstreamResponse.status,
|
|
415
|
-
statusText: upstreamResponse.statusText
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
419
|
-
consola.log(`<-- model: ${payload.model}`);
|
|
420
|
-
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
421
|
-
method: "POST",
|
|
422
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
423
|
-
body: JSON.stringify(payload)
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
427
|
-
consola.log(`<-- model: ${payload.model}`);
|
|
428
|
-
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
429
|
-
method: "POST",
|
|
430
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
431
|
-
body: JSON.stringify(payload)
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
|
|
435
|
-
consola.log(`<-- model: ${payload.model}`);
|
|
436
|
-
return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
|
|
437
|
-
method: "POST",
|
|
438
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
439
|
-
body: JSON.stringify(payload)
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
443
|
-
return await fetch(`${providerConfig.baseUrl}/v1/models`, {
|
|
444
|
-
method: "GET",
|
|
445
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
/** Align with Codex images: long-running generation/edits need a generous cap. */
|
|
449
|
-
const PROVIDER_IMAGES_TIMEOUT_MS = 900 * 1e3;
|
|
450
|
-
const providerImagesDispatcher = { dispatch(options, handler) {
|
|
451
|
-
return getGlobalDispatcher().dispatch({
|
|
452
|
-
...options,
|
|
453
|
-
bodyTimeout: PROVIDER_IMAGES_TIMEOUT_MS,
|
|
454
|
-
headersTimeout: PROVIDER_IMAGES_TIMEOUT_MS
|
|
455
|
-
}, handler);
|
|
456
|
-
} };
|
|
457
|
-
function resolveProviderRequestUrl(providerConfig, requestUrl, path) {
|
|
458
|
-
const upstreamUrl = new URL(`${providerConfig.baseUrl}${path}`);
|
|
459
|
-
upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
|
|
460
|
-
return upstreamUrl.toString();
|
|
461
|
-
}
|
|
462
|
-
async function forwardProviderAlphaSearch(providerConfig, request) {
|
|
463
|
-
const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
|
|
464
|
-
const body = await request.arrayBuffer();
|
|
465
|
-
return await fetch(resolveProviderRequestUrl(providerConfig, request.url, "/v1/alpha/search"), {
|
|
466
|
-
method: "POST",
|
|
467
|
-
headers,
|
|
468
|
-
body
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
async function forwardProviderImages(providerConfig, request, operation) {
|
|
472
|
-
const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
|
|
473
|
-
const contentType = request.headers.get("content-type");
|
|
474
|
-
if (contentType) headers["content-type"] = contentType;
|
|
475
|
-
else if (operation === "edits") delete headers["content-type"];
|
|
476
|
-
const init = {
|
|
477
|
-
method: "POST",
|
|
478
|
-
headers,
|
|
479
|
-
body: request.body,
|
|
480
|
-
duplex: "half",
|
|
481
|
-
signal: AbortSignal.timeout(PROVIDER_IMAGES_TIMEOUT_MS)
|
|
482
|
-
};
|
|
483
|
-
const upstreamUrl = resolveProviderRequestUrl(providerConfig, request.url, `/v1/images/${operation}`);
|
|
484
|
-
if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
|
|
485
|
-
return await fetch$1(upstreamUrl, {
|
|
486
|
-
...init,
|
|
487
|
-
dispatcher: providerImagesDispatcher
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
//#endregion
|
|
491
|
-
//#region src/routes/alpha-search/route.ts
|
|
492
|
-
const logger$14 = createHandlerLogger("alpha-search-handler");
|
|
493
|
-
const alphaSearchRoutes = new Hono();
|
|
494
|
-
function parseDebugBody(body) {
|
|
495
|
-
try {
|
|
496
|
-
return JSON.parse(body);
|
|
497
|
-
} catch {
|
|
498
|
-
return body;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Handles Codex alpha-search proxying. Pass `resolvedProviderConfig` when the
|
|
503
|
-
* caller already resolved the codex provider to avoid a second resolve.
|
|
504
|
-
*/
|
|
505
|
-
async function handleCodexAlphaSearch(c, resolvedProviderConfig) {
|
|
506
|
-
if (!(resolvedProviderConfig ?? await resolveProviderConfig("codex"))) return c.json({ error: {
|
|
507
|
-
message: "Provider 'codex' not found or disabled",
|
|
508
|
-
type: "invalid_request_error"
|
|
509
|
-
} }, 404);
|
|
510
|
-
await debugJsonAsync(logger$14, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await c.req.raw.clone().text()) }));
|
|
511
|
-
const upstreamResponse = await forwardCodexAlphaSearch(c.req.raw);
|
|
512
|
-
await debugJsonAsync(logger$14, "alpha_search.codex.response", async () => ({
|
|
513
|
-
body: parseDebugBody(await upstreamResponse.clone().text()),
|
|
514
|
-
statusCode: upstreamResponse.status
|
|
515
|
-
}));
|
|
516
|
-
return createProviderProxyResponse(upstreamResponse);
|
|
517
|
-
}
|
|
518
|
-
alphaSearchRoutes.post("/", async (c) => {
|
|
519
|
-
try {
|
|
520
|
-
return await handleCodexAlphaSearch(c);
|
|
521
|
-
} catch (error) {
|
|
522
|
-
logger$14.error("alpha_search.codex.error", { error });
|
|
523
|
-
return await forwardError(c, error);
|
|
524
|
-
}
|
|
525
|
-
});
|
|
526
|
-
//#endregion
|
|
527
338
|
//#region src/lib/provider-model.ts
|
|
528
339
|
const parseProviderModelAlias = (model) => {
|
|
529
340
|
const separatorIndex = model.indexOf("/");
|
|
@@ -554,6 +365,31 @@ const createFallbackModel = (modelId) => ({
|
|
|
554
365
|
version: "unknown"
|
|
555
366
|
});
|
|
556
367
|
//#endregion
|
|
368
|
+
//#region src/lib/provider-resolver.ts
|
|
369
|
+
function isMissingCodexCredentialsError(error) {
|
|
370
|
+
return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
|
|
371
|
+
}
|
|
372
|
+
async function resolveProviderConfig(providerName) {
|
|
373
|
+
const normalizedProviderName = providerName.trim();
|
|
374
|
+
if (!normalizedProviderName) return null;
|
|
375
|
+
if (normalizedProviderName === "codex") {
|
|
376
|
+
if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
|
|
377
|
+
try {
|
|
378
|
+
await setupCodexToken();
|
|
379
|
+
} catch (error) {
|
|
380
|
+
if (isMissingCodexCredentialsError(error)) return null;
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
const providerConfig = getProviderConfig(normalizedProviderName);
|
|
384
|
+
if (!providerConfig) return null;
|
|
385
|
+
return {
|
|
386
|
+
...providerConfig,
|
|
387
|
+
apiKey: state.codexAccessToken ?? providerConfig.apiKey
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
return getProviderConfig(normalizedProviderName);
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
557
393
|
//#region src/lib/event-bus.ts
|
|
558
394
|
var EventBus = class {
|
|
559
395
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -1203,6 +1039,7 @@ const BUILTIN_PROVIDER_CURRENCIES = {
|
|
|
1203
1039
|
codex: "USD",
|
|
1204
1040
|
dashscope: "CNY",
|
|
1205
1041
|
deepseek: "CNY",
|
|
1042
|
+
kimi: "USD",
|
|
1206
1043
|
"opencode-go": "USD"
|
|
1207
1044
|
};
|
|
1208
1045
|
const BUILTIN_PROVIDER_PRICING = {
|
|
@@ -1335,7 +1172,12 @@ const BUILTIN_PROVIDER_PRICING = {
|
|
|
1335
1172
|
input: 6,
|
|
1336
1173
|
maxInputTokens: 1e6,
|
|
1337
1174
|
output: 24
|
|
1338
|
-
}] }
|
|
1175
|
+
}] },
|
|
1176
|
+
"kimi/kimi-k3": {
|
|
1177
|
+
cachedInput: 2,
|
|
1178
|
+
input: 20,
|
|
1179
|
+
output: 100
|
|
1180
|
+
}
|
|
1339
1181
|
},
|
|
1340
1182
|
deepseek: {
|
|
1341
1183
|
"deepseek-v4-flash": {
|
|
@@ -1453,6 +1295,18 @@ const BUILTIN_PROVIDER_PRICING = {
|
|
|
1453
1295
|
maxInputTokens: 512e3,
|
|
1454
1296
|
output: 2.4
|
|
1455
1297
|
}] }
|
|
1298
|
+
},
|
|
1299
|
+
kimi: {
|
|
1300
|
+
k3: {
|
|
1301
|
+
cachedInput: .3,
|
|
1302
|
+
input: 3,
|
|
1303
|
+
output: 15
|
|
1304
|
+
},
|
|
1305
|
+
"k3-256k": {
|
|
1306
|
+
cachedInput: .3,
|
|
1307
|
+
input: 3,
|
|
1308
|
+
output: 15
|
|
1309
|
+
}
|
|
1456
1310
|
}
|
|
1457
1311
|
};
|
|
1458
1312
|
function resolveTokenUsageCost(input) {
|
|
@@ -1671,24 +1525,1151 @@ function normalizeOptionalCost(value) {
|
|
|
1671
1525
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1672
1526
|
}
|
|
1673
1527
|
//#endregion
|
|
1674
|
-
//#region src/
|
|
1675
|
-
const
|
|
1676
|
-
const
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1528
|
+
//#region src/routes/alpha-search/alpha-search-types.ts
|
|
1529
|
+
const unsignedInteger = z.number().int().nonnegative();
|
|
1530
|
+
const passthroughRecord = z.record(z.string(), z.unknown());
|
|
1531
|
+
const searchQuerySchema = z.object({
|
|
1532
|
+
q: z.string(),
|
|
1533
|
+
recency: unsignedInteger.optional(),
|
|
1534
|
+
domains: z.array(z.string()).optional()
|
|
1535
|
+
}).loose();
|
|
1536
|
+
const openOperationSchema = z.object({
|
|
1537
|
+
ref_id: z.string(),
|
|
1538
|
+
lineno: unsignedInteger.optional()
|
|
1539
|
+
}).loose();
|
|
1540
|
+
const clickOperationSchema = z.object({
|
|
1541
|
+
ref_id: z.string(),
|
|
1542
|
+
id: unsignedInteger
|
|
1543
|
+
}).loose();
|
|
1544
|
+
const findOperationSchema = z.object({
|
|
1545
|
+
ref_id: z.string(),
|
|
1546
|
+
pattern: z.string()
|
|
1547
|
+
}).loose();
|
|
1548
|
+
const screenshotOperationSchema = z.object({
|
|
1549
|
+
ref_id: z.string(),
|
|
1550
|
+
pageno: unsignedInteger
|
|
1551
|
+
}).loose();
|
|
1552
|
+
const financeOperationSchema = z.object({
|
|
1553
|
+
ticker: z.string(),
|
|
1554
|
+
type: z.enum([
|
|
1555
|
+
"equity",
|
|
1556
|
+
"fund",
|
|
1557
|
+
"crypto",
|
|
1558
|
+
"index"
|
|
1559
|
+
]),
|
|
1560
|
+
market: z.string().optional()
|
|
1561
|
+
}).loose();
|
|
1562
|
+
const weatherOperationSchema = z.object({
|
|
1563
|
+
location: z.string(),
|
|
1564
|
+
start: z.string().optional(),
|
|
1565
|
+
duration: unsignedInteger.optional()
|
|
1566
|
+
}).loose();
|
|
1567
|
+
const sportsOperationSchema = z.object({
|
|
1568
|
+
tool: z.literal("sports").optional(),
|
|
1569
|
+
fn: z.enum(["schedule", "standings"]),
|
|
1570
|
+
league: z.enum([
|
|
1571
|
+
"nba",
|
|
1572
|
+
"wnba",
|
|
1573
|
+
"nfl",
|
|
1574
|
+
"nhl",
|
|
1575
|
+
"mlb",
|
|
1576
|
+
"epl",
|
|
1577
|
+
"ncaamb",
|
|
1578
|
+
"ncaawb",
|
|
1579
|
+
"ipl"
|
|
1580
|
+
]),
|
|
1581
|
+
team: z.string().optional(),
|
|
1582
|
+
opponent: z.string().optional(),
|
|
1583
|
+
date_from: z.string().optional(),
|
|
1584
|
+
date_to: z.string().optional(),
|
|
1585
|
+
num_games: unsignedInteger.optional(),
|
|
1586
|
+
locale: z.string().optional()
|
|
1587
|
+
}).loose();
|
|
1588
|
+
const timeOperationSchema = z.object({ utc_offset: z.string().regex(/^[+-](?:[01]\d|2[0-3]):[0-5]\d$/u) }).loose();
|
|
1589
|
+
const alphaSearchCommandsSchema = z.object({
|
|
1590
|
+
search_query: z.array(searchQuerySchema).optional(),
|
|
1591
|
+
image_query: z.array(searchQuerySchema).optional(),
|
|
1592
|
+
open: z.array(openOperationSchema).optional(),
|
|
1593
|
+
click: z.array(clickOperationSchema).optional(),
|
|
1594
|
+
find: z.array(findOperationSchema).optional(),
|
|
1595
|
+
screenshot: z.array(screenshotOperationSchema).optional(),
|
|
1596
|
+
finance: z.array(financeOperationSchema).optional(),
|
|
1597
|
+
weather: z.array(weatherOperationSchema).optional(),
|
|
1598
|
+
sports: z.array(sportsOperationSchema).optional(),
|
|
1599
|
+
time: z.array(timeOperationSchema).optional(),
|
|
1600
|
+
response_length: z.enum([
|
|
1601
|
+
"short",
|
|
1602
|
+
"medium",
|
|
1603
|
+
"long"
|
|
1604
|
+
]).optional()
|
|
1605
|
+
}).loose();
|
|
1606
|
+
const reasoningSchema = z.object({
|
|
1607
|
+
effort: z.string().min(1).nullable().optional(),
|
|
1608
|
+
summary: z.enum([
|
|
1609
|
+
"auto",
|
|
1610
|
+
"concise",
|
|
1611
|
+
"detailed",
|
|
1612
|
+
"none"
|
|
1613
|
+
]).nullable().optional(),
|
|
1614
|
+
context: z.enum([
|
|
1615
|
+
"auto",
|
|
1616
|
+
"current_turn",
|
|
1617
|
+
"all_turns"
|
|
1618
|
+
]).nullable().optional()
|
|
1619
|
+
}).loose();
|
|
1620
|
+
const settingsSchema = z.object({
|
|
1621
|
+
user_location: z.object({
|
|
1622
|
+
type: z.literal("approximate"),
|
|
1623
|
+
country: z.string().optional(),
|
|
1624
|
+
region: z.string().optional(),
|
|
1625
|
+
city: z.string().optional(),
|
|
1626
|
+
timezone: z.string().optional()
|
|
1627
|
+
}).loose().optional(),
|
|
1628
|
+
search_context_size: z.enum([
|
|
1629
|
+
"low",
|
|
1630
|
+
"medium",
|
|
1631
|
+
"high"
|
|
1632
|
+
]).optional(),
|
|
1633
|
+
filters: z.object({
|
|
1634
|
+
allowed_domains: z.array(z.string()).optional(),
|
|
1635
|
+
blocked_domains: z.array(z.string()).optional()
|
|
1636
|
+
}).loose().optional(),
|
|
1637
|
+
image_settings: z.object({
|
|
1638
|
+
max_results: unsignedInteger.optional(),
|
|
1639
|
+
caption: z.boolean().optional()
|
|
1640
|
+
}).loose().optional(),
|
|
1641
|
+
allowed_callers: z.array(z.enum([
|
|
1642
|
+
"direct",
|
|
1643
|
+
"shell",
|
|
1644
|
+
"code_interpreter"
|
|
1645
|
+
])).optional(),
|
|
1646
|
+
external_web_access: z.union([z.boolean(), z.enum([
|
|
1647
|
+
"cached",
|
|
1648
|
+
"indexed",
|
|
1649
|
+
"live"
|
|
1650
|
+
])]).optional()
|
|
1651
|
+
}).loose();
|
|
1652
|
+
const alphaSearchRequestSchema = z.object({
|
|
1653
|
+
id: z.string(),
|
|
1654
|
+
model: z.string(),
|
|
1655
|
+
reasoning: reasoningSchema.optional(),
|
|
1656
|
+
input: z.union([z.string(), z.array(passthroughRecord)]).optional(),
|
|
1657
|
+
commands: alphaSearchCommandsSchema.optional(),
|
|
1658
|
+
settings: settingsSchema.optional(),
|
|
1659
|
+
max_output_tokens: unsignedInteger.optional()
|
|
1660
|
+
}).loose();
|
|
1661
|
+
//#endregion
|
|
1662
|
+
//#region src/routes/messages/web-search/backend.ts
|
|
1663
|
+
/** Builds the Responses API web_search tool object from normalized config. */
|
|
1664
|
+
const buildResponsesWebSearchTool = (config) => {
|
|
1665
|
+
const tool = { type: "web_search" };
|
|
1666
|
+
const filters = {};
|
|
1667
|
+
if (config.allowedDomains?.length) filters.allowed_domains = config.allowedDomains;
|
|
1668
|
+
if (config.blockedDomains?.length) filters.blocked_domains = config.blockedDomains;
|
|
1669
|
+
if (Object.keys(filters).length > 0) tool.filters = filters;
|
|
1670
|
+
if (config.userLocation) tool.user_location = config.userLocation;
|
|
1671
|
+
if (config.searchContextSize) tool.search_context_size = config.searchContextSize;
|
|
1672
|
+
return tool;
|
|
1673
|
+
};
|
|
1674
|
+
const isMessageItem = (item) => item.type === "message";
|
|
1675
|
+
const isValidUrlCitation = (annotation, seenUrls) => {
|
|
1676
|
+
const ann = annotation;
|
|
1677
|
+
return ann.type === "url_citation" && Boolean(ann.url) && !seenUrls.has(ann.url);
|
|
1678
|
+
};
|
|
1679
|
+
const collectTextParts = (blocks, seenUrls) => {
|
|
1680
|
+
const textParts = [];
|
|
1681
|
+
const sources = [];
|
|
1682
|
+
for (const block of blocks ?? []) {
|
|
1683
|
+
if (block.type !== "output_text") continue;
|
|
1684
|
+
if (block.text) textParts.push(block.text);
|
|
1685
|
+
for (const annotation of block.annotations ?? []) {
|
|
1686
|
+
if (!isValidUrlCitation(annotation, seenUrls)) continue;
|
|
1687
|
+
const ann = annotation;
|
|
1688
|
+
seenUrls.add(ann.url);
|
|
1689
|
+
const start = Math.max(0, (ann.start_index ?? 0) - 120);
|
|
1690
|
+
const end = Math.min(block.text?.length ?? 0, (ann.end_index ?? block.text?.length ?? 0) + 120);
|
|
1691
|
+
sources.push({
|
|
1692
|
+
url: ann.url,
|
|
1693
|
+
title: ann.title ?? ann.url,
|
|
1694
|
+
snippet: block.text?.slice(start, end).trim()
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
return {
|
|
1699
|
+
textParts,
|
|
1700
|
+
sources
|
|
1701
|
+
};
|
|
1702
|
+
};
|
|
1703
|
+
const collectQuery = (item, queries) => {
|
|
1704
|
+
if (item.action?.queries?.length) queries.push(...item.action.queries);
|
|
1705
|
+
else if (item.action?.query) queries.push(item.action.query);
|
|
1706
|
+
};
|
|
1707
|
+
/**
|
|
1708
|
+
* Extracts the answer text, deduped sources, and run queries from a GPT
|
|
1709
|
+
* /responses web_search result.
|
|
1710
|
+
*/
|
|
1711
|
+
const extractWebSearchResult = (result) => {
|
|
1712
|
+
const textParts = [];
|
|
1713
|
+
const sources = [];
|
|
1714
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
1715
|
+
const queries = [];
|
|
1716
|
+
for (const item of result.output) if (isMessageItem(item)) {
|
|
1717
|
+
const collected = collectTextParts(item.content, seenUrls);
|
|
1718
|
+
textParts.push(...collected.textParts);
|
|
1719
|
+
sources.push(...collected.sources);
|
|
1720
|
+
}
|
|
1721
|
+
for (const item of result.output) if (item.type === "web_search_call") {
|
|
1722
|
+
const action = item.action;
|
|
1723
|
+
collectQuery({ action }, queries);
|
|
1724
|
+
for (const source of action?.sources ?? []) {
|
|
1725
|
+
if (!source.url || seenUrls.has(source.url)) continue;
|
|
1726
|
+
seenUrls.add(source.url);
|
|
1727
|
+
sources.push({
|
|
1728
|
+
url: source.url,
|
|
1729
|
+
title: source.url
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
return {
|
|
1734
|
+
answerText: textParts.join("\n\n").trim() || (result.output_text ?? "").trim(),
|
|
1735
|
+
sources,
|
|
1736
|
+
queries
|
|
1737
|
+
};
|
|
1738
|
+
};
|
|
1739
|
+
//#endregion
|
|
1740
|
+
//#region src/lib/copilot-rate-limit.ts
|
|
1741
|
+
const copilotRateLimitTypes = ["session", "weekly"];
|
|
1742
|
+
const copilotRateLimitHeaders = {
|
|
1743
|
+
session: "x-usage-ratelimit-session",
|
|
1744
|
+
weekly: "x-usage-ratelimit-weekly"
|
|
1745
|
+
};
|
|
1746
|
+
const copilotQuotaSnapshotKeys = {
|
|
1747
|
+
session: "5Hour-Session-RateLimits",
|
|
1748
|
+
weekly: "Weekly-Session-RateLimits"
|
|
1749
|
+
};
|
|
1750
|
+
const hasGetMethod = (headers) => {
|
|
1751
|
+
return "get" in headers && typeof headers.get === "function";
|
|
1752
|
+
};
|
|
1753
|
+
const getHeaderValue$1 = (headers, headerName) => {
|
|
1754
|
+
if (hasGetMethod(headers)) return headers.get(headerName);
|
|
1755
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
1756
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
|
|
1757
|
+
};
|
|
1758
|
+
const parseCopilotRateLimitHeader = (headerValue) => {
|
|
1759
|
+
const params = new URLSearchParams(headerValue);
|
|
1760
|
+
const remaining = params.get("rem");
|
|
1761
|
+
const resetAt = params.get("rst");
|
|
1762
|
+
if (!remaining || !resetAt) return null;
|
|
1763
|
+
return {
|
|
1764
|
+
remaining,
|
|
1765
|
+
resetAt
|
|
1766
|
+
};
|
|
1767
|
+
};
|
|
1768
|
+
const getCopilotRateLimitUsage = (headers, type) => {
|
|
1769
|
+
const headerName = copilotRateLimitHeaders[type];
|
|
1770
|
+
const headerValue = getHeaderValue$1(headers, headerName);
|
|
1771
|
+
if (!headerValue) return null;
|
|
1772
|
+
const parsed = parseCopilotRateLimitHeader(headerValue);
|
|
1773
|
+
if (!parsed) return null;
|
|
1774
|
+
return {
|
|
1775
|
+
type,
|
|
1776
|
+
...parsed
|
|
1777
|
+
};
|
|
1778
|
+
};
|
|
1779
|
+
const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
|
|
1780
|
+
const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
|
|
1781
|
+
if (!isCopilotQuotaSnapshot(snapshot)) return null;
|
|
1782
|
+
return {
|
|
1783
|
+
remaining: String(snapshot.percent_remaining),
|
|
1784
|
+
resetAt: snapshot.reset_date,
|
|
1785
|
+
type
|
|
1786
|
+
};
|
|
1787
|
+
};
|
|
1788
|
+
const logCopilotRateLimits = (headers) => {
|
|
1789
|
+
for (const type of copilotRateLimitTypes) {
|
|
1790
|
+
const usage = getCopilotRateLimitUsage(headers, type);
|
|
1791
|
+
if (!usage) continue;
|
|
1792
|
+
logCopilotRateLimitUsage(usage);
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
const logCopilotQuotaSnapshots = (snapshots) => {
|
|
1796
|
+
for (const type of copilotRateLimitTypes) {
|
|
1797
|
+
const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
|
|
1798
|
+
if (!usage) continue;
|
|
1799
|
+
logCopilotRateLimitUsage(usage);
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1802
|
+
const logCopilotRateLimitUsage = (usage) => {
|
|
1803
|
+
const d = new Date(usage.resetAt);
|
|
1804
|
+
const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
|
|
1805
|
+
consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
|
|
1806
|
+
};
|
|
1807
|
+
const isCopilotQuotaSnapshot = (value) => {
|
|
1808
|
+
if (!value || typeof value !== "object") return false;
|
|
1809
|
+
const record = value;
|
|
1810
|
+
return typeof record.entitlement === "string" && typeof record.percent_remaining === "number" && typeof record.overage_permitted === "boolean" && typeof record.overage_count === "number" && typeof record.reset_date === "string";
|
|
1811
|
+
};
|
|
1812
|
+
//#endregion
|
|
1813
|
+
//#region src/services/copilot/create-responses.ts
|
|
1814
|
+
const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
|
|
1815
|
+
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
1816
|
+
const headers = {
|
|
1817
|
+
...copilotHeaders(state, requestId, vision),
|
|
1818
|
+
"x-initiator": initiator
|
|
1819
|
+
};
|
|
1820
|
+
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
1821
|
+
prepareForCompact(headers, compactType);
|
|
1822
|
+
payload.service_tier = void 0;
|
|
1823
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1824
|
+
const effectiveTransport = compactType === 1 ? "http" : transport;
|
|
1825
|
+
if (payload.stream === true && effectiveTransport === "websocket") return createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
|
|
1826
|
+
requestId,
|
|
1827
|
+
subagentMarker
|
|
1828
|
+
}));
|
|
1829
|
+
return await createHttpResponses(payload, headers);
|
|
1830
|
+
};
|
|
1831
|
+
const createHttpResponses = async (payload, headers) => {
|
|
1832
|
+
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
|
1833
|
+
method: "POST",
|
|
1834
|
+
headers,
|
|
1835
|
+
body: JSON.stringify(payload)
|
|
1836
|
+
});
|
|
1837
|
+
logCopilotRateLimits(response.headers);
|
|
1838
|
+
if (!response.ok) {
|
|
1839
|
+
consola.error("Failed to create responses", response);
|
|
1840
|
+
throw new HTTPError("Failed to create responses", response);
|
|
1841
|
+
}
|
|
1842
|
+
if (payload.stream) return events(response);
|
|
1843
|
+
return await response.json();
|
|
1844
|
+
};
|
|
1845
|
+
const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
|
|
1846
|
+
const initiator = getResponsesWebSocketInitiator(preparedHeaders);
|
|
1847
|
+
return {
|
|
1848
|
+
headers: copilotWebSocketHeaders(preparedHeaders),
|
|
1849
|
+
poolKey: buildResponsesWebSocketPoolKey(payload, options),
|
|
1850
|
+
payload: buildResponsesWebSocketPayload(payload, initiator),
|
|
1851
|
+
url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
|
|
1852
|
+
};
|
|
1853
|
+
};
|
|
1854
|
+
const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
|
|
1855
|
+
const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
|
|
1856
|
+
const subagentKey = subagentMarker ? [
|
|
1857
|
+
subagentMarker.session_id,
|
|
1858
|
+
subagentMarker.agent_id,
|
|
1859
|
+
subagentMarker.agent_type
|
|
1860
|
+
].join(":") : "main";
|
|
1861
|
+
return [
|
|
1862
|
+
tokenFingerprint,
|
|
1863
|
+
payload.model,
|
|
1864
|
+
requestId,
|
|
1865
|
+
subagentKey
|
|
1866
|
+
].map(encodePoolKeyPart).join("|");
|
|
1867
|
+
};
|
|
1868
|
+
const getResponsesWebSocketInitiator = (preparedHeaders) => {
|
|
1869
|
+
return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
|
|
1870
|
+
};
|
|
1871
|
+
const createPooledResponsesWebSocketStream = (request) => createResponsesSafeStream(createPooledWebSocketStream(request, {
|
|
1872
|
+
createChunk: createResponsesWebSocketStreamChunk,
|
|
1873
|
+
isTerminalChunk: isTerminalResponsesStreamChunk,
|
|
1874
|
+
openErrorMessage: "Failed to create responses websocket",
|
|
1875
|
+
streamErrorMessage: "Responses websocket stream error",
|
|
1876
|
+
terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
|
|
1877
|
+
}));
|
|
1878
|
+
const createResponsesSafeStream = async function* (source) {
|
|
1879
|
+
try {
|
|
1880
|
+
yield* source;
|
|
1881
|
+
} catch (error) {
|
|
1882
|
+
yield createResponsesErrorServerSentEventChunk(getErrorMessage(error));
|
|
1883
|
+
}
|
|
1884
|
+
};
|
|
1885
|
+
const buildResponsesWebSocketPayload = (payload, initiator) => {
|
|
1886
|
+
const websocketPayload = {
|
|
1887
|
+
...payload,
|
|
1888
|
+
type: "response.create",
|
|
1889
|
+
initiator
|
|
1890
|
+
};
|
|
1891
|
+
delete websocketPayload.stream;
|
|
1892
|
+
delete websocketPayload["background"];
|
|
1893
|
+
delete websocketPayload.service_tier;
|
|
1894
|
+
return websocketPayload;
|
|
1895
|
+
};
|
|
1896
|
+
const buildResponsesWebSocketUrl = (baseUrl) => {
|
|
1897
|
+
return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
|
|
1898
|
+
};
|
|
1899
|
+
const getHeaderValue = (headers, headerName) => {
|
|
1900
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
1901
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
1902
|
+
};
|
|
1903
|
+
const encodePoolKeyPart = (value) => encodeURIComponent(value);
|
|
1904
|
+
const createResponsesWebSocketStreamChunk = (data) => {
|
|
1905
|
+
if (data === "[DONE]") return { data };
|
|
1906
|
+
try {
|
|
1907
|
+
const parsed = JSON.parse(data);
|
|
1908
|
+
if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
|
|
1909
|
+
if (parsed.type === "error" && parsed.error) {
|
|
1910
|
+
consola.warn("Copilot responses websocket stream error:", parsed.error);
|
|
1911
|
+
parsed.code = parsed.error.code;
|
|
1912
|
+
parsed.message = parsed.error.message;
|
|
1913
|
+
}
|
|
1914
|
+
return {
|
|
1915
|
+
event: typeof parsed.type === "string" ? parsed.type : void 0,
|
|
1916
|
+
data: JSON.stringify(parsed),
|
|
1917
|
+
id: typeof parsed.id === "string" ? parsed.id : void 0
|
|
1918
|
+
};
|
|
1919
|
+
} catch {
|
|
1920
|
+
return { data };
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
const isTerminalResponsesStreamChunk = (chunk) => {
|
|
1924
|
+
if (!chunk.data || chunk.data === "[DONE]") return false;
|
|
1925
|
+
try {
|
|
1926
|
+
const parsed = JSON.parse(chunk.data);
|
|
1927
|
+
return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
|
|
1928
|
+
} catch {
|
|
1929
|
+
return false;
|
|
1930
|
+
}
|
|
1931
|
+
};
|
|
1932
|
+
const createResponsesErrorServerSentEventChunk = (message) => {
|
|
1933
|
+
const errorEvent = {
|
|
1934
|
+
code: null,
|
|
1935
|
+
message,
|
|
1936
|
+
param: null,
|
|
1937
|
+
sequence_number: 0,
|
|
1938
|
+
type: "error"
|
|
1939
|
+
};
|
|
1940
|
+
return {
|
|
1941
|
+
event: errorEvent.type,
|
|
1942
|
+
data: JSON.stringify(errorEvent)
|
|
1943
|
+
};
|
|
1944
|
+
};
|
|
1945
|
+
const getErrorMessage = (error) => {
|
|
1946
|
+
if (error instanceof Error && error.message) return error.message;
|
|
1947
|
+
return String(error);
|
|
1948
|
+
};
|
|
1949
|
+
//#endregion
|
|
1950
|
+
//#region src/services/providers/provider-proxy.ts
|
|
1951
|
+
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
1952
|
+
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
1953
|
+
const STRIPPED_RESPONSE_HEADERS = [
|
|
1954
|
+
"connection",
|
|
1955
|
+
"content-encoding",
|
|
1956
|
+
"content-length",
|
|
1957
|
+
"keep-alive",
|
|
1958
|
+
"proxy-authenticate",
|
|
1959
|
+
"proxy-authorization",
|
|
1960
|
+
"te",
|
|
1961
|
+
"trailer",
|
|
1962
|
+
"transfer-encoding",
|
|
1963
|
+
"upgrade"
|
|
1964
|
+
];
|
|
1965
|
+
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
1966
|
+
const authHeaders = {};
|
|
1967
|
+
if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
1968
|
+
else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
1969
|
+
const headers = {
|
|
1970
|
+
"content-type": "application/json",
|
|
1971
|
+
accept: "application/json",
|
|
1972
|
+
...authHeaders
|
|
1973
|
+
};
|
|
1974
|
+
for (const headerName of SHARED_FORWARDABLE_HEADERS) {
|
|
1975
|
+
const headerValue = requestHeaders.get(headerName);
|
|
1976
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
1977
|
+
}
|
|
1978
|
+
if (providerConfig.type !== "anthropic") return headers;
|
|
1979
|
+
for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
|
|
1980
|
+
const headerValue = requestHeaders.get(headerName);
|
|
1981
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
1982
|
+
}
|
|
1983
|
+
return headers;
|
|
1984
|
+
}
|
|
1985
|
+
function createProviderProxyResponse(upstreamResponse, body) {
|
|
1986
|
+
const headers = new Headers(upstreamResponse.headers);
|
|
1987
|
+
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
1988
|
+
return new Response(body ?? upstreamResponse.body, {
|
|
1989
|
+
headers,
|
|
1990
|
+
status: upstreamResponse.status,
|
|
1991
|
+
statusText: upstreamResponse.statusText
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
1995
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1996
|
+
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
1997
|
+
method: "POST",
|
|
1998
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
1999
|
+
body: JSON.stringify(payload)
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
2003
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
2004
|
+
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
2005
|
+
method: "POST",
|
|
2006
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
2007
|
+
body: JSON.stringify(payload)
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
|
|
2011
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
2012
|
+
return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
|
|
2013
|
+
method: "POST",
|
|
2014
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
2015
|
+
body: JSON.stringify(payload)
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
2019
|
+
return await fetch(`${providerConfig.baseUrl}/v1/models`, {
|
|
2020
|
+
method: "GET",
|
|
2021
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
/** Align with Codex images: long-running generation/edits need a generous cap. */
|
|
2025
|
+
const PROVIDER_IMAGES_TIMEOUT_MS = 900 * 1e3;
|
|
2026
|
+
const providerImagesDispatcher = { dispatch(options, handler) {
|
|
2027
|
+
return getGlobalDispatcher().dispatch({
|
|
2028
|
+
...options,
|
|
2029
|
+
bodyTimeout: PROVIDER_IMAGES_TIMEOUT_MS,
|
|
2030
|
+
headersTimeout: PROVIDER_IMAGES_TIMEOUT_MS
|
|
2031
|
+
}, handler);
|
|
2032
|
+
} };
|
|
2033
|
+
function resolveProviderRequestUrl(providerConfig, requestUrl, path) {
|
|
2034
|
+
const upstreamUrl = new URL(`${providerConfig.baseUrl}${path}`);
|
|
2035
|
+
upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
|
|
2036
|
+
return upstreamUrl.toString();
|
|
2037
|
+
}
|
|
2038
|
+
async function forwardProviderAlphaSearch(providerConfig, request) {
|
|
2039
|
+
const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
|
|
2040
|
+
const body = await request.arrayBuffer();
|
|
2041
|
+
return await fetch(resolveProviderRequestUrl(providerConfig, request.url, "/v1/alpha/search"), {
|
|
2042
|
+
method: "POST",
|
|
2043
|
+
headers,
|
|
2044
|
+
body
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
async function forwardProviderImages(providerConfig, request, operation) {
|
|
2048
|
+
const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
|
|
2049
|
+
const contentType = request.headers.get("content-type");
|
|
2050
|
+
if (contentType) headers["content-type"] = contentType;
|
|
2051
|
+
else if (operation === "edits") delete headers["content-type"];
|
|
2052
|
+
const init = {
|
|
2053
|
+
method: "POST",
|
|
2054
|
+
headers,
|
|
2055
|
+
body: request.body,
|
|
2056
|
+
duplex: "half",
|
|
2057
|
+
signal: AbortSignal.timeout(PROVIDER_IMAGES_TIMEOUT_MS)
|
|
2058
|
+
};
|
|
2059
|
+
const upstreamUrl = resolveProviderRequestUrl(providerConfig, request.url, `/v1/images/${operation}`);
|
|
2060
|
+
if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
|
|
2061
|
+
return await fetch$1(upstreamUrl, {
|
|
2062
|
+
...init,
|
|
2063
|
+
dispatcher: providerImagesDispatcher
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
//#endregion
|
|
2067
|
+
//#region src/routes/alpha-search/alpha-search-responses.ts
|
|
2068
|
+
const logger$15 = createHandlerLogger("alpha-search-responses-handler");
|
|
2069
|
+
const SESSION_TTL_MS = 3600 * 1e3;
|
|
2070
|
+
const MAX_SESSIONS = 128;
|
|
2071
|
+
const MAX_URL_REFERENCES = 256;
|
|
2072
|
+
const MAX_SNAPSHOTS = 16;
|
|
2073
|
+
const MAX_SNAPSHOT_CHARACTERS = 2e4;
|
|
2074
|
+
const IMAGE_UNSUPPORTED = "Unsupported by GitHub Copilot web search: image_query. Do not retry this operation; use search_query for image-source pages.";
|
|
2075
|
+
const SCREENSHOT_UNSUPPORTED = "Unsupported by GitHub Copilot web search: screenshot. Do not retry this operation; open the PDF for text content.";
|
|
2076
|
+
const KNOWN_COMMANDS = new Set([
|
|
2077
|
+
"search_query",
|
|
2078
|
+
"image_query",
|
|
2079
|
+
"open",
|
|
2080
|
+
"click",
|
|
2081
|
+
"find",
|
|
2082
|
+
"screenshot",
|
|
2083
|
+
"finance",
|
|
2084
|
+
"weather",
|
|
2085
|
+
"sports",
|
|
2086
|
+
"time",
|
|
2087
|
+
"response_length"
|
|
2088
|
+
]);
|
|
2089
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
2090
|
+
const alphaSearchResponsesDependencies = {
|
|
2091
|
+
createResponses,
|
|
2092
|
+
findEndpointModel,
|
|
2093
|
+
now: () => Date.now(),
|
|
2094
|
+
resolveMappedModel,
|
|
2095
|
+
createUsageRecorder: (model, sessionId) => createCopilotTokenUsageRecorder({
|
|
2096
|
+
endpoint: "responses",
|
|
2097
|
+
fallbackSessionId: sessionId,
|
|
2098
|
+
model
|
|
2099
|
+
})
|
|
2100
|
+
};
|
|
2101
|
+
function reserveSession(id, now) {
|
|
2102
|
+
for (const [sessionId, session] of sessions) if (now - session.touchedAt >= SESSION_TTL_MS) sessions.delete(sessionId);
|
|
2103
|
+
let session = sessions.get(id);
|
|
2104
|
+
if (!session) {
|
|
2105
|
+
if (sessions.size >= MAX_SESSIONS) {
|
|
2106
|
+
const oldestSession = sessions.keys().next();
|
|
2107
|
+
if (!oldestSession.done) sessions.delete(oldestSession.value);
|
|
2108
|
+
}
|
|
2109
|
+
session = {
|
|
2110
|
+
nextTurn: 0,
|
|
2111
|
+
touchedAt: now,
|
|
2112
|
+
referencesById: /* @__PURE__ */ new Map(),
|
|
2113
|
+
referencesByUrl: /* @__PURE__ */ new Map(),
|
|
2114
|
+
snapshots: /* @__PURE__ */ new Map()
|
|
2115
|
+
};
|
|
2116
|
+
} else {
|
|
2117
|
+
sessions.delete(id);
|
|
2118
|
+
session.touchedAt = now;
|
|
2119
|
+
}
|
|
2120
|
+
sessions.set(id, session);
|
|
2121
|
+
const turn = session.nextTurn;
|
|
2122
|
+
session.nextTurn += 1;
|
|
2123
|
+
return {
|
|
2124
|
+
session,
|
|
2125
|
+
turn
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
function parseHttpUrl(value) {
|
|
2129
|
+
try {
|
|
2130
|
+
const url = new URL(value);
|
|
2131
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
|
|
2132
|
+
} catch {
|
|
2133
|
+
return null;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
function extractMarkdownSources(text) {
|
|
2137
|
+
return Array.from(text.matchAll(/(?<!!)\[([^\]\n]+)\]\((https?:\/\/(?:[^\s<>()]|\([^\s<>()]*\))+)\)/gu), ([, title, url]) => ({
|
|
2138
|
+
title,
|
|
2139
|
+
url
|
|
2140
|
+
}));
|
|
2141
|
+
}
|
|
2142
|
+
function addUrlReference(session, source, turn) {
|
|
2143
|
+
const url = parseHttpUrl(source.url);
|
|
2144
|
+
if (!url) return null;
|
|
2145
|
+
const existing = session.referencesByUrl.get(url);
|
|
2146
|
+
if (existing) {
|
|
2147
|
+
if (existing.result.title === existing.result.url && source.title) existing.result.title = source.title;
|
|
2148
|
+
if (source.snippet) existing.result.snippet = source.snippet;
|
|
2149
|
+
return existing;
|
|
2150
|
+
}
|
|
2151
|
+
if (session.referencesById.size >= MAX_URL_REFERENCES) {
|
|
2152
|
+
const oldest = session.referencesById.entries().next().value;
|
|
2153
|
+
if (oldest) {
|
|
2154
|
+
const [refId, reference] = oldest;
|
|
2155
|
+
session.referencesById.delete(refId);
|
|
2156
|
+
session.referencesByUrl.delete(reference.result.url);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
const refId = `turn${turn.number}search${turn.nextReferenceIndex}`;
|
|
2160
|
+
turn.nextReferenceIndex += 1;
|
|
2161
|
+
const reference = { result: {
|
|
2162
|
+
type: "text_result",
|
|
2163
|
+
domain: new URL(url).hostname,
|
|
2164
|
+
ref_id: refId,
|
|
2165
|
+
snippet: source.snippet?.trim() || source.title || url,
|
|
2166
|
+
title: source.title || url,
|
|
2167
|
+
url
|
|
2168
|
+
} };
|
|
2169
|
+
session.referencesById.set(refId, reference);
|
|
2170
|
+
session.referencesByUrl.set(url, reference);
|
|
2171
|
+
return reference;
|
|
2172
|
+
}
|
|
2173
|
+
function resolveUrlReference(session, refId, turn) {
|
|
2174
|
+
const stored = session.referencesById.get(refId);
|
|
2175
|
+
if (stored) return stored;
|
|
2176
|
+
const url = parseHttpUrl(refId);
|
|
2177
|
+
return url ? addUrlReference(session, {
|
|
2178
|
+
title: url,
|
|
2179
|
+
url
|
|
2180
|
+
}, turn) : null;
|
|
2181
|
+
}
|
|
2182
|
+
function getSnapshot(session, refId) {
|
|
2183
|
+
const snapshot = session.snapshots.get(refId);
|
|
2184
|
+
if (!snapshot) return null;
|
|
2185
|
+
session.snapshots.delete(refId);
|
|
2186
|
+
session.snapshots.set(refId, snapshot);
|
|
2187
|
+
return snapshot;
|
|
2188
|
+
}
|
|
2189
|
+
function findSnapshotByUrl(session, url) {
|
|
2190
|
+
for (const snapshot of session.snapshots.values()) if (snapshot.source.result.url === url) return getSnapshot(session, snapshot.refId);
|
|
2191
|
+
return null;
|
|
2192
|
+
}
|
|
2193
|
+
function addSnapshot(session, source, turn, index, text, links) {
|
|
2194
|
+
const existing = findSnapshotByUrl(session, source.result.url);
|
|
2195
|
+
if (existing) {
|
|
2196
|
+
existing.text = text.slice(0, MAX_SNAPSHOT_CHARACTERS);
|
|
2197
|
+
existing.links = links;
|
|
2198
|
+
return existing;
|
|
2199
|
+
}
|
|
2200
|
+
if (session.snapshots.size >= MAX_SNAPSHOTS) {
|
|
2201
|
+
const oldestRefId = session.snapshots.keys().next().value;
|
|
2202
|
+
if (oldestRefId) session.snapshots.delete(oldestRefId);
|
|
2203
|
+
}
|
|
2204
|
+
const snapshot = {
|
|
2205
|
+
refId: `turn${turn}view${index}`,
|
|
2206
|
+
source,
|
|
2207
|
+
text: text.slice(0, MAX_SNAPSHOT_CHARACTERS),
|
|
2208
|
+
links
|
|
2209
|
+
};
|
|
2210
|
+
session.snapshots.set(snapshot.refId, snapshot);
|
|
2211
|
+
return snapshot;
|
|
2212
|
+
}
|
|
2213
|
+
function renderSnapshot(snapshot, lineno) {
|
|
2214
|
+
const lines = snapshot.text.split("\n");
|
|
2215
|
+
const start = lineno === void 0 ? 0 : Math.max(0, Math.min(lineno, lines.length - 1) - 10);
|
|
2216
|
+
const end = lineno === void 0 ? lines.length : Math.min(lines.length, start + 21);
|
|
2217
|
+
const numbered = lines.slice(start, end).map((line, index) => `L${start + index}: ${line}`).join("\n");
|
|
2218
|
+
const links = snapshot.links.map((link, index) => `[${index}] ${link.result.title} — ${link.result.url} (${link.result.ref_id})`).join("\n");
|
|
2219
|
+
return [
|
|
2220
|
+
`Open ${snapshot.refId} (${snapshot.source.result.url})`,
|
|
2221
|
+
numbered,
|
|
2222
|
+
links ? `Links:\n${links}` : ""
|
|
2223
|
+
].filter(Boolean).join("\n");
|
|
2224
|
+
}
|
|
2225
|
+
function findInSnapshot(snapshot, pattern) {
|
|
2226
|
+
const normalizedPattern = pattern.toLowerCase();
|
|
2227
|
+
const matches = snapshot.text.split("\n").map((line, index) => ({
|
|
2228
|
+
line,
|
|
2229
|
+
index
|
|
2230
|
+
})).filter(({ line }) => line.toLowerCase().includes(normalizedPattern)).slice(0, 20);
|
|
2231
|
+
if (matches.length === 0) return null;
|
|
2232
|
+
return [`Find results for ${JSON.stringify(pattern)} in ${snapshot.refId}:`, ...matches.map(({ line, index }) => `L${index}: ${line}`)].join("\n");
|
|
2233
|
+
}
|
|
2234
|
+
function formatTime(utcOffset, now) {
|
|
2235
|
+
const offsetMinutes = (utcOffset.startsWith("+") ? 1 : -1) * (Number.parseInt(utcOffset.slice(1, 3), 10) * 60 + Number.parseInt(utcOffset.slice(4, 6), 10));
|
|
2236
|
+
return `Time at UTC${utcOffset}: ${new Date(now + offsetMinutes * 6e4).toISOString().slice(0, 19).replace("T", " ")}`;
|
|
2237
|
+
}
|
|
2238
|
+
function buildInstruction(operations, responseLength) {
|
|
2239
|
+
return [
|
|
2240
|
+
"The Operations JSON below is the complete and exclusive request for this response.",
|
|
2241
|
+
"Use web_search to execute every listed operation and only those operations. Do not validate, infer, or mention operations absent from the JSON; the adapter already handled them.",
|
|
2242
|
+
"Return only grounded results and citations for these operations. Do not discuss unrelated tasks or conversations.",
|
|
2243
|
+
"For search operations, honor every query, recency, domain, market, date, league, team, and locale constraint.",
|
|
2244
|
+
"For open operations, open the exact URL and return grounded page text plus cited links.",
|
|
2245
|
+
"For find operations, find the exact pattern in the specified URL and return matching context.",
|
|
2246
|
+
`Requested response length: ${responseLength ?? "medium"}.`,
|
|
2247
|
+
`Operations:\n${JSON.stringify(operations, null, 2)}`
|
|
2248
|
+
].join("\n");
|
|
2249
|
+
}
|
|
2250
|
+
function unavailableReference(refId) {
|
|
2251
|
+
return `Reference ${JSON.stringify(refId)} is unavailable or expired. Search or open the URL again.`;
|
|
2252
|
+
}
|
|
2253
|
+
function invalidRequest$1(c, message) {
|
|
2254
|
+
return c.json({ error: {
|
|
2255
|
+
message,
|
|
2256
|
+
type: "invalid_request_error"
|
|
2257
|
+
} }, 400);
|
|
2258
|
+
}
|
|
2259
|
+
function createSearchOperationState(session, turn) {
|
|
2260
|
+
return {
|
|
2261
|
+
session,
|
|
2262
|
+
turn,
|
|
2263
|
+
output: [],
|
|
2264
|
+
warnings: [],
|
|
2265
|
+
resultReferences: /* @__PURE__ */ new Map(),
|
|
2266
|
+
remoteOperations: [],
|
|
2267
|
+
remotePages: []
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
function includeResult(state, reference) {
|
|
2271
|
+
state.resultReferences.set(reference.result.url, reference);
|
|
2272
|
+
}
|
|
2273
|
+
function queuePage(state, kind, source, options = {}) {
|
|
2274
|
+
state.remoteOperations.push({
|
|
2275
|
+
operation: kind,
|
|
2276
|
+
url: source.result.url,
|
|
2277
|
+
...options.lineno === void 0 ? {} : { lineno: options.lineno },
|
|
2278
|
+
...options.pattern === void 0 ? {} : { pattern: options.pattern }
|
|
2279
|
+
});
|
|
2280
|
+
state.remotePages.push({
|
|
2281
|
+
kind,
|
|
2282
|
+
source,
|
|
2283
|
+
...options
|
|
2284
|
+
});
|
|
2285
|
+
includeResult(state, source);
|
|
2286
|
+
}
|
|
2287
|
+
function processSearchCommands(commands, state) {
|
|
2288
|
+
for (const command of commands.search_query ?? []) state.remoteOperations.push({
|
|
2289
|
+
...command,
|
|
2290
|
+
operation: "search_query"
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2293
|
+
function processOpenCommands(commands, state) {
|
|
2294
|
+
for (const command of commands.open ?? []) {
|
|
2295
|
+
const directSnapshot = getSnapshot(state.session, command.ref_id);
|
|
2296
|
+
if (directSnapshot) {
|
|
2297
|
+
state.output.push(renderSnapshot(directSnapshot, command.lineno));
|
|
2298
|
+
includeResult(state, directSnapshot.source);
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
const source = resolveUrlReference(state.session, command.ref_id, state.turn);
|
|
2302
|
+
if (!source) {
|
|
2303
|
+
state.output.push(unavailableReference(command.ref_id));
|
|
2304
|
+
continue;
|
|
2305
|
+
}
|
|
2306
|
+
const cached = findSnapshotByUrl(state.session, source.result.url);
|
|
2307
|
+
if (cached) {
|
|
2308
|
+
state.output.push(renderSnapshot(cached, command.lineno));
|
|
2309
|
+
includeResult(state, source);
|
|
2310
|
+
continue;
|
|
2311
|
+
}
|
|
2312
|
+
queuePage(state, "open", source, { lineno: command.lineno });
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
function processClickCommands(commands, state) {
|
|
2316
|
+
for (const command of commands.click ?? []) {
|
|
2317
|
+
const source = getSnapshot(state.session, command.ref_id)?.links[command.id];
|
|
2318
|
+
if (!source) {
|
|
2319
|
+
state.output.push(unavailableReference(`${command.ref_id} link ${command.id}`));
|
|
2320
|
+
continue;
|
|
2321
|
+
}
|
|
2322
|
+
const cached = findSnapshotByUrl(state.session, source.result.url);
|
|
2323
|
+
if (cached) {
|
|
2324
|
+
state.output.push(renderSnapshot(cached));
|
|
2325
|
+
includeResult(state, source);
|
|
2326
|
+
continue;
|
|
2327
|
+
}
|
|
2328
|
+
queuePage(state, "open", source);
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
function processFindCommands(commands, state) {
|
|
2332
|
+
for (const command of commands.find ?? []) {
|
|
2333
|
+
const directSnapshot = getSnapshot(state.session, command.ref_id);
|
|
2334
|
+
const source = directSnapshot?.source ?? resolveUrlReference(state.session, command.ref_id, state.turn);
|
|
2335
|
+
if (!source) {
|
|
2336
|
+
state.output.push(unavailableReference(command.ref_id));
|
|
2337
|
+
continue;
|
|
2338
|
+
}
|
|
2339
|
+
const snapshot = directSnapshot ?? findSnapshotByUrl(state.session, source.result.url);
|
|
2340
|
+
const localResult = snapshot ? findInSnapshot(snapshot, command.pattern) : null;
|
|
2341
|
+
if (localResult) {
|
|
2342
|
+
state.output.push(localResult);
|
|
2343
|
+
includeResult(state, source);
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
queuePage(state, "find", source, { pattern: command.pattern });
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
function processStructuredCommands(commands, state, now) {
|
|
2350
|
+
for (const command of commands.finance ?? []) state.remoteOperations.push({
|
|
2351
|
+
...command,
|
|
2352
|
+
operation: "finance"
|
|
2353
|
+
});
|
|
2354
|
+
for (const command of commands.weather ?? []) state.remoteOperations.push({
|
|
2355
|
+
...command,
|
|
2356
|
+
operation: "weather"
|
|
2357
|
+
});
|
|
2358
|
+
for (const command of commands.sports ?? []) state.remoteOperations.push({
|
|
2359
|
+
...command,
|
|
2360
|
+
operation: "sports"
|
|
2361
|
+
});
|
|
2362
|
+
for (const command of commands.time ?? []) state.output.push(formatTime(command.utc_offset, now));
|
|
2363
|
+
}
|
|
2364
|
+
function processUnsupportedCommands(commands, state) {
|
|
2365
|
+
for (const commandName of Object.keys(commands)) if (!KNOWN_COMMANDS.has(commandName)) state.warnings.push(`Unsupported by GitHub Copilot web search: ${commandName}. Do not retry this operation.`);
|
|
2366
|
+
}
|
|
2367
|
+
function processCommandOperations(commands, state, now) {
|
|
2368
|
+
processSearchCommands(commands, state);
|
|
2369
|
+
if ((commands.image_query?.length ?? 0) > 0) state.warnings.push(IMAGE_UNSUPPORTED);
|
|
2370
|
+
processOpenCommands(commands, state);
|
|
2371
|
+
processClickCommands(commands, state);
|
|
2372
|
+
processFindCommands(commands, state);
|
|
2373
|
+
if ((commands.screenshot?.length ?? 0) > 0) state.warnings.push(SCREENSHOT_UNSUPPORTED);
|
|
2374
|
+
processStructuredCommands(commands, state, now);
|
|
2375
|
+
processUnsupportedCommands(commands, state);
|
|
2376
|
+
}
|
|
2377
|
+
function supportsLiveAccess(request) {
|
|
2378
|
+
const externalWebAccess = request.settings?.external_web_access;
|
|
2379
|
+
return externalWebAccess === void 0 || externalWebAccess === true || externalWebAccess === "live";
|
|
2380
|
+
}
|
|
2381
|
+
function addLiveAccessWarning(request, state) {
|
|
2382
|
+
if (state.remoteOperations.length === 0 || supportsLiveAccess(request)) return;
|
|
2383
|
+
state.warnings.push(`GitHub Copilot alpha search supports live retrieval only; external_web_access=${JSON.stringify(request.settings?.external_web_access)} is unsupported. Do not retry this request in this mode.`);
|
|
2384
|
+
}
|
|
2385
|
+
function shouldExecuteRemoteOperations(request, state) {
|
|
2386
|
+
return state.remoteOperations.length > 0 && supportsLiveAccess(request);
|
|
2387
|
+
}
|
|
2388
|
+
async function resolveRemoteModel(c, request, provider) {
|
|
2389
|
+
const model = provider ? request.model : alphaSearchResponsesDependencies.resolveMappedModel(request.model);
|
|
2390
|
+
if (provider) {
|
|
2391
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
2392
|
+
if (!providerConfig) return c.json({ error: {
|
|
2393
|
+
message: `Provider '${provider}' not found or disabled`,
|
|
2394
|
+
type: "invalid_request_error"
|
|
2395
|
+
} }, 404);
|
|
2396
|
+
if (resolveEffectiveProviderType(providerConfig, model) !== "openai-responses") return invalidRequest$1(c, `Provider '${provider}' does not support the /v1/responses endpoint required for alpha search`);
|
|
2397
|
+
return {
|
|
2398
|
+
model,
|
|
2399
|
+
providerConfig
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
if (!alphaSearchResponsesDependencies.findEndpointModel(model)?.supported_endpoints?.includes("/responses")) return invalidRequest$1(c, `Model '${model}' does not support the Copilot Responses endpoint required for alpha search`);
|
|
2403
|
+
return { model };
|
|
2404
|
+
}
|
|
2405
|
+
function createRemoteResponsesPayload(request, model, instruction) {
|
|
2406
|
+
return {
|
|
2407
|
+
model,
|
|
2408
|
+
input: instruction,
|
|
2409
|
+
tools: [buildResponsesWebSearchTool({
|
|
2410
|
+
allowedDomains: request.settings?.filters?.allowed_domains,
|
|
2411
|
+
blockedDomains: request.settings?.filters?.blocked_domains,
|
|
2412
|
+
userLocation: request.settings?.user_location,
|
|
2413
|
+
searchContextSize: request.settings?.search_context_size
|
|
2414
|
+
})],
|
|
2415
|
+
tool_choice: "required",
|
|
2416
|
+
store: false,
|
|
2417
|
+
stream: false,
|
|
2418
|
+
include: ["web_search_call.action.sources"],
|
|
2419
|
+
reasoning: request.reasoning,
|
|
2420
|
+
max_output_tokens: request.max_output_tokens
|
|
2421
|
+
};
|
|
2422
|
+
}
|
|
2423
|
+
async function requestProviderSearch(c, providerConfig, payload, model, sessionId) {
|
|
2424
|
+
debugJson(logger$15, "Alpha search provider Responses request:", {
|
|
2425
|
+
payload,
|
|
2426
|
+
provider: providerConfig.name
|
|
2427
|
+
});
|
|
2428
|
+
const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
|
|
2429
|
+
if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${providerConfig.name} responses for alpha search`, upstreamResponse);
|
|
2430
|
+
const result = await upstreamResponse.json();
|
|
2431
|
+
debugJson(logger$15, "Alpha search provider Responses result:", {
|
|
2432
|
+
provider: providerConfig.name,
|
|
2433
|
+
result
|
|
2434
|
+
});
|
|
2435
|
+
createProviderTokenUsageRecorder({
|
|
2436
|
+
endpoint: "responses",
|
|
2437
|
+
fallbackSessionId: sessionId,
|
|
2438
|
+
model,
|
|
2439
|
+
pricing: providerConfig.models?.[model]?.pricing,
|
|
2440
|
+
pricingCurrency: providerConfig.pricingCurrency,
|
|
2441
|
+
providerName: providerConfig.name,
|
|
2442
|
+
sessionId
|
|
2443
|
+
})(normalizeResponsesUsage(result.usage));
|
|
2444
|
+
return result;
|
|
2445
|
+
}
|
|
2446
|
+
async function requestCopilotSearch(payload, model, requestId, sessionId) {
|
|
2447
|
+
debugJson(logger$15, "Alpha search Copilot Responses request:", payload);
|
|
2448
|
+
const result = await alphaSearchResponsesDependencies.createResponses(payload, {
|
|
2449
|
+
vision: false,
|
|
2450
|
+
initiator: "agent",
|
|
2451
|
+
transport: "http",
|
|
2452
|
+
requestId,
|
|
2453
|
+
sessionId
|
|
2454
|
+
});
|
|
2455
|
+
debugJson(logger$15, "Alpha search Copilot Responses result:", result);
|
|
2456
|
+
alphaSearchResponsesDependencies.createUsageRecorder(model, sessionId)({
|
|
2457
|
+
...normalizeResponsesUsage(result.usage),
|
|
2458
|
+
total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
|
|
2459
|
+
});
|
|
2460
|
+
return result;
|
|
2461
|
+
}
|
|
2462
|
+
async function requestRemoteSearch(c, request, state, target) {
|
|
2463
|
+
const instruction = buildInstruction(state.remoteOperations, request.commands?.response_length);
|
|
2464
|
+
const sessionId = getUUID(request.id);
|
|
2465
|
+
const requestId = generateRequestIdFromPayload({ messages: `${state.turn.number}:${instruction}` }, sessionId);
|
|
2466
|
+
const payload = createRemoteResponsesPayload(request, target.model, instruction);
|
|
2467
|
+
if (target.providerConfig) return await requestProviderSearch(c, target.providerConfig, payload, target.model, sessionId);
|
|
2468
|
+
return await requestCopilotSearch(payload, target.model, requestId, sessionId);
|
|
2469
|
+
}
|
|
2470
|
+
function getActiveRemoteReferences(session, references) {
|
|
2471
|
+
return references.filter((reference) => session.referencesById.get(reference.result.ref_id) === reference);
|
|
2472
|
+
}
|
|
2473
|
+
function buildSnapshotLinks(state, target, markdownReferences, remoteReferences) {
|
|
2474
|
+
return [...new Map([...markdownReferences, ...getActiveRemoteReferences(state.session, remoteReferences)].filter((reference) => reference.result.url !== target.result.url && state.session.referencesById.get(reference.result.ref_id) === reference).map((reference) => [reference.result.url, reference])).values()];
|
|
2475
|
+
}
|
|
2476
|
+
function processRemotePages(state, answerText, markdownReferences, remoteReferences) {
|
|
2477
|
+
for (const [index, page] of state.remotePages.entries()) {
|
|
2478
|
+
const target = addUrlReference(state.session, {
|
|
2479
|
+
url: page.source.result.url,
|
|
2480
|
+
title: page.source.result.title,
|
|
2481
|
+
snippet: answerText
|
|
2482
|
+
}, state.turn) ?? page.source;
|
|
2483
|
+
includeResult(state, target);
|
|
2484
|
+
const snapshotLinks = buildSnapshotLinks(state, target, markdownReferences, remoteReferences);
|
|
2485
|
+
if (page.kind === "find") {
|
|
2486
|
+
state.output.push(`Find results for ${JSON.stringify(page.pattern ?? "")} in ${target.result.url}:\n${answerText}`);
|
|
2487
|
+
if (!findSnapshotByUrl(state.session, target.result.url)) addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
|
|
2488
|
+
continue;
|
|
2489
|
+
}
|
|
2490
|
+
const snapshot = addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
|
|
2491
|
+
state.output.push(renderSnapshot(snapshot, page.lineno));
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
function appendActiveSources(state, references) {
|
|
2495
|
+
if (references.length === 0) return;
|
|
2496
|
+
state.output.push(["Sources:", ...references.map((reference) => `- [${reference.result.ref_id}] ${reference.result.title} — ${reference.result.url}`)].join("\n"));
|
|
2497
|
+
}
|
|
2498
|
+
function processRemoteResult(result, state) {
|
|
2499
|
+
const extracted = extractWebSearchResult(result);
|
|
2500
|
+
const citedSources = extracted.sources.filter((source) => source.snippet !== void 0);
|
|
2501
|
+
const relevantSources = citedSources.length > 0 ? citedSources : extracted.sources;
|
|
2502
|
+
consola.log(`--> web search: operations=${[...new Set(state.remoteOperations.map((remoteOperation) => remoteOperation.operation))].join(",")} queries=${JSON.stringify(extracted.queries)} sources=${relevantSources.length}`);
|
|
2503
|
+
const remoteReferences = relevantSources.map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
|
|
2504
|
+
for (const reference of getActiveRemoteReferences(state.session, remoteReferences)) includeResult(state, reference);
|
|
2505
|
+
const answerText = extracted.answerText || "GitHub Copilot web search returned no text.";
|
|
2506
|
+
const markdownReferences = state.remotePages.length === 0 ? [] : extractMarkdownSources(answerText).map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
|
|
2507
|
+
for (const reference of markdownReferences) includeResult(state, reference);
|
|
2508
|
+
if (state.remotePages.length === 0) state.output.push(answerText);
|
|
2509
|
+
processRemotePages(state, answerText, markdownReferences, remoteReferences);
|
|
2510
|
+
appendActiveSources(state, getActiveRemoteReferences(state.session, remoteReferences));
|
|
2511
|
+
}
|
|
2512
|
+
async function executeRemoteOperations(c, request, state, provider) {
|
|
2513
|
+
const target = await resolveRemoteModel(c, request, provider);
|
|
2514
|
+
if (target instanceof Response) return target;
|
|
2515
|
+
processRemoteResult(await requestRemoteSearch(c, request, state, target), state);
|
|
2516
|
+
return null;
|
|
2517
|
+
}
|
|
2518
|
+
function buildAlphaSearchResponse(state) {
|
|
2519
|
+
state.output.push(...state.warnings);
|
|
2520
|
+
if (state.output.length === 0) state.output.push("No supported search operations were requested.");
|
|
2521
|
+
return {
|
|
2522
|
+
encrypted_output: null,
|
|
2523
|
+
output: state.output.join("\n\n"),
|
|
2524
|
+
results: [...state.resultReferences.values()].filter((reference) => state.session.referencesById.get(reference.result.ref_id) === reference).map(({ result }) => result)
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
async function handleAlphaSearchResponses(c, options) {
|
|
2528
|
+
const parsed = alphaSearchRequestSchema.safeParse(options.request);
|
|
2529
|
+
if (!parsed.success) {
|
|
2530
|
+
const issue = parsed.error.issues[0];
|
|
2531
|
+
return invalidRequest$1(c, `Invalid alpha search request at ${issue?.path.join(".") || "body"}: ${issue?.message ?? "invalid value"}`);
|
|
2532
|
+
}
|
|
2533
|
+
const request = parsed.data;
|
|
2534
|
+
const now = alphaSearchResponsesDependencies.now();
|
|
2535
|
+
const reservation = reserveSession(request.id, now);
|
|
2536
|
+
const session = reservation.session;
|
|
2537
|
+
const state = createSearchOperationState(session, {
|
|
2538
|
+
number: reservation.turn,
|
|
2539
|
+
nextReferenceIndex: 0
|
|
2540
|
+
});
|
|
2541
|
+
processCommandOperations(request.commands ?? {}, state, now);
|
|
2542
|
+
addLiveAccessWarning(request, state);
|
|
2543
|
+
if (shouldExecuteRemoteOperations(request, state)) {
|
|
2544
|
+
const remoteResponse = await executeRemoteOperations(c, request, state, options.provider);
|
|
2545
|
+
if (remoteResponse) return remoteResponse;
|
|
2546
|
+
}
|
|
2547
|
+
return c.json(buildAlphaSearchResponse(state));
|
|
2548
|
+
}
|
|
2549
|
+
//#endregion
|
|
2550
|
+
//#region src/services/codex/alpha-search.ts
|
|
2551
|
+
const CODEX_ALPHA_SEARCH_URL = `${CODEX_API_BASE_URL}/codex/alpha/search`;
|
|
2552
|
+
function resolveCodexAlphaSearchUrl(requestUrl) {
|
|
2553
|
+
const upstreamUrl = new URL(CODEX_ALPHA_SEARCH_URL);
|
|
2554
|
+
upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
|
|
2555
|
+
return upstreamUrl.toString();
|
|
2556
|
+
}
|
|
2557
|
+
async function forwardCodexAlphaSearch(request) {
|
|
2558
|
+
const headers = buildCodexRequestHeaders(request.headers);
|
|
2559
|
+
if (!headers.has("accept")) headers.set("accept", "application/json");
|
|
2560
|
+
const body = await request.arrayBuffer();
|
|
2561
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
2562
|
+
return await fetch(resolveCodexAlphaSearchUrl(request.url), {
|
|
2563
|
+
method: "POST",
|
|
2564
|
+
headers,
|
|
2565
|
+
body
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
//#endregion
|
|
2569
|
+
//#region src/routes/alpha-search/route.ts
|
|
2570
|
+
const logger$14 = createHandlerLogger("alpha-search-handler");
|
|
2571
|
+
const alphaSearchRoutes = new Hono();
|
|
2572
|
+
function parseDebugBody(body) {
|
|
2573
|
+
try {
|
|
2574
|
+
return JSON.parse(body);
|
|
2575
|
+
} catch {
|
|
2576
|
+
return body;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
async function forwardCodexAlphaSearchRequest(request) {
|
|
2580
|
+
await debugJsonAsync(logger$14, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await request.clone().text()) }));
|
|
2581
|
+
const upstreamResponse = await forwardCodexAlphaSearch(request);
|
|
2582
|
+
await debugJsonAsync(logger$14, "alpha_search.codex.response", async () => ({
|
|
2583
|
+
body: parseDebugBody(await upstreamResponse.clone().text()),
|
|
2584
|
+
statusCode: upstreamResponse.status
|
|
2585
|
+
}));
|
|
2586
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
2587
|
+
}
|
|
2588
|
+
function createAlphaSearchRequest(request, payload) {
|
|
2589
|
+
return new Request(request, {
|
|
2590
|
+
body: JSON.stringify(payload),
|
|
2591
|
+
method: "post"
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
async function handleCodexRequest(c, request, resolvedProviderConfig) {
|
|
2595
|
+
if (!(resolvedProviderConfig ?? await resolveProviderConfig("codex"))) return c.json({ error: {
|
|
2596
|
+
message: "Provider 'codex' not found or disabled",
|
|
2597
|
+
type: "invalid_request_error"
|
|
2598
|
+
} }, 404);
|
|
2599
|
+
return await forwardCodexAlphaSearchRequest(request);
|
|
2600
|
+
}
|
|
2601
|
+
function invalidRequest(c, message) {
|
|
2602
|
+
return c.json({ error: {
|
|
2603
|
+
message,
|
|
2604
|
+
type: "invalid_request_error"
|
|
2605
|
+
} }, 400);
|
|
2606
|
+
}
|
|
2607
|
+
async function parseAlphaSearchBody(c) {
|
|
2608
|
+
let body;
|
|
2609
|
+
try {
|
|
2610
|
+
body = await c.req.raw.clone().json();
|
|
2611
|
+
} catch {
|
|
2612
|
+
return invalidRequest(c, "Invalid alpha search request: expected JSON body");
|
|
2613
|
+
}
|
|
2614
|
+
if (typeof body?.model !== "string") return invalidRequest(c, "Invalid alpha search request: model must be a string");
|
|
2615
|
+
return body;
|
|
2616
|
+
}
|
|
2617
|
+
/**
|
|
2618
|
+
* Handles top-level alpha-search dispatch. Pass `resolvedProviderConfig` when
|
|
2619
|
+
* the provider-scoped route has already resolved Codex. Codex is preferred
|
|
2620
|
+
* for every top-level request while alphaSearchCodexPriority stays enabled.
|
|
2621
|
+
*/
|
|
2622
|
+
async function handleAlphaSearchRequest(c, resolvedProviderConfig) {
|
|
2623
|
+
if (resolvedProviderConfig) return await handleCodexRequest(c, c.req.raw, resolvedProviderConfig);
|
|
2624
|
+
const payload = await parseAlphaSearchBody(c);
|
|
2625
|
+
if (payload instanceof Response) return payload;
|
|
2626
|
+
const requestedModel = payload.model;
|
|
2627
|
+
payload.model = resolveMappedModel(requestedModel);
|
|
2628
|
+
if (payload.model !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
|
|
2629
|
+
const providerModelAlias = parseProviderModelAlias(payload.model);
|
|
2630
|
+
if (providerModelAlias) {
|
|
2631
|
+
payload.model = providerModelAlias.model;
|
|
2632
|
+
if (providerModelAlias.provider === "codex") return await handleCodexRequest(c, createAlphaSearchRequest(c.req.raw, payload));
|
|
2633
|
+
}
|
|
2634
|
+
if (isAlphaSearchCodexPriorityEnabled()) {
|
|
2635
|
+
if (await resolveProviderConfig("codex")) return await forwardCodexAlphaSearchRequest(createAlphaSearchRequest(c.req.raw, payload));
|
|
2636
|
+
}
|
|
2637
|
+
if (providerModelAlias) return await handleAlphaSearchResponses(c, {
|
|
2638
|
+
provider: providerModelAlias.provider,
|
|
2639
|
+
request: payload
|
|
2640
|
+
});
|
|
2641
|
+
return await handleAlphaSearchResponses(c, { request: {
|
|
2642
|
+
...payload,
|
|
2643
|
+
model: requestedModel
|
|
2644
|
+
} });
|
|
2645
|
+
}
|
|
2646
|
+
alphaSearchRoutes.post("/", async (c) => {
|
|
2647
|
+
try {
|
|
2648
|
+
return await handleAlphaSearchRequest(c);
|
|
2649
|
+
} catch (error) {
|
|
2650
|
+
logger$14.error("alpha_search.error", { error });
|
|
2651
|
+
return await forwardError(c, error);
|
|
2652
|
+
}
|
|
2653
|
+
});
|
|
2654
|
+
//#endregion
|
|
2655
|
+
//#region src/lib/dashscope.ts
|
|
2656
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
|
|
2657
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
|
|
2658
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
|
|
2659
|
+
"system",
|
|
2660
|
+
"user",
|
|
2661
|
+
"assistant",
|
|
2662
|
+
"tool"
|
|
2663
|
+
]);
|
|
2664
|
+
const isDashScopeAliyunProvider = (providerConfig) => providerConfig.name === "dashscope" || providerConfig.baseUrl.includes("aliyuncs.com");
|
|
2665
|
+
const applyDashScopePreserveThinkingDefault = (payload, providerConfig) => {
|
|
2666
|
+
if (!isDashScopeAliyunProvider(providerConfig)) return;
|
|
2667
|
+
if (!Object.hasOwn(payload, "preserve_thinking")) payload.preserve_thinking = true;
|
|
2668
|
+
};
|
|
2669
|
+
const applyOpenAICompatibleContextCache = (payload) => {
|
|
2670
|
+
const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
|
|
2671
|
+
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
2672
|
+
};
|
|
1692
2673
|
const selectContextCacheMessageIndexes = (messages) => {
|
|
1693
2674
|
const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
|
|
1694
2675
|
const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
|
|
@@ -1767,123 +2748,50 @@ const applyMissingExtraBody$1 = (payload, options) => {
|
|
|
1767
2748
|
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
1768
2749
|
};
|
|
1769
2750
|
const applyProviderStreamOptions = (payload) => {
|
|
1770
|
-
if (!payload.stream) return;
|
|
1771
|
-
payload.stream_options = {
|
|
1772
|
-
...payload.stream_options ?? {},
|
|
1773
|
-
include_usage: true
|
|
1774
|
-
};
|
|
1775
|
-
};
|
|
1776
|
-
const applyProviderContextCache = (payload, modelConfig, providerConfig) => {
|
|
1777
|
-
const isDashScopeProvider = isDashScopeAliyunProvider(providerConfig);
|
|
1778
|
-
if (modelConfig?.contextCache ?? isDashScopeProvider) applyOpenAICompatibleContextCache(payload);
|
|
1779
|
-
};
|
|
1780
|
-
const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
|
|
1781
|
-
endpoint: "chat_completions",
|
|
1782
|
-
model: payload.model,
|
|
1783
|
-
pricing: modelConfig?.pricing,
|
|
1784
|
-
pricingCurrency,
|
|
1785
|
-
providerName: provider
|
|
1786
|
-
});
|
|
1787
|
-
const streamProviderChatCompletions = (c, upstreamResponse, options) => {
|
|
1788
|
-
logger$13.debug("provider.chat_completions.streaming", { provider: options.provider });
|
|
1789
|
-
return streamSSE(c, async (stream) => {
|
|
1790
|
-
let usage = {};
|
|
1791
|
-
try {
|
|
1792
|
-
for await (const chunk of events(upstreamResponse)) {
|
|
1793
|
-
debugJson(logger$13, "provider.chat_completions.stream_chunk", chunk);
|
|
1794
|
-
if (chunk.data && chunk.data !== "[DONE]") {
|
|
1795
|
-
const parsedChunk = parseChatCompletionChunkData(chunk.data);
|
|
1796
|
-
if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
|
|
1797
|
-
}
|
|
1798
|
-
await stream.writeSSE({
|
|
1799
|
-
event: chunk.event,
|
|
1800
|
-
data: chunk.data ?? ""
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
} finally {
|
|
1804
|
-
options.recordUsage(usage);
|
|
1805
|
-
}
|
|
1806
|
-
});
|
|
1807
|
-
};
|
|
1808
|
-
const parseChatCompletionChunkData = (data) => {
|
|
1809
|
-
try {
|
|
1810
|
-
return JSON.parse(data);
|
|
1811
|
-
} catch {
|
|
1812
|
-
return null;
|
|
1813
|
-
}
|
|
1814
|
-
};
|
|
1815
|
-
//#endregion
|
|
1816
|
-
//#region src/lib/copilot-rate-limit.ts
|
|
1817
|
-
const copilotRateLimitTypes = ["session", "weekly"];
|
|
1818
|
-
const copilotRateLimitHeaders = {
|
|
1819
|
-
session: "x-usage-ratelimit-session",
|
|
1820
|
-
weekly: "x-usage-ratelimit-weekly"
|
|
1821
|
-
};
|
|
1822
|
-
const copilotQuotaSnapshotKeys = {
|
|
1823
|
-
session: "5Hour-Session-RateLimits",
|
|
1824
|
-
weekly: "Weekly-Session-RateLimits"
|
|
1825
|
-
};
|
|
1826
|
-
const hasGetMethod = (headers) => {
|
|
1827
|
-
return "get" in headers && typeof headers.get === "function";
|
|
1828
|
-
};
|
|
1829
|
-
const getHeaderValue$1 = (headers, headerName) => {
|
|
1830
|
-
if (hasGetMethod(headers)) return headers.get(headerName);
|
|
1831
|
-
const normalizedHeaderName = headerName.toLowerCase();
|
|
1832
|
-
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
|
|
1833
|
-
};
|
|
1834
|
-
const parseCopilotRateLimitHeader = (headerValue) => {
|
|
1835
|
-
const params = new URLSearchParams(headerValue);
|
|
1836
|
-
const remaining = params.get("rem");
|
|
1837
|
-
const resetAt = params.get("rst");
|
|
1838
|
-
if (!remaining || !resetAt) return null;
|
|
1839
|
-
return {
|
|
1840
|
-
remaining,
|
|
1841
|
-
resetAt
|
|
1842
|
-
};
|
|
1843
|
-
};
|
|
1844
|
-
const getCopilotRateLimitUsage = (headers, type) => {
|
|
1845
|
-
const headerName = copilotRateLimitHeaders[type];
|
|
1846
|
-
const headerValue = getHeaderValue$1(headers, headerName);
|
|
1847
|
-
if (!headerValue) return null;
|
|
1848
|
-
const parsed = parseCopilotRateLimitHeader(headerValue);
|
|
1849
|
-
if (!parsed) return null;
|
|
1850
|
-
return {
|
|
1851
|
-
type,
|
|
1852
|
-
...parsed
|
|
1853
|
-
};
|
|
1854
|
-
};
|
|
1855
|
-
const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
|
|
1856
|
-
const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
|
|
1857
|
-
if (!isCopilotQuotaSnapshot(snapshot)) return null;
|
|
1858
|
-
return {
|
|
1859
|
-
remaining: String(snapshot.percent_remaining),
|
|
1860
|
-
resetAt: snapshot.reset_date,
|
|
1861
|
-
type
|
|
2751
|
+
if (!payload.stream) return;
|
|
2752
|
+
payload.stream_options = {
|
|
2753
|
+
...payload.stream_options ?? {},
|
|
2754
|
+
include_usage: true
|
|
1862
2755
|
};
|
|
1863
2756
|
};
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
if (!usage) continue;
|
|
1868
|
-
logCopilotRateLimitUsage(usage);
|
|
1869
|
-
}
|
|
1870
|
-
};
|
|
1871
|
-
const logCopilotQuotaSnapshots = (snapshots) => {
|
|
1872
|
-
for (const type of copilotRateLimitTypes) {
|
|
1873
|
-
const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
|
|
1874
|
-
if (!usage) continue;
|
|
1875
|
-
logCopilotRateLimitUsage(usage);
|
|
1876
|
-
}
|
|
2757
|
+
const applyProviderContextCache = (payload, modelConfig, providerConfig) => {
|
|
2758
|
+
const isDashScopeProvider = isDashScopeAliyunProvider(providerConfig);
|
|
2759
|
+
if (modelConfig?.contextCache ?? isDashScopeProvider) applyOpenAICompatibleContextCache(payload);
|
|
1877
2760
|
};
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
2761
|
+
const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
|
|
2762
|
+
endpoint: "chat_completions",
|
|
2763
|
+
model: payload.model,
|
|
2764
|
+
pricing: modelConfig?.pricing,
|
|
2765
|
+
pricingCurrency,
|
|
2766
|
+
providerName: provider
|
|
2767
|
+
});
|
|
2768
|
+
const streamProviderChatCompletions = (c, upstreamResponse, options) => {
|
|
2769
|
+
logger$13.debug("provider.chat_completions.streaming", { provider: options.provider });
|
|
2770
|
+
return streamSSE(c, async (stream) => {
|
|
2771
|
+
let usage = {};
|
|
2772
|
+
try {
|
|
2773
|
+
for await (const chunk of events(upstreamResponse)) {
|
|
2774
|
+
debugJson(logger$13, "provider.chat_completions.stream_chunk", chunk);
|
|
2775
|
+
if (chunk.data && chunk.data !== "[DONE]") {
|
|
2776
|
+
const parsedChunk = parseChatCompletionChunkData(chunk.data);
|
|
2777
|
+
if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
|
|
2778
|
+
}
|
|
2779
|
+
await stream.writeSSE({
|
|
2780
|
+
event: chunk.event,
|
|
2781
|
+
data: chunk.data ?? ""
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
} finally {
|
|
2785
|
+
options.recordUsage(usage);
|
|
2786
|
+
}
|
|
2787
|
+
});
|
|
1882
2788
|
};
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2789
|
+
const parseChatCompletionChunkData = (data) => {
|
|
2790
|
+
try {
|
|
2791
|
+
return JSON.parse(data);
|
|
2792
|
+
} catch {
|
|
2793
|
+
return null;
|
|
2794
|
+
}
|
|
1887
2795
|
};
|
|
1888
2796
|
//#endregion
|
|
1889
2797
|
//#region src/services/copilot/create-chat-completions.ts
|
|
@@ -3579,143 +4487,6 @@ function closeThinkingBlockIfOpen(state, events) {
|
|
|
3579
4487
|
}
|
|
3580
4488
|
}
|
|
3581
4489
|
//#endregion
|
|
3582
|
-
//#region src/services/copilot/create-responses.ts
|
|
3583
|
-
const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
|
|
3584
|
-
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
3585
|
-
const headers = {
|
|
3586
|
-
...copilotHeaders(state, requestId, vision),
|
|
3587
|
-
"x-initiator": initiator
|
|
3588
|
-
};
|
|
3589
|
-
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
3590
|
-
prepareForCompact(headers, compactType);
|
|
3591
|
-
payload.service_tier = void 0;
|
|
3592
|
-
consola.log(`<-- model: ${payload.model}`);
|
|
3593
|
-
const effectiveTransport = compactType === 1 ? "http" : transport;
|
|
3594
|
-
if (payload.stream === true && effectiveTransport === "websocket") return createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
|
|
3595
|
-
requestId,
|
|
3596
|
-
subagentMarker
|
|
3597
|
-
}));
|
|
3598
|
-
return await createHttpResponses(payload, headers);
|
|
3599
|
-
};
|
|
3600
|
-
const createHttpResponses = async (payload, headers) => {
|
|
3601
|
-
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
|
3602
|
-
method: "POST",
|
|
3603
|
-
headers,
|
|
3604
|
-
body: JSON.stringify(payload)
|
|
3605
|
-
});
|
|
3606
|
-
logCopilotRateLimits(response.headers);
|
|
3607
|
-
if (!response.ok) {
|
|
3608
|
-
consola.error("Failed to create responses", response);
|
|
3609
|
-
throw new HTTPError("Failed to create responses", response);
|
|
3610
|
-
}
|
|
3611
|
-
if (payload.stream) return events(response);
|
|
3612
|
-
return await response.json();
|
|
3613
|
-
};
|
|
3614
|
-
const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
|
|
3615
|
-
const initiator = getResponsesWebSocketInitiator(preparedHeaders);
|
|
3616
|
-
return {
|
|
3617
|
-
headers: copilotWebSocketHeaders(preparedHeaders),
|
|
3618
|
-
poolKey: buildResponsesWebSocketPoolKey(payload, options),
|
|
3619
|
-
payload: buildResponsesWebSocketPayload(payload, initiator),
|
|
3620
|
-
url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
|
|
3621
|
-
};
|
|
3622
|
-
};
|
|
3623
|
-
const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
|
|
3624
|
-
const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
|
|
3625
|
-
const subagentKey = subagentMarker ? [
|
|
3626
|
-
subagentMarker.session_id,
|
|
3627
|
-
subagentMarker.agent_id,
|
|
3628
|
-
subagentMarker.agent_type
|
|
3629
|
-
].join(":") : "main";
|
|
3630
|
-
return [
|
|
3631
|
-
tokenFingerprint,
|
|
3632
|
-
payload.model,
|
|
3633
|
-
requestId,
|
|
3634
|
-
subagentKey
|
|
3635
|
-
].map(encodePoolKeyPart).join("|");
|
|
3636
|
-
};
|
|
3637
|
-
const getResponsesWebSocketInitiator = (preparedHeaders) => {
|
|
3638
|
-
return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
|
|
3639
|
-
};
|
|
3640
|
-
const createPooledResponsesWebSocketStream = (request) => createResponsesSafeStream(createPooledWebSocketStream(request, {
|
|
3641
|
-
createChunk: createResponsesWebSocketStreamChunk,
|
|
3642
|
-
isTerminalChunk: isTerminalResponsesStreamChunk,
|
|
3643
|
-
openErrorMessage: "Failed to create responses websocket",
|
|
3644
|
-
streamErrorMessage: "Responses websocket stream error",
|
|
3645
|
-
terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
|
|
3646
|
-
}));
|
|
3647
|
-
const createResponsesSafeStream = async function* (source) {
|
|
3648
|
-
try {
|
|
3649
|
-
yield* source;
|
|
3650
|
-
} catch (error) {
|
|
3651
|
-
yield createResponsesErrorServerSentEventChunk(getErrorMessage(error));
|
|
3652
|
-
}
|
|
3653
|
-
};
|
|
3654
|
-
const buildResponsesWebSocketPayload = (payload, initiator) => {
|
|
3655
|
-
const websocketPayload = {
|
|
3656
|
-
...payload,
|
|
3657
|
-
type: "response.create",
|
|
3658
|
-
initiator
|
|
3659
|
-
};
|
|
3660
|
-
delete websocketPayload.stream;
|
|
3661
|
-
delete websocketPayload["background"];
|
|
3662
|
-
delete websocketPayload.service_tier;
|
|
3663
|
-
return websocketPayload;
|
|
3664
|
-
};
|
|
3665
|
-
const buildResponsesWebSocketUrl = (baseUrl) => {
|
|
3666
|
-
return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
|
|
3667
|
-
};
|
|
3668
|
-
const getHeaderValue = (headers, headerName) => {
|
|
3669
|
-
const normalizedHeaderName = headerName.toLowerCase();
|
|
3670
|
-
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
3671
|
-
};
|
|
3672
|
-
const encodePoolKeyPart = (value) => encodeURIComponent(value);
|
|
3673
|
-
const createResponsesWebSocketStreamChunk = (data) => {
|
|
3674
|
-
if (data === "[DONE]") return { data };
|
|
3675
|
-
try {
|
|
3676
|
-
const parsed = JSON.parse(data);
|
|
3677
|
-
if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
|
|
3678
|
-
if (parsed.type === "error" && parsed.error) {
|
|
3679
|
-
consola.warn("Copilot responses websocket stream error:", parsed.error);
|
|
3680
|
-
parsed.code = parsed.error.code;
|
|
3681
|
-
parsed.message = parsed.error.message;
|
|
3682
|
-
}
|
|
3683
|
-
return {
|
|
3684
|
-
event: typeof parsed.type === "string" ? parsed.type : void 0,
|
|
3685
|
-
data: JSON.stringify(parsed),
|
|
3686
|
-
id: typeof parsed.id === "string" ? parsed.id : void 0
|
|
3687
|
-
};
|
|
3688
|
-
} catch {
|
|
3689
|
-
return { data };
|
|
3690
|
-
}
|
|
3691
|
-
};
|
|
3692
|
-
const isTerminalResponsesStreamChunk = (chunk) => {
|
|
3693
|
-
if (!chunk.data || chunk.data === "[DONE]") return false;
|
|
3694
|
-
try {
|
|
3695
|
-
const parsed = JSON.parse(chunk.data);
|
|
3696
|
-
return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
|
|
3697
|
-
} catch {
|
|
3698
|
-
return false;
|
|
3699
|
-
}
|
|
3700
|
-
};
|
|
3701
|
-
const createResponsesErrorServerSentEventChunk = (message) => {
|
|
3702
|
-
const errorEvent = {
|
|
3703
|
-
code: null,
|
|
3704
|
-
message,
|
|
3705
|
-
param: null,
|
|
3706
|
-
sequence_number: 0,
|
|
3707
|
-
type: "error"
|
|
3708
|
-
};
|
|
3709
|
-
return {
|
|
3710
|
-
event: errorEvent.type,
|
|
3711
|
-
data: JSON.stringify(errorEvent)
|
|
3712
|
-
};
|
|
3713
|
-
};
|
|
3714
|
-
const getErrorMessage = (error) => {
|
|
3715
|
-
if (error instanceof Error && error.message) return error.message;
|
|
3716
|
-
return String(error);
|
|
3717
|
-
};
|
|
3718
|
-
//#endregion
|
|
3719
4490
|
//#region src/routes/messages/responses-translation.ts
|
|
3720
4491
|
const MESSAGE_TYPE = "message";
|
|
3721
4492
|
const COMPACTION_SIGNATURE_PREFIX = "cm1#";
|
|
@@ -5037,72 +5808,6 @@ const containsVisionContent = (value) => {
|
|
|
5037
5808
|
return false;
|
|
5038
5809
|
};
|
|
5039
5810
|
//#endregion
|
|
5040
|
-
//#region src/routes/messages/web-search/backend.ts
|
|
5041
|
-
/** Builds the Responses API web_search tool object from the Anthropic config. */
|
|
5042
|
-
const buildResponsesWebSearchTool = (config) => {
|
|
5043
|
-
const tool = { type: "web_search" };
|
|
5044
|
-
const filters = {};
|
|
5045
|
-
if (config.allowedDomains?.length) filters.allowed_domains = config.allowedDomains;
|
|
5046
|
-
if (config.blockedDomains?.length) filters.blocked_domains = config.blockedDomains;
|
|
5047
|
-
if (Object.keys(filters).length > 0) tool.filters = filters;
|
|
5048
|
-
if (config.userLocation) tool.user_location = config.userLocation;
|
|
5049
|
-
return tool;
|
|
5050
|
-
};
|
|
5051
|
-
const isMessageItem = (item) => item.type === "message";
|
|
5052
|
-
const isValidUrlCitation = (annotation, seenUrls) => {
|
|
5053
|
-
const ann = annotation;
|
|
5054
|
-
return ann.type === "url_citation" && Boolean(ann.url) && !seenUrls.has(ann.url);
|
|
5055
|
-
};
|
|
5056
|
-
const collectTextParts = (blocks, seenUrls) => {
|
|
5057
|
-
const textParts = [];
|
|
5058
|
-
const sources = [];
|
|
5059
|
-
for (const block of blocks ?? []) {
|
|
5060
|
-
if (block.type !== "output_text") continue;
|
|
5061
|
-
if (block.text) textParts.push(block.text);
|
|
5062
|
-
for (const annotation of block.annotations ?? []) {
|
|
5063
|
-
if (!isValidUrlCitation(annotation, seenUrls)) continue;
|
|
5064
|
-
const ann = annotation;
|
|
5065
|
-
seenUrls.add(ann.url);
|
|
5066
|
-
sources.push({
|
|
5067
|
-
url: ann.url,
|
|
5068
|
-
title: ann.title ?? ann.url
|
|
5069
|
-
});
|
|
5070
|
-
}
|
|
5071
|
-
}
|
|
5072
|
-
return {
|
|
5073
|
-
textParts,
|
|
5074
|
-
sources
|
|
5075
|
-
};
|
|
5076
|
-
};
|
|
5077
|
-
const collectQuery = (item, queries) => {
|
|
5078
|
-
if (item.action?.queries?.length) queries.push(...item.action.queries);
|
|
5079
|
-
else if (item.action?.query) queries.push(item.action.query);
|
|
5080
|
-
};
|
|
5081
|
-
/**
|
|
5082
|
-
* Extracts the answer text, deduped sources, and run queries from a GPT
|
|
5083
|
-
* /responses web_search result.
|
|
5084
|
-
*/
|
|
5085
|
-
const extractWebSearchResult = (result) => {
|
|
5086
|
-
const textParts = [];
|
|
5087
|
-
const sources = [];
|
|
5088
|
-
const seenUrls = /* @__PURE__ */ new Set();
|
|
5089
|
-
const queries = [];
|
|
5090
|
-
for (const item of result.output) {
|
|
5091
|
-
if (isMessageItem(item)) {
|
|
5092
|
-
const collected = collectTextParts(item.content, seenUrls);
|
|
5093
|
-
textParts.push(...collected.textParts);
|
|
5094
|
-
sources.push(...collected.sources);
|
|
5095
|
-
continue;
|
|
5096
|
-
}
|
|
5097
|
-
if (item.type === "web_search_call") collectQuery(item, queries);
|
|
5098
|
-
}
|
|
5099
|
-
return {
|
|
5100
|
-
answerText: textParts.join("\n\n").trim() || (result.output_text ?? "").trim(),
|
|
5101
|
-
sources,
|
|
5102
|
-
queries
|
|
5103
|
-
};
|
|
5104
|
-
};
|
|
5105
|
-
//#endregion
|
|
5106
5811
|
//#region src/routes/messages/web-search/fulfill.ts
|
|
5107
5812
|
const webSearchFlowDependencies = {
|
|
5108
5813
|
createResponses,
|
|
@@ -6686,7 +7391,7 @@ providerAlphaSearchRoutes.post("/", async (c) => {
|
|
|
6686
7391
|
message: `Provider '${provider}' not found or disabled`,
|
|
6687
7392
|
type: "invalid_request_error"
|
|
6688
7393
|
} }, 404);
|
|
6689
|
-
if (providerConfig.name === "codex") return await
|
|
7394
|
+
if (providerConfig.name === "codex") return await handleAlphaSearchRequest(c, providerConfig);
|
|
6690
7395
|
await debugJsonAsync(logger$5, "provider.alpha_search.request", async () => ({
|
|
6691
7396
|
body: await c.req.raw.clone().text(),
|
|
6692
7397
|
provider
|
|
@@ -7259,4 +7964,4 @@ server.route("/:provider/images", providerImageRoutes);
|
|
|
7259
7964
|
//#endregion
|
|
7260
7965
|
export { server };
|
|
7261
7966
|
|
|
7262
|
-
//# sourceMappingURL=server-
|
|
7967
|
+
//# sourceMappingURL=server-DWY7PmxG.js.map
|