copilot-relay 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +39 -18
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2299,6 +2299,22 @@ async function boundModelOutputTokens(config, model, requested) {
|
|
|
2299
2299
|
|
|
2300
2300
|
//#endregion
|
|
2301
2301
|
//#region src/claude/utils.ts
|
|
2302
|
+
const isEffortOnlyControl = (control) => control !== null && typeof control === "object" && !Array.isArray(control) && Object.keys(control).length === 1 && "effort" in control && isConfiguredReasoningEffort(control.effort);
|
|
2303
|
+
function getClaudeTurnEffort(payload) {
|
|
2304
|
+
let requested = getRequestReasoningEffort(payload);
|
|
2305
|
+
let pending;
|
|
2306
|
+
if (Array.isArray(payload.messages)) {
|
|
2307
|
+
for (const message of payload.messages) if (message?.role === "system" && message.clear_at === void 0 && isEffortOnlyControl(message.output_config)) pending = message.output_config.effort;
|
|
2308
|
+
else if (message?.role === "user" && pending !== void 0) {
|
|
2309
|
+
requested = pending;
|
|
2310
|
+
pending = void 0;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
return {
|
|
2314
|
+
requested,
|
|
2315
|
+
effective: resolveReasoningEffort(requested)
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2302
2318
|
function mapOpenAIStopReasonToClaude(finishReason) {
|
|
2303
2319
|
if (finishReason === null) return null;
|
|
2304
2320
|
return {
|
|
@@ -2379,8 +2395,8 @@ function translateToOpenAI(payload, _settings, toolNameMapper) {
|
|
|
2379
2395
|
const model = translateModelName(payload.model);
|
|
2380
2396
|
const mapper = toolNameMapper ?? createClaudeToolNameMapper(payload.tools, { ...getToolNameMapperOptionsForModel(model) });
|
|
2381
2397
|
const tools = translateClaudeToolsToOpenAI(payload.tools, mapper);
|
|
2382
|
-
const effort =
|
|
2383
|
-
validateClaudeMessages(payload.messages
|
|
2398
|
+
const { effective: effort } = getClaudeTurnEffort(payload);
|
|
2399
|
+
validateClaudeMessages(payload.messages);
|
|
2384
2400
|
return {
|
|
2385
2401
|
model,
|
|
2386
2402
|
messages: normalizeFinalAssistantPrefill$1(translateClaudeMessagesToOpenAI(payload.messages, payload.system, mapper)),
|
|
@@ -2395,7 +2411,7 @@ function translateToOpenAI(payload, _settings, toolNameMapper) {
|
|
|
2395
2411
|
tool_choice: tools && tools.length > 0 ? translateClaudeToolChoiceToOpenAI(payload.tool_choice, mapper) : void 0
|
|
2396
2412
|
};
|
|
2397
2413
|
}
|
|
2398
|
-
function validateClaudeMessages(messages, native = false
|
|
2414
|
+
function validateClaudeMessages(messages, native = false) {
|
|
2399
2415
|
if (!Array.isArray(messages)) throw invalidMessage("Messages must be an array.");
|
|
2400
2416
|
for (const message of messages) {
|
|
2401
2417
|
if (!message || ![
|
|
@@ -2405,8 +2421,7 @@ function validateClaudeMessages(messages, native = false, effort) {
|
|
|
2405
2421
|
].includes(message.role)) throw invalidMessage("Unsupported message role.");
|
|
2406
2422
|
if (message.role !== "system") continue;
|
|
2407
2423
|
const control = message.output_config;
|
|
2408
|
-
|
|
2409
|
-
if (!native && (message.clear_at !== void 0 || control !== void 0 && !redundantEffort)) throw invalidMessage("Per-message system controls require the native Messages route unless effort exactly matches the request.");
|
|
2424
|
+
if (!native && (message.clear_at !== void 0 || control !== void 0 && !isEffortOnlyControl(control))) throw invalidMessage("Translated system controls support only output_config.effort with low, medium, high, xhigh, or max; other controls require the native Messages route.");
|
|
2410
2425
|
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.");
|
|
2411
2426
|
}
|
|
2412
2427
|
}
|
|
@@ -2416,7 +2431,9 @@ function translateClaudeMessagesToOpenAI(claudeMessages, system, toolNameMapper)
|
|
|
2416
2431
|
switch (message.role) {
|
|
2417
2432
|
case "user": return handleUserMessage(message);
|
|
2418
2433
|
case "assistant": return handleAssistantMessage(message, toolNameMapper);
|
|
2419
|
-
case "system":
|
|
2434
|
+
case "system":
|
|
2435
|
+
if (message.output_config !== void 0 && (typeof message.content === "string" ? message.content.length === 0 : message.content.every((block) => block.text.length === 0))) return [];
|
|
2436
|
+
return handleSystemPrompt(message.content);
|
|
2420
2437
|
default: throw invalidMessage("Unsupported message role.");
|
|
2421
2438
|
}
|
|
2422
2439
|
});
|
|
@@ -2854,7 +2871,11 @@ const getClaudeWebSearchToolCallFromChatResponse = (response, toolNameMapper) =>
|
|
|
2854
2871
|
};
|
|
2855
2872
|
const buildSearchInput = (payload, requestedQuery) => {
|
|
2856
2873
|
const systemText = typeof payload.system === "string" ? payload.system : Array.isArray(payload.system) ? payload.system.map((block) => block.text).join("\n\n") : "";
|
|
2857
|
-
const messages = payload.messages.
|
|
2874
|
+
const messages = payload.messages.flatMap((message) => {
|
|
2875
|
+
const text$1 = textFromMessageContent(message.content);
|
|
2876
|
+
if (message.role === "system" && message.output_config !== void 0 && (typeof message.content === "string" ? message.content.length === 0 : message.content.every((block) => block.text.length === 0))) return [];
|
|
2877
|
+
return [`${message.role}: ${text$1}`];
|
|
2878
|
+
}).join("\n\n");
|
|
2858
2879
|
return [
|
|
2859
2880
|
"You are fulfilling an Anthropic web_search server tool request for Claude Code.",
|
|
2860
2881
|
"Search the web using the provided web_search_preview tool.",
|
|
@@ -2870,7 +2891,7 @@ const buildWebSearchRequestPayload = (payload, requestedQuery, model) => ({
|
|
|
2870
2891
|
model,
|
|
2871
2892
|
input: buildSearchInput(payload, requestedQuery),
|
|
2872
2893
|
tools: [{ type: "web_search_preview" }],
|
|
2873
|
-
reasoning: { effort:
|
|
2894
|
+
reasoning: { effort: getClaudeTurnEffort(payload).effective },
|
|
2874
2895
|
max_output_tokens: Math.max(256, Math.min(payload.max_tokens ?? 1024, 1200)),
|
|
2875
2896
|
temperature: payload.temperature,
|
|
2876
2897
|
top_p: payload.top_p
|
|
@@ -3096,7 +3117,7 @@ const createClaudeWebSearchExecution = async (config, payload, requestedQuery, o
|
|
|
3096
3117
|
} catch (error) {
|
|
3097
3118
|
if (!(error instanceof SyntaxError)) throw error;
|
|
3098
3119
|
}
|
|
3099
|
-
return interpretSearchResponse(upstream, config, requestedQuery, request,
|
|
3120
|
+
return interpretSearchResponse(upstream, config, requestedQuery, request, getClaudeTurnEffort(payload).requested ?? "unset", options.requestId);
|
|
3100
3121
|
};
|
|
3101
3122
|
const buildSearchResultBlock = (toolUseId, results) => ({
|
|
3102
3123
|
type: "web_search_tool_result",
|
|
@@ -4346,7 +4367,7 @@ async function createNativeMessages(config, payload, options) {
|
|
|
4346
4367
|
};
|
|
4347
4368
|
const beta = options.headers?.get("anthropic-beta");
|
|
4348
4369
|
if (beta) headers["anthropic-beta"] = beta;
|
|
4349
|
-
log.info(sanitizeTerminalString(`request_id=${options.requestId} Model request client=claude requested_model=${payload.model} upstream_model=${payload.model} upstream_api=messages effective_think_effort=${body.
|
|
4370
|
+
log.info(sanitizeTerminalString(`request_id=${options.requestId} Model request client=claude requested_model=${payload.model} upstream_model=${payload.model} upstream_api=messages effective_think_effort=${getClaudeTurnEffort(body).effective}`));
|
|
4350
4371
|
const response = await fetchCopilot(getCopilotProviderContext(config), "/v1/messages", {
|
|
4351
4372
|
method: "POST",
|
|
4352
4373
|
headers,
|
|
@@ -4560,7 +4581,10 @@ async function handleNativeMessages(config, payload, options, write) {
|
|
|
4560
4581
|
}
|
|
4561
4582
|
const searchCall = searches[0];
|
|
4562
4583
|
if (!searchCall.id || !searchCall.name || typeof searchCall.input?.query !== "string" || !searchCall.input.query.trim()) throw new Error("Invalid native search call.");
|
|
4563
|
-
const search = await createClaudeWebSearchExecution(config,
|
|
4584
|
+
const search = await createClaudeWebSearchExecution(config, {
|
|
4585
|
+
...payload,
|
|
4586
|
+
messages: normalizeNativeHistory(payload.messages)
|
|
4587
|
+
}, searchCall.input.query, {
|
|
4564
4588
|
...options,
|
|
4565
4589
|
signal
|
|
4566
4590
|
});
|
|
@@ -4672,7 +4696,7 @@ const createTokenCountModel = (modelId, tokenizer = "o200k_base") => ({
|
|
|
4672
4696
|
id: modelId
|
|
4673
4697
|
});
|
|
4674
4698
|
const isNonStreamingResponse = (response) => typeof response === "object" && response !== null && Object.hasOwn(response, "choices");
|
|
4675
|
-
const getClaudeRequestedThinkEffort = (payload) =>
|
|
4699
|
+
const getClaudeRequestedThinkEffort = (payload) => getClaudeTurnEffort(payload).requested ?? "unset";
|
|
4676
4700
|
const getClaudeRequestedThinking = (payload) => {
|
|
4677
4701
|
if (!payload.thinking) return "none";
|
|
4678
4702
|
return [`type:${payload.thinking.type}`, `budget:${payload.thinking.budget_tokens ?? "none"}`].join(",");
|
|
@@ -4877,10 +4901,7 @@ const handleClaudeMessageRequest = async (config, claudePayload, requestSignal,
|
|
|
4877
4901
|
const webSearchToolCall = shouldLetModelDecideWebSearch ? getClaudeWebSearchToolCallFromChatResponse(effectiveResponse, toolNameMapper) : void 0;
|
|
4878
4902
|
let claudeResponse;
|
|
4879
4903
|
if (webSearchToolCall) {
|
|
4880
|
-
const search = await createClaudeWebSearchExecution(config, {
|
|
4881
|
-
...claudePayload,
|
|
4882
|
-
reasoning_effort: openAIPayload.reasoning_effort
|
|
4883
|
-
}, webSearchToolCall.query, {
|
|
4904
|
+
const search = await createClaudeWebSearchExecution(config, claudePayload, webSearchToolCall.query, {
|
|
4884
4905
|
requestId,
|
|
4885
4906
|
signal: requestSignal,
|
|
4886
4907
|
timeoutMs: config.upstreamTimeoutMs
|
|
@@ -4995,11 +5016,11 @@ claudeRoutes.post("/messages", async (c) => {
|
|
|
4995
5016
|
const claudePayload = await c.req.json();
|
|
4996
5017
|
const requestSignal = createCopilotRequestSignal(c.req.raw.signal, config.upstreamTimeoutMs);
|
|
4997
5018
|
try {
|
|
4998
|
-
|
|
5019
|
+
getRequestReasoningEffort(claudePayload);
|
|
4999
5020
|
const upstreamModel = translateModelName(claudePayload.model);
|
|
5000
5021
|
const validate = () => {
|
|
5001
5022
|
if (shouldUseNativeMessages(config, upstreamModel)) validateNativeMessages(claudePayload);
|
|
5002
|
-
else validateClaudeMessages(claudePayload.messages
|
|
5023
|
+
else validateClaudeMessages(claudePayload.messages);
|
|
5003
5024
|
};
|
|
5004
5025
|
validateClaudeMessages(claudePayload.messages, true);
|
|
5005
5026
|
if (config.claudeUpstreamApi !== "auto") validate();
|