@dianshuv/copilot-api 0.7.10 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +208 -32
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -243,7 +243,7 @@ function compressToolResultContent(content) {
|
|
|
243
243
|
return `${start}\n\n[... ${(content.length - COMPRESSED_SUMMARY_LENGTH).toLocaleString()} characters omitted for brevity ...]\n\n${end}`;
|
|
244
244
|
}
|
|
245
245
|
function calculateLimits(model, config, defaultContextWindow) {
|
|
246
|
-
const rawTokenLimit = getEffectiveTokenLimit(model.id) ?? model.capabilities?.limits?.max_context_window_tokens ?? model.capabilities?.limits?.max_prompt_tokens ?? defaultContextWindow;
|
|
246
|
+
const rawTokenLimit = getEffectiveTokenLimit(config.tokenLimitCacheKeyOverride ?? model.id) ?? config.contextWindowOverride ?? model.capabilities?.limits?.max_context_window_tokens ?? model.capabilities?.limits?.max_prompt_tokens ?? defaultContextWindow;
|
|
247
247
|
return {
|
|
248
248
|
tokenLimit: Math.floor(rawTokenLimit * (1 - config.safetyMarginPercent / 100)),
|
|
249
249
|
byteLimit: getEffectiveByteLimitBytes()
|
|
@@ -1348,7 +1348,7 @@ const patchClaude = defineCommand({
|
|
|
1348
1348
|
|
|
1349
1349
|
//#endregion
|
|
1350
1350
|
//#region package.json
|
|
1351
|
-
var version = "0.
|
|
1351
|
+
var version = "0.8.0";
|
|
1352
1352
|
|
|
1353
1353
|
//#endregion
|
|
1354
1354
|
//#region src/lib/adaptive-rate-limiter.ts
|
|
@@ -3395,9 +3395,66 @@ const getTokenCount = async (payload, model) => {
|
|
|
3395
3395
|
};
|
|
3396
3396
|
};
|
|
3397
3397
|
|
|
3398
|
+
//#endregion
|
|
3399
|
+
//#region src/lib/anthropic/beta.ts
|
|
3400
|
+
/**
|
|
3401
|
+
* Vendor-neutral utilities for manipulating the `anthropic-beta` request header.
|
|
3402
|
+
*
|
|
3403
|
+
* Lives in `lib/anthropic/` (not in either transport module) so both the
|
|
3404
|
+
* Anthropic-native and OpenAI-translated transport layers can share these
|
|
3405
|
+
* helpers without introducing cross-transport imports.
|
|
3406
|
+
*/
|
|
3407
|
+
/** Anthropic beta feature that unlocks the 1M context window. */
|
|
3408
|
+
const CONTEXT_1M_BETA_FEATURE = "context-1m-2025-08-07";
|
|
3409
|
+
/**
|
|
3410
|
+
* Merge two comma-separated anthropic-beta header values. Trims whitespace,
|
|
3411
|
+
* drops empty tokens, and dedupes by exact string match. Returns a canonical
|
|
3412
|
+
* comma-joined string with no spaces.
|
|
3413
|
+
*
|
|
3414
|
+
* Either input may be undefined / empty.
|
|
3415
|
+
*/
|
|
3416
|
+
function mergeBetaFeatures(existing, incoming) {
|
|
3417
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3418
|
+
const out = [];
|
|
3419
|
+
for (const raw of [existing, incoming]) {
|
|
3420
|
+
if (!raw) continue;
|
|
3421
|
+
for (const part of raw.split(",")) {
|
|
3422
|
+
const f = part.trim();
|
|
3423
|
+
if (f.length === 0 || seen.has(f)) continue;
|
|
3424
|
+
seen.add(f);
|
|
3425
|
+
out.push(f);
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
return out.join(",");
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* Append the context-1m feature to an anthropic-beta header value, deduping
|
|
3432
|
+
* any prior occurrence. Returns the merged comma-separated string.
|
|
3433
|
+
*/
|
|
3434
|
+
function appendContext1mBeta(existing) {
|
|
3435
|
+
return mergeBetaFeatures(existing, CONTEXT_1M_BETA_FEATURE);
|
|
3436
|
+
}
|
|
3437
|
+
/**
|
|
3438
|
+
* True iff a model id appears to be the suffixed 1M-context variant of an
|
|
3439
|
+
* Anthropic Claude model (e.g. claude-opus-4-8-1m, claude-opus-4.6-1m).
|
|
3440
|
+
*
|
|
3441
|
+
* Used as a state.models-independent signal for whether to inject the
|
|
3442
|
+
* context-1m-2025-08-07 beta header, so the 1M intent survives a stale or
|
|
3443
|
+
* empty model cache (where `resolveAnthropicModelForDirectPath` would return
|
|
3444
|
+
* undefined). Forwarding the beta is harmless to upstreams that ignore it.
|
|
3445
|
+
*/
|
|
3446
|
+
function isOneMillionSuffixedClaudeId(modelId) {
|
|
3447
|
+
return modelId.startsWith("claude-") && modelId.endsWith("-1m");
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3398
3450
|
//#endregion
|
|
3399
3451
|
//#region src/services/copilot/create-chat-completions.ts
|
|
3400
3452
|
const GPT_MODEL_PATTERN = /^gpt-/i;
|
|
3453
|
+
/** Case-insensitive lookup of a header key in a plain-object header bag. */
|
|
3454
|
+
function findHeaderKey(headers, name) {
|
|
3455
|
+
const lower = name.toLowerCase();
|
|
3456
|
+
return Object.keys(headers).find((k) => k.toLowerCase() === lower);
|
|
3457
|
+
}
|
|
3401
3458
|
const createChatCompletions = async (payload, options) => {
|
|
3402
3459
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
3403
3460
|
const vendor = options?.resolvedModel?.vendor;
|
|
@@ -3415,21 +3472,27 @@ const createChatCompletions = async (payload, options) => {
|
|
|
3415
3472
|
const enableVision = wire.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
|
|
3416
3473
|
const isAgentCall = wire.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
|
|
3417
3474
|
const modelSupportsVision = options?.resolvedModel?.capabilities?.supports?.vision !== false;
|
|
3475
|
+
const headers = {
|
|
3476
|
+
...copilotHeaders(state, {
|
|
3477
|
+
vision: enableVision && modelSupportsVision,
|
|
3478
|
+
modelRequestHeaders: options?.resolvedModel?.request_headers,
|
|
3479
|
+
intent: isAgentCall ? "conversation-agent" : "conversation-panel"
|
|
3480
|
+
}),
|
|
3481
|
+
"X-Initiator": options?.initiator ?? (isAgentCall ? "agent" : "user")
|
|
3482
|
+
};
|
|
3483
|
+
if (options?.anthropicBeta) {
|
|
3484
|
+
const existingKey = findHeaderKey(headers, "anthropic-beta") ?? "anthropic-beta";
|
|
3485
|
+
headers[existingKey] = mergeBetaFeatures(headers[existingKey], options.anthropicBeta);
|
|
3486
|
+
consola.debug(`[ChatCompletions] anthropic-beta after merge: ${headers[existingKey]}`);
|
|
3487
|
+
}
|
|
3418
3488
|
const response = await copilotFetch("/chat/completions", {
|
|
3419
3489
|
method: "POST",
|
|
3420
|
-
headers
|
|
3421
|
-
...copilotHeaders(state, {
|
|
3422
|
-
vision: enableVision && modelSupportsVision,
|
|
3423
|
-
modelRequestHeaders: options?.resolvedModel?.request_headers,
|
|
3424
|
-
intent: isAgentCall ? "conversation-agent" : "conversation-panel"
|
|
3425
|
-
}),
|
|
3426
|
-
"X-Initiator": options?.initiator ?? (isAgentCall ? "agent" : "user")
|
|
3427
|
-
},
|
|
3490
|
+
headers,
|
|
3428
3491
|
body: JSON.stringify(wire)
|
|
3429
3492
|
});
|
|
3430
3493
|
if (!response.ok) {
|
|
3431
3494
|
consola.error("Failed to create chat completions", response);
|
|
3432
|
-
throw await HTTPError.fromResponse("Failed to create chat completions", response, payload.model);
|
|
3495
|
+
throw await HTTPError.fromResponse("Failed to create chat completions", response, options?.errorModelIdOverride ?? payload.model);
|
|
3433
3496
|
}
|
|
3434
3497
|
if (payload.stream) return events(response);
|
|
3435
3498
|
return await response.json();
|
|
@@ -3964,7 +4027,7 @@ function isNonStreaming(response) {
|
|
|
3964
4027
|
return Object.hasOwn(response, "choices");
|
|
3965
4028
|
}
|
|
3966
4029
|
/** Build final payload with auto-truncate if needed */
|
|
3967
|
-
async function buildFinalPayload(payload, model) {
|
|
4030
|
+
async function buildFinalPayload(payload, model, autoTruncateConfig = {}) {
|
|
3968
4031
|
if (!state.autoTruncate || !model) {
|
|
3969
4032
|
if (state.autoTruncate && !model) consola.warn(`Auto-truncate: Model '${payload.model}' not found in cached models, skipping`);
|
|
3970
4033
|
return {
|
|
@@ -3973,7 +4036,7 @@ async function buildFinalPayload(payload, model) {
|
|
|
3973
4036
|
};
|
|
3974
4037
|
}
|
|
3975
4038
|
try {
|
|
3976
|
-
const check = await checkNeedsCompactionOpenAI(payload, model);
|
|
4039
|
+
const check = await checkNeedsCompactionOpenAI(payload, model, autoTruncateConfig);
|
|
3977
4040
|
consola.debug(`Auto-truncate check: ${check.currentTokens} tokens (limit ${check.tokenLimit}), ${Math.round(check.currentBytes / 1024)}KB (limit ${check.byteLimit === Infinity ? "unlimited" : `${Math.round(check.byteLimit / 1024)}KB`}), needed: ${check.needed}${check.reason ? ` (${check.reason})` : ""}`);
|
|
3978
4041
|
if (!check.needed) return {
|
|
3979
4042
|
finalPayload: payload,
|
|
@@ -3984,7 +4047,7 @@ async function buildFinalPayload(payload, model) {
|
|
|
3984
4047
|
else if (check.reason === "bytes") reasonText = "size";
|
|
3985
4048
|
else reasonText = "tokens";
|
|
3986
4049
|
consola.info(`Auto-truncate triggered: exceeds ${reasonText} limit`);
|
|
3987
|
-
const truncateResult = await autoTruncateOpenAI(payload, model);
|
|
4050
|
+
const truncateResult = await autoTruncateOpenAI(payload, model, autoTruncateConfig);
|
|
3988
4051
|
return {
|
|
3989
4052
|
finalPayload: truncateResult.payload,
|
|
3990
4053
|
truncateResult
|
|
@@ -6839,7 +6902,7 @@ function modelSupportsContextEditing(modelId) {
|
|
|
6839
6902
|
}
|
|
6840
6903
|
function modelSupportsToolSearch(modelId) {
|
|
6841
6904
|
const n = normalizeForMatching(modelId);
|
|
6842
|
-
return n.includes("claude") && (n.includes("opus45") || n.includes("opus46") || n.includes("sonnet45") || n.includes("sonnet46"));
|
|
6905
|
+
return n.includes("claude") && (n.includes("opus45") || n.includes("opus46") || n.includes("opus47") || n.includes("opus48") || n.includes("sonnet45") || n.includes("sonnet46"));
|
|
6843
6906
|
}
|
|
6844
6907
|
function isContextEditingEnabled(modelId) {
|
|
6845
6908
|
return modelSupportsContextEditing(modelId) && state.contextEditingMode !== "off";
|
|
@@ -7065,6 +7128,7 @@ async function createAnthropicMessages(payload, options) {
|
|
|
7065
7128
|
};
|
|
7066
7129
|
const betaHeaders = buildAnthropicBetaHeaders(filteredPayload.model, resolvedModel);
|
|
7067
7130
|
Object.assign(headers, betaHeaders);
|
|
7131
|
+
if (options?.injectContext1mBeta) headers["anthropic-beta"] = appendContext1mBeta(headers["anthropic-beta"]);
|
|
7068
7132
|
if (isContextEditingEnabled(filteredPayload.model)) {
|
|
7069
7133
|
const hasThinking = filteredPayload.thinking?.type === "enabled";
|
|
7070
7134
|
const cm = buildContextManagement(state.contextEditingMode, hasThinking);
|
|
@@ -7091,7 +7155,7 @@ async function createAnthropicMessages(payload, options) {
|
|
|
7091
7155
|
thinking: filteredPayload.thinking,
|
|
7092
7156
|
messageCount: filteredPayload.messages.length
|
|
7093
7157
|
});
|
|
7094
|
-
throw await HTTPError.fromResponse("Failed to create Anthropic messages", response, filteredPayload.model);
|
|
7158
|
+
throw await HTTPError.fromResponse("Failed to create Anthropic messages", response, options?.errorModelIdOverride ?? filteredPayload.model);
|
|
7095
7159
|
}
|
|
7096
7160
|
if (payload.stream) return events(response);
|
|
7097
7161
|
return await response.json();
|
|
@@ -7114,13 +7178,58 @@ function stripServerToolsFromPayload(tools) {
|
|
|
7114
7178
|
}
|
|
7115
7179
|
return result.length > 0 ? result : void 0;
|
|
7116
7180
|
}
|
|
7181
|
+
/** Context window unlocked by the context-1m-2025-08-07 beta header. */
|
|
7182
|
+
const ONE_MILLION_CONTEXT_WINDOW_TOKENS = 1e6;
|
|
7183
|
+
/**
|
|
7184
|
+
* Convert a Claude model id from the client-facing dash convention to the
|
|
7185
|
+
* upstream Copilot dot convention. The two conventions co-exist because
|
|
7186
|
+
* Anthropic-style clients use dashes ("claude-opus-4-8") and Copilot lists
|
|
7187
|
+
* the same model with dots ("claude-opus-4.8"). Only the first dash inside
|
|
7188
|
+
* the version segment is converted (a model id like "claude-opus-4.8-1m"
|
|
7189
|
+
* already in dot form is returned unchanged).
|
|
7190
|
+
*/
|
|
7191
|
+
function dashToDotClaudeId(modelId) {
|
|
7192
|
+
if (!modelId.startsWith("claude-")) return modelId;
|
|
7193
|
+
return modelId.replace(/^(claude-[a-z]+-)(\d+)-(\d+)/, "$1$2.$3");
|
|
7194
|
+
}
|
|
7195
|
+
function resolveAnthropicModelForDirectPath(modelId) {
|
|
7196
|
+
const exact = findModelById(modelId);
|
|
7197
|
+
if (exact?.vendor === "Anthropic") return {
|
|
7198
|
+
model: exact,
|
|
7199
|
+
baseModelId: modelId,
|
|
7200
|
+
oneMillionFallback: false,
|
|
7201
|
+
effectiveContextWindowTokens: exact.capabilities?.limits?.max_context_window_tokens ?? 2e5
|
|
7202
|
+
};
|
|
7203
|
+
const dotted = dashToDotClaudeId(modelId);
|
|
7204
|
+
if (dotted !== modelId) {
|
|
7205
|
+
const dottedExact = findModelById(dotted);
|
|
7206
|
+
if (dottedExact?.vendor === "Anthropic") return {
|
|
7207
|
+
model: dottedExact,
|
|
7208
|
+
baseModelId: dotted,
|
|
7209
|
+
oneMillionFallback: false,
|
|
7210
|
+
effectiveContextWindowTokens: dottedExact.capabilities?.limits?.max_context_window_tokens ?? 2e5
|
|
7211
|
+
};
|
|
7212
|
+
}
|
|
7213
|
+
if (modelId.endsWith("-1m")) {
|
|
7214
|
+
const baseId = modelId.slice(0, -3);
|
|
7215
|
+
for (const candidateId of [baseId, dashToDotClaudeId(baseId)]) {
|
|
7216
|
+
const base = findModelById(candidateId);
|
|
7217
|
+
if (base?.vendor === "Anthropic") return {
|
|
7218
|
+
model: base,
|
|
7219
|
+
baseModelId: candidateId,
|
|
7220
|
+
oneMillionFallback: true,
|
|
7221
|
+
effectiveContextWindowTokens: ONE_MILLION_CONTEXT_WINDOW_TOKENS
|
|
7222
|
+
};
|
|
7223
|
+
}
|
|
7224
|
+
}
|
|
7225
|
+
}
|
|
7117
7226
|
/**
|
|
7118
7227
|
* Check if a model supports direct Anthropic API.
|
|
7119
7228
|
* Returns true if redirect is disabled (direct API is on) and the model is from Anthropic vendor.
|
|
7120
7229
|
*/
|
|
7121
7230
|
function supportsDirectAnthropicApi(modelId) {
|
|
7122
7231
|
if (state.redirectAnthropic) return false;
|
|
7123
|
-
return
|
|
7232
|
+
return resolveAnthropicModelForDirectPath(modelId) !== void 0;
|
|
7124
7233
|
}
|
|
7125
7234
|
|
|
7126
7235
|
//#endregion
|
|
@@ -7359,18 +7468,42 @@ function findLatestModel(familyPrefix, fallback) {
|
|
|
7359
7468
|
const candidates = models.filter((m) => m.id.startsWith(familyPrefix));
|
|
7360
7469
|
if (candidates.length === 0) return fallback;
|
|
7361
7470
|
candidates.sort((a, b) => {
|
|
7362
|
-
const
|
|
7363
|
-
|
|
7471
|
+
const [aMajor, aMinor] = extractVersion(a.id, familyPrefix);
|
|
7472
|
+
const [bMajor, bMinor] = extractVersion(b.id, familyPrefix);
|
|
7473
|
+
if (aMajor !== bMajor) return bMajor - aMajor;
|
|
7474
|
+
return bMinor - aMinor;
|
|
7364
7475
|
});
|
|
7365
7476
|
return candidates[0].id;
|
|
7366
7477
|
}
|
|
7367
7478
|
/**
|
|
7368
|
-
* Extract numeric version from model
|
|
7369
|
-
*
|
|
7479
|
+
* Extract numeric [major, minor] version from a model id.
|
|
7480
|
+
*
|
|
7481
|
+
* Supports both naming conventions Anthropic/Copilot have used:
|
|
7482
|
+
* - dot: "claude-opus-4.5" → [4, 5]
|
|
7483
|
+
* - dash: "claude-opus-4-8" → [4, 8]
|
|
7484
|
+
* - dash double-digit: "claude-opus-4-10" → [4, 10]
|
|
7485
|
+
*
|
|
7486
|
+
* The dash form previously parsed as just the major via the regex
|
|
7487
|
+
* /^(\d+(?:\.\d+)?)/ (the dash stopped the match), which silently
|
|
7488
|
+
* downgraded dash-named candidates against any dot-named candidate in
|
|
7489
|
+
* findLatestModel. Parsing into a tuple also avoids the parseFloat
|
|
7490
|
+
* lossiness on double-digit minors ("4.10" → 4.1).
|
|
7491
|
+
*
|
|
7492
|
+
* Anything after the major/minor segment (date stamps, "-1m") is ignored.
|
|
7493
|
+
* The minor capture is bounded to 1-3 digits so that an 8-digit date suffix
|
|
7494
|
+
* directly after the major (e.g. "claude-opus-4-20250514") is NOT mistaken
|
|
7495
|
+
* for a minor version of 20_250_514 — without that bound, dated ids would
|
|
7496
|
+
* outrank legitimate dotted candidates like "claude-opus-4.8" in
|
|
7497
|
+
* findLatestModel's sort.
|
|
7498
|
+
*
|
|
7499
|
+
* Returns [0, 0] when no version can be extracted.
|
|
7370
7500
|
*/
|
|
7371
7501
|
function extractVersion(modelId, prefix) {
|
|
7372
|
-
const match = modelId.slice(prefix.length + 1).match(/^(\d+(
|
|
7373
|
-
|
|
7502
|
+
const match = modelId.slice(prefix.length + 1).match(/^(\d+)(?:[.-](\d{1,3}))?/);
|
|
7503
|
+
if (!match) return [0, 0];
|
|
7504
|
+
const major = Number.parseInt(match[1], 10);
|
|
7505
|
+
const rawMinor = match[2];
|
|
7506
|
+
return [major, rawMinor === void 0 ? 0 : Number.parseInt(rawMinor, 10) || 0];
|
|
7374
7507
|
}
|
|
7375
7508
|
function translateModelName(model) {
|
|
7376
7509
|
const aliasMap = {
|
|
@@ -7384,6 +7517,8 @@ function translateModelName(model) {
|
|
|
7384
7517
|
}
|
|
7385
7518
|
if (/^claude-sonnet-4-5-\d+$/.test(model)) return "claude-sonnet-4.5";
|
|
7386
7519
|
if (/^claude-sonnet-4-\d+$/.test(model)) return "claude-sonnet-4";
|
|
7520
|
+
if (model === "claude-opus-4-8-1m") return "claude-opus-4.8";
|
|
7521
|
+
if (model === "claude-opus-4-8") return "claude-opus-4.8";
|
|
7387
7522
|
if (model === "claude-opus-4-7-1m") return "claude-opus-4.7-1m-internal";
|
|
7388
7523
|
if (/^claude-opus-4-7$/.test(model)) return "claude-opus-4.7";
|
|
7389
7524
|
if (model === "claude-opus-4-6-1m") return "claude-opus-4.6-1m";
|
|
@@ -7763,14 +7898,30 @@ function translateErrorToAnthropicErrorEvent(error) {
|
|
|
7763
7898
|
*/
|
|
7764
7899
|
async function handleDirectAnthropicCompletion(c, anthropicPayload, ctx, initiatorOverride) {
|
|
7765
7900
|
consola.debug("Using direct Anthropic API path for model:", anthropicPayload.model);
|
|
7766
|
-
const
|
|
7767
|
-
|
|
7901
|
+
const resolution = resolveAnthropicModelForDirectPath(anthropicPayload.model);
|
|
7902
|
+
const selectedModel = resolution?.model;
|
|
7903
|
+
const baseModelId = resolution?.baseModelId ?? anthropicPayload.model;
|
|
7904
|
+
const needsContext1mBeta = resolution?.oneMillionFallback ?? false;
|
|
7905
|
+
const originalModelId = anthropicPayload.model;
|
|
7906
|
+
const resolvedPayload = resolution && baseModelId !== originalModelId ? {
|
|
7907
|
+
...anthropicPayload,
|
|
7908
|
+
model: baseModelId
|
|
7909
|
+
} : anthropicPayload;
|
|
7910
|
+
if (baseModelId !== originalModelId) {
|
|
7911
|
+
consola.debug(`[Anthropic] Mapping model for upstream: ${originalModelId} → ${baseModelId}${needsContext1mBeta ? " (+context-1m beta)" : ""}`);
|
|
7912
|
+
updateTrackerResolvedModel(ctx.trackingId, baseModelId);
|
|
7913
|
+
}
|
|
7914
|
+
const autoTruncateConfig = resolution && resolution.oneMillionFallback ? {
|
|
7915
|
+
contextWindowOverride: resolution.effectiveContextWindowTokens,
|
|
7916
|
+
tokenLimitCacheKeyOverride: originalModelId
|
|
7917
|
+
} : {};
|
|
7918
|
+
let effectivePayload = resolvedPayload;
|
|
7768
7919
|
let truncateResult;
|
|
7769
7920
|
if (state.autoTruncate && selectedModel) {
|
|
7770
|
-
const check = await checkNeedsCompactionAnthropic(
|
|
7921
|
+
const check = await checkNeedsCompactionAnthropic(resolvedPayload, selectedModel, autoTruncateConfig);
|
|
7771
7922
|
consola.debug(`[Anthropic] Auto-truncate check: ${check.currentTokens} tokens (limit ${check.tokenLimit}), ${Math.round(check.currentBytes / 1024)}KB (limit ${check.byteLimit === Infinity ? "unlimited" : `${Math.round(check.byteLimit / 1024)}KB`}), needed: ${check.needed}${check.reason ? ` (${check.reason})` : ""}`);
|
|
7772
7923
|
if (check.needed) try {
|
|
7773
|
-
truncateResult = await autoTruncateAnthropic(
|
|
7924
|
+
truncateResult = await autoTruncateAnthropic(resolvedPayload, selectedModel, autoTruncateConfig);
|
|
7774
7925
|
if (truncateResult.wasCompacted) effectivePayload = truncateResult.payload;
|
|
7775
7926
|
} catch (error) {
|
|
7776
7927
|
consola.warn("[Anthropic] Auto-truncate failed, proceeding with original payload:", error instanceof Error ? error.message : error);
|
|
@@ -7778,7 +7929,11 @@ async function handleDirectAnthropicCompletion(c, anthropicPayload, ctx, initiat
|
|
|
7778
7929
|
} else if (state.autoTruncate && !selectedModel) consola.debug(`[Anthropic] Model '${anthropicPayload.model}' not found, skipping auto-truncate`);
|
|
7779
7930
|
if (state.manualApprove) await awaitApproval();
|
|
7780
7931
|
try {
|
|
7781
|
-
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createAnthropicMessages(effectivePayload, {
|
|
7932
|
+
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createAnthropicMessages(effectivePayload, {
|
|
7933
|
+
initiator: initiatorOverride,
|
|
7934
|
+
injectContext1mBeta: needsContext1mBeta,
|
|
7935
|
+
errorModelIdOverride: needsContext1mBeta ? originalModelId : void 0
|
|
7936
|
+
}));
|
|
7782
7937
|
ctx.queueWaitMs = queueWaitMs;
|
|
7783
7938
|
if (Symbol.asyncIterator in response) {
|
|
7784
7939
|
consola.debug("Streaming response from Copilot (direct Anthropic)");
|
|
@@ -7976,17 +8131,33 @@ const parseSubagentMarkerFromSystemReminder = (text) => {
|
|
|
7976
8131
|
* Handle completion using OpenAI translation path (legacy)
|
|
7977
8132
|
*/
|
|
7978
8133
|
async function handleTranslatedCompletion(c, anthropicPayload, ctx, initiatorOverride) {
|
|
8134
|
+
const originalModelId = anthropicPayload.model;
|
|
8135
|
+
const hasOneMillionSuffix = isOneMillionSuffixedClaudeId(originalModelId);
|
|
8136
|
+
const oneMResolution = resolveAnthropicModelForDirectPath(originalModelId);
|
|
8137
|
+
const needsContext1mBeta = hasOneMillionSuffix || (oneMResolution?.oneMillionFallback ?? false);
|
|
7979
8138
|
const { payload: translatedPayload, toolNameMapping } = translateToOpenAI(anthropicPayload);
|
|
7980
8139
|
consola.debug("Translated OpenAI request payload:", JSON.stringify(translatedPayload));
|
|
7981
8140
|
updateTrackerResolvedModel(ctx.trackingId, translatedPayload.model);
|
|
7982
8141
|
const selectedModel = findModelById(translatedPayload.model);
|
|
7983
|
-
const
|
|
8142
|
+
const autoTruncateConfig = oneMResolution?.oneMillionFallback ? {
|
|
8143
|
+
contextWindowOverride: oneMResolution.effectiveContextWindowTokens,
|
|
8144
|
+
tokenLimitCacheKeyOverride: originalModelId
|
|
8145
|
+
} : {};
|
|
8146
|
+
const { finalPayload: openAIPayload, truncateResult } = await buildFinalPayload(translatedPayload, selectedModel, autoTruncateConfig);
|
|
7984
8147
|
if (truncateResult) ctx.truncateResult = truncateResult;
|
|
8148
|
+
let anthropicBeta = c.req.header("anthropic-beta");
|
|
8149
|
+
if (needsContext1mBeta) anthropicBeta = appendContext1mBeta(anthropicBeta);
|
|
7985
8150
|
if (state.manualApprove) await awaitApproval();
|
|
8151
|
+
let errorModelIdOverride;
|
|
8152
|
+
if (autoTruncateConfig.tokenLimitCacheKeyOverride !== void 0) errorModelIdOverride = autoTruncateConfig.tokenLimitCacheKeyOverride;
|
|
8153
|
+
else if (needsContext1mBeta && selectedModel) errorModelIdOverride = selectedModel.id;
|
|
8154
|
+
else if (hasOneMillionSuffix) errorModelIdOverride = originalModelId;
|
|
7986
8155
|
try {
|
|
7987
8156
|
const { result: response, queueWaitMs } = await executeWithAdaptiveRateLimit(() => createChatCompletions(openAIPayload, {
|
|
7988
8157
|
initiator: initiatorOverride,
|
|
7989
|
-
resolvedModel: selectedModel
|
|
8158
|
+
resolvedModel: selectedModel,
|
|
8159
|
+
anthropicBeta,
|
|
8160
|
+
errorModelIdOverride
|
|
7990
8161
|
}));
|
|
7991
8162
|
ctx.queueWaitMs = queueWaitMs;
|
|
7992
8163
|
if (isNonStreaming(response)) return handleNonStreamingResponse({
|
|
@@ -8254,10 +8425,15 @@ async function handleCountTokens(c) {
|
|
|
8254
8425
|
consola.warn("Model not found, returning default token count");
|
|
8255
8426
|
return c.json({ input_tokens: 1 });
|
|
8256
8427
|
}
|
|
8428
|
+
const directResolution = resolveAnthropicModelForDirectPath(anthropicPayload.model);
|
|
8429
|
+
const autoTruncateConfig = directResolution?.oneMillionFallback ? {
|
|
8430
|
+
contextWindowOverride: directResolution.effectiveContextWindowTokens,
|
|
8431
|
+
tokenLimitCacheKeyOverride: anthropicPayload.model
|
|
8432
|
+
} : {};
|
|
8257
8433
|
if (state.autoTruncate) {
|
|
8258
|
-
const truncateCheck = await checkNeedsCompactionAnthropic(anthropicPayload, selectedModel);
|
|
8434
|
+
const truncateCheck = await checkNeedsCompactionAnthropic(anthropicPayload, selectedModel, autoTruncateConfig);
|
|
8259
8435
|
if (truncateCheck.needed) {
|
|
8260
|
-
const contextWindow = selectedModel.capabilities?.limits?.max_context_window_tokens ?? 2e5;
|
|
8436
|
+
const contextWindow = autoTruncateConfig.contextWindowOverride ?? selectedModel.capabilities?.limits?.max_context_window_tokens ?? 2e5;
|
|
8261
8437
|
const inflatedTokens = Math.floor(contextWindow * .95);
|
|
8262
8438
|
consola.debug(`[count_tokens] Would trigger auto-truncate: ${truncateCheck.currentTokens} tokens > ${truncateCheck.tokenLimit}, returning inflated count: ${inflatedTokens}`);
|
|
8263
8439
|
return c.json({ input_tokens: inflatedTokens });
|