copilot-relay 0.4.4 → 0.4.5
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.js +56 -21
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -831,6 +831,7 @@ const normalizeClaudeModelId = (model, contextWindowTokens) => {
|
|
|
831
831
|
return contextWindowTokens !== void 0 && contextWindowTokens !== 1e6 ? normalized : `${normalized}[1m]`;
|
|
832
832
|
};
|
|
833
833
|
const normalizeCopilotModelId = (model) => normalizeOneMillionContextModel(model) ?? model;
|
|
834
|
+
const isClaudeModelId = (model) => model.startsWith("claude-");
|
|
834
835
|
const getConfiguredModelRouting = () => getRuntimeState().modelRouting ?? defaultModelRouting;
|
|
835
836
|
const getModelRouting = () => {
|
|
836
837
|
const routing = getConfiguredModelRouting();
|
|
@@ -2570,12 +2571,12 @@ const copilotEndpoints = [
|
|
|
2570
2571
|
"/responses",
|
|
2571
2572
|
"/v1/messages"
|
|
2572
2573
|
];
|
|
2573
|
-
const legacyResponsesPattern = /^(?:gpt-5\.5|gpt-5\.6|gpt-6-astra)(?:-|$)/i;
|
|
2574
|
+
const legacyResponsesPattern = /^(?:gpt-5\.4(?!-mini)|gpt-5\.5|gpt-5\.6|gpt-6-astra)(?:-|$)/i;
|
|
2574
2575
|
function selectCopilotEndpoint(config, model) {
|
|
2575
2576
|
const capabilities = getCachedCopilotModel(config, model);
|
|
2576
2577
|
const advertisedEndpoints = capabilities?.supportedEndpoints;
|
|
2577
2578
|
if (capabilities?.type !== void 0 && capabilities.type !== "chat") return { reason: "unsupported-model-type" };
|
|
2578
|
-
const isClaude = model
|
|
2579
|
+
const isClaude = isClaudeModelId(model);
|
|
2579
2580
|
const protocolMode = config.claudeUpstreamApi ?? "chat-completions";
|
|
2580
2581
|
if (isClaude && protocolMode === "messages") return {
|
|
2581
2582
|
endpoint: "/v1/messages",
|
|
@@ -2911,7 +2912,7 @@ const createClaudeToolNameMapper = (tools, options = {}) => {
|
|
|
2911
2912
|
function translateModelName(model) {
|
|
2912
2913
|
return routeModelId(model);
|
|
2913
2914
|
}
|
|
2914
|
-
function translateToOpenAI(payload, _settings, toolNameMapper) {
|
|
2915
|
+
function translateToOpenAI(payload, _settings, toolNameMapper, options = {}) {
|
|
2915
2916
|
const model = translateModelName(payload.model);
|
|
2916
2917
|
const mapper = toolNameMapper ?? createClaudeToolNameMapper(payload.tools, { ...getToolNameMapperOptionsForModel(model) });
|
|
2917
2918
|
const tools = translateClaudeToolsToOpenAI(payload.tools, mapper);
|
|
@@ -2919,7 +2920,7 @@ function translateToOpenAI(payload, _settings, toolNameMapper) {
|
|
|
2919
2920
|
validateClaudeMessages(payload.messages);
|
|
2920
2921
|
return {
|
|
2921
2922
|
model,
|
|
2922
|
-
messages: normalizeFinalAssistantPrefill$1(translateClaudeMessagesToOpenAI(payload.messages, payload.system, mapper)),
|
|
2923
|
+
messages: normalizeFinalAssistantPrefill$1(translateClaudeMessagesToOpenAI(payload.messages, payload.system, mapper, options.endpoint === "/chat/completions" && isClaudeModelId(model))),
|
|
2923
2924
|
max_tokens: payload.max_tokens,
|
|
2924
2925
|
stop: payload.stop_sequences,
|
|
2925
2926
|
stream: payload.stream,
|
|
@@ -2945,20 +2946,50 @@ function validateClaudeMessages(messages, native = false) {
|
|
|
2945
2946
|
if (typeof message.content !== "string" && (!Array.isArray(message.content) || message.content.some((block) => !block || block.type !== "text" || typeof block.text !== "string"))) throw invalidMessage("System message content must contain only text.");
|
|
2946
2947
|
}
|
|
2947
2948
|
}
|
|
2948
|
-
function translateClaudeMessagesToOpenAI(claudeMessages, system, toolNameMapper) {
|
|
2949
|
-
const
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2949
|
+
function translateClaudeMessagesToOpenAI(claudeMessages, system, toolNameMapper, claudeChatRoute) {
|
|
2950
|
+
const translated = handleSystemPrompt(system);
|
|
2951
|
+
if (claudeChatRoute && hasCacheBreakpoint(system)) markCacheBreakpoint(translated.at(-1));
|
|
2952
|
+
for (const message of claudeMessages) {
|
|
2953
|
+
const start$1 = translated.length;
|
|
2954
|
+
translated.push(...translateClaudeMessage(message, toolNameMapper, claudeChatRoute));
|
|
2955
|
+
if (claudeChatRoute) markMessageBreakpoints(message, translated, start$1);
|
|
2956
|
+
}
|
|
2957
|
+
return translated;
|
|
2958
|
+
}
|
|
2959
|
+
function markMessageBreakpoints(message, translated, start$1) {
|
|
2960
|
+
if (message.role !== "user" || !Array.isArray(message.content)) {
|
|
2961
|
+
if (hasCacheBreakpoint(message.content)) markCacheBreakpoint(translated.at(-1));
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
let toolIndex = start$1;
|
|
2965
|
+
for (const block of message.content) if (block.type === "tool_result") {
|
|
2966
|
+
if (isCacheBreakpoint(block)) markCacheBreakpoint(translated[toolIndex]);
|
|
2967
|
+
toolIndex += 1;
|
|
2968
|
+
} else if (isCacheBreakpoint(block)) markCacheBreakpoint(translated.at(-1));
|
|
2969
|
+
}
|
|
2970
|
+
function translateClaudeMessage(message, toolNameMapper, claudeChatRoute) {
|
|
2971
|
+
switch (message.role) {
|
|
2972
|
+
case "user": return handleUserMessage(message);
|
|
2973
|
+
case "assistant": return handleAssistantMessage(message, toolNameMapper);
|
|
2974
|
+
case "system":
|
|
2975
|
+
if (message.output_config !== void 0 && (typeof message.content === "string" ? message.content.length === 0 : message.content.every((block) => block.text.length === 0))) return [];
|
|
2976
|
+
return claudeChatRoute ? toSystemReminder(message.content) : handleSystemPrompt(message.content);
|
|
2977
|
+
default: throw invalidMessage("Unsupported message role.");
|
|
2978
|
+
}
|
|
2961
2979
|
}
|
|
2980
|
+
function toSystemReminder(content) {
|
|
2981
|
+
const text$1 = typeof content === "string" ? content : content.map((block) => block.text).join("\n\n");
|
|
2982
|
+
if (text$1.trim().length === 0) return [];
|
|
2983
|
+
return [{
|
|
2984
|
+
role: "user",
|
|
2985
|
+
content: `<system-reminder>\n${text$1}\n</system-reminder>`
|
|
2986
|
+
}];
|
|
2987
|
+
}
|
|
2988
|
+
const isCacheBreakpoint = (block) => "cache_control" in block && Boolean(block.cache_control);
|
|
2989
|
+
const hasCacheBreakpoint = (content) => Array.isArray(content) && content.some(isCacheBreakpoint);
|
|
2990
|
+
const markCacheBreakpoint = (message) => {
|
|
2991
|
+
if (message) message.copilot_cache_control = { type: "ephemeral" };
|
|
2992
|
+
};
|
|
2962
2993
|
const invalidMessage = (message) => new HTTPError(message, new Response(JSON.stringify({
|
|
2963
2994
|
type: "error",
|
|
2964
2995
|
error: {
|
|
@@ -4818,14 +4849,14 @@ async function logUpstreamError(message, response, context, signal, timeoutMs) {
|
|
|
4818
4849
|
route: context.route,
|
|
4819
4850
|
model: context.model,
|
|
4820
4851
|
requestId: context.requestId,
|
|
4821
|
-
request: context.request,
|
|
4822
4852
|
response: {
|
|
4823
4853
|
status: response.status,
|
|
4824
4854
|
statusText: response.statusText || void 0,
|
|
4825
4855
|
url: response.url || void 0,
|
|
4826
4856
|
headers: Object.fromEntries(response.headers.entries()),
|
|
4827
4857
|
body: errorBody || void 0
|
|
4828
|
-
}
|
|
4858
|
+
},
|
|
4859
|
+
request: context.request
|
|
4829
4860
|
});
|
|
4830
4861
|
return detail;
|
|
4831
4862
|
}
|
|
@@ -5452,6 +5483,10 @@ const createQueuedClaudeStreamWriter = (write) => {
|
|
|
5452
5483
|
const writeClaudeStreamEvents = async (events$1, writeEvent) => {
|
|
5453
5484
|
for (const event of events$1) await writeEvent(event);
|
|
5454
5485
|
};
|
|
5486
|
+
const translationEndpoint = (config, model) => {
|
|
5487
|
+
const selection = selectCopilotEndpoint(config, normalizeCopilotModelId(model));
|
|
5488
|
+
return selection.endpoint && !selection.responsesFallback ? selection.endpoint : void 0;
|
|
5489
|
+
};
|
|
5455
5490
|
const handleClaudeMessageRequest = async (config, claudePayload, requestSignal, requestId, writeEvent, requestHeaders) => {
|
|
5456
5491
|
const upstreamModel = translateModelName(claudePayload.model);
|
|
5457
5492
|
if (shouldUseNativeMessages(config, upstreamModel)) return handleNativeMessages(config, {
|
|
@@ -5465,7 +5500,7 @@ const handleClaudeMessageRequest = async (config, claudePayload, requestSignal,
|
|
|
5465
5500
|
const shouldLetModelDecideWebSearch = hasClaudeWebSearch(claudePayload);
|
|
5466
5501
|
const decisionPayload = shouldLetModelDecideWebSearch ? prepareClaudeWebSearchDecisionPayload(claudePayload) : claudePayload;
|
|
5467
5502
|
const toolNameMapper = createClaudeToolNameMapper(decisionPayload.tools, { ...getToolNameMapperOptionsForModel(upstreamModel) });
|
|
5468
|
-
const openAIPayload = translateToOpenAI(decisionPayload, void 0, toolNameMapper);
|
|
5503
|
+
const openAIPayload = translateToOpenAI(decisionPayload, void 0, toolNameMapper, { endpoint: translationEndpoint(config, upstreamModel) });
|
|
5469
5504
|
const canStreamWebSearchDecision = shouldLetModelDecideWebSearch && !!writeEvent;
|
|
5470
5505
|
if (shouldLetModelDecideWebSearch && !canStreamWebSearchDecision) openAIPayload.stream = false;
|
|
5471
5506
|
const response = await createChatCompletions(config, openAIPayload, {
|
|
@@ -5717,7 +5752,7 @@ claudeRoutes.post("/messages/count_tokens", async (c) => {
|
|
|
5717
5752
|
const { output_config: _outputConfig, clear_at: _clearAt,...textMessage } = message;
|
|
5718
5753
|
return textMessage;
|
|
5719
5754
|
})
|
|
5720
|
-
} : claudePayload);
|
|
5755
|
+
} : claudePayload, void 0, void 0, { endpoint: translationEndpoint(c.get("config"), translateModelName(claudePayload.model)) });
|
|
5721
5756
|
const exposedModels = getExposedModelIds();
|
|
5722
5757
|
const upstreamModel = getCachedCopilotModel(c.get("config"), openAIPayload.model);
|
|
5723
5758
|
const hasDiscoveredTokenizer = upstreamModel?.tokenizer !== void 0 && isSupportedTokenizer(upstreamModel.tokenizer);
|