@nick3/copilot-api 1.10.29 → 1.10.34
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 +41 -9
- package/README.zh-CN.md +39 -7
- package/dist/admin/assets/index-Cl_ViIW_.js +110 -0
- package/dist/admin/index.html +1 -1
- package/dist/{auth-nO-eHeO_.js → auth-Cc11G9V9.js} +2 -2
- package/dist/{auth-nO-eHeO_.js.map → auth-Cc11G9V9.js.map} +1 -1
- package/dist/{check-usage-ZifYvA3w.js → check-usage-C2QE6R93.js} +2 -2
- package/dist/{check-usage-ZifYvA3w.js.map → check-usage-C2QE6R93.js.map} +1 -1
- package/dist/{config-CmhIPHn_.js → config-BaU_aWgi.js} +35 -4
- package/dist/config-BaU_aWgi.js.map +1 -0
- package/dist/{debug-DvpksqEL.js → debug-BKqoXB_p.js} +2 -2
- package/dist/{debug-DvpksqEL.js.map → debug-BKqoXB_p.js.map} +1 -1
- package/dist/main.js +4 -4
- package/dist/{responses-bridge-registry-BJ5Sbh6-.js → responses-bridge-registry-DqCoY6Ex.js} +14 -7
- package/dist/responses-bridge-registry-DqCoY6Ex.js.map +1 -0
- package/dist/{server-DJ3_UGc4.js → server-C7pCkArb.js} +636 -187
- package/dist/server-C7pCkArb.js.map +1 -0
- package/dist/{start-DaB0AcjZ.js → start-CdLbBkRA.js} +4 -4
- package/dist/{start-DaB0AcjZ.js.map → start-CdLbBkRA.js.map} +1 -1
- package/dist/token-671YFxgv.js +947 -0
- package/dist/token-671YFxgv.js.map +1 -0
- package/package.json +2 -2
- package/dist/admin/assets/index-BAh4eOwM.js +0 -110
- package/dist/config-CmhIPHn_.js.map +0 -1
- package/dist/responses-bridge-registry-BJ5Sbh6-.js.map +0 -1
- package/dist/server-DJ3_UGc4.js.map +0 -1
- package/dist/token-DrFDLVxa.js +0 -365
- package/dist/token-DrFDLVxa.js.map +0 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { A as accountFromState, B as compactSystemPromptStarts, C as copilotHeaders, D as prepareForCompact, E as normalizeDomain, F as resolveTraceId, L as compactAutoContinuePromptStarts, M as captureOutboundHeadersSnapshot, O as prepareInteractionHeaders, P as requestContext, R as compactMessageSections, S as copilotBaseUrl, T as copilotWebSocketHeaders, b as HTTPError, c as getUUID, d as parseUserIdMetadata, f as resolveAffinityKey, g as getCopilotUsage, h as getDeviceCode, j as state, k as prepareMessageProxyHeaders, l as isNullish, m as getGitHubUser, o as generateRequestIdFromPayload, p as sleep, s as getRootSessionId, t as pollAccessToken, u as normalizeStableSessionId, v as getProxyEnvDispatcher, x as forwardError } from "./poll-access-token-GzVkiTH8.js";
|
|
2
2
|
import { a as getAccountClientIdentityByLoginAndApp, b as getCurrentIdentityEnvironment, d as loadRegistry, g as saveRegistry, h as saveAccountToken, l as listAccountsFromRegistry, m as removeAccountToken, p as removeAccountFromRegistry, r as addAccountToRegistry, t as isAccountType } from "./account-DpW8RaT6.js";
|
|
3
3
|
import { r as ensurePaths, t as PATHS } from "./paths-Bpsb62LK.js";
|
|
4
|
-
import { C as
|
|
4
|
+
import { A as shouldCompactUseSmallModel, C as isResponsesApiWebSearchEnabled, D as resolveMappedModel, E as mergeConfigWithDefaults, O as resolveModelAlias, S as isResponsesApiContextManagementEnabled, _ as getSmallModel, a as getConfig, b as isMessageStartInputTokensFallbackEnabled, c as getModelAliases, d as getModelResponsesApiCompactThreshold$1, f as getProviderConfig, h as getReasoningEffortForModel, i as getClaudeTokenMultiplier, l as getModelAliasesInfo, m as getRawProviderConfig, n as getAliasTargetSet, o as getExtraPromptForModel, r as getAnthropicApiKey, s as getLogLevel, t as PROVIDER_TYPE_ANTHROPIC, u as getModelRefreshIntervalMs, v as isAccountAffinityEnabled, w as isResponsesApiWebSocketEnabled, x as isMessagesApiEnabled, y as isForceAgentEnabled } from "./config-BaU_aWgi.js";
|
|
5
|
+
import { a as forwardCodexResponses, n as setupCodexToken } from "./token-671YFxgv.js";
|
|
5
6
|
import { i as getRequestOutboundStore, r as getRedactedHeaderKeys } from "./request-outbound-DZTxxtcx.js";
|
|
6
7
|
import { i as isMcpHttpEnabledFromEnv, n as DEFAULT_MCP_HTTP_PATH } from "./mcp-http-config-DMdUDz1D.js";
|
|
7
8
|
import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, i as isBridgeToolSearchName, l as selectDeferredToolsByNames, n as BRIDGE_TOOL_SEARCH_NAME, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, u as shouldEnableResponsesToolSearch } from "./mcp-server-DEqHrXFq.js";
|
|
8
9
|
import { n as handleStreamableHttpMcpRequest, r as mcpHttpCorsOptions } from "./mcp-http-DI4Vz01p.js";
|
|
9
|
-
import {
|
|
10
|
+
import { C as createAuthMiddleware, S as isDevModeEnabled, _ as normalizeMessagesUsage, b as copilotFetch, c as accountsManager, d as extractResponsesUsageFromStreamEvent, f as getClientIpInfo, g as normalizeEmbeddingsUsage, h as normalizeChatCompletionsUsage, l as applySharedSessionAffinityRetention, m as getStatsStore, p as getRequestHistoryStore, s as updateQuotaRefreshSchedulerFromConfig, t as closeResponsesBridge, u as extractResponsesUsageFromResult, v as normalizeResponsesUsage, x as flushPendingCapture, y as toLocalDateString } from "./responses-bridge-registry-DqCoY6Ex.js";
|
|
10
11
|
import consola from "consola";
|
|
11
12
|
import fs, { readFile } from "node:fs/promises";
|
|
12
13
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -953,6 +954,44 @@ const stripCacheControl = (payload) => {
|
|
|
953
954
|
}
|
|
954
955
|
}
|
|
955
956
|
};
|
|
957
|
+
const normalizeCacheControl = (cacheControl) => {
|
|
958
|
+
if (!cacheControl || typeof cacheControl !== "object" || Array.isArray(cacheControl)) return;
|
|
959
|
+
const type = cacheControl.type;
|
|
960
|
+
return type === "ephemeral" ? { type } : void 0;
|
|
961
|
+
};
|
|
962
|
+
const applyTopLevelCacheControl = (payload) => {
|
|
963
|
+
const topLevel = normalizeCacheControl(payload.cache_control);
|
|
964
|
+
if (!topLevel) {
|
|
965
|
+
if (payload.cache_control !== void 0) delete payload.cache_control;
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
delete payload.cache_control;
|
|
969
|
+
for (let m = payload.messages.length - 1; m >= 0; m--) {
|
|
970
|
+
const message = payload.messages[m];
|
|
971
|
+
if (typeof message.content === "string") {
|
|
972
|
+
message.content = [{
|
|
973
|
+
type: "text",
|
|
974
|
+
text: message.content,
|
|
975
|
+
cache_control: { ...topLevel }
|
|
976
|
+
}];
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (!Array.isArray(message.content)) continue;
|
|
980
|
+
for (let b = message.content.length - 1; b >= 0; b--) {
|
|
981
|
+
const block = message.content[b];
|
|
982
|
+
if (block.type !== "text" && block.type !== "image" && block.type !== "tool_use" && block.type !== "tool_result") continue;
|
|
983
|
+
block.cache_control ??= { ...topLevel };
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
const stripToolEagerInputStreaming = (payload) => {
|
|
989
|
+
if (!payload.tools || payload.tools.length === 0) return;
|
|
990
|
+
for (const tool of payload.tools) {
|
|
991
|
+
const extended = tool;
|
|
992
|
+
if ("eager_input_streaming" in extended) delete extended.eager_input_streaming;
|
|
993
|
+
}
|
|
994
|
+
};
|
|
956
995
|
const filterAssistantThinkingBlocks = (payload) => {
|
|
957
996
|
for (const msg of payload.messages) if (msg.role === "assistant" && Array.isArray(msg.content)) msg.content = msg.content.filter((block) => {
|
|
958
997
|
if (block.type !== "thinking") return true;
|
|
@@ -961,6 +1000,8 @@ const filterAssistantThinkingBlocks = (payload) => {
|
|
|
961
1000
|
};
|
|
962
1001
|
const prepareMessagesApiPayload = (payload, selectedModel) => {
|
|
963
1002
|
stripCacheControl(payload);
|
|
1003
|
+
applyTopLevelCacheControl(payload);
|
|
1004
|
+
stripToolEagerInputStreaming(payload);
|
|
964
1005
|
filterAssistantThinkingBlocks(payload);
|
|
965
1006
|
const hasThinking = Boolean(payload.thinking);
|
|
966
1007
|
const toolChoice = payload.tool_choice;
|
|
@@ -2499,13 +2540,22 @@ adminApiRoutes.get("/accounts", async (c) => {
|
|
|
2499
2540
|
unlimited: s.unlimited,
|
|
2500
2541
|
failed: s.failed,
|
|
2501
2542
|
failureReason: s.failureReason,
|
|
2502
|
-
enabled: s.enabled
|
|
2543
|
+
enabled: s.enabled,
|
|
2544
|
+
lastModelsFetch: s.lastModelsFetch,
|
|
2545
|
+
isRefreshingModels: s.isRefreshingModels
|
|
2503
2546
|
},
|
|
2504
2547
|
stats
|
|
2505
2548
|
};
|
|
2506
2549
|
});
|
|
2507
2550
|
return c.json({ items });
|
|
2508
2551
|
});
|
|
2552
|
+
adminApiRoutes.post("/accounts/models/refresh", async (c) => {
|
|
2553
|
+
const { failedCount } = await accountsManager.refreshAllModelsNow();
|
|
2554
|
+
return c.json({
|
|
2555
|
+
ok: true,
|
|
2556
|
+
failedCount
|
|
2557
|
+
});
|
|
2558
|
+
});
|
|
2509
2559
|
adminApiRoutes.get("/requests", (c) => {
|
|
2510
2560
|
const p = new URL(c.req.url, "http://local").searchParams;
|
|
2511
2561
|
const limit = parseFiniteNumber(p.get("limit")) ?? 50;
|
|
@@ -3736,6 +3786,41 @@ const createHandlerLogger = (name) => {
|
|
|
3736
3786
|
return instance;
|
|
3737
3787
|
};
|
|
3738
3788
|
//#endregion
|
|
3789
|
+
//#region src/lib/provider-model.ts
|
|
3790
|
+
const parseProviderModelAlias = (model) => {
|
|
3791
|
+
const separatorIndex = model.indexOf("/");
|
|
3792
|
+
if (separatorIndex <= 0 || separatorIndex === model.length - 1) return null;
|
|
3793
|
+
const provider = model.slice(0, separatorIndex).trim();
|
|
3794
|
+
const providerModel = model.slice(separatorIndex + 1).trim();
|
|
3795
|
+
if (!provider || !providerModel) return null;
|
|
3796
|
+
return {
|
|
3797
|
+
model: providerModel,
|
|
3798
|
+
provider
|
|
3799
|
+
};
|
|
3800
|
+
};
|
|
3801
|
+
const resolveExistingProviderModelAlias = (model, resolveProvider) => {
|
|
3802
|
+
const alias = parseProviderModelAlias(model);
|
|
3803
|
+
if (!alias) return null;
|
|
3804
|
+
return resolveProvider(alias.provider) ? alias : null;
|
|
3805
|
+
};
|
|
3806
|
+
const createFallbackModel = (modelId) => ({
|
|
3807
|
+
capabilities: {
|
|
3808
|
+
family: "provider",
|
|
3809
|
+
limits: {},
|
|
3810
|
+
object: "model_capabilities",
|
|
3811
|
+
supports: {},
|
|
3812
|
+
tokenizer: "o200k_base",
|
|
3813
|
+
type: "chat"
|
|
3814
|
+
},
|
|
3815
|
+
id: modelId,
|
|
3816
|
+
model_picker_enabled: false,
|
|
3817
|
+
name: modelId,
|
|
3818
|
+
object: "model",
|
|
3819
|
+
preview: false,
|
|
3820
|
+
vendor: "provider",
|
|
3821
|
+
version: "unknown"
|
|
3822
|
+
});
|
|
3823
|
+
//#endregion
|
|
3739
3824
|
//#region src/lib/rate-limit.ts
|
|
3740
3825
|
async function checkRateLimit(state) {
|
|
3741
3826
|
if (state.rateLimitSeconds === void 0) return;
|
|
@@ -3761,6 +3846,210 @@ async function checkRateLimit(state) {
|
|
|
3761
3846
|
consola.info("Rate limit wait completed, proceeding with request");
|
|
3762
3847
|
}
|
|
3763
3848
|
//#endregion
|
|
3849
|
+
//#region src/lib/provider-resolver.ts
|
|
3850
|
+
function isMissingCodexCredentialsError(error) {
|
|
3851
|
+
return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
|
|
3852
|
+
}
|
|
3853
|
+
async function resolveProviderConfig$3(providerName) {
|
|
3854
|
+
const normalizedProviderName = providerName.trim();
|
|
3855
|
+
if (!normalizedProviderName) return null;
|
|
3856
|
+
if (normalizedProviderName === "codex") {
|
|
3857
|
+
if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
|
|
3858
|
+
try {
|
|
3859
|
+
await setupCodexToken();
|
|
3860
|
+
} catch (error) {
|
|
3861
|
+
if (isMissingCodexCredentialsError(error)) return null;
|
|
3862
|
+
throw error;
|
|
3863
|
+
}
|
|
3864
|
+
const providerConfig = getProviderConfig(normalizedProviderName);
|
|
3865
|
+
if (!providerConfig) return null;
|
|
3866
|
+
return {
|
|
3867
|
+
...providerConfig,
|
|
3868
|
+
apiKey: state.codexAccessToken ?? providerConfig.apiKey
|
|
3869
|
+
};
|
|
3870
|
+
}
|
|
3871
|
+
return getProviderConfig(normalizedProviderName);
|
|
3872
|
+
}
|
|
3873
|
+
//#endregion
|
|
3874
|
+
//#region src/lib/token-usage.ts
|
|
3875
|
+
function normalizeOpenAIUsage$1(usage) {
|
|
3876
|
+
const cacheCreationInputTokens = usage?.prompt_tokens_details?.cache_creation_input_tokens;
|
|
3877
|
+
const cacheReadInputTokens = usage?.prompt_tokens_details?.cached_tokens;
|
|
3878
|
+
return {
|
|
3879
|
+
inputTokens: usage?.prompt_tokens === void 0 ? void 0 : Math.max(0, usage.prompt_tokens - (cacheCreationInputTokens ?? 0) - (cacheReadInputTokens ?? 0)),
|
|
3880
|
+
outputTokens: usage?.completion_tokens,
|
|
3881
|
+
cacheCreationInputTokens,
|
|
3882
|
+
cacheReadInputTokens
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
function createProviderTokenUsageRecorder(options) {
|
|
3886
|
+
const logger = createHandlerLogger(`provider-${options.providerName}`);
|
|
3887
|
+
return (usage) => {
|
|
3888
|
+
logger.debug(`${options.endpoint} usage`, {
|
|
3889
|
+
model: options.model,
|
|
3890
|
+
provider: options.providerName,
|
|
3891
|
+
...usage
|
|
3892
|
+
});
|
|
3893
|
+
};
|
|
3894
|
+
}
|
|
3895
|
+
//#endregion
|
|
3896
|
+
//#region src/services/providers/provider-proxy.ts
|
|
3897
|
+
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
3898
|
+
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
3899
|
+
const STRIPPED_RESPONSE_HEADERS = [
|
|
3900
|
+
"connection",
|
|
3901
|
+
"content-encoding",
|
|
3902
|
+
"content-length",
|
|
3903
|
+
"keep-alive",
|
|
3904
|
+
"proxy-authenticate",
|
|
3905
|
+
"proxy-authorization",
|
|
3906
|
+
"te",
|
|
3907
|
+
"trailer",
|
|
3908
|
+
"transfer-encoding",
|
|
3909
|
+
"upgrade"
|
|
3910
|
+
];
|
|
3911
|
+
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
3912
|
+
const authHeaders = {};
|
|
3913
|
+
if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
3914
|
+
else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
3915
|
+
const headers = {
|
|
3916
|
+
"content-type": "application/json",
|
|
3917
|
+
accept: "application/json",
|
|
3918
|
+
...authHeaders
|
|
3919
|
+
};
|
|
3920
|
+
for (const headerName of SHARED_FORWARDABLE_HEADERS) {
|
|
3921
|
+
const headerValue = requestHeaders.get(headerName);
|
|
3922
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
3923
|
+
}
|
|
3924
|
+
if (providerConfig.type !== "anthropic") return headers;
|
|
3925
|
+
for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
|
|
3926
|
+
const headerValue = requestHeaders.get(headerName);
|
|
3927
|
+
if (headerValue) headers[headerName] = headerValue;
|
|
3928
|
+
}
|
|
3929
|
+
return headers;
|
|
3930
|
+
}
|
|
3931
|
+
function createProviderProxyResponse(upstreamResponse, body) {
|
|
3932
|
+
const headers = new Headers(upstreamResponse.headers);
|
|
3933
|
+
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
3934
|
+
return new Response(body ?? upstreamResponse.body, {
|
|
3935
|
+
headers,
|
|
3936
|
+
status: upstreamResponse.status,
|
|
3937
|
+
statusText: upstreamResponse.statusText
|
|
3938
|
+
});
|
|
3939
|
+
}
|
|
3940
|
+
async function forwardProviderMessages(providerConfig, payload, requestHeaders, fetchImpl = fetch) {
|
|
3941
|
+
return await fetchImpl(`${providerConfig.baseUrl}/v1/messages`, {
|
|
3942
|
+
method: "POST",
|
|
3943
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3944
|
+
body: JSON.stringify(payload)
|
|
3945
|
+
});
|
|
3946
|
+
}
|
|
3947
|
+
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders, fetchImpl = fetch) {
|
|
3948
|
+
return await fetchImpl(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
3949
|
+
method: "POST",
|
|
3950
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3951
|
+
body: JSON.stringify(payload)
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
|
|
3955
|
+
return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
|
|
3956
|
+
method: "POST",
|
|
3957
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3958
|
+
body: JSON.stringify(payload)
|
|
3959
|
+
});
|
|
3960
|
+
}
|
|
3961
|
+
async function forwardProviderModels(providerConfig, requestHeaders, fetchImpl = fetch) {
|
|
3962
|
+
return await fetchImpl(`${providerConfig.baseUrl}/v1/models`, {
|
|
3963
|
+
method: "GET",
|
|
3964
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
3965
|
+
});
|
|
3966
|
+
}
|
|
3967
|
+
//#endregion
|
|
3968
|
+
//#region src/routes/provider/chat-completions/handler.ts
|
|
3969
|
+
const logger$8 = createHandlerLogger("provider-chat-completions-handler");
|
|
3970
|
+
async function handleProviderChatCompletionsForProvider(c, options) {
|
|
3971
|
+
const { payload, provider } = options;
|
|
3972
|
+
const providerConfig = await (c.get("providerConfigResolver") ?? resolveProviderConfig$3)(provider);
|
|
3973
|
+
if (providerConfig?.type !== "openai-compatible") return c.json({ error: {
|
|
3974
|
+
message: `Provider '${provider}' does not support the /v1/chat/completions endpoint`,
|
|
3975
|
+
type: "invalid_request_error"
|
|
3976
|
+
} }, 400);
|
|
3977
|
+
const modelConfig = providerConfig.models?.[payload.model];
|
|
3978
|
+
applyProviderModelDefaults(payload, modelConfig);
|
|
3979
|
+
applyMissingExtraBody$1(payload, { extraBody: modelConfig?.extraBody });
|
|
3980
|
+
applyProviderStreamOptions(payload);
|
|
3981
|
+
debugJson(logger$8, "provider.chat_completions.request", {
|
|
3982
|
+
payload,
|
|
3983
|
+
provider
|
|
3984
|
+
});
|
|
3985
|
+
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, payload, c.req.raw.headers);
|
|
3986
|
+
if (!upstreamResponse.ok) {
|
|
3987
|
+
logger$8.error("Failed to create provider chat completions", {
|
|
3988
|
+
provider,
|
|
3989
|
+
statusCode: upstreamResponse.status
|
|
3990
|
+
});
|
|
3991
|
+
throw new HTTPError(`Failed to create ${provider} chat completions`, upstreamResponse);
|
|
3992
|
+
}
|
|
3993
|
+
const recordUsage = createProviderChatCompletionsUsageRecorder(payload, provider);
|
|
3994
|
+
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
3995
|
+
if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderChatCompletions(c, upstreamResponse, {
|
|
3996
|
+
provider,
|
|
3997
|
+
recordUsage
|
|
3998
|
+
});
|
|
3999
|
+
const responseBody = await upstreamResponse.clone().json();
|
|
4000
|
+
recordUsage(normalizeOpenAIUsage$1(responseBody.usage));
|
|
4001
|
+
debugJson(logger$8, "provider.chat_completions.response", responseBody);
|
|
4002
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
4003
|
+
}
|
|
4004
|
+
const applyProviderModelDefaults = (payload, modelConfig) => {
|
|
4005
|
+
payload.temperature ??= modelConfig?.temperature;
|
|
4006
|
+
payload.top_p ??= modelConfig?.topP;
|
|
4007
|
+
payload.top_k ??= modelConfig?.topK;
|
|
4008
|
+
};
|
|
4009
|
+
const applyMissingExtraBody$1 = (payload, options) => {
|
|
4010
|
+
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
4011
|
+
};
|
|
4012
|
+
const applyProviderStreamOptions = (payload) => {
|
|
4013
|
+
if (!payload.stream) return;
|
|
4014
|
+
payload.stream_options = {
|
|
4015
|
+
...payload.stream_options ?? {},
|
|
4016
|
+
include_usage: true
|
|
4017
|
+
};
|
|
4018
|
+
};
|
|
4019
|
+
const createProviderChatCompletionsUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
|
|
4020
|
+
endpoint: "chat_completions",
|
|
4021
|
+
model: payload.model,
|
|
4022
|
+
providerName: provider
|
|
4023
|
+
});
|
|
4024
|
+
const streamProviderChatCompletions = (c, upstreamResponse, options) => {
|
|
4025
|
+
logger$8.debug("provider.chat_completions.streaming", { provider: options.provider });
|
|
4026
|
+
return streamSSE(c, async (stream) => {
|
|
4027
|
+
let usage = {};
|
|
4028
|
+
try {
|
|
4029
|
+
for await (const chunk of events(upstreamResponse)) {
|
|
4030
|
+
debugJson(logger$8, "provider.chat_completions.stream_chunk", chunk);
|
|
4031
|
+
if (chunk.data && chunk.data !== "[DONE]") {
|
|
4032
|
+
const parsedChunk = parseChatCompletionChunkData(chunk.data);
|
|
4033
|
+
if (parsedChunk?.usage) usage = normalizeOpenAIUsage$1(parsedChunk.usage);
|
|
4034
|
+
}
|
|
4035
|
+
await stream.writeSSE({
|
|
4036
|
+
event: chunk.event,
|
|
4037
|
+
data: chunk.data ?? ""
|
|
4038
|
+
});
|
|
4039
|
+
}
|
|
4040
|
+
} finally {
|
|
4041
|
+
options.recordUsage(usage);
|
|
4042
|
+
}
|
|
4043
|
+
});
|
|
4044
|
+
};
|
|
4045
|
+
const parseChatCompletionChunkData = (data) => {
|
|
4046
|
+
try {
|
|
4047
|
+
return JSON.parse(data);
|
|
4048
|
+
} catch {
|
|
4049
|
+
return null;
|
|
4050
|
+
}
|
|
4051
|
+
};
|
|
4052
|
+
//#endregion
|
|
3764
4053
|
//#region src/routes/chat-completions/support.ts
|
|
3765
4054
|
const CHAT_COMPLETIONS_ENDPOINT$1 = "/chat/completions";
|
|
3766
4055
|
const GPT_5_4_MODEL_ID = "gpt-5.4";
|
|
@@ -3870,7 +4159,7 @@ function selectionFailureResponse$2(c, params) {
|
|
|
3870
4159
|
}
|
|
3871
4160
|
//#endregion
|
|
3872
4161
|
//#region src/routes/chat-completions/handler.ts
|
|
3873
|
-
const logger$
|
|
4162
|
+
const logger$7 = createHandlerLogger("chat-completions-handler");
|
|
3874
4163
|
function buildChatCompletionCandidates(clientModel) {
|
|
3875
4164
|
return [{
|
|
3876
4165
|
modelId: clientModel,
|
|
@@ -3905,10 +4194,21 @@ function maybeRejectChatCompletionsClientModel(c, store, params) {
|
|
|
3905
4194
|
return null;
|
|
3906
4195
|
}
|
|
3907
4196
|
async function handleCompletion$1(c) {
|
|
4197
|
+
const payload = await c.req.json();
|
|
4198
|
+
const mappedModelResolver = c.get("resolveMappedModel") ?? resolveMappedModel;
|
|
4199
|
+
const providerConfigResolver = c.get("providerConfigResolver") ?? getProviderConfig;
|
|
4200
|
+
payload.model = mappedModelResolver(payload.model);
|
|
4201
|
+
const providerModelAlias = resolveExistingProviderModelAlias(payload.model, providerConfigResolver);
|
|
4202
|
+
if (providerModelAlias) {
|
|
4203
|
+
payload.model = providerModelAlias.model;
|
|
4204
|
+
return await handleProviderChatCompletionsForProvider(c, {
|
|
4205
|
+
payload,
|
|
4206
|
+
provider: providerModelAlias.provider
|
|
4207
|
+
});
|
|
4208
|
+
}
|
|
3908
4209
|
await checkRateLimit(state);
|
|
3909
4210
|
const store = getRequestHistoryStore();
|
|
3910
4211
|
const request = buildRequestContext$1(c);
|
|
3911
|
-
const payload = await c.req.json();
|
|
3912
4212
|
const clientModel = payload.model;
|
|
3913
4213
|
const streamRequested = Boolean(payload.stream);
|
|
3914
4214
|
const normalizedPromptCacheKey = applyChatRequestMetadata(request, payload, getChatInitiator(payload.messages));
|
|
@@ -4007,12 +4307,12 @@ async function writeChatCompletionsStreamError(stream, message) {
|
|
|
4007
4307
|
} }) });
|
|
4008
4308
|
await stream.writeSSE({ data: "[DONE]" });
|
|
4009
4309
|
} catch (streamError) {
|
|
4010
|
-
logger$
|
|
4310
|
+
logger$7.warn("Failed to write chat completions stream error event:", streamError);
|
|
4011
4311
|
}
|
|
4012
4312
|
}
|
|
4013
4313
|
async function selectChatCompletionAccount(params) {
|
|
4014
4314
|
const { c, store, request, payload, clientModel, streamRequested, normalizedPromptCacheKey } = params;
|
|
4015
|
-
debugJsonTail(logger$
|
|
4315
|
+
debugJsonTail(logger$7, "Request payload:", {
|
|
4016
4316
|
value: payload,
|
|
4017
4317
|
tailLength: 400
|
|
4018
4318
|
});
|
|
@@ -4047,9 +4347,9 @@ async function selectChatCompletionAccount(params) {
|
|
|
4047
4347
|
async function logTokenCountForRequest(params) {
|
|
4048
4348
|
try {
|
|
4049
4349
|
const tokenCount = await getTokenCount(params.payload, params.selectedModel);
|
|
4050
|
-
logger$
|
|
4350
|
+
logger$7.info("Current token count:", tokenCount);
|
|
4051
4351
|
} catch (error) {
|
|
4052
|
-
logger$
|
|
4352
|
+
logger$7.warn("Failed to calculate token count:", error);
|
|
4053
4353
|
}
|
|
4054
4354
|
}
|
|
4055
4355
|
function applyDefaultMaxTokens(payload, selectedModel) {
|
|
@@ -4058,7 +4358,7 @@ function applyDefaultMaxTokens(payload, selectedModel) {
|
|
|
4058
4358
|
...payload,
|
|
4059
4359
|
max_tokens: selectedModel.capabilities.limits.max_output_tokens
|
|
4060
4360
|
};
|
|
4061
|
-
debugJson(logger$
|
|
4361
|
+
debugJson(logger$7, "Set max_tokens to:", updated.max_tokens);
|
|
4062
4362
|
return updated;
|
|
4063
4363
|
}
|
|
4064
4364
|
async function handleStreamingRequest(params) {
|
|
@@ -4092,7 +4392,7 @@ async function handleStreamingRequest(params) {
|
|
|
4092
4392
|
premiumUnlimitedBefore,
|
|
4093
4393
|
response
|
|
4094
4394
|
});
|
|
4095
|
-
logger$
|
|
4395
|
+
logger$7.debug("Streaming response");
|
|
4096
4396
|
return streamSSE(c, (stream) => streamChatCompletionsAndLog$1({
|
|
4097
4397
|
stream,
|
|
4098
4398
|
response,
|
|
@@ -4147,7 +4447,7 @@ async function handleNonStreamingUpstreamResponse(params) {
|
|
|
4147
4447
|
let upstreamErrorMessageRaw;
|
|
4148
4448
|
const finishedAtMs = Date.now();
|
|
4149
4449
|
try {
|
|
4150
|
-
debugJson(logger$
|
|
4450
|
+
debugJson(logger$7, "Non-streaming response:", response);
|
|
4151
4451
|
return c.json(response);
|
|
4152
4452
|
} catch (error) {
|
|
4153
4453
|
const details = await extractErrorObservability(error);
|
|
@@ -4200,7 +4500,7 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
4200
4500
|
if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
|
|
4201
4501
|
const usage = await extractUsageFromChunk(chunk);
|
|
4202
4502
|
if (usage) lastUsage = usage;
|
|
4203
|
-
debugJson(logger$
|
|
4503
|
+
debugJson(logger$7, "Streaming chunk:", chunk);
|
|
4204
4504
|
await stream.writeSSE(chunk);
|
|
4205
4505
|
}
|
|
4206
4506
|
} catch (error) {
|
|
@@ -4209,7 +4509,7 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
4209
4509
|
errorStatus = details.errorStatus;
|
|
4210
4510
|
errorMessage = details.errorMessage;
|
|
4211
4511
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
4212
|
-
logger$
|
|
4512
|
+
logger$7.warn("Streaming error:", error);
|
|
4213
4513
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
|
|
4214
4514
|
await writeChatCompletionsStreamError(stream, getUserVisibleErrorMessage(details));
|
|
4215
4515
|
} finally {
|
|
@@ -4247,7 +4547,7 @@ async function extractUsageFromChunk(chunk) {
|
|
|
4247
4547
|
try {
|
|
4248
4548
|
data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
|
|
4249
4549
|
} catch (error) {
|
|
4250
|
-
logger$
|
|
4550
|
+
logger$7.warn("Failed to read chat completions usage chunk:", error);
|
|
4251
4551
|
return;
|
|
4252
4552
|
}
|
|
4253
4553
|
if (!data || data === "[DONE]") return;
|
|
@@ -4256,7 +4556,7 @@ async function extractUsageFromChunk(chunk) {
|
|
|
4256
4556
|
if (!parsed.usage) return void 0;
|
|
4257
4557
|
return normalizeChatCompletionsUsage(parsed.usage);
|
|
4258
4558
|
} catch (error) {
|
|
4259
|
-
logger$
|
|
4559
|
+
logger$7.warn("Failed to parse chat completions usage chunk:", {
|
|
4260
4560
|
error,
|
|
4261
4561
|
data
|
|
4262
4562
|
});
|
|
@@ -4283,7 +4583,7 @@ async function handleNonStreamingRequest(params) {
|
|
|
4283
4583
|
selection.confirmAffinity?.();
|
|
4284
4584
|
finishedAtMs = Date.now();
|
|
4285
4585
|
usage = normalizeChatCompletionsUsage(response.usage);
|
|
4286
|
-
debugJson(logger$
|
|
4586
|
+
debugJson(logger$7, "Non-streaming response:", response);
|
|
4287
4587
|
return c.json(response);
|
|
4288
4588
|
} catch (error) {
|
|
4289
4589
|
finishedAtMs = Date.now();
|
|
@@ -4505,43 +4805,8 @@ async function runEmbeddingsWithAccount({ c, store, ctx, payload, clientModel, s
|
|
|
4505
4805
|
}
|
|
4506
4806
|
}
|
|
4507
4807
|
//#endregion
|
|
4508
|
-
//#region src/lib/provider-model.ts
|
|
4509
|
-
const parseProviderModelAlias = (model) => {
|
|
4510
|
-
const separatorIndex = model.indexOf("/");
|
|
4511
|
-
if (separatorIndex <= 0 || separatorIndex === model.length - 1) return null;
|
|
4512
|
-
const provider = model.slice(0, separatorIndex).trim();
|
|
4513
|
-
const providerModel = model.slice(separatorIndex + 1).trim();
|
|
4514
|
-
if (!provider || !providerModel) return null;
|
|
4515
|
-
return {
|
|
4516
|
-
model: providerModel,
|
|
4517
|
-
provider
|
|
4518
|
-
};
|
|
4519
|
-
};
|
|
4520
|
-
const resolveExistingProviderModelAlias = (model, resolveProvider) => {
|
|
4521
|
-
const alias = parseProviderModelAlias(model);
|
|
4522
|
-
if (!alias) return null;
|
|
4523
|
-
return resolveProvider(alias.provider) ? alias : null;
|
|
4524
|
-
};
|
|
4525
|
-
const createFallbackModel = (modelId) => ({
|
|
4526
|
-
capabilities: {
|
|
4527
|
-
family: "provider",
|
|
4528
|
-
limits: {},
|
|
4529
|
-
object: "model_capabilities",
|
|
4530
|
-
supports: {},
|
|
4531
|
-
tokenizer: "o200k_base",
|
|
4532
|
-
type: "chat"
|
|
4533
|
-
},
|
|
4534
|
-
id: modelId,
|
|
4535
|
-
model_picker_enabled: false,
|
|
4536
|
-
name: modelId,
|
|
4537
|
-
object: "model",
|
|
4538
|
-
preview: false,
|
|
4539
|
-
vendor: "provider",
|
|
4540
|
-
version: "unknown"
|
|
4541
|
-
});
|
|
4542
|
-
//#endregion
|
|
4543
4808
|
//#region src/routes/provider/messages/count-tokens-handler.ts
|
|
4544
|
-
const logger$
|
|
4809
|
+
const logger$6 = createHandlerLogger("provider-count-tokens-handler");
|
|
4545
4810
|
const resolveProviderConfig$2 = (c, provider) => {
|
|
4546
4811
|
return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
|
|
4547
4812
|
};
|
|
@@ -4573,14 +4838,14 @@ async function handleProviderCountTokensForProvider(c, options) {
|
|
|
4573
4838
|
try {
|
|
4574
4839
|
const tokenCount = await getTokenCount(translateToOpenAI(anthropicPayload, translationOptions), findEndpointModel(modelId) ?? createFallbackModel(modelId));
|
|
4575
4840
|
const finalTokenCount = tokenCount.input + tokenCount.output;
|
|
4576
|
-
logger$
|
|
4841
|
+
logger$6.debug("provider.count_tokens.success", {
|
|
4577
4842
|
provider,
|
|
4578
4843
|
model: anthropicPayload.model,
|
|
4579
4844
|
input_tokens: finalTokenCount
|
|
4580
4845
|
});
|
|
4581
4846
|
return c.json({ input_tokens: finalTokenCount });
|
|
4582
4847
|
} catch (error) {
|
|
4583
|
-
logger$
|
|
4848
|
+
logger$6.error("provider.count_tokens.error", {
|
|
4584
4849
|
provider,
|
|
4585
4850
|
error
|
|
4586
4851
|
});
|
|
@@ -5035,7 +5300,11 @@ const consumeResponsesWebSocketStream = async (stream) => {
|
|
|
5035
5300
|
for await (const chunk of stream) {
|
|
5036
5301
|
if (!chunk.data || chunk.data === "[DONE]") continue;
|
|
5037
5302
|
const event = JSON.parse(chunk.data);
|
|
5038
|
-
if (event.type === "error")
|
|
5303
|
+
if (event.type === "error") {
|
|
5304
|
+
const status = typeof event.code === "string" ? parseInt(event.code, 10) : NaN;
|
|
5305
|
+
const httpStatus = Number.isFinite(status) && status >= 100 && status < 600 ? status : 500;
|
|
5306
|
+
throw new HTTPError(event.message, new Response(JSON.stringify({ error: { message: event.message } }), { status: httpStatus }));
|
|
5307
|
+
}
|
|
5039
5308
|
if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
|
|
5040
5309
|
}
|
|
5041
5310
|
throw new Error("Responses websocket ended without a terminal response");
|
|
@@ -6015,73 +6284,8 @@ function closeThinkingBlockIfOpen(state, events) {
|
|
|
6015
6284
|
}
|
|
6016
6285
|
}
|
|
6017
6286
|
//#endregion
|
|
6018
|
-
//#region src/services/providers/provider-proxy.ts
|
|
6019
|
-
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
6020
|
-
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
6021
|
-
const STRIPPED_RESPONSE_HEADERS = [
|
|
6022
|
-
"connection",
|
|
6023
|
-
"content-encoding",
|
|
6024
|
-
"content-length",
|
|
6025
|
-
"keep-alive",
|
|
6026
|
-
"proxy-authenticate",
|
|
6027
|
-
"proxy-authorization",
|
|
6028
|
-
"te",
|
|
6029
|
-
"trailer",
|
|
6030
|
-
"transfer-encoding",
|
|
6031
|
-
"upgrade"
|
|
6032
|
-
];
|
|
6033
|
-
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
6034
|
-
const authHeaders = {};
|
|
6035
|
-
if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
6036
|
-
else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
6037
|
-
const headers = {
|
|
6038
|
-
"content-type": "application/json",
|
|
6039
|
-
accept: "application/json",
|
|
6040
|
-
...authHeaders
|
|
6041
|
-
};
|
|
6042
|
-
for (const headerName of SHARED_FORWARDABLE_HEADERS) {
|
|
6043
|
-
const headerValue = requestHeaders.get(headerName);
|
|
6044
|
-
if (headerValue) headers[headerName] = headerValue;
|
|
6045
|
-
}
|
|
6046
|
-
if (providerConfig.type !== "anthropic") return headers;
|
|
6047
|
-
for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
|
|
6048
|
-
const headerValue = requestHeaders.get(headerName);
|
|
6049
|
-
if (headerValue) headers[headerName] = headerValue;
|
|
6050
|
-
}
|
|
6051
|
-
return headers;
|
|
6052
|
-
}
|
|
6053
|
-
function createProviderProxyResponse(upstreamResponse, body) {
|
|
6054
|
-
const headers = new Headers(upstreamResponse.headers);
|
|
6055
|
-
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
6056
|
-
return new Response(body ?? upstreamResponse.body, {
|
|
6057
|
-
headers,
|
|
6058
|
-
status: upstreamResponse.status,
|
|
6059
|
-
statusText: upstreamResponse.statusText
|
|
6060
|
-
});
|
|
6061
|
-
}
|
|
6062
|
-
async function forwardProviderMessages(providerConfig, payload, requestHeaders, fetchImpl = fetch) {
|
|
6063
|
-
return await fetchImpl(`${providerConfig.baseUrl}/v1/messages`, {
|
|
6064
|
-
method: "POST",
|
|
6065
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
6066
|
-
body: JSON.stringify(payload)
|
|
6067
|
-
});
|
|
6068
|
-
}
|
|
6069
|
-
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders, fetchImpl = fetch) {
|
|
6070
|
-
return await fetchImpl(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
6071
|
-
method: "POST",
|
|
6072
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
6073
|
-
body: JSON.stringify(payload)
|
|
6074
|
-
});
|
|
6075
|
-
}
|
|
6076
|
-
async function forwardProviderModels(providerConfig, requestHeaders, fetchImpl = fetch) {
|
|
6077
|
-
return await fetchImpl(`${providerConfig.baseUrl}/v1/models`, {
|
|
6078
|
-
method: "GET",
|
|
6079
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
6080
|
-
});
|
|
6081
|
-
}
|
|
6082
|
-
//#endregion
|
|
6083
6287
|
//#region src/routes/provider/messages/handler.ts
|
|
6084
|
-
const logger$
|
|
6288
|
+
const logger$5 = createHandlerLogger("provider-messages-handler");
|
|
6085
6289
|
const getProviderFetch$1 = (c) => c.get("providerFetch") ?? fetch;
|
|
6086
6290
|
const resolveProviderConfig$1 = (c, provider) => {
|
|
6087
6291
|
return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
|
|
@@ -6107,7 +6311,7 @@ const writeProviderStreamError = async (stream, message) => {
|
|
|
6107
6311
|
})
|
|
6108
6312
|
});
|
|
6109
6313
|
} catch (error) {
|
|
6110
|
-
logger$
|
|
6314
|
+
logger$5.warn("Failed to write provider stream error event", error);
|
|
6111
6315
|
}
|
|
6112
6316
|
};
|
|
6113
6317
|
async function handleProviderMessages(c) {
|
|
@@ -6136,7 +6340,7 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
6136
6340
|
try {
|
|
6137
6341
|
const modelConfig = providerConfig.models?.[payload.model];
|
|
6138
6342
|
applyModelDefaults(payload, modelConfig);
|
|
6139
|
-
debugJson(logger$
|
|
6343
|
+
debugJson(logger$5, "provider.messages.request", {
|
|
6140
6344
|
payload,
|
|
6141
6345
|
provider
|
|
6142
6346
|
});
|
|
@@ -6150,7 +6354,7 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
6150
6354
|
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
6151
6355
|
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers, getProviderFetch$1(c));
|
|
6152
6356
|
if (!upstreamResponse.ok) {
|
|
6153
|
-
logger$
|
|
6357
|
+
logger$5.error("Failed to create responses", upstreamResponse);
|
|
6154
6358
|
throw new HTTPError("Failed to create responses", upstreamResponse);
|
|
6155
6359
|
}
|
|
6156
6360
|
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
@@ -6170,7 +6374,7 @@ async function handleProviderMessagesForProvider(c, options) {
|
|
|
6170
6374
|
providerConfig
|
|
6171
6375
|
});
|
|
6172
6376
|
} catch (error) {
|
|
6173
|
-
logger$
|
|
6377
|
+
logger$5.error("provider.messages.error", {
|
|
6174
6378
|
provider,
|
|
6175
6379
|
error
|
|
6176
6380
|
});
|
|
@@ -6207,13 +6411,13 @@ const applyOpenAICompatibleExtraBodyThinkingBudget = (payload, options) => {
|
|
|
6207
6411
|
const handleOpenAICompatibleProviderMessages = async (c, options) => {
|
|
6208
6412
|
const { instrumentation, modelConfig, payload, provider, providerConfig } = options;
|
|
6209
6413
|
const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
|
|
6210
|
-
debugJson(logger$
|
|
6414
|
+
debugJson(logger$5, "provider.messages.openai_compatible.request", {
|
|
6211
6415
|
payload: openAIPayload,
|
|
6212
6416
|
provider
|
|
6213
6417
|
});
|
|
6214
6418
|
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers, getProviderFetch$1(c));
|
|
6215
6419
|
if (!upstreamResponse.ok) {
|
|
6216
|
-
logger$
|
|
6420
|
+
logger$5.error("Failed to create openai-compatible responses", upstreamResponse);
|
|
6217
6421
|
throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
|
|
6218
6422
|
}
|
|
6219
6423
|
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
@@ -6297,13 +6501,13 @@ const setContextCacheControl = (part) => {
|
|
|
6297
6501
|
part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
|
|
6298
6502
|
};
|
|
6299
6503
|
const streamProviderMessages = ({ c, instrumentation, providerConfig, upstreamResponse }) => {
|
|
6300
|
-
logger$
|
|
6504
|
+
logger$5.debug("provider.messages.streaming");
|
|
6301
6505
|
return streamSSE(c, async (stream) => {
|
|
6302
6506
|
let usage = {};
|
|
6303
6507
|
try {
|
|
6304
6508
|
let completed = false;
|
|
6305
6509
|
for await (const chunk of events(upstreamResponse)) {
|
|
6306
|
-
logger$
|
|
6510
|
+
logger$5.debug("provider.messages.raw_stream_event:", chunk.data);
|
|
6307
6511
|
const eventName = chunk.event;
|
|
6308
6512
|
if (eventName === "ping") {
|
|
6309
6513
|
await stream.writeSSE({
|
|
@@ -6339,14 +6543,14 @@ const streamProviderMessages = ({ c, instrumentation, providerConfig, upstreamRe
|
|
|
6339
6543
|
instrumentation?.onComplete?.(usage);
|
|
6340
6544
|
} catch (error) {
|
|
6341
6545
|
const details = await extractErrorObservability(error);
|
|
6342
|
-
logger$
|
|
6546
|
+
logger$5.warn("provider.messages.streaming.error", error);
|
|
6343
6547
|
instrumentation?.onError?.(details);
|
|
6344
6548
|
await writeProviderStreamError(stream, getUserVisibleErrorMessage(details));
|
|
6345
6549
|
}
|
|
6346
6550
|
});
|
|
6347
6551
|
};
|
|
6348
6552
|
const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamResponse }) => {
|
|
6349
|
-
logger$
|
|
6553
|
+
logger$5.debug("provider.messages.openai_compatible.streaming");
|
|
6350
6554
|
return streamSSE(c, async (stream) => {
|
|
6351
6555
|
let usage = {};
|
|
6352
6556
|
const streamState = {
|
|
@@ -6359,7 +6563,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6359
6563
|
try {
|
|
6360
6564
|
let completed = false;
|
|
6361
6565
|
for await (const chunk of events(upstreamResponse)) {
|
|
6362
|
-
logger$
|
|
6566
|
+
logger$5.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
|
|
6363
6567
|
if (chunk.event === "ping") {
|
|
6364
6568
|
await stream.writeSSE({
|
|
6365
6569
|
event: "ping",
|
|
@@ -6377,7 +6581,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6377
6581
|
const events = translateChunkToAnthropicEvents(parsed, streamState);
|
|
6378
6582
|
for (const event of events) {
|
|
6379
6583
|
const eventData = JSON.stringify(event);
|
|
6380
|
-
debugLazy(logger$
|
|
6584
|
+
debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
6381
6585
|
await stream.writeSSE({
|
|
6382
6586
|
event: event.type,
|
|
6383
6587
|
data: eventData
|
|
@@ -6387,7 +6591,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6387
6591
|
}
|
|
6388
6592
|
for (const event of flushPendingAnthropicStreamEvents(streamState)) {
|
|
6389
6593
|
const eventData = JSON.stringify(event);
|
|
6390
|
-
debugLazy(logger$
|
|
6594
|
+
debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
|
|
6391
6595
|
await stream.writeSSE({
|
|
6392
6596
|
event: event.type,
|
|
6393
6597
|
data: eventData
|
|
@@ -6398,7 +6602,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
|
|
|
6398
6602
|
instrumentation?.onComplete?.(usage);
|
|
6399
6603
|
} catch (error) {
|
|
6400
6604
|
const details = await extractErrorObservability(error);
|
|
6401
|
-
logger$
|
|
6605
|
+
logger$5.warn("provider.messages.openai_compatible.streaming.error", error);
|
|
6402
6606
|
instrumentation?.onError?.(details);
|
|
6403
6607
|
await writeProviderStreamError(stream, getUserVisibleErrorMessage(details));
|
|
6404
6608
|
}
|
|
@@ -6409,7 +6613,7 @@ const parseOpenAICompatibleStreamChunk = (data) => {
|
|
|
6409
6613
|
try {
|
|
6410
6614
|
parsed = JSON.parse(data);
|
|
6411
6615
|
} catch (error) {
|
|
6412
|
-
logger$
|
|
6616
|
+
logger$5.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
6413
6617
|
data,
|
|
6414
6618
|
error
|
|
6415
6619
|
});
|
|
@@ -6466,7 +6670,7 @@ const parseProviderStreamEvent = (data, providerConfig) => {
|
|
|
6466
6670
|
usage: {}
|
|
6467
6671
|
};
|
|
6468
6672
|
} catch (error) {
|
|
6469
|
-
logger$
|
|
6673
|
+
logger$5.error("provider.messages.streaming.adjust_tokens_error", {
|
|
6470
6674
|
error,
|
|
6471
6675
|
originalData: data
|
|
6472
6676
|
});
|
|
@@ -6476,7 +6680,7 @@ const parseProviderStreamEvent = (data, providerConfig) => {
|
|
|
6476
6680
|
const respondProviderMessagesJson = (c, options) => {
|
|
6477
6681
|
const { body, instrumentation, providerConfig } = options;
|
|
6478
6682
|
adjustInputTokens(providerConfig, body.usage);
|
|
6479
|
-
debugJson(logger$
|
|
6683
|
+
debugJson(logger$5, "provider.messages.no_stream result:", body);
|
|
6480
6684
|
const response = c.json(body);
|
|
6481
6685
|
instrumentation?.onComplete?.(normalizeAnthropicUsage(body.usage));
|
|
6482
6686
|
return response;
|
|
@@ -6484,7 +6688,7 @@ const respondProviderMessagesJson = (c, options) => {
|
|
|
6484
6688
|
const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
|
|
6485
6689
|
const { body, instrumentation } = options;
|
|
6486
6690
|
const anthropicResponse = translateToAnthropic(body);
|
|
6487
|
-
debugJson(logger$
|
|
6691
|
+
debugJson(logger$5, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
|
|
6488
6692
|
const response = c.json(anthropicResponse);
|
|
6489
6693
|
instrumentation?.onComplete?.(normalizeOpenAIUsage(body.usage));
|
|
6490
6694
|
return response;
|
|
@@ -6514,7 +6718,7 @@ const mergeAnthropicUsage = (current, next) => ({
|
|
|
6514
6718
|
const adjustInputTokens = (providerConfig, usage) => {
|
|
6515
6719
|
if (!providerConfig.adjustInputTokens || !usage) return;
|
|
6516
6720
|
usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
|
|
6517
|
-
debugJson(logger$
|
|
6721
|
+
debugJson(logger$5, "provider.messages.adjusted_usage:", usage);
|
|
6518
6722
|
};
|
|
6519
6723
|
//#endregion
|
|
6520
6724
|
//#region src/routes/messages/responses-stream-translation.ts
|
|
@@ -6991,6 +7195,7 @@ const stringifyToolSearchArguments = (argumentsValue) => {
|
|
|
6991
7195
|
};
|
|
6992
7196
|
const DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO = .9;
|
|
6993
7197
|
const responsesUtilsDependencies = {
|
|
7198
|
+
getModelResponsesApiCompactThreshold: getModelResponsesApiCompactThreshold$1,
|
|
6994
7199
|
isResponsesApiContextManagementEnabled,
|
|
6995
7200
|
isResponsesApiWebSocketEnabled
|
|
6996
7201
|
};
|
|
@@ -7021,7 +7226,8 @@ const isAgentRole = (item) => {
|
|
|
7021
7226
|
const hasVisionInput$1 = (payload) => {
|
|
7022
7227
|
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
7023
7228
|
};
|
|
7024
|
-
""
|
|
7229
|
+
const DATA_URL_PREFIX = "data:";
|
|
7230
|
+
const REDACTED_IMAGE_PLACEHOLDER_DATA_URL = "data:image/png;base64," + [
|
|
7025
7231
|
"iVBORw0KGgoAAAANSUhEUgAAAGAAAAAgCAMAAADaHo1mAAADAFBMVEX///8fKTfR1dsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7026
7232
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
7027
7233
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
@@ -7038,10 +7244,71 @@ const hasVisionInput$1 = (payload) => {
|
|
|
7038
7244
|
"ucSQ4s8JkKDDIYr3IuR8vEWgqroKP9b1bYKk2wfgeVmqATQLXdXamsXdEKkz3QXEEeTTuWWImMhW6qci94/+hwSVf99HqVoD",
|
|
7039
7245
|
"OAuj2SEAAAAASUVORK5CYII="
|
|
7040
7246
|
].join("");
|
|
7247
|
+
const sanitizeOversizedInputImages = (payload, maxPromptImageSize) => {
|
|
7248
|
+
const limit = typeof maxPromptImageSize === "number" && maxPromptImageSize > 0 ? maxPromptImageSize : void 0;
|
|
7249
|
+
if (limit === void 0 || !Array.isArray(payload.input)) return 0;
|
|
7250
|
+
return sanitizeInputImages(payload.input, (image) => image.decodedBytes > limit);
|
|
7251
|
+
};
|
|
7252
|
+
const sanitizeInputImages = (input, shouldReplace) => {
|
|
7253
|
+
let count = 0;
|
|
7254
|
+
for (const image of collectInputImageDataUrls(input)) {
|
|
7255
|
+
if (!shouldReplace(image)) continue;
|
|
7256
|
+
replaceInputImageWithPlaceholder(image);
|
|
7257
|
+
count += 1;
|
|
7258
|
+
}
|
|
7259
|
+
return count;
|
|
7260
|
+
};
|
|
7261
|
+
const collectInputImageDataUrls = (input, images = []) => {
|
|
7262
|
+
for (const item of input) collectInputItemImageDataUrls(item, images);
|
|
7263
|
+
return images;
|
|
7264
|
+
};
|
|
7265
|
+
const collectInputItemImageDataUrls = (item, images) => {
|
|
7266
|
+
if (isResponseInputMessage(item)) collectContentImageDataUrls(item.content, images);
|
|
7267
|
+
else if (isResponseFunctionCallOutputItem(item)) collectContentImageDataUrls(item.output, images);
|
|
7268
|
+
};
|
|
7269
|
+
const collectContentImageDataUrls = (content, images) => {
|
|
7270
|
+
if (!Array.isArray(content)) return;
|
|
7271
|
+
for (const block of content) {
|
|
7272
|
+
const image = getInputImageDataUrl(block);
|
|
7273
|
+
if (image) images.push(image);
|
|
7274
|
+
}
|
|
7275
|
+
};
|
|
7276
|
+
const getInputImageDataUrl = (content) => {
|
|
7277
|
+
if (!isResponseInputImage(content) || typeof content.image_url !== "string") return null;
|
|
7278
|
+
const imageUrl = content.image_url;
|
|
7279
|
+
if (!imageUrl.startsWith(DATA_URL_PREFIX)) return null;
|
|
7280
|
+
return {
|
|
7281
|
+
decodedBytes: estimateDataUrlByteLength(imageUrl),
|
|
7282
|
+
record: content
|
|
7283
|
+
};
|
|
7284
|
+
};
|
|
7285
|
+
const estimateDataUrlByteLength = (value) => {
|
|
7286
|
+
return Math.max(0, Math.floor(value.length * 3 / 4));
|
|
7287
|
+
};
|
|
7288
|
+
const replaceInputImageWithPlaceholder = (image) => {
|
|
7289
|
+
image.record.type = "input_image";
|
|
7290
|
+
image.record.image_url = REDACTED_IMAGE_PLACEHOLDER_DATA_URL;
|
|
7291
|
+
image.record.detail = "low";
|
|
7292
|
+
delete image.record.file_id;
|
|
7293
|
+
};
|
|
7294
|
+
const isResponseInputMessage = (item) => {
|
|
7295
|
+
return typeof item === "object" && item !== null && "role" in item && typeof item.role === "string";
|
|
7296
|
+
};
|
|
7297
|
+
const isResponseFunctionCallOutputItem = (item) => {
|
|
7298
|
+
return typeof item === "object" && item !== null && "type" in item && item.type === "function_call_output";
|
|
7299
|
+
};
|
|
7300
|
+
const isResponseInputImage = (content) => {
|
|
7301
|
+
return typeof content === "object" && content !== null && "type" in content && content.type === "input_image";
|
|
7302
|
+
};
|
|
7041
7303
|
const resolveResponsesCompactThreshold = (maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
|
|
7042
7304
|
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * compactThresholdRatio);
|
|
7043
7305
|
return 2e5 * compactThresholdRatio;
|
|
7044
7306
|
};
|
|
7307
|
+
const getModelResponsesApiCompactThreshold = (model) => {
|
|
7308
|
+
const threshold = responsesUtilsDependencies.getModelResponsesApiCompactThreshold(model);
|
|
7309
|
+
if (typeof threshold !== "number" || !Number.isFinite(threshold) || threshold <= 0) return;
|
|
7310
|
+
return threshold;
|
|
7311
|
+
};
|
|
7045
7312
|
const createCompactionContextManagement = (compactThreshold) => [{
|
|
7046
7313
|
type: "compaction",
|
|
7047
7314
|
compact_threshold: compactThreshold
|
|
@@ -7049,7 +7316,7 @@ const createCompactionContextManagement = (compactThreshold) => [{
|
|
|
7049
7316
|
const applyResponsesApiContextManagement = (payload, maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
|
|
7050
7317
|
if (payload.context_management !== void 0) return;
|
|
7051
7318
|
if (!responsesUtilsDependencies.isResponsesApiContextManagementEnabled()) return;
|
|
7052
|
-
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens, compactThresholdRatio));
|
|
7319
|
+
payload.context_management = createCompactionContextManagement(getModelResponsesApiCompactThreshold(payload.model) ?? resolveResponsesCompactThreshold(maxPromptTokens, compactThresholdRatio));
|
|
7053
7320
|
};
|
|
7054
7321
|
const compactInputByLatestCompaction = (payload) => {
|
|
7055
7322
|
if (!Array.isArray(payload.input) || payload.input.length === 0) return;
|
|
@@ -7291,7 +7558,7 @@ const extractBalancedJson = (text) => {
|
|
|
7291
7558
|
};
|
|
7292
7559
|
//#endregion
|
|
7293
7560
|
//#region src/routes/messages/handler.ts
|
|
7294
|
-
const logger$
|
|
7561
|
+
const logger$4 = createHandlerLogger("messages-handler");
|
|
7295
7562
|
const CHAT_COMPLETIONS_ENDPOINT = "/chat/completions";
|
|
7296
7563
|
const RESPONSES_ENDPOINT$1 = "/responses";
|
|
7297
7564
|
const MESSAGES_ENDPOINT = "/v1/messages";
|
|
@@ -7337,7 +7604,7 @@ async function handleProviderAliasCompletion(c, options) {
|
|
|
7337
7604
|
let requestRecorded = false;
|
|
7338
7605
|
const insertProviderAliasLog = (record) => {
|
|
7339
7606
|
if (requestRecorded) {
|
|
7340
|
-
logger$
|
|
7607
|
+
logger$4.warn("provider alias request already recorded", { requestId });
|
|
7341
7608
|
return;
|
|
7342
7609
|
}
|
|
7343
7610
|
requestRecorded = true;
|
|
@@ -7412,14 +7679,14 @@ async function handleCompletion(c) {
|
|
|
7412
7679
|
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
7413
7680
|
normalizeSystemMessages(anthropicPayload);
|
|
7414
7681
|
sanitizeIdeTools(anthropicPayload);
|
|
7415
|
-
debugJson(logger$
|
|
7682
|
+
debugJson(logger$4, "Anthropic request payload:", anthropicPayload);
|
|
7416
7683
|
const markerInspection = inspectSubagentMarkerFromFirstUser(anthropicPayload);
|
|
7417
7684
|
const subagentMarker = markerInspection.kind === "valid" ? markerInspection.marker : null;
|
|
7418
7685
|
const isSubagentRequest = subagentMarker !== null;
|
|
7419
7686
|
const invalidSubagentMarkerSelectionReason = markerInspection.kind === "invalid" ? "subagent_marker_invalid_fallback" : void 0;
|
|
7420
|
-
if (subagentMarker) debugJson(logger$
|
|
7687
|
+
if (subagentMarker) debugJson(logger$4, "Detected Subagent marker:", subagentMarker);
|
|
7421
7688
|
const sessionId = getRootSessionId(anthropicPayload, c);
|
|
7422
|
-
logger$
|
|
7689
|
+
logger$4.debug("Extracted session ID:", sessionId);
|
|
7423
7690
|
const ownershipLookupSessionId = markerInspection.kind === "valid" ? normalizeStableSessionId(markerInspection.marker.session_id) : void 0;
|
|
7424
7691
|
const ownershipWriteSessionId = markerInspection.kind === "none" ? sessionId : void 0;
|
|
7425
7692
|
const anthropicBeta = c.req.header("anthropic-beta");
|
|
@@ -7427,7 +7694,7 @@ async function handleCompletion(c) {
|
|
|
7427
7694
|
const isCompact = compactType !== 0;
|
|
7428
7695
|
const originalRequestModel = anthropicPayload.model;
|
|
7429
7696
|
if (anthropicBeta && isWarmupProbeRequest(anthropicPayload)) anthropicPayload.model = getSmallModel();
|
|
7430
|
-
if (compactType !== 0) logger$
|
|
7697
|
+
if (compactType !== 0) logger$4.debug("Compact request type:", compactType);
|
|
7431
7698
|
const lastMessageCacheControl = getLastMessageContentCacheControl(anthropicPayload.messages.at(-1));
|
|
7432
7699
|
if (compactType === 1 && shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
|
|
7433
7700
|
if (compactType === 0) {
|
|
@@ -7436,7 +7703,7 @@ async function handleCompletion(c) {
|
|
|
7436
7703
|
}
|
|
7437
7704
|
applyLastMessageCacheControl(anthropicPayload, lastMessageCacheControl);
|
|
7438
7705
|
const upstreamRequestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
|
|
7439
|
-
logger$
|
|
7706
|
+
logger$4.debug("Generated request ID:", upstreamRequestId);
|
|
7440
7707
|
const clientModel = anthropicPayload.model;
|
|
7441
7708
|
anthropicPayload.model = resolveModelAlias(anthropicPayload.model);
|
|
7442
7709
|
const routingModel = anthropicPayload.model;
|
|
@@ -7593,7 +7860,7 @@ async function handleCompletion(c) {
|
|
|
7593
7860
|
}
|
|
7594
7861
|
const handleWithChatCompletions = async (params) => {
|
|
7595
7862
|
const { c, openAIPayload, subagentMarker, sessionId, selectedModel, instr, compactType } = params;
|
|
7596
|
-
debugJson(logger$
|
|
7863
|
+
debugJson(logger$4, "Translated OpenAI request payload:", openAIPayload);
|
|
7597
7864
|
const ctx = toAccountContext(instr.account);
|
|
7598
7865
|
const effectiveInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
|
|
7599
7866
|
isCompact: compactType !== 0,
|
|
@@ -7624,9 +7891,9 @@ const handleWithChatCompletions = async (params) => {
|
|
|
7624
7891
|
response,
|
|
7625
7892
|
instr
|
|
7626
7893
|
});
|
|
7627
|
-
logger$
|
|
7894
|
+
logger$4.debug("Streaming response from Copilot");
|
|
7628
7895
|
const fallbackEnabled = isMessageStartInputTokensFallbackEnabled();
|
|
7629
|
-
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$
|
|
7896
|
+
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$4) : void 0;
|
|
7630
7897
|
const historicalUsage = fallbackEnabled && instr.promptCacheKey && instr.safetyIdentifier ? instr.store.getLastCompletedUsageBySession({
|
|
7631
7898
|
promptCacheKey: instr.promptCacheKey,
|
|
7632
7899
|
safetyIdentifier: instr.safetyIdentifier,
|
|
@@ -7648,7 +7915,7 @@ const handleWithResponsesApi = async (params) => {
|
|
|
7648
7915
|
});
|
|
7649
7916
|
applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
|
|
7650
7917
|
compactInputByLatestCompaction(responsesPayload);
|
|
7651
|
-
debugJson(logger$
|
|
7918
|
+
debugJson(logger$4, "Translated Responses payload:", responsesPayload);
|
|
7652
7919
|
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
7653
7920
|
const transport = getResponsesTransportForModel(selectedModel, { compactType }) ?? "http";
|
|
7654
7921
|
const effectiveInitiator = resolveEffectiveInitiator(initiator, {
|
|
@@ -7679,9 +7946,9 @@ const handleWithResponsesApi = async (params) => {
|
|
|
7679
7946
|
});
|
|
7680
7947
|
}
|
|
7681
7948
|
if (responsesPayload.stream && isAsyncIterable(response)) {
|
|
7682
|
-
logger$
|
|
7949
|
+
logger$4.debug("Streaming response from Copilot (Responses API)");
|
|
7683
7950
|
const fallbackEnabled = isMessageStartInputTokensFallbackEnabled();
|
|
7684
|
-
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$
|
|
7951
|
+
const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$4) : void 0;
|
|
7685
7952
|
const historicalUsage = fallbackEnabled && instr.promptCacheKey && instr.safetyIdentifier ? instr.store.getLastCompletedUsageBySession({
|
|
7686
7953
|
promptCacheKey: instr.promptCacheKey,
|
|
7687
7954
|
safetyIdentifier: instr.safetyIdentifier,
|
|
@@ -7779,9 +8046,9 @@ async function handleChatCompletionsNonStreaming(params) {
|
|
|
7779
8046
|
let upstreamErrorMessageRaw;
|
|
7780
8047
|
const finishedAtMs = Date.now();
|
|
7781
8048
|
try {
|
|
7782
|
-
logger$
|
|
8049
|
+
logger$4.debug("Non-streaming response from Copilot:", JSON.stringify(response));
|
|
7783
8050
|
const anthropicResponse = translateToAnthropic(response);
|
|
7784
|
-
debugJson(logger$
|
|
8051
|
+
debugJson(logger$4, "Translated Anthropic response:", anthropicResponse);
|
|
7785
8052
|
return c.json(anthropicResponse);
|
|
7786
8053
|
} catch (error) {
|
|
7787
8054
|
const details = await extractErrorObservability(error);
|
|
@@ -7832,7 +8099,7 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
7832
8099
|
try {
|
|
7833
8100
|
for await (const rawEvent of response) {
|
|
7834
8101
|
if (ttfbMs === void 0) ttfbMs = Date.now() - instr.startedAtMs;
|
|
7835
|
-
logger$
|
|
8102
|
+
logger$4.debug("Copilot raw stream event:", JSON.stringify(rawEvent));
|
|
7836
8103
|
const { data: rawData } = rawEvent;
|
|
7837
8104
|
const data = typeof rawData === "string" ? rawData : await rawData;
|
|
7838
8105
|
if (data === "[DONE]") break;
|
|
@@ -7841,7 +8108,7 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
7841
8108
|
if (chunk.usage) lastUsage = normalizeChatCompletionsUsage(chunk.usage);
|
|
7842
8109
|
const events = translateChunkToAnthropicEvents(chunk, streamState);
|
|
7843
8110
|
for (const event of events) {
|
|
7844
|
-
logger$
|
|
8111
|
+
logger$4.debug("Translated Anthropic event:", JSON.stringify(event));
|
|
7845
8112
|
await stream.writeSSE({
|
|
7846
8113
|
event: event.type,
|
|
7847
8114
|
data: JSON.stringify(event)
|
|
@@ -7854,7 +8121,7 @@ async function streamChatCompletionsAndLog(params) {
|
|
|
7854
8121
|
errorStatus = details.errorStatus;
|
|
7855
8122
|
errorMessage = details.errorMessage;
|
|
7856
8123
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
7857
|
-
logger$
|
|
8124
|
+
logger$4.warn("Streaming error:", error);
|
|
7858
8125
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
7859
8126
|
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
7860
8127
|
} finally {
|
|
@@ -7915,9 +8182,9 @@ async function handleResponsesNonStreaming(params) {
|
|
|
7915
8182
|
usage = extractResponsesUsageFromResult(result);
|
|
7916
8183
|
const responseOwnerKeys = extractResponsesResultOwnerKeys(result);
|
|
7917
8184
|
instr.responsesItemOwnerRecordedKeys = responseOwnerKeys;
|
|
7918
|
-
logger$
|
|
8185
|
+
logger$4.debug("Non-streaming Responses result:", JSON.stringify(result).slice(-400));
|
|
7919
8186
|
const anthropicResponse = translateResponsesResultToAnthropic(result);
|
|
7920
|
-
debugJson(logger$
|
|
8187
|
+
debugJson(logger$4, "Translated Anthropic response:", anthropicResponse);
|
|
7921
8188
|
const response = c.json(anthropicResponse);
|
|
7922
8189
|
if (result.status === "completed") accountsManager.recordResponsesItemOwnership(responseOwnerKeys, instr.account.id);
|
|
7923
8190
|
return response;
|
|
@@ -7951,7 +8218,7 @@ async function handleResponsesNonStreaming(params) {
|
|
|
7951
8218
|
async function ensureResponsesStreamCompleted(params) {
|
|
7952
8219
|
const { stream, streamState, setStreamError } = params;
|
|
7953
8220
|
if (streamState.messageCompleted) return;
|
|
7954
|
-
logger$
|
|
8221
|
+
logger$4.warn("Responses stream ended without completion; sending error event");
|
|
7955
8222
|
const msg = "Responses stream ended without completion";
|
|
7956
8223
|
const errorEvent = buildErrorEvent(msg);
|
|
7957
8224
|
setStreamError("StreamIncomplete", msg);
|
|
@@ -7968,7 +8235,7 @@ async function writeAnthropicStreamError(stream, message) {
|
|
|
7968
8235
|
data: JSON.stringify(errorEvent)
|
|
7969
8236
|
});
|
|
7970
8237
|
} catch (streamError) {
|
|
7971
|
-
logger$
|
|
8238
|
+
logger$4.warn("Failed to write Anthropic stream error event:", streamError);
|
|
7972
8239
|
}
|
|
7973
8240
|
}
|
|
7974
8241
|
function collectResponsesStreamOwnerKeys(event, responseOwnerKeys) {
|
|
@@ -8011,7 +8278,7 @@ function getResponsesStreamEventError(event) {
|
|
|
8011
8278
|
async function writeTranslatedAnthropicStreamEvents(stream, events) {
|
|
8012
8279
|
for (const event of events) {
|
|
8013
8280
|
const eventData = JSON.stringify(event);
|
|
8014
|
-
logger$
|
|
8281
|
+
logger$4.debug("Translated Anthropic event:", eventData);
|
|
8015
8282
|
await stream.writeSSE({
|
|
8016
8283
|
event: event.type,
|
|
8017
8284
|
data: eventData
|
|
@@ -8040,7 +8307,7 @@ async function streamResponsesAndLog$1(params) {
|
|
|
8040
8307
|
}
|
|
8041
8308
|
const data = chunk.data;
|
|
8042
8309
|
if (!data) continue;
|
|
8043
|
-
logger$
|
|
8310
|
+
logger$4.debug("Responses raw stream event:", data);
|
|
8044
8311
|
const parsed = JSON.parse(data);
|
|
8045
8312
|
const streamEventError = getResponsesStreamEventError(parsed);
|
|
8046
8313
|
if (streamEventError) {
|
|
@@ -8054,7 +8321,7 @@ async function streamResponsesAndLog$1(params) {
|
|
|
8054
8321
|
if (u.usageJson) lastUsage = u;
|
|
8055
8322
|
await writeTranslatedAnthropicStreamEvents(stream, translateResponsesStreamEvent(parsed, streamState));
|
|
8056
8323
|
if (streamState.messageCompleted) {
|
|
8057
|
-
logger$
|
|
8324
|
+
logger$4.debug("Message completed, ending stream");
|
|
8058
8325
|
break;
|
|
8059
8326
|
}
|
|
8060
8327
|
}
|
|
@@ -8073,7 +8340,7 @@ async function streamResponsesAndLog$1(params) {
|
|
|
8073
8340
|
errorStatus = details.errorStatus;
|
|
8074
8341
|
errorMessage = details.errorMessage;
|
|
8075
8342
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
8076
|
-
logger$
|
|
8343
|
+
logger$4.warn("Streaming error:", error);
|
|
8077
8344
|
invalidateAffinityOnOwnershipMismatch(details.ownershipMismatch, instr);
|
|
8078
8345
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
8079
8346
|
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
@@ -8128,7 +8395,7 @@ async function handleMessagesNonStreaming(params) {
|
|
|
8128
8395
|
let upstreamErrorMessageRaw;
|
|
8129
8396
|
const finishedAtMs = Date.now();
|
|
8130
8397
|
try {
|
|
8131
|
-
logger$
|
|
8398
|
+
logger$4.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
|
|
8132
8399
|
return c.json(response);
|
|
8133
8400
|
} catch (error) {
|
|
8134
8401
|
const details = await extractErrorObservability(error);
|
|
@@ -8165,7 +8432,7 @@ const parseMessagesStreamUsage = (data) => {
|
|
|
8165
8432
|
if (parsed.type !== "message_delta" || !parsed.usage) return null;
|
|
8166
8433
|
return normalizeMessagesUsage(parsed.usage);
|
|
8167
8434
|
} catch (error) {
|
|
8168
|
-
logger$
|
|
8435
|
+
logger$4.warn("Failed to parse messages stream event", error);
|
|
8169
8436
|
throw new Error("Failed to parse messages stream event", { cause: error });
|
|
8170
8437
|
}
|
|
8171
8438
|
};
|
|
@@ -8183,7 +8450,7 @@ async function streamMessagesAndLog(params) {
|
|
|
8183
8450
|
const eventNameRaw = rawEvent.event;
|
|
8184
8451
|
const eventName = typeof eventNameRaw === "string" && eventNameRaw.length > 0 ? eventNameRaw : "message";
|
|
8185
8452
|
const data = rawEvent.data ?? "";
|
|
8186
|
-
logger$
|
|
8453
|
+
logger$4.debug("Messages raw stream event:", data);
|
|
8187
8454
|
const usage = parseMessagesStreamUsage(data);
|
|
8188
8455
|
if (usage) lastUsage = usage;
|
|
8189
8456
|
await stream.writeSSE({
|
|
@@ -8197,7 +8464,7 @@ async function streamMessagesAndLog(params) {
|
|
|
8197
8464
|
errorStatus = details.errorStatus;
|
|
8198
8465
|
errorMessage = details.errorMessage;
|
|
8199
8466
|
upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
|
|
8200
|
-
logger$
|
|
8467
|
+
logger$4.warn("Streaming error:", error);
|
|
8201
8468
|
if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
|
|
8202
8469
|
await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
|
|
8203
8470
|
} finally {
|
|
@@ -8223,7 +8490,7 @@ async function streamMessagesAndLog(params) {
|
|
|
8223
8490
|
const handleWithMessagesApi = async (params) => {
|
|
8224
8491
|
const { c, anthropicPayload, anthropicBetaHeader, subagentMarker, sessionId, instr, selectedModel, compactType } = params;
|
|
8225
8492
|
prepareMessagesApiPayload(anthropicPayload, selectedModel);
|
|
8226
|
-
debugJson(logger$
|
|
8493
|
+
debugJson(logger$4, "Translated Messages payload:", anthropicPayload);
|
|
8227
8494
|
const ctx = toAccountContext(instr.account);
|
|
8228
8495
|
const effectiveInitiator = resolveEffectiveInitiator(getMessagesInitiator(anthropicPayload), {
|
|
8229
8496
|
isCompact: compactType !== 0,
|
|
@@ -8251,7 +8518,7 @@ const handleWithMessagesApi = async (params) => {
|
|
|
8251
8518
|
});
|
|
8252
8519
|
}
|
|
8253
8520
|
if (isAsyncIterable(response)) {
|
|
8254
|
-
logger$
|
|
8521
|
+
logger$4.debug("Streaming response from Copilot (Messages API)");
|
|
8255
8522
|
return streamSSE(c, (stream) => streamMessagesAndLog({
|
|
8256
8523
|
stream,
|
|
8257
8524
|
response,
|
|
@@ -8416,7 +8683,7 @@ function getModels() {
|
|
|
8416
8683
|
}
|
|
8417
8684
|
//#endregion
|
|
8418
8685
|
//#region src/routes/provider/models/route.ts
|
|
8419
|
-
const logger$
|
|
8686
|
+
const logger$3 = createHandlerLogger("provider-models-handler");
|
|
8420
8687
|
const getProviderFetch = (c) => c.get("providerFetch") ?? fetch;
|
|
8421
8688
|
const resolveProviderConfig = (c, provider) => {
|
|
8422
8689
|
return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
|
|
@@ -8439,13 +8706,13 @@ providerModelRoutes.get("/", async (c) => {
|
|
|
8439
8706
|
});
|
|
8440
8707
|
}
|
|
8441
8708
|
const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers, getProviderFetch(c));
|
|
8442
|
-
logger$
|
|
8709
|
+
logger$3.debug("provider.models.response", {
|
|
8443
8710
|
provider,
|
|
8444
8711
|
statusCode: upstreamResponse.status
|
|
8445
8712
|
});
|
|
8446
8713
|
return createProviderProxyResponse(upstreamResponse);
|
|
8447
8714
|
} catch (error) {
|
|
8448
|
-
logger$
|
|
8715
|
+
logger$3.error("provider.models.error", {
|
|
8449
8716
|
provider,
|
|
8450
8717
|
error
|
|
8451
8718
|
});
|
|
@@ -8453,6 +8720,141 @@ providerModelRoutes.get("/", async (c) => {
|
|
|
8453
8720
|
}
|
|
8454
8721
|
});
|
|
8455
8722
|
//#endregion
|
|
8723
|
+
//#region src/lib/codex-rate-limit.ts
|
|
8724
|
+
const codexRateLimitScopes = ["primary", "secondary"];
|
|
8725
|
+
const formatCodexRateLimitResetAt = (resetAt) => {
|
|
8726
|
+
const date = /* @__PURE__ */ new Date(resetAt * 1e3);
|
|
8727
|
+
return Number.isNaN(date.getTime()) ? String(resetAt) : date.toLocaleString();
|
|
8728
|
+
};
|
|
8729
|
+
const logCodexRateLimitsEvent = (event) => {
|
|
8730
|
+
if (!event || typeof event !== "object") return;
|
|
8731
|
+
const eventRecord = event;
|
|
8732
|
+
if (eventRecord.type !== "codex.rate_limits") return;
|
|
8733
|
+
const rateLimits = eventRecord.rate_limits;
|
|
8734
|
+
if (!rateLimits || typeof rateLimits !== "object") return;
|
|
8735
|
+
const planType = typeof eventRecord.plan_type === "string" ? eventRecord.plan_type : null;
|
|
8736
|
+
const rateLimitRecord = rateLimits;
|
|
8737
|
+
const allowed = typeof rateLimitRecord.allowed === "boolean" ? rateLimitRecord.allowed : null;
|
|
8738
|
+
const limitReached = typeof rateLimitRecord.limit_reached === "boolean" ? rateLimitRecord.limit_reached : null;
|
|
8739
|
+
for (const scope of codexRateLimitScopes) {
|
|
8740
|
+
const window = rateLimitRecord[scope];
|
|
8741
|
+
if (!isCodexRateLimitWindow(window)) continue;
|
|
8742
|
+
const summary = [];
|
|
8743
|
+
if (allowed !== null) summary.push(`allowed=${allowed}`);
|
|
8744
|
+
if (limitReached !== null) summary.push(`limit_reached=${limitReached}`);
|
|
8745
|
+
summary.push(`used=${window.used_percent}%`, `reset_at=${formatCodexRateLimitResetAt(window.reset_at)}`);
|
|
8746
|
+
const label = planType ? `Codex ${scope} rate limit (${planType})` : `Codex ${scope} rate limit`;
|
|
8747
|
+
consola.log(`${label}: ${summary.join(", ")}`);
|
|
8748
|
+
}
|
|
8749
|
+
};
|
|
8750
|
+
const isCodexRateLimitWindow = (value) => {
|
|
8751
|
+
if (!value || typeof value !== "object") return false;
|
|
8752
|
+
const record = value;
|
|
8753
|
+
return typeof record.reset_after_seconds === "number" && typeof record.reset_at === "number" && typeof record.used_percent === "number" && typeof record.window_minutes === "number";
|
|
8754
|
+
};
|
|
8755
|
+
//#endregion
|
|
8756
|
+
//#region src/routes/provider/responses/handler.ts
|
|
8757
|
+
const logger$2 = createHandlerLogger("provider-responses-handler");
|
|
8758
|
+
async function handleProviderResponsesForProvider(c, options) {
|
|
8759
|
+
const { payload, provider } = options;
|
|
8760
|
+
debugJson(logger$2, "Responses request payload:", {
|
|
8761
|
+
payload,
|
|
8762
|
+
provider
|
|
8763
|
+
});
|
|
8764
|
+
const providerConfig = await resolveProviderConfig$3(provider);
|
|
8765
|
+
if (providerConfig?.type !== "openai-responses") return c.json({ error: {
|
|
8766
|
+
message: `Provider '${provider}' does not support the /v1/responses endpoint`,
|
|
8767
|
+
type: "invalid_request_error"
|
|
8768
|
+
} }, 400);
|
|
8769
|
+
applyResponsesApiContextManagement(payload, (providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0)?.capabilities.limits.max_prompt_tokens ?? 0, .8);
|
|
8770
|
+
const contextManagement = payload.context_management;
|
|
8771
|
+
debugJson(logger$2, "Translated Responses request payload:", {
|
|
8772
|
+
contextManagement,
|
|
8773
|
+
provider
|
|
8774
|
+
});
|
|
8775
|
+
compactInputByLatestCompaction(payload);
|
|
8776
|
+
if (providerConfig.name === "codex") {
|
|
8777
|
+
const upstreamResponse = await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl);
|
|
8778
|
+
const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
|
|
8779
|
+
if (payload.stream && isResponsesStream(upstreamResponse)) return streamProviderResponses(c, upstreamResponse, {
|
|
8780
|
+
normalizeCodex: true,
|
|
8781
|
+
provider,
|
|
8782
|
+
recordUsage
|
|
8783
|
+
});
|
|
8784
|
+
const responseBody = upstreamResponse;
|
|
8785
|
+
recordUsage(normalizeResponsesUsage(responseBody.usage));
|
|
8786
|
+
return c.json(responseBody);
|
|
8787
|
+
}
|
|
8788
|
+
const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
|
|
8789
|
+
if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${provider} responses`, upstreamResponse);
|
|
8790
|
+
const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
|
|
8791
|
+
if (payload.stream) return streamProviderResponses(c, getResponsesEvents(upstreamResponse), {
|
|
8792
|
+
normalizeCodex: false,
|
|
8793
|
+
provider,
|
|
8794
|
+
recordUsage
|
|
8795
|
+
});
|
|
8796
|
+
recordUsage(normalizeResponsesUsage((await upstreamResponse.clone().json()).usage));
|
|
8797
|
+
return createProviderProxyResponse(upstreamResponse);
|
|
8798
|
+
}
|
|
8799
|
+
const createProviderResponsesUsageRecorder = (_payload, _provider) => {
|
|
8800
|
+
return (_usage) => {};
|
|
8801
|
+
};
|
|
8802
|
+
const streamProviderResponses = (c, upstreamResponse, options) => {
|
|
8803
|
+
return streamSSE(c, async (stream) => {
|
|
8804
|
+
let usage = {};
|
|
8805
|
+
try {
|
|
8806
|
+
for await (const chunk of upstreamResponse) {
|
|
8807
|
+
debugJson(logger$2, "Responses stream chunk:", chunk);
|
|
8808
|
+
let responseChunk = chunk;
|
|
8809
|
+
let event = null;
|
|
8810
|
+
if (chunk.data && chunk.data !== "[DONE]") {
|
|
8811
|
+
event = parseProviderResponsesStreamEvent(chunk.data, {
|
|
8812
|
+
normalizeCodex: options.normalizeCodex,
|
|
8813
|
+
provider: options.provider
|
|
8814
|
+
});
|
|
8815
|
+
if (event && options.normalizeCodex) responseChunk = {
|
|
8816
|
+
...chunk,
|
|
8817
|
+
data: JSON.stringify(event),
|
|
8818
|
+
event: event.type
|
|
8819
|
+
};
|
|
8820
|
+
}
|
|
8821
|
+
if (event) {
|
|
8822
|
+
const nextUsage = getResponsesStreamEventUsage(event);
|
|
8823
|
+
if (nextUsage) usage = nextUsage;
|
|
8824
|
+
}
|
|
8825
|
+
await stream.writeSSE({
|
|
8826
|
+
data: responseChunk.data ?? "",
|
|
8827
|
+
event: responseChunk.event
|
|
8828
|
+
});
|
|
8829
|
+
}
|
|
8830
|
+
} finally {
|
|
8831
|
+
options.recordUsage(usage);
|
|
8832
|
+
}
|
|
8833
|
+
});
|
|
8834
|
+
};
|
|
8835
|
+
const parseProviderResponsesStreamEvent = (data, options) => {
|
|
8836
|
+
try {
|
|
8837
|
+
const parsed = JSON.parse(data);
|
|
8838
|
+
if (options.normalizeCodex) logCodexRateLimitsEvent(parsed);
|
|
8839
|
+
return parsed;
|
|
8840
|
+
} catch (error) {
|
|
8841
|
+
logger$2.error("provider.responses.parse_chunk_error", {
|
|
8842
|
+
provider: options.provider,
|
|
8843
|
+
data,
|
|
8844
|
+
error
|
|
8845
|
+
});
|
|
8846
|
+
return null;
|
|
8847
|
+
}
|
|
8848
|
+
};
|
|
8849
|
+
const getResponsesStreamEventUsage = (event) => {
|
|
8850
|
+
if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return normalizeResponsesUsage(event.response.usage);
|
|
8851
|
+
return null;
|
|
8852
|
+
};
|
|
8853
|
+
const getResponsesEvents = (response) => events(response);
|
|
8854
|
+
const isResponsesStream = (value) => {
|
|
8855
|
+
return Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
8856
|
+
};
|
|
8857
|
+
//#endregion
|
|
8456
8858
|
//#region src/routes/responses/stream-id-sync.ts
|
|
8457
8859
|
const createStreamIdTracker = () => ({ outputItems: /* @__PURE__ */ new Map() });
|
|
8458
8860
|
const fixStreamIds = (data, event, tracker) => {
|
|
@@ -8493,16 +8895,31 @@ const handleItemId = (parsed, tracker) => {
|
|
|
8493
8895
|
const logger$1 = createHandlerLogger("responses-handler");
|
|
8494
8896
|
const RESPONSES_ENDPOINT = "/responses";
|
|
8495
8897
|
const handleResponses = async (c) => {
|
|
8898
|
+
const payload = await c.req.json();
|
|
8899
|
+
debugJson(logger$1, "Responses request payload:", payload);
|
|
8900
|
+
const requestedModel = payload.model;
|
|
8901
|
+
payload.model = resolveMappedModel(payload.model);
|
|
8902
|
+
if (payload.model !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
|
|
8903
|
+
const providerModelAlias = parseProviderModelAlias(payload.model);
|
|
8904
|
+
if (providerModelAlias) {
|
|
8905
|
+
payload.model = providerModelAlias.model;
|
|
8906
|
+
return await handleProviderResponsesForProvider(c, {
|
|
8907
|
+
payload,
|
|
8908
|
+
provider: providerModelAlias.provider
|
|
8909
|
+
});
|
|
8910
|
+
}
|
|
8911
|
+
const subagentMarker = getCodexResponsesSubagentMarker(c);
|
|
8912
|
+
if (subagentMarker) debugJson(logger$1, "Detected Codex subagent headers:", subagentMarker);
|
|
8913
|
+
const incomingSessionId = subagentMarker ? getIncomingResponsesSessionId(c) : void 0;
|
|
8496
8914
|
await checkRateLimit(state);
|
|
8497
8915
|
const store = getRequestHistoryStore();
|
|
8498
8916
|
const request = buildRequestContext(c);
|
|
8499
|
-
const payload = await c.req.json();
|
|
8500
8917
|
const clientModel = payload.model;
|
|
8501
|
-
debugJson(logger$1, "Responses request payload:", payload);
|
|
8502
8918
|
if (!isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
|
|
8503
8919
|
compactInputByLatestCompaction(payload);
|
|
8504
8920
|
const streamRequested = Boolean(payload.stream);
|
|
8505
|
-
const { initiator:
|
|
8921
|
+
const { initiator: inferredInitiator } = getResponsesRequestOptions(payload);
|
|
8922
|
+
const initialInitiator = subagentMarker ? "agent" : inferredInitiator;
|
|
8506
8923
|
const userId = payload.metadata?.user_id;
|
|
8507
8924
|
const requestBodyPromptCacheKey = typeof payload.prompt_cache_key === "string" ? payload.prompt_cache_key : null;
|
|
8508
8925
|
const { safetyIdentifier, sessionId: metadataSessionId } = parseUserIdMetadata(userId);
|
|
@@ -8524,8 +8941,8 @@ const handleResponses = async (c) => {
|
|
|
8524
8941
|
message: "This model is only available via an alias. Please use the alias model name."
|
|
8525
8942
|
});
|
|
8526
8943
|
}
|
|
8527
|
-
const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, normalizedPromptCacheKey);
|
|
8528
8944
|
const headerSessionId = c.req.header("x-session-id") ?? null;
|
|
8945
|
+
const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, incomingSessionId ?? normalizedPromptCacheKey);
|
|
8529
8946
|
const affinityKey = resolveAffinityKey({
|
|
8530
8947
|
promptCacheKey: requestBodyPromptCacheKey,
|
|
8531
8948
|
metadataSessionId,
|
|
@@ -8556,16 +8973,19 @@ const handleResponses = async (c) => {
|
|
|
8556
8973
|
model: selectedModel.id
|
|
8557
8974
|
};
|
|
8558
8975
|
removeUnsupportedTools(upstreamPayload);
|
|
8976
|
+
const sanitizedImageCount = sanitizeOversizedInputImages(upstreamPayload, selectedModel.capabilities.limits.vision?.max_prompt_image_size);
|
|
8977
|
+
if (sanitizedImageCount > 0) logger$1.warn(`Omitted ${sanitizedImageCount} oversized input image(s) before forwarding to Copilot Responses`);
|
|
8559
8978
|
applyResponsesApiContextManagement(upstreamPayload, selectedModel.capabilities.limits.max_prompt_tokens);
|
|
8560
8979
|
compactInputByLatestCompaction(upstreamPayload);
|
|
8561
8980
|
const premiumRemainingBefore = account.premiumRemaining;
|
|
8562
8981
|
const premiumUnlimitedBefore = account.unlimited;
|
|
8563
8982
|
const transport = getResponsesTransportForModel(selectedModel) ?? "http";
|
|
8564
|
-
const { vision, initiator } = getResponsesRequestOptions(upstreamPayload);
|
|
8983
|
+
const { vision, initiator: inferredUpstreamInitiator } = getResponsesRequestOptions(upstreamPayload);
|
|
8984
|
+
const initiator = subagentMarker ? "agent" : inferredUpstreamInitiator;
|
|
8565
8985
|
request.initiator = initiator;
|
|
8566
8986
|
if (state.manualApprove) await awaitApproval();
|
|
8567
8987
|
const accountCtx = toAccountContext(account);
|
|
8568
|
-
const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
|
|
8988
|
+
const upstreamSessionId = getUUID(incomingSessionId ?? normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
|
|
8569
8989
|
request.upstreamRequestId = upstreamRequestId;
|
|
8570
8990
|
request.upstreamSessionId = upstreamSessionId;
|
|
8571
8991
|
const bridgeId = c.req.header("x-responses-bridge-id") ?? void 0;
|
|
@@ -8579,6 +8999,7 @@ const handleResponses = async (c) => {
|
|
|
8579
8999
|
accountCtx,
|
|
8580
9000
|
vision,
|
|
8581
9001
|
initiator,
|
|
9002
|
+
subagentMarker,
|
|
8582
9003
|
premiumRemainingBefore,
|
|
8583
9004
|
premiumUnlimitedBefore,
|
|
8584
9005
|
transport,
|
|
@@ -8594,6 +9015,7 @@ const handleResponses = async (c) => {
|
|
|
8594
9015
|
accountCtx,
|
|
8595
9016
|
vision,
|
|
8596
9017
|
initiator,
|
|
9018
|
+
subagentMarker,
|
|
8597
9019
|
premiumRemainingBefore,
|
|
8598
9020
|
premiumUnlimitedBefore,
|
|
8599
9021
|
transport,
|
|
@@ -8705,12 +9127,13 @@ function extractUsageFromChunkData(data) {
|
|
|
8705
9127
|
}
|
|
8706
9128
|
}
|
|
8707
9129
|
async function handleStreamingResponses(params) {
|
|
8708
|
-
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
|
|
9130
|
+
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, subagentMarker, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
|
|
8709
9131
|
let response;
|
|
8710
9132
|
try {
|
|
8711
9133
|
response = await createResponses(payload, {
|
|
8712
9134
|
vision,
|
|
8713
9135
|
initiator,
|
|
9136
|
+
subagentMarker,
|
|
8714
9137
|
upstreamRequestId: request.upstreamRequestId,
|
|
8715
9138
|
sessionId: request.upstreamSessionId,
|
|
8716
9139
|
requestId: request.requestId,
|
|
@@ -8903,7 +9326,7 @@ async function streamResponsesAndLog(params) {
|
|
|
8903
9326
|
}
|
|
8904
9327
|
}
|
|
8905
9328
|
async function handleNonStreamingResponses(params) {
|
|
8906
|
-
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
|
|
9329
|
+
const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, subagentMarker, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
|
|
8907
9330
|
const { account, reservation, selectedModel, endpoint, costUnits } = selection;
|
|
8908
9331
|
let usage = {};
|
|
8909
9332
|
let errorState = { httpStatus: 200 };
|
|
@@ -8912,6 +9335,7 @@ async function handleNonStreamingResponses(params) {
|
|
|
8912
9335
|
const response = await createResponses(payload, {
|
|
8913
9336
|
vision,
|
|
8914
9337
|
initiator,
|
|
9338
|
+
subagentMarker,
|
|
8915
9339
|
upstreamRequestId: request.upstreamRequestId,
|
|
8916
9340
|
sessionId: request.upstreamSessionId,
|
|
8917
9341
|
requestId: request.requestId,
|
|
@@ -8978,6 +9402,31 @@ const removeUnsupportedTools = (payload) => {
|
|
|
8978
9402
|
});
|
|
8979
9403
|
if (dropped.length > 0) logger$1.debug("Removed unsupported tools:", dropped);
|
|
8980
9404
|
};
|
|
9405
|
+
const getTrimmedHeader = (c, name) => {
|
|
9406
|
+
const value = c.req.header(name)?.trim();
|
|
9407
|
+
return value ? value : void 0;
|
|
9408
|
+
};
|
|
9409
|
+
const getIncomingResponsesSessionId = (c) => getTrimmedHeader(c, "session-id") ?? getTrimmedHeader(c, "x-session-id");
|
|
9410
|
+
const codexSubagentHeaderValues = new Set([
|
|
9411
|
+
"collab_spawn",
|
|
9412
|
+
"compact",
|
|
9413
|
+
"memory_consolidation",
|
|
9414
|
+
"review"
|
|
9415
|
+
]);
|
|
9416
|
+
const getCodexResponsesSubagentMarker = (c) => {
|
|
9417
|
+
const agentType = getTrimmedHeader(c, "x-openai-subagent");
|
|
9418
|
+
if (!agentType || !codexSubagentHeaderValues.has(agentType)) return null;
|
|
9419
|
+
const threadId = getTrimmedHeader(c, "thread-id");
|
|
9420
|
+
const rootSessionId = getIncomingResponsesSessionId(c);
|
|
9421
|
+
const parentThreadId = getTrimmedHeader(c, "x-codex-parent-thread-id");
|
|
9422
|
+
if (!threadId && !rootSessionId && !parentThreadId) return null;
|
|
9423
|
+
const agentId = threadId ?? parentThreadId ?? rootSessionId;
|
|
9424
|
+
return {
|
|
9425
|
+
agent_id: agentId,
|
|
9426
|
+
agent_type: agentType,
|
|
9427
|
+
session_id: threadId ?? rootSessionId ?? agentId
|
|
9428
|
+
};
|
|
9429
|
+
};
|
|
8981
9430
|
//#endregion
|
|
8982
9431
|
//#region src/routes/responses/route.ts
|
|
8983
9432
|
const responsesRoutes = new Hono();
|
|
@@ -9079,4 +9528,4 @@ createServer();
|
|
|
9079
9528
|
//#endregion
|
|
9080
9529
|
export { createServer };
|
|
9081
9530
|
|
|
9082
|
-
//# sourceMappingURL=server-
|
|
9531
|
+
//# sourceMappingURL=server-C7pCkArb.js.map
|