copilot-relay 0.3.7 → 0.3.8
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 +10 -4
- package/config.default.yaml +4 -5
- package/dist/main.js +38 -15
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,16 +117,19 @@ for live model discovery, compatible thinking efforts, and copy-paste updates.
|
|
|
117
117
|
| Level | Logs |
|
|
118
118
|
| --- | --- |
|
|
119
119
|
| `error` | Startup, preflight, and request failures |
|
|
120
|
-
| `info` | Errors plus startup
|
|
121
|
-
| `debug` | Info plus
|
|
120
|
+
| `info` | Errors plus startup/preflight status, request IDs, model/effort summaries, upstream lifecycle, and local HTTP status codes |
|
|
121
|
+
| `debug` | Info plus detailed Copilot timings and request payloads |
|
|
122
122
|
|
|
123
123
|
Any other `logLevel` value is invalid and stops startup.
|
|
124
124
|
|
|
125
|
-
Valid `thinkEffort
|
|
125
|
+
Valid `thinkEffort` defaults: `low`, `medium`, `high`, `xhigh`, `max`.
|
|
126
126
|
It is a fallback, not a forced override: Claude Code's `output_config.effort`
|
|
127
127
|
wins, followed by the legacy `reasoning_effort` field, then `thinkEffort` when
|
|
128
128
|
neither request field supplies a value. The selected effort is preserved through
|
|
129
129
|
all model and WebSearch passes; existing config values are not rewritten.
|
|
130
|
+
`none` and other invalid explicit defaults fail startup before authentication or
|
|
131
|
+
config write-back, with a clear list of valid choices. They are not silently
|
|
132
|
+
replaced with `max`. Explicit request-level `none` remains model-dependent.
|
|
130
133
|
|
|
131
134
|
`upstreamTimeoutSeconds` controls the maximum time a single Claude request can
|
|
132
135
|
spend waiting on upstream Copilot calls, including chat, Responses, preflight,
|
|
@@ -212,7 +215,10 @@ health probe, so a relay that cannot answer `/healthz` never reports success.
|
|
|
212
215
|
|
|
213
216
|
## Logging
|
|
214
217
|
|
|
215
|
-
At `
|
|
218
|
+
At `info`, every model request logs the requested model, upstream model, requested
|
|
219
|
+
think effort, requested thinking, and effective think effort. Missing request
|
|
220
|
+
effort is shown as `unset`, distinct from explicit `none`; full normal request
|
|
221
|
+
payloads remain at `debug`.
|
|
216
222
|
|
|
217
223
|
Upstream failures are logged at `error` with full request and response context in the same log file.
|
|
218
224
|
|
package/config.default.yaml
CHANGED
|
@@ -26,10 +26,9 @@ claudeSetup: true
|
|
|
26
26
|
|
|
27
27
|
# Log verbosity:
|
|
28
28
|
# error - startup/preflight/request failures only
|
|
29
|
-
# info - error logs plus startup status,
|
|
30
|
-
# status codes
|
|
31
|
-
# debug - info logs plus
|
|
32
|
-
# and request payloads
|
|
29
|
+
# info - error logs plus startup/preflight status, model/effort summaries,
|
|
30
|
+
# and local HTTP status codes
|
|
31
|
+
# debug - info logs plus detailed Copilot timings and request payloads
|
|
33
32
|
# Any other value is invalid and stops startup.
|
|
34
33
|
logLevel: info
|
|
35
34
|
|
|
@@ -41,7 +40,7 @@ logRetentionDays: 3
|
|
|
41
40
|
|
|
42
41
|
# Fallback upstream effort when Claude Code does not specify one.
|
|
43
42
|
# Request output_config.effort wins, followed by legacy reasoning_effort.
|
|
44
|
-
# Valid values:
|
|
43
|
+
# Valid values: low, medium, high, xhigh, max.
|
|
45
44
|
thinkEffort: max
|
|
46
45
|
|
|
47
46
|
# Max seconds to wait for one Claude request's upstream Copilot calls.
|
package/dist/main.js
CHANGED
|
@@ -541,12 +541,20 @@ const runtimeState = {};
|
|
|
541
541
|
|
|
542
542
|
//#endregion
|
|
543
543
|
//#region src/lib/models.ts
|
|
544
|
+
const configurableReasoningEfforts = [
|
|
545
|
+
"low",
|
|
546
|
+
"medium",
|
|
547
|
+
"high",
|
|
548
|
+
"xhigh",
|
|
549
|
+
"max"
|
|
550
|
+
];
|
|
544
551
|
const defaultReasoningEffort = "max";
|
|
545
552
|
const defaultModelRouting = {
|
|
546
553
|
gptModel: "gpt-6-astra",
|
|
547
554
|
opusModel: "claude-opus-5"
|
|
548
555
|
};
|
|
549
|
-
const
|
|
556
|
+
const isConfiguredReasoningEffort = (value) => configurableReasoningEfforts.some((effort) => effort === value);
|
|
557
|
+
const isReasoningEffort = (value) => value === "none" || isConfiguredReasoningEffort(value);
|
|
550
558
|
const invalidRequestEffort = (message) => new HTTPError(message, new Response(JSON.stringify({
|
|
551
559
|
type: "error",
|
|
552
560
|
error: {
|
|
@@ -736,10 +744,19 @@ const normalizeCopilotBaseUrl = (value) => {
|
|
|
736
744
|
if (parsed.username || parsed.password) throw new Error(copilotBaseUrlCredentialsMessage);
|
|
737
745
|
return trimmed;
|
|
738
746
|
};
|
|
747
|
+
var InvalidThinkEffortError = class extends Error {
|
|
748
|
+
constructor() {
|
|
749
|
+
super(`Invalid thinkEffort. Valid values: ${configurableReasoningEfforts.join(", ")}. "none" is not allowed as a configured default.`);
|
|
750
|
+
this.name = "InvalidThinkEffortError";
|
|
751
|
+
}
|
|
752
|
+
};
|
|
739
753
|
const normalizeThinkEffort = (value) => {
|
|
740
|
-
if (
|
|
741
|
-
|
|
742
|
-
|
|
754
|
+
if (value === void 0) return void 0;
|
|
755
|
+
if (typeof value === "string") {
|
|
756
|
+
const normalized = value.toLowerCase() === "minimal" ? "low" : value.toLowerCase();
|
|
757
|
+
if (isConfiguredReasoningEffort(normalized)) return normalized;
|
|
758
|
+
}
|
|
759
|
+
throw new InvalidThinkEffortError();
|
|
743
760
|
};
|
|
744
761
|
const normalizeUpstreamTimeoutSeconds = (value) => {
|
|
745
762
|
if (value === void 0) return;
|
|
@@ -858,16 +875,15 @@ const serializeConfig = (config) => [
|
|
|
858
875
|
"",
|
|
859
876
|
"# Log verbosity:",
|
|
860
877
|
"# error - startup/preflight/request failures only",
|
|
861
|
-
"# info - error logs plus startup status,
|
|
862
|
-
"# status codes",
|
|
863
|
-
"# debug - info logs plus
|
|
864
|
-
"# and request payloads",
|
|
878
|
+
"# info - error logs plus startup/preflight status, model/effort summaries,",
|
|
879
|
+
"# and local HTTP status codes",
|
|
880
|
+
"# debug - info logs plus detailed Copilot timings and request payloads",
|
|
865
881
|
`logLevel: ${config.logLevel}`,
|
|
866
882
|
"",
|
|
867
883
|
"# Number of days to keep files in ~/.copilot-relay/logs.",
|
|
868
884
|
`logRetentionDays: ${config.logRetentionDays}`,
|
|
869
885
|
"",
|
|
870
|
-
|
|
886
|
+
`# Fallback effort when the request omits it: ${configurableReasoningEfforts.join(", ")}.`,
|
|
871
887
|
`thinkEffort: ${config.thinkEffort}`,
|
|
872
888
|
"",
|
|
873
889
|
"# Max seconds for one request's upstream calls; 0 disables the relay deadline.",
|
|
@@ -926,7 +942,10 @@ const watchAppConfig = (onReload) => {
|
|
|
926
942
|
if (nextMtime === lastMtime) return;
|
|
927
943
|
lastMtime = nextMtime;
|
|
928
944
|
onReload(await readAppConfig());
|
|
929
|
-
} catch {
|
|
945
|
+
} catch (error) {
|
|
946
|
+
if (error instanceof InvalidThinkEffortError) log.error(`${error.message} Keeping the previous runtime settings.`);
|
|
947
|
+
else log.error(`Could not reload config. Check ${paths.configPath} for invalid values or file errors.`);
|
|
948
|
+
}
|
|
930
949
|
}, 1e3);
|
|
931
950
|
if (typeof timer.unref === "function") timer.unref();
|
|
932
951
|
getConfigMtime().then((mtime) => {
|
|
@@ -2081,7 +2100,7 @@ const messagesIncludeImage = (messages) => messages.some((msg) => typeof msg.con
|
|
|
2081
2100
|
const createChatCompletions = async (config, payload, options = {}) => {
|
|
2082
2101
|
const client = options.client ?? "generic";
|
|
2083
2102
|
const requestedModel = options.requestedModel ?? payload.model;
|
|
2084
|
-
const requestedThinkEffort = options.requestedThinkEffort ?? getRequestReasoningEffort(payload) ?? "
|
|
2103
|
+
const requestedThinkEffort = options.requestedThinkEffort ?? getRequestReasoningEffort(payload) ?? "unset";
|
|
2085
2104
|
const reasoningEffort = getRequestedReasoningEffort(payload);
|
|
2086
2105
|
const requestedThinking = options.requestedThinking ?? "none";
|
|
2087
2106
|
const signal = createCopilotRequestSignal(options.signal, options.timeoutMs);
|
|
@@ -2103,7 +2122,7 @@ const createChatCompletions = async (config, payload, options = {}) => {
|
|
|
2103
2122
|
const enableVision = messagesIncludeImage(compatiblePayload.messages);
|
|
2104
2123
|
const initiator = isAgentInitiator(compatiblePayload.messages);
|
|
2105
2124
|
const requestPayload = buildRequestPayload(compatiblePayload);
|
|
2106
|
-
log.
|
|
2125
|
+
log.info(sanitizeTerminalString([
|
|
2107
2126
|
"Model request",
|
|
2108
2127
|
`client=${client}`,
|
|
2109
2128
|
`requested_model=${requestedModel}`,
|
|
@@ -2111,8 +2130,8 @@ const createChatCompletions = async (config, payload, options = {}) => {
|
|
|
2111
2130
|
`upstream_model=${compatiblePayload.model}`,
|
|
2112
2131
|
`requested_think_effort=${requestedThinkEffort}`,
|
|
2113
2132
|
`requested_thinking=${requestedThinking}`,
|
|
2114
|
-
`effective_think_effort=${requestPayload.reasoning_effort ?? "
|
|
2115
|
-
].join(" "));
|
|
2133
|
+
`effective_think_effort=${requestPayload.reasoning_effort ?? "unset"}`
|
|
2134
|
+
].join(" ")));
|
|
2116
2135
|
log.debug("Full request payload", { payload: requestPayload });
|
|
2117
2136
|
if (useResponsesApi) return completeResponse(await createResponses(provider, compatiblePayload, {
|
|
2118
2137
|
vision: enableVision,
|
|
@@ -3216,7 +3235,7 @@ const createTokenCountModel = (modelId, tokenizer = "o200k_base") => ({
|
|
|
3216
3235
|
id: modelId
|
|
3217
3236
|
});
|
|
3218
3237
|
const isNonStreamingResponse = (response) => typeof response === "object" && response !== null && Object.hasOwn(response, "choices");
|
|
3219
|
-
const getClaudeRequestedThinkEffort = (payload) => getRequestReasoningEffort(payload) ?? "
|
|
3238
|
+
const getClaudeRequestedThinkEffort = (payload) => getRequestReasoningEffort(payload) ?? "unset";
|
|
3220
3239
|
const getClaudeRequestedThinking = (payload) => {
|
|
3221
3240
|
if (!payload.thinking) return "none";
|
|
3222
3241
|
return [`type:${payload.thinking.type}`, `budget:${payload.thinking.budget_tokens ?? "none"}`].join(",");
|
|
@@ -3645,6 +3664,10 @@ const startServer = (config) => new Promise((resolve$1, reject) => {
|
|
|
3645
3664
|
const canCloseConnections = (server) => typeof server.closeIdleConnections === "function" && typeof server.closeAllConnections === "function";
|
|
3646
3665
|
async function startRelay(appConfig) {
|
|
3647
3666
|
appConfig ??= await readAppConfig();
|
|
3667
|
+
appConfig = {
|
|
3668
|
+
...appConfig,
|
|
3669
|
+
thinkEffort: normalizeThinkEffort(appConfig.thinkEffort) ?? defaultReasoningEffort
|
|
3670
|
+
};
|
|
3648
3671
|
setLogLevel(appConfig.logLevel);
|
|
3649
3672
|
await cleanupLogs(appConfig.logRetentionDays);
|
|
3650
3673
|
const claudeConfigPath$1 = claudeConfigPath;
|