@jeffreycao/copilot-api 1.10.36 → 1.11.1
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 +5 -4
- package/README.zh-CN.md +5 -4
- package/dist/{auth-DzjQQuSk.js → auth-D3ta3JW0.js} +3 -3
- package/dist/{auth-DzjQQuSk.js.map → auth-D3ta3JW0.js.map} +1 -1
- package/dist/{check-usage-DBfggFvQ.js → check-usage-Dh0WqiLC.js} +3 -3
- package/dist/{check-usage-DBfggFvQ.js.map → check-usage-Dh0WqiLC.js.map} +1 -1
- package/dist/{config-CgDUUqnp.js → config-DA-Jdm0G.js} +9 -4
- package/dist/config-DA-Jdm0G.js.map +1 -0
- package/dist/{debug-BEVHlRGL.js → debug-BiX0ewij.js} +2 -2
- package/dist/{debug-BEVHlRGL.js.map → debug-BiX0ewij.js.map} +1 -1
- package/dist/main.js +4 -4
- package/dist/{server-DYlw1xSW.js → server-BxCf-DN5.js} +461 -61
- package/dist/server-BxCf-DN5.js.map +1 -0
- package/dist/{start-B-xjF4KT.js → start-CFeefs3X.js} +4 -4
- package/dist/{start-B-xjF4KT.js.map → start-CFeefs3X.js.map} +1 -1
- package/dist/{token-CHTEbXZd.js → token-mo4KkQSp.js} +11 -4
- package/dist/token-mo4KkQSp.js.map +1 -0
- package/package.json +1 -1
- package/dist/config-CgDUUqnp.js.map +0 -1
- package/dist/server-DYlw1xSW.js.map +0 -1
- package/dist/token-CHTEbXZd.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { B as forwardError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as createPooledWebSocketStream, I as createWebSocketUrl, M as generateTraceId, N as requestContext, O as compactSystemPromptStarts, P as resolveTraceId$1, R as state, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, _ as getCopilotUsage, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, j as forwardCodexResponses, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as HTTPError } from "./token-
|
|
1
|
+
import { a as getMessageApiWebSearchModel, c as getProviderConfig, d as getSmallModel, f as isMessagesApiEnabled, h as isResponsesApiWebSocketEnabled, i as getExtraPromptForModel, l as getRawProviderConfig, m as isResponsesApiWebSearchEnabled, n as getClaudeTokenMultiplier, o as getModelMappings, p as isResponsesApiContextManagementEnabled, r as getConfig, s as getModelResponsesApiCompactThreshold$1, t as getAnthropicApiKey, u as getReasoningEffortForModel, v as resolveMappedModel, x as PATHS, y as setModelMappings } from "./config-DA-Jdm0G.js";
|
|
2
|
+
import { B as forwardError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as createPooledWebSocketStream, I as createWebSocketUrl, M as generateTraceId, N as requestContext, O as compactSystemPromptStarts, P as resolveTraceId$1, R as state, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, _ as getCopilotUsage, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, j as forwardCodexResponses, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as HTTPError } from "./token-mo4KkQSp.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-wA-fLduL.js";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -1020,6 +1020,7 @@ function createProviderProxyResponse(upstreamResponse, body) {
|
|
|
1020
1020
|
});
|
|
1021
1021
|
}
|
|
1022
1022
|
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
1023
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1023
1024
|
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
1024
1025
|
method: "POST",
|
|
1025
1026
|
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
@@ -1027,6 +1028,7 @@ async function forwardProviderMessages(providerConfig, payload, requestHeaders)
|
|
|
1027
1028
|
});
|
|
1028
1029
|
}
|
|
1029
1030
|
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
1031
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1030
1032
|
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
1031
1033
|
method: "POST",
|
|
1032
1034
|
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
@@ -1034,6 +1036,7 @@ async function forwardProviderChatCompletions(providerConfig, payload, requestHe
|
|
|
1034
1036
|
});
|
|
1035
1037
|
}
|
|
1036
1038
|
async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
|
|
1039
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
1037
1040
|
return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
|
|
1038
1041
|
method: "POST",
|
|
1039
1042
|
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
@@ -4001,6 +4004,52 @@ const stringifyToolSearchArguments = (argumentsValue) => {
|
|
|
4001
4004
|
return;
|
|
4002
4005
|
}
|
|
4003
4006
|
};
|
|
4007
|
+
//#endregion
|
|
4008
|
+
//#region src/lib/subagent.ts
|
|
4009
|
+
const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
|
|
4010
|
+
//#endregion
|
|
4011
|
+
//#region src/routes/messages/subagent-marker.ts
|
|
4012
|
+
const parseSubagentMarkerFromFirstUser = (payload) => {
|
|
4013
|
+
const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
|
|
4014
|
+
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
|
|
4015
|
+
for (const block of firstUserMessage.content) {
|
|
4016
|
+
if (block.type !== "text") continue;
|
|
4017
|
+
const marker = parseSubagentMarkerFromSystemReminder(block.text);
|
|
4018
|
+
if (marker) return marker;
|
|
4019
|
+
}
|
|
4020
|
+
return null;
|
|
4021
|
+
};
|
|
4022
|
+
const parseSubagentMarkerFromSystemReminder = (text) => {
|
|
4023
|
+
const startTag = "<system-reminder>";
|
|
4024
|
+
const endTag = "</system-reminder>";
|
|
4025
|
+
let searchFrom = 0;
|
|
4026
|
+
while (true) {
|
|
4027
|
+
const reminderStart = text.indexOf(startTag, searchFrom);
|
|
4028
|
+
if (reminderStart === -1) break;
|
|
4029
|
+
const contentStart = reminderStart + 17;
|
|
4030
|
+
const reminderEnd = text.indexOf(endTag, contentStart);
|
|
4031
|
+
if (reminderEnd === -1) break;
|
|
4032
|
+
const reminderContent = text.slice(contentStart, reminderEnd);
|
|
4033
|
+
const markerIndex = reminderContent.indexOf(subagentMarkerPrefix);
|
|
4034
|
+
if (markerIndex === -1) {
|
|
4035
|
+
searchFrom = reminderEnd + 18;
|
|
4036
|
+
continue;
|
|
4037
|
+
}
|
|
4038
|
+
const markerJson = reminderContent.slice(markerIndex + 19).trim();
|
|
4039
|
+
try {
|
|
4040
|
+
const parsed = JSON.parse(markerJson);
|
|
4041
|
+
if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) {
|
|
4042
|
+
searchFrom = reminderEnd + 18;
|
|
4043
|
+
continue;
|
|
4044
|
+
}
|
|
4045
|
+
return parsed;
|
|
4046
|
+
} catch {
|
|
4047
|
+
searchFrom = reminderEnd + 18;
|
|
4048
|
+
continue;
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
return null;
|
|
4052
|
+
};
|
|
4004
4053
|
const DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO = .9;
|
|
4005
4054
|
const responsesUtilsDependencies = {
|
|
4006
4055
|
getModelResponsesApiCompactThreshold: getModelResponsesApiCompactThreshold$1,
|
|
@@ -4149,6 +4198,340 @@ const containsVisionContent = (value) => {
|
|
|
4149
4198
|
return false;
|
|
4150
4199
|
};
|
|
4151
4200
|
//#endregion
|
|
4201
|
+
//#region src/routes/messages/web-search/backend.ts
|
|
4202
|
+
/** Builds the Responses API web_search tool object from the Anthropic config. */
|
|
4203
|
+
const buildResponsesWebSearchTool = (config) => {
|
|
4204
|
+
const tool = { type: "web_search" };
|
|
4205
|
+
const filters = {};
|
|
4206
|
+
if (config.allowedDomains?.length) filters.allowed_domains = config.allowedDomains;
|
|
4207
|
+
if (config.blockedDomains?.length) filters.blocked_domains = config.blockedDomains;
|
|
4208
|
+
if (Object.keys(filters).length > 0) tool.filters = filters;
|
|
4209
|
+
if (config.userLocation) tool.user_location = config.userLocation;
|
|
4210
|
+
return tool;
|
|
4211
|
+
};
|
|
4212
|
+
const isMessageItem = (item) => item.type === "message";
|
|
4213
|
+
const isValidUrlCitation = (annotation, seenUrls) => {
|
|
4214
|
+
const ann = annotation;
|
|
4215
|
+
return ann.type === "url_citation" && Boolean(ann.url) && !seenUrls.has(ann.url);
|
|
4216
|
+
};
|
|
4217
|
+
const collectTextParts = (blocks, seenUrls) => {
|
|
4218
|
+
const textParts = [];
|
|
4219
|
+
const sources = [];
|
|
4220
|
+
for (const block of blocks ?? []) {
|
|
4221
|
+
if (block.type !== "output_text") continue;
|
|
4222
|
+
if (block.text) textParts.push(block.text);
|
|
4223
|
+
for (const annotation of block.annotations ?? []) {
|
|
4224
|
+
if (!isValidUrlCitation(annotation, seenUrls)) continue;
|
|
4225
|
+
const ann = annotation;
|
|
4226
|
+
seenUrls.add(ann.url);
|
|
4227
|
+
sources.push({
|
|
4228
|
+
url: ann.url,
|
|
4229
|
+
title: ann.title ?? ann.url
|
|
4230
|
+
});
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
return {
|
|
4234
|
+
textParts,
|
|
4235
|
+
sources
|
|
4236
|
+
};
|
|
4237
|
+
};
|
|
4238
|
+
const collectQuery = (item, queries) => {
|
|
4239
|
+
if (item.action?.queries?.length) queries.push(...item.action.queries);
|
|
4240
|
+
else if (item.action?.query) queries.push(item.action.query);
|
|
4241
|
+
};
|
|
4242
|
+
/**
|
|
4243
|
+
* Extracts the answer text, deduped sources, and run queries from a GPT
|
|
4244
|
+
* /responses web_search result.
|
|
4245
|
+
*/
|
|
4246
|
+
const extractWebSearchResult = (result) => {
|
|
4247
|
+
const textParts = [];
|
|
4248
|
+
const sources = [];
|
|
4249
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
4250
|
+
const queries = [];
|
|
4251
|
+
for (const item of result.output) {
|
|
4252
|
+
if (isMessageItem(item)) {
|
|
4253
|
+
const collected = collectTextParts(item.content, seenUrls);
|
|
4254
|
+
textParts.push(...collected.textParts);
|
|
4255
|
+
sources.push(...collected.sources);
|
|
4256
|
+
continue;
|
|
4257
|
+
}
|
|
4258
|
+
if (item.type === "web_search_call") collectQuery(item, queries);
|
|
4259
|
+
}
|
|
4260
|
+
return {
|
|
4261
|
+
answerText: textParts.join("\n\n").trim() || (result.output_text ?? "").trim(),
|
|
4262
|
+
sources,
|
|
4263
|
+
queries
|
|
4264
|
+
};
|
|
4265
|
+
};
|
|
4266
|
+
//#endregion
|
|
4267
|
+
//#region src/routes/messages/web-search/fulfill.ts
|
|
4268
|
+
const webSearchFlowDependencies = {
|
|
4269
|
+
createResponses,
|
|
4270
|
+
createUsageRecorder: (payload, sessionId, webSearchModel) => createCopilotTokenUsageRecorder({
|
|
4271
|
+
endpoint: "responses",
|
|
4272
|
+
fallbackSessionId: sessionId,
|
|
4273
|
+
model: webSearchModel ?? payload.model,
|
|
4274
|
+
sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
|
|
4275
|
+
})
|
|
4276
|
+
};
|
|
4277
|
+
const isWebSearchServerTool = (tool) => typeof tool.type === "string" && tool.type.startsWith("web_search") && !tool.input_schema;
|
|
4278
|
+
/** True when the payload carries an Anthropic server-side web_search tool. */
|
|
4279
|
+
const hasWebSearchServerTool = (payload) => Array.isArray(payload.tools) && payload.tools.some(isWebSearchServerTool);
|
|
4280
|
+
/**
|
|
4281
|
+
* True when web_search is the ONLY tool in the request. Mixing web_search with
|
|
4282
|
+
* other tools is intentionally unsupported, so only these requests are switched
|
|
4283
|
+
* to the web search model.
|
|
4284
|
+
*/
|
|
4285
|
+
const isWebSearchOnlyRequest = (payload) => Array.isArray(payload.tools) && payload.tools.length > 0 && payload.tools.every(isWebSearchServerTool);
|
|
4286
|
+
/** Removes web_search server tools (used for unsupported mixed-tool requests). */
|
|
4287
|
+
const stripWebSearchServerTool = (payload) => {
|
|
4288
|
+
if (!Array.isArray(payload.tools)) return;
|
|
4289
|
+
payload.tools = payload.tools.filter((tool) => !isWebSearchServerTool(tool));
|
|
4290
|
+
};
|
|
4291
|
+
const resolveWebSearchRoute = (payload, options) => {
|
|
4292
|
+
const { webSearchModel, responsesWebSearchEnabled } = options;
|
|
4293
|
+
if (!webSearchModel || !isWebSearchOnlyRequest(payload)) return { kind: "strip" };
|
|
4294
|
+
const alias = parseProviderModelAlias(webSearchModel);
|
|
4295
|
+
if (alias) return {
|
|
4296
|
+
kind: "provider",
|
|
4297
|
+
alias
|
|
4298
|
+
};
|
|
4299
|
+
if (responsesWebSearchEnabled) return {
|
|
4300
|
+
kind: "responses",
|
|
4301
|
+
model: webSearchModel
|
|
4302
|
+
};
|
|
4303
|
+
return { kind: "strip" };
|
|
4304
|
+
};
|
|
4305
|
+
const extractWebSearchConfig = (payload) => {
|
|
4306
|
+
const tool = payload.tools?.find(isWebSearchServerTool);
|
|
4307
|
+
return {
|
|
4308
|
+
allowedDomains: tool?.allowed_domains,
|
|
4309
|
+
blockedDomains: tool?.blocked_domains,
|
|
4310
|
+
userLocation: tool?.user_location
|
|
4311
|
+
};
|
|
4312
|
+
};
|
|
4313
|
+
const buildWebSearchResultBlock = (toolUseId, extract) => {
|
|
4314
|
+
return {
|
|
4315
|
+
type: "web_search_tool_result",
|
|
4316
|
+
tool_use_id: toolUseId,
|
|
4317
|
+
content: extract.sources.map((source) => ({
|
|
4318
|
+
type: "web_search_result",
|
|
4319
|
+
url: source.url,
|
|
4320
|
+
title: source.title,
|
|
4321
|
+
page_age: source.page_age ?? null,
|
|
4322
|
+
encrypted_content: ""
|
|
4323
|
+
}))
|
|
4324
|
+
};
|
|
4325
|
+
};
|
|
4326
|
+
/**
|
|
4327
|
+
* Reconstructs a native Anthropic assistant response from the GPT web search
|
|
4328
|
+
* result: one server_tool_use + web_search_tool_result pair, then the answer.
|
|
4329
|
+
*/
|
|
4330
|
+
const buildResponseContent = (requestId, extract) => {
|
|
4331
|
+
const blocks = [];
|
|
4332
|
+
const query = extract.queries[0] ?? "";
|
|
4333
|
+
if (extract.sources.length > 0 || query) {
|
|
4334
|
+
const toolUseId = `srvtoolu_${getUUID(requestId)}`;
|
|
4335
|
+
blocks.push({
|
|
4336
|
+
type: "server_tool_use",
|
|
4337
|
+
id: toolUseId,
|
|
4338
|
+
name: "web_search",
|
|
4339
|
+
input: { query }
|
|
4340
|
+
}, buildWebSearchResultBlock(toolUseId, extract));
|
|
4341
|
+
}
|
|
4342
|
+
blocks.push({
|
|
4343
|
+
type: "text",
|
|
4344
|
+
text: extract.answerText
|
|
4345
|
+
});
|
|
4346
|
+
return blocks;
|
|
4347
|
+
};
|
|
4348
|
+
const prepareWebSearchResponsesPayload = (payload, options = {}) => {
|
|
4349
|
+
const config = extractWebSearchConfig(payload);
|
|
4350
|
+
const responsesPayload = translateAnthropicMessagesToResponsesPayload({
|
|
4351
|
+
...payload,
|
|
4352
|
+
model: options.model ?? payload.model,
|
|
4353
|
+
tools: [],
|
|
4354
|
+
stream: false
|
|
4355
|
+
}, options.subagentAgentId);
|
|
4356
|
+
responsesPayload.tools = [buildResponsesWebSearchTool(config)];
|
|
4357
|
+
responsesPayload.tool_choice = void 0;
|
|
4358
|
+
return responsesPayload;
|
|
4359
|
+
};
|
|
4360
|
+
const reconstructWebSearchResponse = (payload, result, options) => {
|
|
4361
|
+
const extract = extractWebSearchResult(result);
|
|
4362
|
+
return {
|
|
4363
|
+
extract,
|
|
4364
|
+
response: {
|
|
4365
|
+
id: result.id || getUUID(options.requestId),
|
|
4366
|
+
type: "message",
|
|
4367
|
+
role: "assistant",
|
|
4368
|
+
content: buildResponseContent(options.requestId, extract),
|
|
4369
|
+
model: payload.model,
|
|
4370
|
+
stop_reason: "end_turn",
|
|
4371
|
+
stop_sequence: null,
|
|
4372
|
+
usage: {
|
|
4373
|
+
input_tokens: result.usage?.input_tokens ?? 0,
|
|
4374
|
+
output_tokens: result.usage?.output_tokens ?? 0,
|
|
4375
|
+
server_tool_use: { web_search_requests: Math.max(extract.queries.length, 1) }
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4378
|
+
};
|
|
4379
|
+
};
|
|
4380
|
+
const createUsageRecorder = (payload, sessionId, webSearchModel) => webSearchFlowDependencies.createUsageRecorder(payload, sessionId, webSearchModel);
|
|
4381
|
+
/**
|
|
4382
|
+
* Entry point for web-search detection and routing on /v1/messages.
|
|
4383
|
+
* Called after model mapping but before provider alias resolution and
|
|
4384
|
+
* preprocessing. Returns a Response when web search is handled (provider
|
|
4385
|
+
* reroute or responses-native), or `null` when no web_search tool is
|
|
4386
|
+
* present or the tool was stripped (caller continues normal flow).
|
|
4387
|
+
*
|
|
4388
|
+
* Uses a callback for provider forwarding to avoid circular imports.
|
|
4389
|
+
*/
|
|
4390
|
+
const tryHandleWebSearch = async (c, payload, options) => {
|
|
4391
|
+
if (!hasWebSearchServerTool(payload)) return null;
|
|
4392
|
+
const route = resolveWebSearchRoute(payload, {
|
|
4393
|
+
webSearchModel: getMessageApiWebSearchModel(),
|
|
4394
|
+
responsesWebSearchEnabled: isResponsesApiWebSearchEnabled()
|
|
4395
|
+
});
|
|
4396
|
+
if (route.kind === "provider") {
|
|
4397
|
+
payload.model = route.alias.model;
|
|
4398
|
+
return await options.forwardToProvider(c, payload, route.alias.provider);
|
|
4399
|
+
}
|
|
4400
|
+
if (route.kind === "responses") {
|
|
4401
|
+
const subagentMarker = parseSubagentMarkerFromFirstUser(payload);
|
|
4402
|
+
let sessionId = getRootSessionId(payload, c);
|
|
4403
|
+
const requestId = generateRequestIdFromPayload(payload, sessionId);
|
|
4404
|
+
if (!sessionId) sessionId = getUUID(requestId);
|
|
4405
|
+
const compactType = getCompactType(payload);
|
|
4406
|
+
return await handleWebSearchViaResponses(c, payload, {
|
|
4407
|
+
subagentMarker,
|
|
4408
|
+
webSearchModel: route.model,
|
|
4409
|
+
requestId,
|
|
4410
|
+
sessionId,
|
|
4411
|
+
compactType,
|
|
4412
|
+
logger: options.logger
|
|
4413
|
+
});
|
|
4414
|
+
}
|
|
4415
|
+
stripWebSearchServerTool(payload);
|
|
4416
|
+
return null;
|
|
4417
|
+
};
|
|
4418
|
+
/**
|
|
4419
|
+
* Handles a web-search-only Claude (Messages API) request by switching it to a
|
|
4420
|
+
* Responses-capable GPT model (`webSearchModel`), running Copilot's native
|
|
4421
|
+
* /responses web_search in a single call, and reconstructing native Anthropic
|
|
4422
|
+
* server_tool_use + web_search_tool_result blocks. Streaming and non-streaming
|
|
4423
|
+
* are both supported (streaming replays the result as a synthetic SSE stream).
|
|
4424
|
+
*/
|
|
4425
|
+
const handleWebSearchViaResponses = async (c, payload, options) => {
|
|
4426
|
+
const { logger, webSearchModel } = options;
|
|
4427
|
+
const wantsStream = Boolean(payload.stream);
|
|
4428
|
+
const responsesPayload = prepareWebSearchResponsesPayload(payload, {
|
|
4429
|
+
model: webSearchModel,
|
|
4430
|
+
subagentAgentId: options.subagentMarker?.agent_id
|
|
4431
|
+
});
|
|
4432
|
+
const selectedModel = findEndpointModel(webSearchModel);
|
|
4433
|
+
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
4434
|
+
const transport = getResponsesTransportForModel(selectedModel, { compactType: options.compactType }) ?? "http";
|
|
4435
|
+
logger.debug(`Switching web search request to model: ${webSearchModel} ${JSON.stringify(responsesPayload)}`);
|
|
4436
|
+
const result = await webSearchFlowDependencies.createResponses(responsesPayload, {
|
|
4437
|
+
vision,
|
|
4438
|
+
initiator,
|
|
4439
|
+
transport,
|
|
4440
|
+
subagentMarker: options.subagentMarker,
|
|
4441
|
+
requestId: options.requestId,
|
|
4442
|
+
sessionId: options.sessionId,
|
|
4443
|
+
compactType: options.compactType
|
|
4444
|
+
});
|
|
4445
|
+
const { extract, response } = reconstructWebSearchResponse(payload, result, { requestId: options.requestId });
|
|
4446
|
+
logger.debug(`Web search via responses: ${extract.queries.length} quer(y/ies), ${extract.sources.length} source(s), ${JSON.stringify(result)}`);
|
|
4447
|
+
createUsageRecorder(payload, options.sessionId, webSearchModel)(normalizeResponsesUsage(result.usage));
|
|
4448
|
+
if (!wantsStream) return c.json(response);
|
|
4449
|
+
return streamSSE(c, async (stream) => {
|
|
4450
|
+
for (const event of buildSyntheticStreamEvents(response)) await stream.writeSSE({
|
|
4451
|
+
event: event.type,
|
|
4452
|
+
data: JSON.stringify(event)
|
|
4453
|
+
});
|
|
4454
|
+
});
|
|
4455
|
+
};
|
|
4456
|
+
const blockToStreamEvents = (block, index) => {
|
|
4457
|
+
const start = (contentBlock) => ({
|
|
4458
|
+
type: "content_block_start",
|
|
4459
|
+
index,
|
|
4460
|
+
content_block: contentBlock
|
|
4461
|
+
});
|
|
4462
|
+
const stop = {
|
|
4463
|
+
type: "content_block_stop",
|
|
4464
|
+
index
|
|
4465
|
+
};
|
|
4466
|
+
switch (block.type) {
|
|
4467
|
+
case "text": return [
|
|
4468
|
+
start({
|
|
4469
|
+
type: "text",
|
|
4470
|
+
text: ""
|
|
4471
|
+
}),
|
|
4472
|
+
{
|
|
4473
|
+
type: "content_block_delta",
|
|
4474
|
+
index,
|
|
4475
|
+
delta: {
|
|
4476
|
+
type: "text_delta",
|
|
4477
|
+
text: block.text
|
|
4478
|
+
}
|
|
4479
|
+
},
|
|
4480
|
+
stop
|
|
4481
|
+
];
|
|
4482
|
+
case "server_tool_use": return [
|
|
4483
|
+
start({
|
|
4484
|
+
type: "server_tool_use",
|
|
4485
|
+
id: block.id,
|
|
4486
|
+
name: block.name,
|
|
4487
|
+
input: {}
|
|
4488
|
+
}),
|
|
4489
|
+
{
|
|
4490
|
+
type: "content_block_delta",
|
|
4491
|
+
index,
|
|
4492
|
+
delta: {
|
|
4493
|
+
type: "input_json_delta",
|
|
4494
|
+
partial_json: JSON.stringify(block.input)
|
|
4495
|
+
}
|
|
4496
|
+
},
|
|
4497
|
+
stop
|
|
4498
|
+
];
|
|
4499
|
+
case "web_search_tool_result": return [start(block), stop];
|
|
4500
|
+
default: return [start(block), stop];
|
|
4501
|
+
}
|
|
4502
|
+
};
|
|
4503
|
+
const buildSyntheticStreamEvents = (response) => {
|
|
4504
|
+
const events = [];
|
|
4505
|
+
events.push({
|
|
4506
|
+
type: "message_start",
|
|
4507
|
+
message: {
|
|
4508
|
+
id: response.id,
|
|
4509
|
+
type: "message",
|
|
4510
|
+
role: "assistant",
|
|
4511
|
+
content: [],
|
|
4512
|
+
model: response.model,
|
|
4513
|
+
stop_reason: null,
|
|
4514
|
+
stop_sequence: null,
|
|
4515
|
+
usage: {
|
|
4516
|
+
...response.usage,
|
|
4517
|
+
output_tokens: 0
|
|
4518
|
+
}
|
|
4519
|
+
}
|
|
4520
|
+
});
|
|
4521
|
+
response.content.forEach((block, index) => {
|
|
4522
|
+
events.push(...blockToStreamEvents(block, index));
|
|
4523
|
+
});
|
|
4524
|
+
events.push({
|
|
4525
|
+
type: "message_delta",
|
|
4526
|
+
delta: {
|
|
4527
|
+
stop_reason: response.stop_reason,
|
|
4528
|
+
stop_sequence: response.stop_sequence
|
|
4529
|
+
},
|
|
4530
|
+
usage: { output_tokens: response.usage.output_tokens }
|
|
4531
|
+
}, { type: "message_stop" });
|
|
4532
|
+
return events;
|
|
4533
|
+
};
|
|
4534
|
+
//#endregion
|
|
4152
4535
|
//#region src/services/codex/get-models.ts
|
|
4153
4536
|
const CODEX_MODELS = [
|
|
4154
4537
|
{
|
|
@@ -4257,18 +4640,31 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
4257
4640
|
});
|
|
4258
4641
|
normalizeSystemMessages(payload);
|
|
4259
4642
|
applyModelDefaults(payload, modelConfig);
|
|
4260
|
-
if (providerConfig.type === "openai-responses")
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4643
|
+
if (providerConfig.type === "openai-responses") {
|
|
4644
|
+
if (hasWebSearchServerTool(payload)) {
|
|
4645
|
+
if (isWebSearchOnlyRequest(payload)) return await handleOpenAIResponsesProviderWebSearchMessages(c, {
|
|
4646
|
+
payload,
|
|
4647
|
+
provider,
|
|
4648
|
+
providerConfig
|
|
4649
|
+
});
|
|
4650
|
+
stripWebSearchServerTool(payload);
|
|
4651
|
+
}
|
|
4652
|
+
return await handleOpenAIResponsesProviderMessages(c, {
|
|
4653
|
+
modelConfig,
|
|
4654
|
+
payload,
|
|
4655
|
+
provider,
|
|
4656
|
+
providerConfig
|
|
4657
|
+
});
|
|
4658
|
+
}
|
|
4659
|
+
if (providerConfig.type === "openai-compatible") {
|
|
4660
|
+
stripWebSearchServerTool(payload);
|
|
4661
|
+
return await handleOpenAICompatibleProviderMessages(c, {
|
|
4662
|
+
modelConfig,
|
|
4663
|
+
payload,
|
|
4664
|
+
provider,
|
|
4665
|
+
providerConfig
|
|
4666
|
+
});
|
|
4667
|
+
}
|
|
4272
4668
|
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
4273
4669
|
debugJson(logger$5, "Translated provider.messages.request", {
|
|
4274
4670
|
payload,
|
|
@@ -4301,6 +4697,35 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
4301
4697
|
throw error;
|
|
4302
4698
|
}
|
|
4303
4699
|
}
|
|
4700
|
+
const handleOpenAIResponsesProviderWebSearchMessages = async (c, options) => {
|
|
4701
|
+
const { payload, provider, providerConfig } = options;
|
|
4702
|
+
const selectedModel = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
|
|
4703
|
+
const responsesPayload = prepareWebSearchResponsesPayload(payload);
|
|
4704
|
+
applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
|
|
4705
|
+
compactInputByLatestCompaction(responsesPayload);
|
|
4706
|
+
debugJson(logger$5, "provider.messages.responses.web_search.request", {
|
|
4707
|
+
payload: responsesPayload,
|
|
4708
|
+
provider
|
|
4709
|
+
});
|
|
4710
|
+
if (providerConfig.name === "codex") return respondWebSearchProviderMessagesJson(c, {
|
|
4711
|
+
body: await forwardCodexResponses(responsesPayload, c.req.raw.headers, providerConfig.baseUrl),
|
|
4712
|
+
payload,
|
|
4713
|
+
provider
|
|
4714
|
+
});
|
|
4715
|
+
const upstreamResponse = await forwardProviderResponses(providerConfig, responsesPayload, c.req.raw.headers);
|
|
4716
|
+
if (!upstreamResponse.ok) {
|
|
4717
|
+
logger$5.error("Failed to create provider web search responses", {
|
|
4718
|
+
provider,
|
|
4719
|
+
upstreamResponse
|
|
4720
|
+
});
|
|
4721
|
+
throw new HTTPError("Failed to create provider web search responses", upstreamResponse);
|
|
4722
|
+
}
|
|
4723
|
+
return respondWebSearchProviderMessagesJson(c, {
|
|
4724
|
+
body: await upstreamResponse.json(),
|
|
4725
|
+
payload,
|
|
4726
|
+
provider
|
|
4727
|
+
});
|
|
4728
|
+
};
|
|
4304
4729
|
const handleOpenAIResponsesProviderMessages = async (c, options) => {
|
|
4305
4730
|
const { payload, provider, providerConfig } = options;
|
|
4306
4731
|
const selectedModel = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
|
|
@@ -4666,6 +5091,19 @@ const respondResponsesProviderMessagesJson = (c, options) => {
|
|
|
4666
5091
|
if (providerConfig.name === "codex") logger$5.debug("provider.messages.codex.no_stream.result");
|
|
4667
5092
|
return c.json(anthropicResponse);
|
|
4668
5093
|
};
|
|
5094
|
+
const respondWebSearchProviderMessagesJson = (c, options) => {
|
|
5095
|
+
const { body, payload, provider } = options;
|
|
5096
|
+
createProviderMessagesUsageRecorder(payload, provider)(normalizeResponsesUsage(body.usage));
|
|
5097
|
+
const { extract, response } = reconstructWebSearchResponse(payload, body, { requestId: body.id || `${provider}:${payload.model}` });
|
|
5098
|
+
logger$5.debug(`provider.messages.responses.web_search: ${extract.queries.length} quer(y/ies), ${extract.sources.length} source(s)`);
|
|
5099
|
+
if (!payload.stream) return c.json(response);
|
|
5100
|
+
return streamSSE(c, async (stream) => {
|
|
5101
|
+
for (const event of buildSyntheticStreamEvents(response)) await stream.writeSSE({
|
|
5102
|
+
event: event.type,
|
|
5103
|
+
data: JSON.stringify(event)
|
|
5104
|
+
});
|
|
5105
|
+
});
|
|
5106
|
+
};
|
|
4669
5107
|
const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
|
|
4670
5108
|
endpoint: "provider_messages",
|
|
4671
5109
|
model: payload.model,
|
|
@@ -4951,52 +5389,6 @@ const parseAnthropicStreamEvent = (data) => {
|
|
|
4951
5389
|
}
|
|
4952
5390
|
};
|
|
4953
5391
|
//#endregion
|
|
4954
|
-
//#region src/lib/subagent.ts
|
|
4955
|
-
const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
|
|
4956
|
-
//#endregion
|
|
4957
|
-
//#region src/routes/messages/subagent-marker.ts
|
|
4958
|
-
const parseSubagentMarkerFromFirstUser = (payload) => {
|
|
4959
|
-
const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
|
|
4960
|
-
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
|
|
4961
|
-
for (const block of firstUserMessage.content) {
|
|
4962
|
-
if (block.type !== "text") continue;
|
|
4963
|
-
const marker = parseSubagentMarkerFromSystemReminder(block.text);
|
|
4964
|
-
if (marker) return marker;
|
|
4965
|
-
}
|
|
4966
|
-
return null;
|
|
4967
|
-
};
|
|
4968
|
-
const parseSubagentMarkerFromSystemReminder = (text) => {
|
|
4969
|
-
const startTag = "<system-reminder>";
|
|
4970
|
-
const endTag = "</system-reminder>";
|
|
4971
|
-
let searchFrom = 0;
|
|
4972
|
-
while (true) {
|
|
4973
|
-
const reminderStart = text.indexOf(startTag, searchFrom);
|
|
4974
|
-
if (reminderStart === -1) break;
|
|
4975
|
-
const contentStart = reminderStart + 17;
|
|
4976
|
-
const reminderEnd = text.indexOf(endTag, contentStart);
|
|
4977
|
-
if (reminderEnd === -1) break;
|
|
4978
|
-
const reminderContent = text.slice(contentStart, reminderEnd);
|
|
4979
|
-
const markerIndex = reminderContent.indexOf(subagentMarkerPrefix);
|
|
4980
|
-
if (markerIndex === -1) {
|
|
4981
|
-
searchFrom = reminderEnd + 18;
|
|
4982
|
-
continue;
|
|
4983
|
-
}
|
|
4984
|
-
const markerJson = reminderContent.slice(markerIndex + 19).trim();
|
|
4985
|
-
try {
|
|
4986
|
-
const parsed = JSON.parse(markerJson);
|
|
4987
|
-
if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) {
|
|
4988
|
-
searchFrom = reminderEnd + 18;
|
|
4989
|
-
continue;
|
|
4990
|
-
}
|
|
4991
|
-
return parsed;
|
|
4992
|
-
} catch {
|
|
4993
|
-
searchFrom = reminderEnd + 18;
|
|
4994
|
-
continue;
|
|
4995
|
-
}
|
|
4996
|
-
}
|
|
4997
|
-
return null;
|
|
4998
|
-
};
|
|
4999
|
-
//#endregion
|
|
5000
5392
|
//#region src/routes/messages/handler.ts
|
|
5001
5393
|
const logger$4 = createHandlerLogger("messages-handler");
|
|
5002
5394
|
const messagesFlowHandlers = {
|
|
@@ -5009,6 +5401,14 @@ async function handleCompletion(c) {
|
|
|
5009
5401
|
const requestedModel = anthropicPayload.model;
|
|
5010
5402
|
anthropicPayload.model = resolveMappedModel(anthropicPayload.model);
|
|
5011
5403
|
if (anthropicPayload.model !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${anthropicPayload.model}`);
|
|
5404
|
+
const webSearchResult = await tryHandleWebSearch(c, anthropicPayload, {
|
|
5405
|
+
logger: logger$4,
|
|
5406
|
+
forwardToProvider: (ctx, payload, provider) => handleProviderMessagesForProvider(ctx, {
|
|
5407
|
+
payload,
|
|
5408
|
+
provider
|
|
5409
|
+
})
|
|
5410
|
+
});
|
|
5411
|
+
if (webSearchResult) return webSearchResult;
|
|
5012
5412
|
const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
|
|
5013
5413
|
if (providerModelAlias) {
|
|
5014
5414
|
anthropicPayload.model = providerModelAlias.model;
|
|
@@ -5570,4 +5970,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
5570
5970
|
//#endregion
|
|
5571
5971
|
export { server };
|
|
5572
5972
|
|
|
5573
|
-
//# sourceMappingURL=server-
|
|
5973
|
+
//# sourceMappingURL=server-BxCf-DN5.js.map
|