@jeffreycao/copilot-api 1.14.23 → 1.15.0
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/auth-BuVLgbbO.js +2 -0
- package/dist/{auth-Dx98-OPn.js → auth-DqCboaUp.js} +3 -3
- package/dist/{auth-Dx98-OPn.js.map → auth-DqCboaUp.js.map} +1 -1
- 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-DZtBoW6V.js} +1223 -544
- package/dist/server-DZtBoW6V.js.map +1 -0
- package/dist/{start-C1hWSQR4.js → start-i6nxPCwq.js} +6 -6
- package/dist/{start-C1hWSQR4.js.map → start-i6nxPCwq.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/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";
|
|
@@ -327,203 +327,6 @@ const createHandlerLogger = (name) => {
|
|
|
327
327
|
return instance;
|
|
328
328
|
};
|
|
329
329
|
//#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
330
|
//#region src/lib/provider-model.ts
|
|
528
331
|
const parseProviderModelAlias = (model) => {
|
|
529
332
|
const separatorIndex = model.indexOf("/");
|
|
@@ -554,6 +357,31 @@ const createFallbackModel = (modelId) => ({
|
|
|
554
357
|
version: "unknown"
|
|
555
358
|
});
|
|
556
359
|
//#endregion
|
|
360
|
+
//#region src/lib/provider-resolver.ts
|
|
361
|
+
function isMissingCodexCredentialsError(error) {
|
|
362
|
+
return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
|
|
363
|
+
}
|
|
364
|
+
async function resolveProviderConfig(providerName) {
|
|
365
|
+
const normalizedProviderName = providerName.trim();
|
|
366
|
+
if (!normalizedProviderName) return null;
|
|
367
|
+
if (normalizedProviderName === "codex") {
|
|
368
|
+
if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
|
|
369
|
+
try {
|
|
370
|
+
await setupCodexToken();
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (isMissingCodexCredentialsError(error)) return null;
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
const providerConfig = getProviderConfig(normalizedProviderName);
|
|
376
|
+
if (!providerConfig) return null;
|
|
377
|
+
return {
|
|
378
|
+
...providerConfig,
|
|
379
|
+
apiKey: state.codexAccessToken ?? providerConfig.apiKey
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
return getProviderConfig(normalizedProviderName);
|
|
383
|
+
}
|
|
384
|
+
//#endregion
|
|
557
385
|
//#region src/lib/event-bus.ts
|
|
558
386
|
var EventBus = class {
|
|
559
387
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -1671,34 +1499,1161 @@ function normalizeOptionalCost(value) {
|
|
|
1671
1499
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1672
1500
|
}
|
|
1673
1501
|
//#endregion
|
|
1674
|
-
//#region src/
|
|
1675
|
-
const
|
|
1676
|
-
const
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1502
|
+
//#region src/routes/alpha-search/alpha-search-types.ts
|
|
1503
|
+
const unsignedInteger = z.number().int().nonnegative();
|
|
1504
|
+
const passthroughRecord = z.record(z.string(), z.unknown());
|
|
1505
|
+
const searchQuerySchema = z.object({
|
|
1506
|
+
q: z.string(),
|
|
1507
|
+
recency: unsignedInteger.optional(),
|
|
1508
|
+
domains: z.array(z.string()).optional()
|
|
1509
|
+
}).loose();
|
|
1510
|
+
const openOperationSchema = z.object({
|
|
1511
|
+
ref_id: z.string(),
|
|
1512
|
+
lineno: unsignedInteger.optional()
|
|
1513
|
+
}).loose();
|
|
1514
|
+
const clickOperationSchema = z.object({
|
|
1515
|
+
ref_id: z.string(),
|
|
1516
|
+
id: unsignedInteger
|
|
1517
|
+
}).loose();
|
|
1518
|
+
const findOperationSchema = z.object({
|
|
1519
|
+
ref_id: z.string(),
|
|
1520
|
+
pattern: z.string()
|
|
1521
|
+
}).loose();
|
|
1522
|
+
const screenshotOperationSchema = z.object({
|
|
1523
|
+
ref_id: z.string(),
|
|
1524
|
+
pageno: unsignedInteger
|
|
1525
|
+
}).loose();
|
|
1526
|
+
const financeOperationSchema = z.object({
|
|
1527
|
+
ticker: z.string(),
|
|
1528
|
+
type: z.enum([
|
|
1529
|
+
"equity",
|
|
1530
|
+
"fund",
|
|
1531
|
+
"crypto",
|
|
1532
|
+
"index"
|
|
1533
|
+
]),
|
|
1534
|
+
market: z.string().optional()
|
|
1535
|
+
}).loose();
|
|
1536
|
+
const weatherOperationSchema = z.object({
|
|
1537
|
+
location: z.string(),
|
|
1538
|
+
start: z.string().optional(),
|
|
1539
|
+
duration: unsignedInteger.optional()
|
|
1540
|
+
}).loose();
|
|
1541
|
+
const sportsOperationSchema = z.object({
|
|
1542
|
+
tool: z.literal("sports").optional(),
|
|
1543
|
+
fn: z.enum(["schedule", "standings"]),
|
|
1544
|
+
league: z.enum([
|
|
1545
|
+
"nba",
|
|
1546
|
+
"wnba",
|
|
1547
|
+
"nfl",
|
|
1548
|
+
"nhl",
|
|
1549
|
+
"mlb",
|
|
1550
|
+
"epl",
|
|
1551
|
+
"ncaamb",
|
|
1552
|
+
"ncaawb",
|
|
1553
|
+
"ipl"
|
|
1554
|
+
]),
|
|
1555
|
+
team: z.string().optional(),
|
|
1556
|
+
opponent: z.string().optional(),
|
|
1557
|
+
date_from: z.string().optional(),
|
|
1558
|
+
date_to: z.string().optional(),
|
|
1559
|
+
num_games: unsignedInteger.optional(),
|
|
1560
|
+
locale: z.string().optional()
|
|
1561
|
+
}).loose();
|
|
1562
|
+
const timeOperationSchema = z.object({ utc_offset: z.string().regex(/^[+-](?:[01]\d|2[0-3]):[0-5]\d$/u) }).loose();
|
|
1563
|
+
const alphaSearchCommandsSchema = z.object({
|
|
1564
|
+
search_query: z.array(searchQuerySchema).optional(),
|
|
1565
|
+
image_query: z.array(searchQuerySchema).optional(),
|
|
1566
|
+
open: z.array(openOperationSchema).optional(),
|
|
1567
|
+
click: z.array(clickOperationSchema).optional(),
|
|
1568
|
+
find: z.array(findOperationSchema).optional(),
|
|
1569
|
+
screenshot: z.array(screenshotOperationSchema).optional(),
|
|
1570
|
+
finance: z.array(financeOperationSchema).optional(),
|
|
1571
|
+
weather: z.array(weatherOperationSchema).optional(),
|
|
1572
|
+
sports: z.array(sportsOperationSchema).optional(),
|
|
1573
|
+
time: z.array(timeOperationSchema).optional(),
|
|
1574
|
+
response_length: z.enum([
|
|
1575
|
+
"short",
|
|
1576
|
+
"medium",
|
|
1577
|
+
"long"
|
|
1578
|
+
]).optional()
|
|
1579
|
+
}).loose();
|
|
1580
|
+
const reasoningSchema = z.object({
|
|
1581
|
+
effort: z.string().min(1).nullable().optional(),
|
|
1582
|
+
summary: z.enum([
|
|
1583
|
+
"auto",
|
|
1584
|
+
"concise",
|
|
1585
|
+
"detailed",
|
|
1586
|
+
"none"
|
|
1587
|
+
]).nullable().optional(),
|
|
1588
|
+
context: z.enum([
|
|
1589
|
+
"auto",
|
|
1590
|
+
"current_turn",
|
|
1591
|
+
"all_turns"
|
|
1592
|
+
]).nullable().optional()
|
|
1593
|
+
}).loose();
|
|
1594
|
+
const settingsSchema = z.object({
|
|
1595
|
+
user_location: z.object({
|
|
1596
|
+
type: z.literal("approximate"),
|
|
1597
|
+
country: z.string().optional(),
|
|
1598
|
+
region: z.string().optional(),
|
|
1599
|
+
city: z.string().optional(),
|
|
1600
|
+
timezone: z.string().optional()
|
|
1601
|
+
}).loose().optional(),
|
|
1602
|
+
search_context_size: z.enum([
|
|
1603
|
+
"low",
|
|
1604
|
+
"medium",
|
|
1605
|
+
"high"
|
|
1606
|
+
]).optional(),
|
|
1607
|
+
filters: z.object({
|
|
1608
|
+
allowed_domains: z.array(z.string()).optional(),
|
|
1609
|
+
blocked_domains: z.array(z.string()).optional()
|
|
1610
|
+
}).loose().optional(),
|
|
1611
|
+
image_settings: z.object({
|
|
1612
|
+
max_results: unsignedInteger.optional(),
|
|
1613
|
+
caption: z.boolean().optional()
|
|
1614
|
+
}).loose().optional(),
|
|
1615
|
+
allowed_callers: z.array(z.enum([
|
|
1616
|
+
"direct",
|
|
1617
|
+
"shell",
|
|
1618
|
+
"code_interpreter"
|
|
1619
|
+
])).optional(),
|
|
1620
|
+
external_web_access: z.union([z.boolean(), z.enum([
|
|
1621
|
+
"cached",
|
|
1622
|
+
"indexed",
|
|
1623
|
+
"live"
|
|
1624
|
+
])]).optional()
|
|
1625
|
+
}).loose();
|
|
1626
|
+
const alphaSearchRequestSchema = z.object({
|
|
1627
|
+
id: z.string(),
|
|
1628
|
+
model: z.string(),
|
|
1629
|
+
reasoning: reasoningSchema.optional(),
|
|
1630
|
+
input: z.union([z.string(), z.array(passthroughRecord)]).optional(),
|
|
1631
|
+
commands: alphaSearchCommandsSchema.optional(),
|
|
1632
|
+
settings: settingsSchema.optional(),
|
|
1633
|
+
max_output_tokens: unsignedInteger.optional()
|
|
1634
|
+
}).loose();
|
|
1635
|
+
//#endregion
|
|
1636
|
+
//#region src/routes/messages/web-search/backend.ts
|
|
1637
|
+
/** Builds the Responses API web_search tool object from normalized config. */
|
|
1638
|
+
const buildResponsesWebSearchTool = (config) => {
|
|
1639
|
+
const tool = { type: "web_search" };
|
|
1640
|
+
const filters = {};
|
|
1641
|
+
if (config.allowedDomains?.length) filters.allowed_domains = config.allowedDomains;
|
|
1642
|
+
if (config.blockedDomains?.length) filters.blocked_domains = config.blockedDomains;
|
|
1643
|
+
if (Object.keys(filters).length > 0) tool.filters = filters;
|
|
1644
|
+
if (config.userLocation) tool.user_location = config.userLocation;
|
|
1645
|
+
if (config.searchContextSize) tool.search_context_size = config.searchContextSize;
|
|
1646
|
+
return tool;
|
|
1691
1647
|
};
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1694
|
-
const
|
|
1695
|
-
|
|
1696
|
-
return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
|
|
1648
|
+
const isMessageItem = (item) => item.type === "message";
|
|
1649
|
+
const isValidUrlCitation = (annotation, seenUrls) => {
|
|
1650
|
+
const ann = annotation;
|
|
1651
|
+
return ann.type === "url_citation" && Boolean(ann.url) && !seenUrls.has(ann.url);
|
|
1697
1652
|
};
|
|
1698
|
-
const
|
|
1699
|
-
const
|
|
1700
|
-
|
|
1701
|
-
|
|
1653
|
+
const collectTextParts = (blocks, seenUrls) => {
|
|
1654
|
+
const textParts = [];
|
|
1655
|
+
const sources = [];
|
|
1656
|
+
for (const block of blocks ?? []) {
|
|
1657
|
+
if (block.type !== "output_text") continue;
|
|
1658
|
+
if (block.text) textParts.push(block.text);
|
|
1659
|
+
for (const annotation of block.annotations ?? []) {
|
|
1660
|
+
if (!isValidUrlCitation(annotation, seenUrls)) continue;
|
|
1661
|
+
const ann = annotation;
|
|
1662
|
+
seenUrls.add(ann.url);
|
|
1663
|
+
const start = Math.max(0, (ann.start_index ?? 0) - 120);
|
|
1664
|
+
const end = Math.min(block.text?.length ?? 0, (ann.end_index ?? block.text?.length ?? 0) + 120);
|
|
1665
|
+
sources.push({
|
|
1666
|
+
url: ann.url,
|
|
1667
|
+
title: ann.title ?? ann.url,
|
|
1668
|
+
snippet: block.text?.slice(start, end).trim()
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return {
|
|
1673
|
+
textParts,
|
|
1674
|
+
sources
|
|
1675
|
+
};
|
|
1676
|
+
};
|
|
1677
|
+
const collectQuery = (item, queries) => {
|
|
1678
|
+
if (item.action?.queries?.length) queries.push(...item.action.queries);
|
|
1679
|
+
else if (item.action?.query) queries.push(item.action.query);
|
|
1680
|
+
};
|
|
1681
|
+
/**
|
|
1682
|
+
* Extracts the answer text, deduped sources, and run queries from a GPT
|
|
1683
|
+
* /responses web_search result.
|
|
1684
|
+
*/
|
|
1685
|
+
const extractWebSearchResult = (result) => {
|
|
1686
|
+
const textParts = [];
|
|
1687
|
+
const sources = [];
|
|
1688
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
1689
|
+
const queries = [];
|
|
1690
|
+
for (const item of result.output) if (isMessageItem(item)) {
|
|
1691
|
+
const collected = collectTextParts(item.content, seenUrls);
|
|
1692
|
+
textParts.push(...collected.textParts);
|
|
1693
|
+
sources.push(...collected.sources);
|
|
1694
|
+
}
|
|
1695
|
+
for (const item of result.output) if (item.type === "web_search_call") {
|
|
1696
|
+
const action = item.action;
|
|
1697
|
+
collectQuery({ action }, queries);
|
|
1698
|
+
for (const source of action?.sources ?? []) {
|
|
1699
|
+
if (!source.url || seenUrls.has(source.url)) continue;
|
|
1700
|
+
seenUrls.add(source.url);
|
|
1701
|
+
sources.push({
|
|
1702
|
+
url: source.url,
|
|
1703
|
+
title: source.url
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
return {
|
|
1708
|
+
answerText: textParts.join("\n\n").trim() || (result.output_text ?? "").trim(),
|
|
1709
|
+
sources,
|
|
1710
|
+
queries
|
|
1711
|
+
};
|
|
1712
|
+
};
|
|
1713
|
+
//#endregion
|
|
1714
|
+
//#region src/lib/copilot-rate-limit.ts
|
|
1715
|
+
const copilotRateLimitTypes = ["session", "weekly"];
|
|
1716
|
+
const copilotRateLimitHeaders = {
|
|
1717
|
+
session: "x-usage-ratelimit-session",
|
|
1718
|
+
weekly: "x-usage-ratelimit-weekly"
|
|
1719
|
+
};
|
|
1720
|
+
const copilotQuotaSnapshotKeys = {
|
|
1721
|
+
session: "5Hour-Session-RateLimits",
|
|
1722
|
+
weekly: "Weekly-Session-RateLimits"
|
|
1723
|
+
};
|
|
1724
|
+
const hasGetMethod = (headers) => {
|
|
1725
|
+
return "get" in headers && typeof headers.get === "function";
|
|
1726
|
+
};
|
|
1727
|
+
const getHeaderValue$1 = (headers, headerName) => {
|
|
1728
|
+
if (hasGetMethod(headers)) return headers.get(headerName);
|
|
1729
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
1730
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
|
|
1731
|
+
};
|
|
1732
|
+
const parseCopilotRateLimitHeader = (headerValue) => {
|
|
1733
|
+
const params = new URLSearchParams(headerValue);
|
|
1734
|
+
const remaining = params.get("rem");
|
|
1735
|
+
const resetAt = params.get("rst");
|
|
1736
|
+
if (!remaining || !resetAt) return null;
|
|
1737
|
+
return {
|
|
1738
|
+
remaining,
|
|
1739
|
+
resetAt
|
|
1740
|
+
};
|
|
1741
|
+
};
|
|
1742
|
+
const getCopilotRateLimitUsage = (headers, type) => {
|
|
1743
|
+
const headerName = copilotRateLimitHeaders[type];
|
|
1744
|
+
const headerValue = getHeaderValue$1(headers, headerName);
|
|
1745
|
+
if (!headerValue) return null;
|
|
1746
|
+
const parsed = parseCopilotRateLimitHeader(headerValue);
|
|
1747
|
+
if (!parsed) return null;
|
|
1748
|
+
return {
|
|
1749
|
+
type,
|
|
1750
|
+
...parsed
|
|
1751
|
+
};
|
|
1752
|
+
};
|
|
1753
|
+
const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
|
|
1754
|
+
const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
|
|
1755
|
+
if (!isCopilotQuotaSnapshot(snapshot)) return null;
|
|
1756
|
+
return {
|
|
1757
|
+
remaining: String(snapshot.percent_remaining),
|
|
1758
|
+
resetAt: snapshot.reset_date,
|
|
1759
|
+
type
|
|
1760
|
+
};
|
|
1761
|
+
};
|
|
1762
|
+
const logCopilotRateLimits = (headers) => {
|
|
1763
|
+
for (const type of copilotRateLimitTypes) {
|
|
1764
|
+
const usage = getCopilotRateLimitUsage(headers, type);
|
|
1765
|
+
if (!usage) continue;
|
|
1766
|
+
logCopilotRateLimitUsage(usage);
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
const logCopilotQuotaSnapshots = (snapshots) => {
|
|
1770
|
+
for (const type of copilotRateLimitTypes) {
|
|
1771
|
+
const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
|
|
1772
|
+
if (!usage) continue;
|
|
1773
|
+
logCopilotRateLimitUsage(usage);
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
const logCopilotRateLimitUsage = (usage) => {
|
|
1777
|
+
const d = new Date(usage.resetAt);
|
|
1778
|
+
const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
|
|
1779
|
+
consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
|
|
1780
|
+
};
|
|
1781
|
+
const isCopilotQuotaSnapshot = (value) => {
|
|
1782
|
+
if (!value || typeof value !== "object") return false;
|
|
1783
|
+
const record = value;
|
|
1784
|
+
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";
|
|
1785
|
+
};
|
|
1786
|
+
//#endregion
|
|
1787
|
+
//#region src/services/copilot/create-responses.ts
|
|
1788
|
+
const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
|
|
1789
|
+
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
1790
|
+
const headers = {
|
|
1791
|
+
...copilotHeaders(state, requestId, vision),
|
|
1792
|
+
"x-initiator": initiator
|
|
1793
|
+
};
|
|
1794
|
+
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
1795
|
+
prepareForCompact(headers, compactType);
|
|
1796
|
+
payload.service_tier = void 0;
|
|
1797
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1798
|
+
const effectiveTransport = compactType === 1 ? "http" : transport;
|
|
1799
|
+
if (payload.stream === true && effectiveTransport === "websocket") return createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
|
|
1800
|
+
requestId,
|
|
1801
|
+
subagentMarker
|
|
1802
|
+
}));
|
|
1803
|
+
return await createHttpResponses(payload, headers);
|
|
1804
|
+
};
|
|
1805
|
+
const createHttpResponses = async (payload, headers) => {
|
|
1806
|
+
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
|
1807
|
+
method: "POST",
|
|
1808
|
+
headers,
|
|
1809
|
+
body: JSON.stringify(payload)
|
|
1810
|
+
});
|
|
1811
|
+
logCopilotRateLimits(response.headers);
|
|
1812
|
+
if (!response.ok) {
|
|
1813
|
+
consola.error("Failed to create responses", response);
|
|
1814
|
+
throw new HTTPError("Failed to create responses", response);
|
|
1815
|
+
}
|
|
1816
|
+
if (payload.stream) return events(response);
|
|
1817
|
+
return await response.json();
|
|
1818
|
+
};
|
|
1819
|
+
const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
|
|
1820
|
+
const initiator = getResponsesWebSocketInitiator(preparedHeaders);
|
|
1821
|
+
return {
|
|
1822
|
+
headers: copilotWebSocketHeaders(preparedHeaders),
|
|
1823
|
+
poolKey: buildResponsesWebSocketPoolKey(payload, options),
|
|
1824
|
+
payload: buildResponsesWebSocketPayload(payload, initiator),
|
|
1825
|
+
url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
|
|
1826
|
+
};
|
|
1827
|
+
};
|
|
1828
|
+
const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
|
|
1829
|
+
const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
|
|
1830
|
+
const subagentKey = subagentMarker ? [
|
|
1831
|
+
subagentMarker.session_id,
|
|
1832
|
+
subagentMarker.agent_id,
|
|
1833
|
+
subagentMarker.agent_type
|
|
1834
|
+
].join(":") : "main";
|
|
1835
|
+
return [
|
|
1836
|
+
tokenFingerprint,
|
|
1837
|
+
payload.model,
|
|
1838
|
+
requestId,
|
|
1839
|
+
subagentKey
|
|
1840
|
+
].map(encodePoolKeyPart).join("|");
|
|
1841
|
+
};
|
|
1842
|
+
const getResponsesWebSocketInitiator = (preparedHeaders) => {
|
|
1843
|
+
return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
|
|
1844
|
+
};
|
|
1845
|
+
const createPooledResponsesWebSocketStream = (request) => createResponsesSafeStream(createPooledWebSocketStream(request, {
|
|
1846
|
+
createChunk: createResponsesWebSocketStreamChunk,
|
|
1847
|
+
isTerminalChunk: isTerminalResponsesStreamChunk,
|
|
1848
|
+
openErrorMessage: "Failed to create responses websocket",
|
|
1849
|
+
streamErrorMessage: "Responses websocket stream error",
|
|
1850
|
+
terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
|
|
1851
|
+
}));
|
|
1852
|
+
const createResponsesSafeStream = async function* (source) {
|
|
1853
|
+
try {
|
|
1854
|
+
yield* source;
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
yield createResponsesErrorServerSentEventChunk(getErrorMessage(error));
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
const buildResponsesWebSocketPayload = (payload, initiator) => {
|
|
1860
|
+
const websocketPayload = {
|
|
1861
|
+
...payload,
|
|
1862
|
+
type: "response.create",
|
|
1863
|
+
initiator
|
|
1864
|
+
};
|
|
1865
|
+
delete websocketPayload.stream;
|
|
1866
|
+
delete websocketPayload["background"];
|
|
1867
|
+
delete websocketPayload.service_tier;
|
|
1868
|
+
return websocketPayload;
|
|
1869
|
+
};
|
|
1870
|
+
const buildResponsesWebSocketUrl = (baseUrl) => {
|
|
1871
|
+
return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
|
|
1872
|
+
};
|
|
1873
|
+
const getHeaderValue = (headers, headerName) => {
|
|
1874
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
1875
|
+
return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
|
|
1876
|
+
};
|
|
1877
|
+
const encodePoolKeyPart = (value) => encodeURIComponent(value);
|
|
1878
|
+
const createResponsesWebSocketStreamChunk = (data) => {
|
|
1879
|
+
if (data === "[DONE]") return { data };
|
|
1880
|
+
try {
|
|
1881
|
+
const parsed = JSON.parse(data);
|
|
1882
|
+
if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
|
|
1883
|
+
if (parsed.type === "error" && parsed.error) {
|
|
1884
|
+
consola.warn("Copilot responses websocket stream error:", parsed.error);
|
|
1885
|
+
parsed.code = parsed.error.code;
|
|
1886
|
+
parsed.message = parsed.error.message;
|
|
1887
|
+
}
|
|
1888
|
+
return {
|
|
1889
|
+
event: typeof parsed.type === "string" ? parsed.type : void 0,
|
|
1890
|
+
data: JSON.stringify(parsed),
|
|
1891
|
+
id: typeof parsed.id === "string" ? parsed.id : void 0
|
|
1892
|
+
};
|
|
1893
|
+
} catch {
|
|
1894
|
+
return { data };
|
|
1895
|
+
}
|
|
1896
|
+
};
|
|
1897
|
+
const isTerminalResponsesStreamChunk = (chunk) => {
|
|
1898
|
+
if (!chunk.data || chunk.data === "[DONE]") return false;
|
|
1899
|
+
try {
|
|
1900
|
+
const parsed = JSON.parse(chunk.data);
|
|
1901
|
+
return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
|
|
1902
|
+
} catch {
|
|
1903
|
+
return false;
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1906
|
+
const createResponsesErrorServerSentEventChunk = (message) => {
|
|
1907
|
+
const errorEvent = {
|
|
1908
|
+
code: null,
|
|
1909
|
+
message,
|
|
1910
|
+
param: null,
|
|
1911
|
+
sequence_number: 0,
|
|
1912
|
+
type: "error"
|
|
1913
|
+
};
|
|
1914
|
+
return {
|
|
1915
|
+
event: errorEvent.type,
|
|
1916
|
+
data: JSON.stringify(errorEvent)
|
|
1917
|
+
};
|
|
1918
|
+
};
|
|
1919
|
+
const getErrorMessage = (error) => {
|
|
1920
|
+
if (error instanceof Error && error.message) return error.message;
|
|
1921
|
+
return String(error);
|
|
1922
|
+
};
|
|
1923
|
+
//#endregion
|
|
1924
|
+
//#region src/services/providers/provider-proxy.ts
|
|
1925
|
+
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
1926
|
+
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
1927
|
+
const STRIPPED_RESPONSE_HEADERS = [
|
|
1928
|
+
"connection",
|
|
1929
|
+
"content-encoding",
|
|
1930
|
+
"content-length",
|
|
1931
|
+
"keep-alive",
|
|
1932
|
+
"proxy-authenticate",
|
|
1933
|
+
"proxy-authorization",
|
|
1934
|
+
"te",
|
|
1935
|
+
"trailer",
|
|
1936
|
+
"transfer-encoding",
|
|
1937
|
+
"upgrade"
|
|
1938
|
+
];
|
|
1939
|
+
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
1940
|
+
const authHeaders = {};
|
|
1941
|
+
if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
1942
|
+
else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
1943
|
+
const headers = {
|
|
1944
|
+
"content-type": "application/json",
|
|
1945
|
+
accept: "application/json",
|
|
1946
|
+
...authHeaders
|
|
1947
|
+
};
|
|
1948
|
+
for (const headerName of SHARED_FORWARDABLE_HEADERS) {
|
|
1949
|
+
const headerValue = requestHeaders.get(headerName);
|
|
1950
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
1951
|
+
}
|
|
1952
|
+
if (providerConfig.type !== "anthropic") return headers;
|
|
1953
|
+
for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
|
|
1954
|
+
const headerValue = requestHeaders.get(headerName);
|
|
1955
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
1956
|
+
}
|
|
1957
|
+
return headers;
|
|
1958
|
+
}
|
|
1959
|
+
function createProviderProxyResponse(upstreamResponse, body) {
|
|
1960
|
+
const headers = new Headers(upstreamResponse.headers);
|
|
1961
|
+
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
1962
|
+
return new Response(body ?? upstreamResponse.body, {
|
|
1963
|
+
headers,
|
|
1964
|
+
status: upstreamResponse.status,
|
|
1965
|
+
statusText: upstreamResponse.statusText
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
1969
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1970
|
+
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
1971
|
+
method: "POST",
|
|
1972
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
1973
|
+
body: JSON.stringify(payload)
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
1977
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1978
|
+
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
1979
|
+
method: "POST",
|
|
1980
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
1981
|
+
body: JSON.stringify(payload)
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
|
|
1985
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1986
|
+
return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
|
|
1987
|
+
method: "POST",
|
|
1988
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
1989
|
+
body: JSON.stringify(payload)
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
1993
|
+
return await fetch(`${providerConfig.baseUrl}/v1/models`, {
|
|
1994
|
+
method: "GET",
|
|
1995
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
/** Align with Codex images: long-running generation/edits need a generous cap. */
|
|
1999
|
+
const PROVIDER_IMAGES_TIMEOUT_MS = 900 * 1e3;
|
|
2000
|
+
const providerImagesDispatcher = { dispatch(options, handler) {
|
|
2001
|
+
return getGlobalDispatcher().dispatch({
|
|
2002
|
+
...options,
|
|
2003
|
+
bodyTimeout: PROVIDER_IMAGES_TIMEOUT_MS,
|
|
2004
|
+
headersTimeout: PROVIDER_IMAGES_TIMEOUT_MS
|
|
2005
|
+
}, handler);
|
|
2006
|
+
} };
|
|
2007
|
+
function resolveProviderRequestUrl(providerConfig, requestUrl, path) {
|
|
2008
|
+
const upstreamUrl = new URL(`${providerConfig.baseUrl}${path}`);
|
|
2009
|
+
upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
|
|
2010
|
+
return upstreamUrl.toString();
|
|
2011
|
+
}
|
|
2012
|
+
async function forwardProviderAlphaSearch(providerConfig, request) {
|
|
2013
|
+
const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
|
|
2014
|
+
const body = await request.arrayBuffer();
|
|
2015
|
+
return await fetch(resolveProviderRequestUrl(providerConfig, request.url, "/v1/alpha/search"), {
|
|
2016
|
+
method: "POST",
|
|
2017
|
+
headers,
|
|
2018
|
+
body
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
async function forwardProviderImages(providerConfig, request, operation) {
|
|
2022
|
+
const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
|
|
2023
|
+
const contentType = request.headers.get("content-type");
|
|
2024
|
+
if (contentType) headers["content-type"] = contentType;
|
|
2025
|
+
else if (operation === "edits") delete headers["content-type"];
|
|
2026
|
+
const init = {
|
|
2027
|
+
method: "POST",
|
|
2028
|
+
headers,
|
|
2029
|
+
body: request.body,
|
|
2030
|
+
duplex: "half",
|
|
2031
|
+
signal: AbortSignal.timeout(PROVIDER_IMAGES_TIMEOUT_MS)
|
|
2032
|
+
};
|
|
2033
|
+
const upstreamUrl = resolveProviderRequestUrl(providerConfig, request.url, `/v1/images/${operation}`);
|
|
2034
|
+
if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
|
|
2035
|
+
return await fetch$1(upstreamUrl, {
|
|
2036
|
+
...init,
|
|
2037
|
+
dispatcher: providerImagesDispatcher
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
//#endregion
|
|
2041
|
+
//#region src/routes/alpha-search/alpha-search-responses.ts
|
|
2042
|
+
const logger$15 = createHandlerLogger("alpha-search-responses-handler");
|
|
2043
|
+
const SESSION_TTL_MS = 3600 * 1e3;
|
|
2044
|
+
const MAX_SESSIONS = 128;
|
|
2045
|
+
const MAX_URL_REFERENCES = 256;
|
|
2046
|
+
const MAX_SNAPSHOTS = 16;
|
|
2047
|
+
const MAX_SNAPSHOT_CHARACTERS = 2e4;
|
|
2048
|
+
const IMAGE_UNSUPPORTED = "Unsupported by GitHub Copilot web search: image_query. Do not retry this operation; use search_query for image-source pages.";
|
|
2049
|
+
const SCREENSHOT_UNSUPPORTED = "Unsupported by GitHub Copilot web search: screenshot. Do not retry this operation; open the PDF for text content.";
|
|
2050
|
+
const KNOWN_COMMANDS = new Set([
|
|
2051
|
+
"search_query",
|
|
2052
|
+
"image_query",
|
|
2053
|
+
"open",
|
|
2054
|
+
"click",
|
|
2055
|
+
"find",
|
|
2056
|
+
"screenshot",
|
|
2057
|
+
"finance",
|
|
2058
|
+
"weather",
|
|
2059
|
+
"sports",
|
|
2060
|
+
"time",
|
|
2061
|
+
"response_length"
|
|
2062
|
+
]);
|
|
2063
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
2064
|
+
const alphaSearchResponsesDependencies = {
|
|
2065
|
+
createResponses,
|
|
2066
|
+
findEndpointModel,
|
|
2067
|
+
now: () => Date.now(),
|
|
2068
|
+
resolveMappedModel,
|
|
2069
|
+
createUsageRecorder: (model, sessionId) => createCopilotTokenUsageRecorder({
|
|
2070
|
+
endpoint: "responses",
|
|
2071
|
+
fallbackSessionId: sessionId,
|
|
2072
|
+
model
|
|
2073
|
+
})
|
|
2074
|
+
};
|
|
2075
|
+
function reserveSession(id, now) {
|
|
2076
|
+
for (const [sessionId, session] of sessions) if (now - session.touchedAt >= SESSION_TTL_MS) sessions.delete(sessionId);
|
|
2077
|
+
let session = sessions.get(id);
|
|
2078
|
+
if (!session) {
|
|
2079
|
+
if (sessions.size >= MAX_SESSIONS) {
|
|
2080
|
+
const oldestSession = sessions.keys().next();
|
|
2081
|
+
if (!oldestSession.done) sessions.delete(oldestSession.value);
|
|
2082
|
+
}
|
|
2083
|
+
session = {
|
|
2084
|
+
nextTurn: 0,
|
|
2085
|
+
touchedAt: now,
|
|
2086
|
+
referencesById: /* @__PURE__ */ new Map(),
|
|
2087
|
+
referencesByUrl: /* @__PURE__ */ new Map(),
|
|
2088
|
+
snapshots: /* @__PURE__ */ new Map()
|
|
2089
|
+
};
|
|
2090
|
+
} else {
|
|
2091
|
+
sessions.delete(id);
|
|
2092
|
+
session.touchedAt = now;
|
|
2093
|
+
}
|
|
2094
|
+
sessions.set(id, session);
|
|
2095
|
+
const turn = session.nextTurn;
|
|
2096
|
+
session.nextTurn += 1;
|
|
2097
|
+
return {
|
|
2098
|
+
session,
|
|
2099
|
+
turn
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
function parseHttpUrl(value) {
|
|
2103
|
+
try {
|
|
2104
|
+
const url = new URL(value);
|
|
2105
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
|
|
2106
|
+
} catch {
|
|
2107
|
+
return null;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
function extractMarkdownSources(text) {
|
|
2111
|
+
return Array.from(text.matchAll(/(?<!!)\[([^\]\n]+)\]\((https?:\/\/(?:[^\s<>()]|\([^\s<>()]*\))+)\)/gu), ([, title, url]) => ({
|
|
2112
|
+
title,
|
|
2113
|
+
url
|
|
2114
|
+
}));
|
|
2115
|
+
}
|
|
2116
|
+
function addUrlReference(session, source, turn) {
|
|
2117
|
+
const url = parseHttpUrl(source.url);
|
|
2118
|
+
if (!url) return null;
|
|
2119
|
+
const existing = session.referencesByUrl.get(url);
|
|
2120
|
+
if (existing) {
|
|
2121
|
+
if (existing.result.title === existing.result.url && source.title) existing.result.title = source.title;
|
|
2122
|
+
if (source.snippet) existing.result.snippet = source.snippet;
|
|
2123
|
+
return existing;
|
|
2124
|
+
}
|
|
2125
|
+
if (session.referencesById.size >= MAX_URL_REFERENCES) {
|
|
2126
|
+
const oldest = session.referencesById.entries().next().value;
|
|
2127
|
+
if (oldest) {
|
|
2128
|
+
const [refId, reference] = oldest;
|
|
2129
|
+
session.referencesById.delete(refId);
|
|
2130
|
+
session.referencesByUrl.delete(reference.result.url);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
const refId = `turn${turn.number}search${turn.nextReferenceIndex}`;
|
|
2134
|
+
turn.nextReferenceIndex += 1;
|
|
2135
|
+
const reference = { result: {
|
|
2136
|
+
type: "text_result",
|
|
2137
|
+
domain: new URL(url).hostname,
|
|
2138
|
+
ref_id: refId,
|
|
2139
|
+
snippet: source.snippet?.trim() || source.title || url,
|
|
2140
|
+
title: source.title || url,
|
|
2141
|
+
url
|
|
2142
|
+
} };
|
|
2143
|
+
session.referencesById.set(refId, reference);
|
|
2144
|
+
session.referencesByUrl.set(url, reference);
|
|
2145
|
+
return reference;
|
|
2146
|
+
}
|
|
2147
|
+
function resolveUrlReference(session, refId, turn) {
|
|
2148
|
+
const stored = session.referencesById.get(refId);
|
|
2149
|
+
if (stored) return stored;
|
|
2150
|
+
const url = parseHttpUrl(refId);
|
|
2151
|
+
return url ? addUrlReference(session, {
|
|
2152
|
+
title: url,
|
|
2153
|
+
url
|
|
2154
|
+
}, turn) : null;
|
|
2155
|
+
}
|
|
2156
|
+
function getSnapshot(session, refId) {
|
|
2157
|
+
const snapshot = session.snapshots.get(refId);
|
|
2158
|
+
if (!snapshot) return null;
|
|
2159
|
+
session.snapshots.delete(refId);
|
|
2160
|
+
session.snapshots.set(refId, snapshot);
|
|
2161
|
+
return snapshot;
|
|
2162
|
+
}
|
|
2163
|
+
function findSnapshotByUrl(session, url) {
|
|
2164
|
+
for (const snapshot of session.snapshots.values()) if (snapshot.source.result.url === url) return getSnapshot(session, snapshot.refId);
|
|
2165
|
+
return null;
|
|
2166
|
+
}
|
|
2167
|
+
function addSnapshot(session, source, turn, index, text, links) {
|
|
2168
|
+
const existing = findSnapshotByUrl(session, source.result.url);
|
|
2169
|
+
if (existing) {
|
|
2170
|
+
existing.text = text.slice(0, MAX_SNAPSHOT_CHARACTERS);
|
|
2171
|
+
existing.links = links;
|
|
2172
|
+
return existing;
|
|
2173
|
+
}
|
|
2174
|
+
if (session.snapshots.size >= MAX_SNAPSHOTS) {
|
|
2175
|
+
const oldestRefId = session.snapshots.keys().next().value;
|
|
2176
|
+
if (oldestRefId) session.snapshots.delete(oldestRefId);
|
|
2177
|
+
}
|
|
2178
|
+
const snapshot = {
|
|
2179
|
+
refId: `turn${turn}view${index}`,
|
|
2180
|
+
source,
|
|
2181
|
+
text: text.slice(0, MAX_SNAPSHOT_CHARACTERS),
|
|
2182
|
+
links
|
|
2183
|
+
};
|
|
2184
|
+
session.snapshots.set(snapshot.refId, snapshot);
|
|
2185
|
+
return snapshot;
|
|
2186
|
+
}
|
|
2187
|
+
function renderSnapshot(snapshot, lineno) {
|
|
2188
|
+
const lines = snapshot.text.split("\n");
|
|
2189
|
+
const start = lineno === void 0 ? 0 : Math.max(0, Math.min(lineno, lines.length - 1) - 10);
|
|
2190
|
+
const end = lineno === void 0 ? lines.length : Math.min(lines.length, start + 21);
|
|
2191
|
+
const numbered = lines.slice(start, end).map((line, index) => `L${start + index}: ${line}`).join("\n");
|
|
2192
|
+
const links = snapshot.links.map((link, index) => `[${index}] ${link.result.title} — ${link.result.url} (${link.result.ref_id})`).join("\n");
|
|
2193
|
+
return [
|
|
2194
|
+
`Open ${snapshot.refId} (${snapshot.source.result.url})`,
|
|
2195
|
+
numbered,
|
|
2196
|
+
links ? `Links:\n${links}` : ""
|
|
2197
|
+
].filter(Boolean).join("\n");
|
|
2198
|
+
}
|
|
2199
|
+
function findInSnapshot(snapshot, pattern) {
|
|
2200
|
+
const normalizedPattern = pattern.toLowerCase();
|
|
2201
|
+
const matches = snapshot.text.split("\n").map((line, index) => ({
|
|
2202
|
+
line,
|
|
2203
|
+
index
|
|
2204
|
+
})).filter(({ line }) => line.toLowerCase().includes(normalizedPattern)).slice(0, 20);
|
|
2205
|
+
if (matches.length === 0) return null;
|
|
2206
|
+
return [`Find results for ${JSON.stringify(pattern)} in ${snapshot.refId}:`, ...matches.map(({ line, index }) => `L${index}: ${line}`)].join("\n");
|
|
2207
|
+
}
|
|
2208
|
+
function formatTime(utcOffset, now) {
|
|
2209
|
+
const offsetMinutes = (utcOffset.startsWith("+") ? 1 : -1) * (Number.parseInt(utcOffset.slice(1, 3), 10) * 60 + Number.parseInt(utcOffset.slice(4, 6), 10));
|
|
2210
|
+
return `Time at UTC${utcOffset}: ${new Date(now + offsetMinutes * 6e4).toISOString().slice(0, 19).replace("T", " ")}`;
|
|
2211
|
+
}
|
|
2212
|
+
function buildInstruction(operations, responseLength) {
|
|
2213
|
+
return [
|
|
2214
|
+
"The Operations JSON below is the complete and exclusive request for this response.",
|
|
2215
|
+
"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.",
|
|
2216
|
+
"Return only grounded results and citations for these operations. Do not discuss unrelated tasks or conversations.",
|
|
2217
|
+
"For search operations, honor every query, recency, domain, market, date, league, team, and locale constraint.",
|
|
2218
|
+
"For open operations, open the exact URL and return grounded page text plus cited links.",
|
|
2219
|
+
"For find operations, find the exact pattern in the specified URL and return matching context.",
|
|
2220
|
+
`Requested response length: ${responseLength ?? "medium"}.`,
|
|
2221
|
+
`Operations:\n${JSON.stringify(operations, null, 2)}`
|
|
2222
|
+
].join("\n");
|
|
2223
|
+
}
|
|
2224
|
+
function unavailableReference(refId) {
|
|
2225
|
+
return `Reference ${JSON.stringify(refId)} is unavailable or expired. Search or open the URL again.`;
|
|
2226
|
+
}
|
|
2227
|
+
function invalidRequest$1(c, message) {
|
|
2228
|
+
return c.json({ error: {
|
|
2229
|
+
message,
|
|
2230
|
+
type: "invalid_request_error"
|
|
2231
|
+
} }, 400);
|
|
2232
|
+
}
|
|
2233
|
+
function createSearchOperationState(session, turn) {
|
|
2234
|
+
return {
|
|
2235
|
+
session,
|
|
2236
|
+
turn,
|
|
2237
|
+
output: [],
|
|
2238
|
+
warnings: [],
|
|
2239
|
+
resultReferences: /* @__PURE__ */ new Map(),
|
|
2240
|
+
remoteOperations: [],
|
|
2241
|
+
remotePages: []
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
function includeResult(state, reference) {
|
|
2245
|
+
state.resultReferences.set(reference.result.url, reference);
|
|
2246
|
+
}
|
|
2247
|
+
function queuePage(state, kind, source, options = {}) {
|
|
2248
|
+
state.remoteOperations.push({
|
|
2249
|
+
operation: kind,
|
|
2250
|
+
url: source.result.url,
|
|
2251
|
+
...options.lineno === void 0 ? {} : { lineno: options.lineno },
|
|
2252
|
+
...options.pattern === void 0 ? {} : { pattern: options.pattern }
|
|
2253
|
+
});
|
|
2254
|
+
state.remotePages.push({
|
|
2255
|
+
kind,
|
|
2256
|
+
source,
|
|
2257
|
+
...options
|
|
2258
|
+
});
|
|
2259
|
+
includeResult(state, source);
|
|
2260
|
+
}
|
|
2261
|
+
function processSearchCommands(commands, state) {
|
|
2262
|
+
for (const command of commands.search_query ?? []) state.remoteOperations.push({
|
|
2263
|
+
...command,
|
|
2264
|
+
operation: "search_query"
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
function processOpenCommands(commands, state) {
|
|
2268
|
+
for (const command of commands.open ?? []) {
|
|
2269
|
+
const directSnapshot = getSnapshot(state.session, command.ref_id);
|
|
2270
|
+
if (directSnapshot) {
|
|
2271
|
+
state.output.push(renderSnapshot(directSnapshot, command.lineno));
|
|
2272
|
+
includeResult(state, directSnapshot.source);
|
|
2273
|
+
continue;
|
|
2274
|
+
}
|
|
2275
|
+
const source = resolveUrlReference(state.session, command.ref_id, state.turn);
|
|
2276
|
+
if (!source) {
|
|
2277
|
+
state.output.push(unavailableReference(command.ref_id));
|
|
2278
|
+
continue;
|
|
2279
|
+
}
|
|
2280
|
+
const cached = findSnapshotByUrl(state.session, source.result.url);
|
|
2281
|
+
if (cached) {
|
|
2282
|
+
state.output.push(renderSnapshot(cached, command.lineno));
|
|
2283
|
+
includeResult(state, source);
|
|
2284
|
+
continue;
|
|
2285
|
+
}
|
|
2286
|
+
queuePage(state, "open", source, { lineno: command.lineno });
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
function processClickCommands(commands, state) {
|
|
2290
|
+
for (const command of commands.click ?? []) {
|
|
2291
|
+
const source = getSnapshot(state.session, command.ref_id)?.links[command.id];
|
|
2292
|
+
if (!source) {
|
|
2293
|
+
state.output.push(unavailableReference(`${command.ref_id} link ${command.id}`));
|
|
2294
|
+
continue;
|
|
2295
|
+
}
|
|
2296
|
+
const cached = findSnapshotByUrl(state.session, source.result.url);
|
|
2297
|
+
if (cached) {
|
|
2298
|
+
state.output.push(renderSnapshot(cached));
|
|
2299
|
+
includeResult(state, source);
|
|
2300
|
+
continue;
|
|
2301
|
+
}
|
|
2302
|
+
queuePage(state, "open", source);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
function processFindCommands(commands, state) {
|
|
2306
|
+
for (const command of commands.find ?? []) {
|
|
2307
|
+
const directSnapshot = getSnapshot(state.session, command.ref_id);
|
|
2308
|
+
const source = directSnapshot?.source ?? resolveUrlReference(state.session, command.ref_id, state.turn);
|
|
2309
|
+
if (!source) {
|
|
2310
|
+
state.output.push(unavailableReference(command.ref_id));
|
|
2311
|
+
continue;
|
|
2312
|
+
}
|
|
2313
|
+
const snapshot = directSnapshot ?? findSnapshotByUrl(state.session, source.result.url);
|
|
2314
|
+
const localResult = snapshot ? findInSnapshot(snapshot, command.pattern) : null;
|
|
2315
|
+
if (localResult) {
|
|
2316
|
+
state.output.push(localResult);
|
|
2317
|
+
includeResult(state, source);
|
|
2318
|
+
continue;
|
|
2319
|
+
}
|
|
2320
|
+
queuePage(state, "find", source, { pattern: command.pattern });
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
function processStructuredCommands(commands, state, now) {
|
|
2324
|
+
for (const command of commands.finance ?? []) state.remoteOperations.push({
|
|
2325
|
+
...command,
|
|
2326
|
+
operation: "finance"
|
|
2327
|
+
});
|
|
2328
|
+
for (const command of commands.weather ?? []) state.remoteOperations.push({
|
|
2329
|
+
...command,
|
|
2330
|
+
operation: "weather"
|
|
2331
|
+
});
|
|
2332
|
+
for (const command of commands.sports ?? []) state.remoteOperations.push({
|
|
2333
|
+
...command,
|
|
2334
|
+
operation: "sports"
|
|
2335
|
+
});
|
|
2336
|
+
for (const command of commands.time ?? []) state.output.push(formatTime(command.utc_offset, now));
|
|
2337
|
+
}
|
|
2338
|
+
function processUnsupportedCommands(commands, state) {
|
|
2339
|
+
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.`);
|
|
2340
|
+
}
|
|
2341
|
+
function processCommandOperations(commands, state, now) {
|
|
2342
|
+
processSearchCommands(commands, state);
|
|
2343
|
+
if ((commands.image_query?.length ?? 0) > 0) state.warnings.push(IMAGE_UNSUPPORTED);
|
|
2344
|
+
processOpenCommands(commands, state);
|
|
2345
|
+
processClickCommands(commands, state);
|
|
2346
|
+
processFindCommands(commands, state);
|
|
2347
|
+
if ((commands.screenshot?.length ?? 0) > 0) state.warnings.push(SCREENSHOT_UNSUPPORTED);
|
|
2348
|
+
processStructuredCommands(commands, state, now);
|
|
2349
|
+
processUnsupportedCommands(commands, state);
|
|
2350
|
+
}
|
|
2351
|
+
function supportsLiveAccess(request) {
|
|
2352
|
+
const externalWebAccess = request.settings?.external_web_access;
|
|
2353
|
+
return externalWebAccess === void 0 || externalWebAccess === true || externalWebAccess === "live";
|
|
2354
|
+
}
|
|
2355
|
+
function addLiveAccessWarning(request, state) {
|
|
2356
|
+
if (state.remoteOperations.length === 0 || supportsLiveAccess(request)) return;
|
|
2357
|
+
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.`);
|
|
2358
|
+
}
|
|
2359
|
+
function shouldExecuteRemoteOperations(request, state) {
|
|
2360
|
+
return state.remoteOperations.length > 0 && supportsLiveAccess(request);
|
|
2361
|
+
}
|
|
2362
|
+
async function resolveRemoteModel(c, request, provider) {
|
|
2363
|
+
const model = provider ? request.model : alphaSearchResponsesDependencies.resolveMappedModel(request.model);
|
|
2364
|
+
if (provider) {
|
|
2365
|
+
const providerConfig = await resolveProviderConfig(provider);
|
|
2366
|
+
if (!providerConfig) return c.json({ error: {
|
|
2367
|
+
message: `Provider '${provider}' not found or disabled`,
|
|
2368
|
+
type: "invalid_request_error"
|
|
2369
|
+
} }, 404);
|
|
2370
|
+
if (resolveEffectiveProviderType(providerConfig, model) !== "openai-responses") return invalidRequest$1(c, `Provider '${provider}' does not support the /v1/responses endpoint required for alpha search`);
|
|
2371
|
+
return {
|
|
2372
|
+
model,
|
|
2373
|
+
providerConfig
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
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`);
|
|
2377
|
+
return { model };
|
|
2378
|
+
}
|
|
2379
|
+
function createRemoteResponsesPayload(request, model, instruction) {
|
|
2380
|
+
return {
|
|
2381
|
+
model,
|
|
2382
|
+
input: instruction,
|
|
2383
|
+
tools: [buildResponsesWebSearchTool({
|
|
2384
|
+
allowedDomains: request.settings?.filters?.allowed_domains,
|
|
2385
|
+
blockedDomains: request.settings?.filters?.blocked_domains,
|
|
2386
|
+
userLocation: request.settings?.user_location,
|
|
2387
|
+
searchContextSize: request.settings?.search_context_size
|
|
2388
|
+
})],
|
|
2389
|
+
tool_choice: "required",
|
|
2390
|
+
store: false,
|
|
2391
|
+
stream: false,
|
|
2392
|
+
include: ["web_search_call.action.sources"],
|
|
2393
|
+
reasoning: request.reasoning,
|
|
2394
|
+
max_output_tokens: request.max_output_tokens
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
async function requestProviderSearch(c, providerConfig, payload, model, sessionId) {
|
|
2398
|
+
debugJson(logger$15, "Alpha search provider Responses request:", {
|
|
2399
|
+
payload,
|
|
2400
|
+
provider: providerConfig.name
|
|
2401
|
+
});
|
|
2402
|
+
const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
|
|
2403
|
+
if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${providerConfig.name} responses for alpha search`, upstreamResponse);
|
|
2404
|
+
const result = await upstreamResponse.json();
|
|
2405
|
+
debugJson(logger$15, "Alpha search provider Responses result:", {
|
|
2406
|
+
provider: providerConfig.name,
|
|
2407
|
+
result
|
|
2408
|
+
});
|
|
2409
|
+
createProviderTokenUsageRecorder({
|
|
2410
|
+
endpoint: "responses",
|
|
2411
|
+
fallbackSessionId: sessionId,
|
|
2412
|
+
model,
|
|
2413
|
+
pricing: providerConfig.models?.[model]?.pricing,
|
|
2414
|
+
pricingCurrency: providerConfig.pricingCurrency,
|
|
2415
|
+
providerName: providerConfig.name,
|
|
2416
|
+
sessionId
|
|
2417
|
+
})(normalizeResponsesUsage(result.usage));
|
|
2418
|
+
return result;
|
|
2419
|
+
}
|
|
2420
|
+
async function requestCopilotSearch(payload, model, requestId, sessionId) {
|
|
2421
|
+
debugJson(logger$15, "Alpha search Copilot Responses request:", payload);
|
|
2422
|
+
const result = await alphaSearchResponsesDependencies.createResponses(payload, {
|
|
2423
|
+
vision: false,
|
|
2424
|
+
initiator: "agent",
|
|
2425
|
+
transport: "http",
|
|
2426
|
+
requestId,
|
|
2427
|
+
sessionId
|
|
2428
|
+
});
|
|
2429
|
+
debugJson(logger$15, "Alpha search Copilot Responses result:", result);
|
|
2430
|
+
alphaSearchResponsesDependencies.createUsageRecorder(model, sessionId)({
|
|
2431
|
+
...normalizeResponsesUsage(result.usage),
|
|
2432
|
+
total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
|
|
2433
|
+
});
|
|
2434
|
+
return result;
|
|
2435
|
+
}
|
|
2436
|
+
async function requestRemoteSearch(c, request, state, target) {
|
|
2437
|
+
const instruction = buildInstruction(state.remoteOperations, request.commands?.response_length);
|
|
2438
|
+
const sessionId = getUUID(request.id);
|
|
2439
|
+
const requestId = generateRequestIdFromPayload({ messages: `${state.turn.number}:${instruction}` }, sessionId);
|
|
2440
|
+
const payload = createRemoteResponsesPayload(request, target.model, instruction);
|
|
2441
|
+
if (target.providerConfig) return await requestProviderSearch(c, target.providerConfig, payload, target.model, sessionId);
|
|
2442
|
+
return await requestCopilotSearch(payload, target.model, requestId, sessionId);
|
|
2443
|
+
}
|
|
2444
|
+
function getActiveRemoteReferences(session, references) {
|
|
2445
|
+
return references.filter((reference) => session.referencesById.get(reference.result.ref_id) === reference);
|
|
2446
|
+
}
|
|
2447
|
+
function buildSnapshotLinks(state, target, markdownReferences, remoteReferences) {
|
|
2448
|
+
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()];
|
|
2449
|
+
}
|
|
2450
|
+
function processRemotePages(state, answerText, markdownReferences, remoteReferences) {
|
|
2451
|
+
for (const [index, page] of state.remotePages.entries()) {
|
|
2452
|
+
const target = addUrlReference(state.session, {
|
|
2453
|
+
url: page.source.result.url,
|
|
2454
|
+
title: page.source.result.title,
|
|
2455
|
+
snippet: answerText
|
|
2456
|
+
}, state.turn) ?? page.source;
|
|
2457
|
+
includeResult(state, target);
|
|
2458
|
+
const snapshotLinks = buildSnapshotLinks(state, target, markdownReferences, remoteReferences);
|
|
2459
|
+
if (page.kind === "find") {
|
|
2460
|
+
state.output.push(`Find results for ${JSON.stringify(page.pattern ?? "")} in ${target.result.url}:\n${answerText}`);
|
|
2461
|
+
if (!findSnapshotByUrl(state.session, target.result.url)) addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
|
|
2462
|
+
continue;
|
|
2463
|
+
}
|
|
2464
|
+
const snapshot = addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
|
|
2465
|
+
state.output.push(renderSnapshot(snapshot, page.lineno));
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
function appendActiveSources(state, references) {
|
|
2469
|
+
if (references.length === 0) return;
|
|
2470
|
+
state.output.push(["Sources:", ...references.map((reference) => `- [${reference.result.ref_id}] ${reference.result.title} — ${reference.result.url}`)].join("\n"));
|
|
2471
|
+
}
|
|
2472
|
+
function processRemoteResult(result, state) {
|
|
2473
|
+
const extracted = extractWebSearchResult(result);
|
|
2474
|
+
const citedSources = extracted.sources.filter((source) => source.snippet !== void 0);
|
|
2475
|
+
const relevantSources = citedSources.length > 0 ? citedSources : extracted.sources;
|
|
2476
|
+
consola.log(`--> web search: operations=${[...new Set(state.remoteOperations.map((remoteOperation) => remoteOperation.operation))].join(",")} queries=${JSON.stringify(extracted.queries)} sources=${relevantSources.length}`);
|
|
2477
|
+
const remoteReferences = relevantSources.map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
|
|
2478
|
+
for (const reference of getActiveRemoteReferences(state.session, remoteReferences)) includeResult(state, reference);
|
|
2479
|
+
const answerText = extracted.answerText || "GitHub Copilot web search returned no text.";
|
|
2480
|
+
const markdownReferences = state.remotePages.length === 0 ? [] : extractMarkdownSources(answerText).map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
|
|
2481
|
+
for (const reference of markdownReferences) includeResult(state, reference);
|
|
2482
|
+
if (state.remotePages.length === 0) state.output.push(answerText);
|
|
2483
|
+
processRemotePages(state, answerText, markdownReferences, remoteReferences);
|
|
2484
|
+
appendActiveSources(state, getActiveRemoteReferences(state.session, remoteReferences));
|
|
2485
|
+
}
|
|
2486
|
+
async function executeRemoteOperations(c, request, state, provider) {
|
|
2487
|
+
const target = await resolveRemoteModel(c, request, provider);
|
|
2488
|
+
if (target instanceof Response) return target;
|
|
2489
|
+
processRemoteResult(await requestRemoteSearch(c, request, state, target), state);
|
|
2490
|
+
return null;
|
|
2491
|
+
}
|
|
2492
|
+
function buildAlphaSearchResponse(state) {
|
|
2493
|
+
state.output.push(...state.warnings);
|
|
2494
|
+
if (state.output.length === 0) state.output.push("No supported search operations were requested.");
|
|
2495
|
+
return {
|
|
2496
|
+
encrypted_output: null,
|
|
2497
|
+
output: state.output.join("\n\n"),
|
|
2498
|
+
results: [...state.resultReferences.values()].filter((reference) => state.session.referencesById.get(reference.result.ref_id) === reference).map(({ result }) => result)
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
async function handleAlphaSearchResponses(c, options) {
|
|
2502
|
+
const parsed = alphaSearchRequestSchema.safeParse(options.request);
|
|
2503
|
+
if (!parsed.success) {
|
|
2504
|
+
const issue = parsed.error.issues[0];
|
|
2505
|
+
return invalidRequest$1(c, `Invalid alpha search request at ${issue?.path.join(".") || "body"}: ${issue?.message ?? "invalid value"}`);
|
|
2506
|
+
}
|
|
2507
|
+
const request = parsed.data;
|
|
2508
|
+
const now = alphaSearchResponsesDependencies.now();
|
|
2509
|
+
const reservation = reserveSession(request.id, now);
|
|
2510
|
+
const session = reservation.session;
|
|
2511
|
+
const state = createSearchOperationState(session, {
|
|
2512
|
+
number: reservation.turn,
|
|
2513
|
+
nextReferenceIndex: 0
|
|
2514
|
+
});
|
|
2515
|
+
processCommandOperations(request.commands ?? {}, state, now);
|
|
2516
|
+
addLiveAccessWarning(request, state);
|
|
2517
|
+
if (shouldExecuteRemoteOperations(request, state)) {
|
|
2518
|
+
const remoteResponse = await executeRemoteOperations(c, request, state, options.provider);
|
|
2519
|
+
if (remoteResponse) return remoteResponse;
|
|
2520
|
+
}
|
|
2521
|
+
return c.json(buildAlphaSearchResponse(state));
|
|
2522
|
+
}
|
|
2523
|
+
//#endregion
|
|
2524
|
+
//#region src/services/codex/alpha-search.ts
|
|
2525
|
+
const CODEX_ALPHA_SEARCH_URL = `${CODEX_API_BASE_URL}/codex/alpha/search`;
|
|
2526
|
+
function resolveCodexAlphaSearchUrl(requestUrl) {
|
|
2527
|
+
const upstreamUrl = new URL(CODEX_ALPHA_SEARCH_URL);
|
|
2528
|
+
upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
|
|
2529
|
+
return upstreamUrl.toString();
|
|
2530
|
+
}
|
|
2531
|
+
async function forwardCodexAlphaSearch(request) {
|
|
2532
|
+
const headers = buildCodexRequestHeaders(request.headers);
|
|
2533
|
+
if (!headers.has("accept")) headers.set("accept", "application/json");
|
|
2534
|
+
const body = await request.arrayBuffer();
|
|
2535
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
2536
|
+
return await fetch(resolveCodexAlphaSearchUrl(request.url), {
|
|
2537
|
+
method: "POST",
|
|
2538
|
+
headers,
|
|
2539
|
+
body
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
//#endregion
|
|
2543
|
+
//#region src/routes/alpha-search/route.ts
|
|
2544
|
+
const logger$14 = createHandlerLogger("alpha-search-handler");
|
|
2545
|
+
const alphaSearchRoutes = new Hono();
|
|
2546
|
+
function parseDebugBody(body) {
|
|
2547
|
+
try {
|
|
2548
|
+
return JSON.parse(body);
|
|
2549
|
+
} catch {
|
|
2550
|
+
return body;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
async function forwardCodexAlphaSearchRequest(request) {
|
|
2554
|
+
await debugJsonAsync(logger$14, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await request.clone().text()) }));
|
|
2555
|
+
const upstreamResponse = await forwardCodexAlphaSearch(request);
|
|
2556
|
+
await debugJsonAsync(logger$14, "alpha_search.codex.response", async () => ({
|
|
2557
|
+
body: parseDebugBody(await upstreamResponse.clone().text()),
|
|
2558
|
+
statusCode: upstreamResponse.status
|
|
2559
|
+
}));
|
|
2560
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
2561
|
+
}
|
|
2562
|
+
function createAlphaSearchRequest(request, payload) {
|
|
2563
|
+
return new Request(request, {
|
|
2564
|
+
body: JSON.stringify(payload),
|
|
2565
|
+
method: "post"
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
async function handleCodexRequest(c, request, resolvedProviderConfig) {
|
|
2569
|
+
if (!(resolvedProviderConfig ?? await resolveProviderConfig("codex"))) return c.json({ error: {
|
|
2570
|
+
message: "Provider 'codex' not found or disabled",
|
|
2571
|
+
type: "invalid_request_error"
|
|
2572
|
+
} }, 404);
|
|
2573
|
+
return await forwardCodexAlphaSearchRequest(request);
|
|
2574
|
+
}
|
|
2575
|
+
function invalidRequest(c, message) {
|
|
2576
|
+
return c.json({ error: {
|
|
2577
|
+
message,
|
|
2578
|
+
type: "invalid_request_error"
|
|
2579
|
+
} }, 400);
|
|
2580
|
+
}
|
|
2581
|
+
async function parseAlphaSearchBody(c) {
|
|
2582
|
+
let body;
|
|
2583
|
+
try {
|
|
2584
|
+
body = await c.req.raw.clone().json();
|
|
2585
|
+
} catch {
|
|
2586
|
+
return invalidRequest(c, "Invalid alpha search request: expected JSON body");
|
|
2587
|
+
}
|
|
2588
|
+
if (typeof body?.model !== "string") return invalidRequest(c, "Invalid alpha search request: model must be a string");
|
|
2589
|
+
return body;
|
|
2590
|
+
}
|
|
2591
|
+
/**
|
|
2592
|
+
* Handles top-level alpha-search dispatch. Pass `resolvedProviderConfig` when
|
|
2593
|
+
* the provider-scoped route has already resolved Codex. Codex is preferred
|
|
2594
|
+
* for every top-level request while alphaSearchCodexPriority stays enabled.
|
|
2595
|
+
*/
|
|
2596
|
+
async function handleAlphaSearchRequest(c, resolvedProviderConfig) {
|
|
2597
|
+
if (resolvedProviderConfig) return await handleCodexRequest(c, c.req.raw, resolvedProviderConfig);
|
|
2598
|
+
const payload = await parseAlphaSearchBody(c);
|
|
2599
|
+
if (payload instanceof Response) return payload;
|
|
2600
|
+
const requestedModel = payload.model;
|
|
2601
|
+
payload.model = resolveMappedModel(requestedModel);
|
|
2602
|
+
if (payload.model !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
|
|
2603
|
+
const providerModelAlias = parseProviderModelAlias(payload.model);
|
|
2604
|
+
if (providerModelAlias) {
|
|
2605
|
+
payload.model = providerModelAlias.model;
|
|
2606
|
+
if (providerModelAlias.provider === "codex") return await handleCodexRequest(c, createAlphaSearchRequest(c.req.raw, payload));
|
|
2607
|
+
}
|
|
2608
|
+
if (isAlphaSearchCodexPriorityEnabled()) {
|
|
2609
|
+
if (await resolveProviderConfig("codex")) return await forwardCodexAlphaSearchRequest(createAlphaSearchRequest(c.req.raw, payload));
|
|
2610
|
+
}
|
|
2611
|
+
if (providerModelAlias) return await handleAlphaSearchResponses(c, {
|
|
2612
|
+
provider: providerModelAlias.provider,
|
|
2613
|
+
request: payload
|
|
2614
|
+
});
|
|
2615
|
+
return await handleAlphaSearchResponses(c, { request: {
|
|
2616
|
+
...payload,
|
|
2617
|
+
model: requestedModel
|
|
2618
|
+
} });
|
|
2619
|
+
}
|
|
2620
|
+
alphaSearchRoutes.post("/", async (c) => {
|
|
2621
|
+
try {
|
|
2622
|
+
return await handleAlphaSearchRequest(c);
|
|
2623
|
+
} catch (error) {
|
|
2624
|
+
logger$14.error("alpha_search.error", { error });
|
|
2625
|
+
return await forwardError(c, error);
|
|
2626
|
+
}
|
|
2627
|
+
});
|
|
2628
|
+
//#endregion
|
|
2629
|
+
//#region src/lib/dashscope.ts
|
|
2630
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
|
|
2631
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
|
|
2632
|
+
const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
|
|
2633
|
+
"system",
|
|
2634
|
+
"user",
|
|
2635
|
+
"assistant",
|
|
2636
|
+
"tool"
|
|
2637
|
+
]);
|
|
2638
|
+
const isDashScopeAliyunProvider = (providerConfig) => providerConfig.name === "dashscope" || providerConfig.baseUrl.includes("aliyuncs.com");
|
|
2639
|
+
const applyDashScopePreserveThinkingDefault = (payload, providerConfig) => {
|
|
2640
|
+
if (!isDashScopeAliyunProvider(providerConfig)) return;
|
|
2641
|
+
if (!Object.hasOwn(payload, "preserve_thinking")) payload.preserve_thinking = true;
|
|
2642
|
+
};
|
|
2643
|
+
const applyOpenAICompatibleContextCache = (payload) => {
|
|
2644
|
+
const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
|
|
2645
|
+
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
2646
|
+
};
|
|
2647
|
+
const selectContextCacheMessageIndexes = (messages) => {
|
|
2648
|
+
const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
|
|
2649
|
+
const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
|
|
2650
|
+
const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-1);
|
|
2651
|
+
return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
|
|
2652
|
+
};
|
|
2653
|
+
const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
|
|
2654
|
+
const isContextCacheMarkerEligible = (message) => {
|
|
2655
|
+
if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
|
|
2656
|
+
if (typeof message.content === "string") return message.content.length > 0;
|
|
1702
2657
|
return Array.isArray(message.content) && message.content.length > 0;
|
|
1703
2658
|
};
|
|
1704
2659
|
const applyContextCacheControl = (message) => {
|
|
@@ -1767,123 +2722,50 @@ const applyMissingExtraBody$1 = (payload, options) => {
|
|
|
1767
2722
|
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
1768
2723
|
};
|
|
1769
2724
|
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
|
|
2725
|
+
if (!payload.stream) return;
|
|
2726
|
+
payload.stream_options = {
|
|
2727
|
+
...payload.stream_options ?? {},
|
|
2728
|
+
include_usage: true
|
|
1862
2729
|
};
|
|
1863
2730
|
};
|
|
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
|
-
}
|
|
2731
|
+
const applyProviderContextCache = (payload, modelConfig, providerConfig) => {
|
|
2732
|
+
const isDashScopeProvider = isDashScopeAliyunProvider(providerConfig);
|
|
2733
|
+
if (modelConfig?.contextCache ?? isDashScopeProvider) applyOpenAICompatibleContextCache(payload);
|
|
1877
2734
|
};
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
2735
|
+
const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
|
|
2736
|
+
endpoint: "chat_completions",
|
|
2737
|
+
model: payload.model,
|
|
2738
|
+
pricing: modelConfig?.pricing,
|
|
2739
|
+
pricingCurrency,
|
|
2740
|
+
providerName: provider
|
|
2741
|
+
});
|
|
2742
|
+
const streamProviderChatCompletions = (c, upstreamResponse, options) => {
|
|
2743
|
+
logger$13.debug("provider.chat_completions.streaming", { provider: options.provider });
|
|
2744
|
+
return streamSSE(c, async (stream) => {
|
|
2745
|
+
let usage = {};
|
|
2746
|
+
try {
|
|
2747
|
+
for await (const chunk of events(upstreamResponse)) {
|
|
2748
|
+
debugJson(logger$13, "provider.chat_completions.stream_chunk", chunk);
|
|
2749
|
+
if (chunk.data && chunk.data !== "[DONE]") {
|
|
2750
|
+
const parsedChunk = parseChatCompletionChunkData(chunk.data);
|
|
2751
|
+
if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
|
|
2752
|
+
}
|
|
2753
|
+
await stream.writeSSE({
|
|
2754
|
+
event: chunk.event,
|
|
2755
|
+
data: chunk.data ?? ""
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
} finally {
|
|
2759
|
+
options.recordUsage(usage);
|
|
2760
|
+
}
|
|
2761
|
+
});
|
|
1882
2762
|
};
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2763
|
+
const parseChatCompletionChunkData = (data) => {
|
|
2764
|
+
try {
|
|
2765
|
+
return JSON.parse(data);
|
|
2766
|
+
} catch {
|
|
2767
|
+
return null;
|
|
2768
|
+
}
|
|
1887
2769
|
};
|
|
1888
2770
|
//#endregion
|
|
1889
2771
|
//#region src/services/copilot/create-chat-completions.ts
|
|
@@ -3579,143 +4461,6 @@ function closeThinkingBlockIfOpen(state, events) {
|
|
|
3579
4461
|
}
|
|
3580
4462
|
}
|
|
3581
4463
|
//#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
4464
|
//#region src/routes/messages/responses-translation.ts
|
|
3720
4465
|
const MESSAGE_TYPE = "message";
|
|
3721
4466
|
const COMPACTION_SIGNATURE_PREFIX = "cm1#";
|
|
@@ -5037,72 +5782,6 @@ const containsVisionContent = (value) => {
|
|
|
5037
5782
|
return false;
|
|
5038
5783
|
};
|
|
5039
5784
|
//#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
5785
|
//#region src/routes/messages/web-search/fulfill.ts
|
|
5107
5786
|
const webSearchFlowDependencies = {
|
|
5108
5787
|
createResponses,
|
|
@@ -6686,7 +7365,7 @@ providerAlphaSearchRoutes.post("/", async (c) => {
|
|
|
6686
7365
|
message: `Provider '${provider}' not found or disabled`,
|
|
6687
7366
|
type: "invalid_request_error"
|
|
6688
7367
|
} }, 404);
|
|
6689
|
-
if (providerConfig.name === "codex") return await
|
|
7368
|
+
if (providerConfig.name === "codex") return await handleAlphaSearchRequest(c, providerConfig);
|
|
6690
7369
|
await debugJsonAsync(logger$5, "provider.alpha_search.request", async () => ({
|
|
6691
7370
|
body: await c.req.raw.clone().text(),
|
|
6692
7371
|
provider
|
|
@@ -7259,4 +7938,4 @@ server.route("/:provider/images", providerImageRoutes);
|
|
|
7259
7938
|
//#endregion
|
|
7260
7939
|
export { server };
|
|
7261
7940
|
|
|
7262
|
-
//# sourceMappingURL=server-
|
|
7941
|
+
//# sourceMappingURL=server-DZtBoW6V.js.map
|