betahi-copilot-bridge 0.1.8 → 0.20.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/README.md +19 -16
- package/dist/main.js +151 -42
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">copilot-bridge</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<a href="https://www.npmjs.com/package/betahi-copilot-bridge"><img src="https://img.shields.io/npm/v/betahi-copilot-bridge.svg?v=0.
|
|
4
|
+
<a href="https://www.npmjs.com/package/betahi-copilot-bridge"><img src="https://img.shields.io/npm/v/betahi-copilot-bridge.svg?v=0.20.0" alt="npm version"></a>
|
|
5
5
|
<a href="https://github.com/betahi/copilot-bridge/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/betahi-copilot-bridge.svg" alt="license"></a>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
@@ -83,6 +83,7 @@ this block; the bridge regenerates it on every start.
|
|
|
83
83
|
```toml
|
|
84
84
|
# >>> copilot-bridge managed block — auto-generated, do not edit between markers >>>
|
|
85
85
|
model_provider = "bridge"
|
|
86
|
+
model_supports_reasoning_summaries = true
|
|
86
87
|
|
|
87
88
|
[model_providers.bridge]
|
|
88
89
|
name = "Copilot Bridge"
|
|
@@ -102,11 +103,6 @@ model = "gpt-5.3-codex"
|
|
|
102
103
|
model_reasoning_effort = "high"
|
|
103
104
|
```
|
|
104
105
|
|
|
105
|
-
If `model_reasoning_effort` is omitted, the bridge leaves it unset and does not
|
|
106
|
-
send a reasoning effort for Codex requests (a startup info log also reminds you).
|
|
107
|
-
|
|
108
|
-
`model_reasoning_effort` only affects Codex requests; it does not affect Claude.
|
|
109
|
-
|
|
110
106
|
That's it — `codex exec '...'` will now route through the bridge to Copilot.
|
|
111
107
|
|
|
112
108
|
Use `--no-codex-setup` to skip the managed-block writer entirely (e.g. if you
|
|
@@ -194,16 +190,23 @@ accepts upstream.
|
|
|
194
190
|
|
|
195
191
|
### Claude family — translated to chat completions
|
|
196
192
|
|
|
197
|
-
| Model
|
|
198
|
-
|
|
|
199
|
-
| `claude-opus-4.7`
|
|
200
|
-
| `claude-opus-4.
|
|
201
|
-
| `claude-opus-4.
|
|
202
|
-
| `claude-
|
|
203
|
-
| `claude-opus-4.
|
|
204
|
-
| `claude-
|
|
205
|
-
| `claude-sonnet-4`
|
|
206
|
-
| `claude-
|
|
193
|
+
| Model | Reasoning efforts | Notes |
|
|
194
|
+
| -------------------------------- | --------------------------------------- | -------------------------------------- |
|
|
195
|
+
| `claude-opus-4.7` | `medium` | Effort sent as `output_config.effort`. |
|
|
196
|
+
| `claude-opus-4.7-1m` | `low`, `medium`, `high`, `xhigh` | 1M-token context window; effort sent as `output_config.effort`. |
|
|
197
|
+
| `claude-opus-4.7-high` | `high` | Fixed high reasoning; effort sent as `output_config.effort`. |
|
|
198
|
+
| `claude-opus-4.7-xhigh` | `xhigh` | Fixed extra-high reasoning; effort sent as `output_config.effort`. |
|
|
199
|
+
| `claude-opus-4.6` | `low`, `medium`, `high` | |
|
|
200
|
+
| `claude-opus-4.6-1m` | `low`, `medium`, `high` | 1M-token context window. |
|
|
201
|
+
| `claude-sonnet-4.6` | `low`, `medium`, `high` | |
|
|
202
|
+
| `claude-opus-4.5` | — | Reasoning not accepted upstream. |
|
|
203
|
+
| `claude-sonnet-4.5` | — | Reasoning not accepted upstream. |
|
|
204
|
+
| `claude-sonnet-4` | — | Reasoning not accepted upstream. |
|
|
205
|
+
| `claude-haiku-4.5` | — | Reasoning not accepted upstream. |
|
|
206
|
+
|
|
207
|
+
For Claude Opus 4.7, both Codex CLI and Claude Code can use
|
|
208
|
+
`claude-opus-4.7` with reasoning effort `high` or `xhigh`; the bridge routes the
|
|
209
|
+
request to the matching upstream reasoning variant.
|
|
207
210
|
|
|
208
211
|
### Gemini family — translated to chat completions
|
|
209
212
|
|
package/dist/main.js
CHANGED
|
@@ -439,6 +439,18 @@ const BEGIN_MARK = "# >>> copilot-bridge managed block — auto-generated, do no
|
|
|
439
439
|
const END_MARK = "# <<< copilot-bridge managed block — edits outside this block are preserved <<<";
|
|
440
440
|
const LEGACY_BEGIN_MARK = "# >>> copilot-bridge managed (do not edit) >>>";
|
|
441
441
|
const LEGACY_END_MARK = "# <<< copilot-bridge managed (do not edit) <<<";
|
|
442
|
+
function normalizeCodexConfigReasoningEffort(value) {
|
|
443
|
+
switch (value?.toLowerCase()) {
|
|
444
|
+
case "none":
|
|
445
|
+
case "minimal":
|
|
446
|
+
case "low":
|
|
447
|
+
case "medium":
|
|
448
|
+
case "high":
|
|
449
|
+
case "xhigh": return value.toLowerCase();
|
|
450
|
+
case "max": return "xhigh";
|
|
451
|
+
default: return;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
442
454
|
function tomlEscape(value) {
|
|
443
455
|
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
444
456
|
}
|
|
@@ -447,6 +459,7 @@ function buildManagedBlock(input) {
|
|
|
447
459
|
const lines = [];
|
|
448
460
|
lines.push(BEGIN_MARK);
|
|
449
461
|
if (settings.setAsDefault) lines.push(`model_provider = "${tomlEscape(settings.providerId)}"`);
|
|
462
|
+
lines.push("model_supports_reasoning_summaries = true");
|
|
450
463
|
lines.push("");
|
|
451
464
|
lines.push(`[model_providers.${settings.providerId}]`);
|
|
452
465
|
lines.push(`name = "${tomlEscape(settings.providerName)}"`);
|
|
@@ -502,11 +515,25 @@ function setTopScalar(topSection, key, value) {
|
|
|
502
515
|
if (topSection.length === 0) return `${line}\n`;
|
|
503
516
|
return `${line}\n${topSection.startsWith("\n") ? "" : ""}${topSection}`;
|
|
504
517
|
}
|
|
518
|
+
function removeTopKey(topSection, key) {
|
|
519
|
+
const lines = topSection.split("\n");
|
|
520
|
+
const re = /* @__PURE__ */ new RegExp(`^\\s*${key}\\s*=`);
|
|
521
|
+
return lines.filter((line) => !re.test(line)).join("\n");
|
|
522
|
+
}
|
|
523
|
+
function sanitizeTopReasoningEffort(topSection) {
|
|
524
|
+
const match = topSection.match(scalarRegex("model_reasoning_effort"));
|
|
525
|
+
if (!match) return topSection;
|
|
526
|
+
const normalized = normalizeCodexConfigReasoningEffort(match[1]);
|
|
527
|
+
if (!normalized) return removeTopKey(topSection, "model_reasoning_effort");
|
|
528
|
+
if (normalized === match[1]) return topSection;
|
|
529
|
+
return topSection.replace(scalarRegex("model_reasoning_effort"), `model_reasoning_effort = "${tomlEscape(normalized)}"`);
|
|
530
|
+
}
|
|
505
531
|
function applyUserScalars(content, input) {
|
|
506
532
|
const { top, rest } = splitTopSection(content);
|
|
507
|
-
let nextTop = top;
|
|
533
|
+
let nextTop = removeTopKey(top, "model_supports_reasoning_summaries");
|
|
534
|
+
nextTop = sanitizeTopReasoningEffort(nextTop);
|
|
508
535
|
nextTop = setTopScalar(nextTop, "model", input.model);
|
|
509
|
-
nextTop = setTopScalar(nextTop, "model_reasoning_effort", input.modelReasoningEffort);
|
|
536
|
+
nextTop = setTopScalar(nextTop, "model_reasoning_effort", normalizeCodexConfigReasoningEffort(input.modelReasoningEffort));
|
|
510
537
|
if (nextTop === top) return content;
|
|
511
538
|
if (rest.length === 0) return nextTop.endsWith("\n") ? nextTop : `${nextTop}\n`;
|
|
512
539
|
const sep = nextTop.endsWith("\n") ? "" : "\n";
|
|
@@ -649,21 +676,43 @@ const MODEL_CAPABILITIES = [
|
|
|
649
676
|
default: "medium"
|
|
650
677
|
}
|
|
651
678
|
},
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
679
|
+
...[
|
|
680
|
+
{
|
|
681
|
+
id: "claude-opus-4.7",
|
|
682
|
+
supported: ["medium"],
|
|
683
|
+
defaultEffort: "medium"
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
id: "claude-opus-4.7-1m-internal",
|
|
687
|
+
aliases: ["claude-opus-4.7-1m"],
|
|
657
688
|
supported: [
|
|
658
689
|
"low",
|
|
659
690
|
"medium",
|
|
660
691
|
"high",
|
|
661
|
-
"xhigh"
|
|
662
|
-
"max"
|
|
692
|
+
"xhigh"
|
|
663
693
|
],
|
|
664
|
-
|
|
694
|
+
defaultEffort: "medium"
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
id: "claude-opus-4.7-high",
|
|
698
|
+
supported: ["high"],
|
|
699
|
+
defaultEffort: "high"
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
id: "claude-opus-4.7-xhigh",
|
|
703
|
+
supported: ["xhigh"],
|
|
704
|
+
defaultEffort: "xhigh"
|
|
665
705
|
}
|
|
666
|
-
}
|
|
706
|
+
].map(({ id, aliases, supported, defaultEffort }) => ({
|
|
707
|
+
id,
|
|
708
|
+
aliases,
|
|
709
|
+
fallback: "chat-completions",
|
|
710
|
+
reasoningField: "output_config.effort",
|
|
711
|
+
reasoning: {
|
|
712
|
+
supported,
|
|
713
|
+
default: defaultEffort
|
|
714
|
+
}
|
|
715
|
+
})),
|
|
667
716
|
{
|
|
668
717
|
id: "claude-opus-4.6",
|
|
669
718
|
fallback: "chat-completions",
|
|
@@ -1155,7 +1204,7 @@ const normalizeClaudeOpus47Effort = (value) => {
|
|
|
1155
1204
|
default: return;
|
|
1156
1205
|
}
|
|
1157
1206
|
};
|
|
1158
|
-
const getEnvValueCaseInsensitive = (env, key) => {
|
|
1207
|
+
const getEnvValueCaseInsensitive$1 = (env, key) => {
|
|
1159
1208
|
const direct = env[key];
|
|
1160
1209
|
if (typeof direct === "string") return direct;
|
|
1161
1210
|
const lower = key.toLowerCase();
|
|
@@ -1163,7 +1212,7 @@ const getEnvValueCaseInsensitive = (env, key) => {
|
|
|
1163
1212
|
};
|
|
1164
1213
|
const getConfiguredReasoningEffort = (client, claudeSettingsEnv) => {
|
|
1165
1214
|
if (client !== "claude") return;
|
|
1166
|
-
return process.env.MODEL_REASONING_EFFORT ?? getEnvValueCaseInsensitive(claudeSettingsEnv, "MODEL_REASONING_EFFORT");
|
|
1215
|
+
return process.env.MODEL_REASONING_EFFORT ?? getEnvValueCaseInsensitive$1(claudeSettingsEnv, "MODEL_REASONING_EFFORT");
|
|
1167
1216
|
};
|
|
1168
1217
|
const getRequestedReasoningEffort = (payload, claudeSettingsEnv, client) => {
|
|
1169
1218
|
const configuredReasoningEffort = getConfiguredReasoningEffort(client, claudeSettingsEnv);
|
|
@@ -1173,7 +1222,9 @@ const getRequestedReasoningEffort = (payload, claudeSettingsEnv, client) => {
|
|
|
1173
1222
|
const getRequestedClaudeOpus47Effort = (payload, claudeSettingsEnv, client) => {
|
|
1174
1223
|
if (!isClaudeOpus47Model$1(payload.model)) return;
|
|
1175
1224
|
const configuredReasoningEffort = getConfiguredReasoningEffort(client, claudeSettingsEnv);
|
|
1176
|
-
|
|
1225
|
+
const requested = normalizeClaudeOpus47Effort(payload.reasoning_effort) ?? normalizeClaudeOpus47Effort(configuredReasoningEffort) ?? payload.output_config?.effort;
|
|
1226
|
+
if (!requested) return;
|
|
1227
|
+
return clampReasoningEffort(payload.model, requested)?.effort;
|
|
1177
1228
|
};
|
|
1178
1229
|
const sanitizeUserIdentifier = (user) => {
|
|
1179
1230
|
if (!user) return;
|
|
@@ -1366,20 +1417,21 @@ embeddingRoutes.post("/", async (c) => {
|
|
|
1366
1417
|
const normalizeModelId = (modelId) => modelId.trim().toLowerCase().replaceAll(/[\s._-]+/g, "");
|
|
1367
1418
|
const stripSnapshotSuffix = (modelId) => {
|
|
1368
1419
|
const id = modelId.trim().toLowerCase().replace(/\[1m\]$/, "");
|
|
1369
|
-
|
|
1420
|
+
if (id === "claude-opus-4.7-1m") return "claude-opus-4.7-1m-internal";
|
|
1421
|
+
const claudeSnapshotMatch = id.match(/^claude-(opus|sonnet|haiku)-(\d)-(\d)((?:-[a-z0-9]+)*?)(?:-\d{8})?$/);
|
|
1370
1422
|
if (claudeSnapshotMatch) {
|
|
1371
|
-
const [, family, major, minor,
|
|
1372
|
-
return `claude-${family}-${major}.${minor}${
|
|
1423
|
+
const [, family, major, minor, suffix = ""] = claudeSnapshotMatch;
|
|
1424
|
+
return `claude-${family}-${major}.${minor}${suffix}`;
|
|
1373
1425
|
}
|
|
1374
1426
|
return id;
|
|
1375
1427
|
};
|
|
1376
1428
|
const getAliasCandidates = (modelId) => {
|
|
1377
1429
|
const canonicalModelId = stripSnapshotSuffix(modelId);
|
|
1378
1430
|
const aliases = new Set([canonicalModelId]);
|
|
1379
|
-
const canonicalClaudeMatch = canonicalModelId.match(/^claude-(opus|sonnet|haiku)-(\d+)\.(\d+)(-
|
|
1431
|
+
const canonicalClaudeMatch = canonicalModelId.match(/^claude-(opus|sonnet|haiku)-(\d+)\.(\d+)((?:-[a-z0-9]+)*)$/);
|
|
1380
1432
|
if (canonicalClaudeMatch) {
|
|
1381
|
-
const [, family, major, minor,
|
|
1382
|
-
aliases.add(`claude-${family}-${major}-${minor}${
|
|
1433
|
+
const [, family, major, minor, suffix = ""] = canonicalClaudeMatch;
|
|
1434
|
+
aliases.add(`claude-${family}-${major}-${minor}${suffix}`);
|
|
1383
1435
|
return [...aliases];
|
|
1384
1436
|
}
|
|
1385
1437
|
const familyMatch = canonicalModelId.match(/^[a-z]+(?:-[a-z]+)*-\d+(?:\.\d+)?/);
|
|
@@ -1526,12 +1578,42 @@ const normalizeClaudeModelAlias = (model) => {
|
|
|
1526
1578
|
const trimmed = model.trim().toLowerCase();
|
|
1527
1579
|
if (trimmed === "opus[1m]") return "claude-opus-4.6-1m";
|
|
1528
1580
|
const normalized = trimmed.replace(/\[1m\]$/, "");
|
|
1529
|
-
|
|
1530
|
-
if (
|
|
1531
|
-
if (
|
|
1532
|
-
if (
|
|
1533
|
-
|
|
1534
|
-
|
|
1581
|
+
const prefixed = normalized.startsWith("opus-") ? `claude-${normalized}` : normalized;
|
|
1582
|
+
if (prefixed === "opus") return "claude-opus";
|
|
1583
|
+
if (prefixed === "sonnet") return "claude-sonnet";
|
|
1584
|
+
if (prefixed === "haiku") return "claude-haiku";
|
|
1585
|
+
const claudeSnapshotMatch = prefixed.match(/^claude-(opus|sonnet|haiku)-(\d)-(\d)((?:-[a-z0-9]+)*?)(?:-\d{8})?$/);
|
|
1586
|
+
if (claudeSnapshotMatch) {
|
|
1587
|
+
const [, family, major, minor, suffix = ""] = claudeSnapshotMatch;
|
|
1588
|
+
return `claude-${family}-${major}.${minor}${suffix}`;
|
|
1589
|
+
}
|
|
1590
|
+
return prefixed;
|
|
1591
|
+
};
|
|
1592
|
+
function normalizeClaudeReasoningEffortForRouting(value) {
|
|
1593
|
+
switch (value?.toLowerCase()) {
|
|
1594
|
+
case "low":
|
|
1595
|
+
case "medium":
|
|
1596
|
+
case "high":
|
|
1597
|
+
case "xhigh":
|
|
1598
|
+
case "max": return value.toLowerCase();
|
|
1599
|
+
default: return;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
const getEnvValueCaseInsensitive = (env, key) => {
|
|
1603
|
+
const direct = env[key];
|
|
1604
|
+
if (typeof direct === "string") return direct;
|
|
1605
|
+
const lower = key.toLowerCase();
|
|
1606
|
+
return Object.entries(env).find(([k]) => k.toLowerCase() === lower)?.[1];
|
|
1607
|
+
};
|
|
1608
|
+
const getConfiguredClaudeReasoningEffort = (settings) => process.env.MODEL_REASONING_EFFORT ?? getEnvValueCaseInsensitive(settings?.env ?? {}, "MODEL_REASONING_EFFORT");
|
|
1609
|
+
const routeClaudeOpus47ByEffort = (model, requestedEffort) => {
|
|
1610
|
+
if (model !== "claude-opus-4.7") return model;
|
|
1611
|
+
switch (normalizeClaudeReasoningEffortForRouting(requestedEffort)) {
|
|
1612
|
+
case "high": return "claude-opus-4.7-high";
|
|
1613
|
+
case "xhigh":
|
|
1614
|
+
case "max": return "claude-opus-4.7-xhigh";
|
|
1615
|
+
default: return model;
|
|
1616
|
+
}
|
|
1535
1617
|
};
|
|
1536
1618
|
const getConfiguredClaudeDefaultModel = (settings) => {
|
|
1537
1619
|
if (!settings) return;
|
|
@@ -1551,8 +1633,8 @@ const resolveClaudeRequestedModel = (model, settings) => {
|
|
|
1551
1633
|
if (model.trim().toLowerCase() !== "definitely-not-a-real-model") return model;
|
|
1552
1634
|
return getConfiguredClaudeDefaultModel(settings) ?? model;
|
|
1553
1635
|
};
|
|
1554
|
-
function translateModelName(model, settings) {
|
|
1555
|
-
return resolveUpstreamModelId(normalizeClaudeModelAlias(resolveClaudeRequestedModel(model, settings)));
|
|
1636
|
+
function translateModelName(model, settings, requestedReasoningEffort) {
|
|
1637
|
+
return resolveUpstreamModelId(routeClaudeOpus47ByEffort(normalizeClaudeModelAlias(resolveClaudeRequestedModel(model, settings)), requestedReasoningEffort ?? getConfiguredClaudeReasoningEffort(settings)));
|
|
1556
1638
|
}
|
|
1557
1639
|
function isClaudeModel(modelId) {
|
|
1558
1640
|
return modelId.startsWith("claude-");
|
|
@@ -1582,12 +1664,12 @@ function getClaudeOpus47Effort(payload) {
|
|
|
1582
1664
|
return "xhigh";
|
|
1583
1665
|
}
|
|
1584
1666
|
function translateThinking(payload, settings) {
|
|
1585
|
-
if (!isClaudeOpus47Model(translateModelName(payload.model, settings))) return;
|
|
1667
|
+
if (!isClaudeOpus47Model(translateModelName(payload.model, settings, payload.reasoning_effort))) return;
|
|
1586
1668
|
if (payload.thinking?.type === "adaptive") return { type: "adaptive" };
|
|
1587
1669
|
return payload.thinking?.type === "enabled" ? { type: "adaptive" } : void 0;
|
|
1588
1670
|
}
|
|
1589
1671
|
function translateOutputConfig(payload, settings) {
|
|
1590
|
-
if (!isClaudeOpus47Model(translateModelName(payload.model, settings))) return;
|
|
1672
|
+
if (!isClaudeOpus47Model(translateModelName(payload.model, settings, payload.reasoning_effort))) return;
|
|
1591
1673
|
const effort = getClaudeOpus47Effort(payload);
|
|
1592
1674
|
return effort ? { effort } : void 0;
|
|
1593
1675
|
}
|
|
@@ -1603,7 +1685,7 @@ function normalizeReasoningEffort(value) {
|
|
|
1603
1685
|
}
|
|
1604
1686
|
}
|
|
1605
1687
|
function translateReasoningEffort(payload, settings) {
|
|
1606
|
-
const modelId = translateModelName(payload.model, settings);
|
|
1688
|
+
const modelId = translateModelName(payload.model, settings, payload.reasoning_effort);
|
|
1607
1689
|
if (isClaudeOpus47Model(modelId)) return;
|
|
1608
1690
|
if (payload.reasoning_effort) return sanitizeReasoningEffortForModel(modelId, normalizeReasoningEffort(payload.reasoning_effort));
|
|
1609
1691
|
if (isClaudeModel(modelId)) return;
|
|
@@ -1616,7 +1698,7 @@ function translateReasoningEffort(payload, settings) {
|
|
|
1616
1698
|
return sanitizeReasoningEffortForModel(modelId, "xhigh");
|
|
1617
1699
|
}
|
|
1618
1700
|
function translateToOpenAI(payload, settings, toolNameMapper) {
|
|
1619
|
-
const model = translateModelName(payload.model, settings);
|
|
1701
|
+
const model = translateModelName(payload.model, settings, payload.reasoning_effort);
|
|
1620
1702
|
const mapper = toolNameMapper ?? createAnthropicToolNameMapper(payload.tools, { ...getToolNameMapperOptionsForModel(model) });
|
|
1621
1703
|
const tools = translateAnthropicToolsToOpenAI(payload.tools, mapper);
|
|
1622
1704
|
return {
|
|
@@ -2532,9 +2614,19 @@ const removeReasoningEffort = (reasoning) => {
|
|
|
2532
2614
|
return Object.keys(next).length > 0 ? next : void 0;
|
|
2533
2615
|
};
|
|
2534
2616
|
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2535
|
-
const
|
|
2617
|
+
const routeClaudeOpus47ByReasoningEffort = (model, effort) => {
|
|
2618
|
+
if (model !== "claude-opus-4.7") return model;
|
|
2619
|
+
switch (typeof effort === "string" ? effort.toLowerCase() : void 0) {
|
|
2620
|
+
case "high": return "claude-opus-4.7-high";
|
|
2621
|
+
case "xhigh":
|
|
2622
|
+
case "max": return "claude-opus-4.7-xhigh";
|
|
2623
|
+
default: return model;
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
const normalizeCodexResponsesRequest = (payload, configuredReasoningEffort) => {
|
|
2536
2627
|
const parsed = codexResponsesRequestSchema.parse(payload);
|
|
2537
|
-
const
|
|
2628
|
+
const requestedReasoningEffort = (isPlainObject(parsed.reasoning) ? parsed.reasoning : void 0)?.effort ?? configuredReasoningEffort;
|
|
2629
|
+
const canonical = routeClaudeOpus47ByReasoningEffort(resolveModelId(parsed.model), requestedReasoningEffort);
|
|
2538
2630
|
const capability = getModelCapability(canonical);
|
|
2539
2631
|
if (!capability) return parsed;
|
|
2540
2632
|
const next = {
|
|
@@ -2545,21 +2637,30 @@ const normalizeCodexResponsesRequest = (payload) => {
|
|
|
2545
2637
|
if ("reasoning" in next) delete next.reasoning;
|
|
2546
2638
|
return next;
|
|
2547
2639
|
}
|
|
2548
|
-
if ("reasoning" in next) if (!isPlainObject(next.reasoning)) delete next.reasoning;
|
|
2640
|
+
if ("reasoning" in next) if (!isPlainObject(next.reasoning)) if (configuredReasoningEffort === void 0 || configuredReasoningEffort === null) delete next.reasoning;
|
|
2641
|
+
else {
|
|
2642
|
+
const clamped = clampReasoningEffort(canonical, configuredReasoningEffort);
|
|
2643
|
+
if (clamped) next.reasoning = { effort: clamped.effort };
|
|
2644
|
+
}
|
|
2549
2645
|
else {
|
|
2550
2646
|
const incoming = next.reasoning;
|
|
2551
|
-
|
|
2647
|
+
const effort = incoming.effort ?? configuredReasoningEffort;
|
|
2648
|
+
if (effort === void 0 || effort === null) {
|
|
2552
2649
|
const reasoning = removeReasoningEffort(incoming);
|
|
2553
2650
|
if (reasoning) next.reasoning = reasoning;
|
|
2554
2651
|
else delete next.reasoning;
|
|
2555
2652
|
} else {
|
|
2556
|
-
const clamped = clampReasoningEffort(canonical,
|
|
2653
|
+
const clamped = clampReasoningEffort(canonical, effort);
|
|
2557
2654
|
if (clamped) next.reasoning = {
|
|
2558
2655
|
...incoming,
|
|
2559
2656
|
effort: clamped.effort
|
|
2560
2657
|
};
|
|
2561
2658
|
}
|
|
2562
2659
|
}
|
|
2660
|
+
else if (configuredReasoningEffort !== void 0 && configuredReasoningEffort !== null) {
|
|
2661
|
+
const clamped = clampReasoningEffort(canonical, configuredReasoningEffort);
|
|
2662
|
+
if (clamped) next.reasoning = { effort: clamped.effort };
|
|
2663
|
+
}
|
|
2563
2664
|
const text = isPlainObject(next.text) ? next.text : void 0;
|
|
2564
2665
|
if (text && capability.textVerbosity && "verbosity" in text) {
|
|
2565
2666
|
const { supported, default: defaultVerbosity } = capability.textVerbosity;
|
|
@@ -2597,6 +2698,9 @@ const logResponsesUpstreamError = async (message, response, context) => {
|
|
|
2597
2698
|
request: JSON.stringify(summarizeResponsesRequestForDiagnostics(context.request))
|
|
2598
2699
|
});
|
|
2599
2700
|
};
|
|
2701
|
+
const readConfiguredCodexReasoningEffort = async () => {
|
|
2702
|
+
return normalizeCodexConfigReasoningEffort((await readCodexUserConfigFromDisk(process.env.CODEX_CONFIG_PATH ?? CODEX_DEFAULTS.configPath)).modelReasoningEffort);
|
|
2703
|
+
};
|
|
2600
2704
|
responsesRoutes.post("/", async (c) => {
|
|
2601
2705
|
try {
|
|
2602
2706
|
await checkRateLimit();
|
|
@@ -2604,7 +2708,7 @@ responsesRoutes.post("/", async (c) => {
|
|
|
2604
2708
|
if (error instanceof RateLimitError) return c.json({ error: { message: error.message } }, 429);
|
|
2605
2709
|
throw error;
|
|
2606
2710
|
}
|
|
2607
|
-
const payload = normalizeCodexResponsesRequest(await c.req.json());
|
|
2711
|
+
const payload = normalizeCodexResponsesRequest(await c.req.json(), await readConfiguredCodexReasoningEffort());
|
|
2608
2712
|
const provider = getCopilotProviderContext(c.get("config"));
|
|
2609
2713
|
const search = new URL(c.req.url).search;
|
|
2610
2714
|
const capability = getModelCapability(payload.model);
|
|
@@ -2758,6 +2862,8 @@ const startServer = (config) => serve({
|
|
|
2758
2862
|
|
|
2759
2863
|
//#endregion
|
|
2760
2864
|
//#region src/start.ts
|
|
2865
|
+
const getPublicModelId = (id) => id === "claude-opus-4.7-1m-internal" ? "claude-opus-4.7-1m" : id;
|
|
2866
|
+
const unique = (ids) => [...new Set(ids)];
|
|
2761
2867
|
const fetchAvailableModels = async (config) => {
|
|
2762
2868
|
try {
|
|
2763
2869
|
const response = await fetchCopilot(getCopilotProviderContext(config), "/models", {
|
|
@@ -2897,10 +3003,13 @@ const start = defineCommand({
|
|
|
2897
3003
|
consola.warn(`Could not update codex config (${codexConfigPath}):`, error);
|
|
2898
3004
|
}
|
|
2899
3005
|
const models = await fetchAvailableModels(config);
|
|
2900
|
-
const supportedIds = new Set(MODEL_CAPABILITIES.
|
|
2901
|
-
const
|
|
2902
|
-
const
|
|
2903
|
-
|
|
3006
|
+
const supportedIds = new Set(MODEL_CAPABILITIES.flatMap((m) => [m.id, ...m.aliases ?? []]));
|
|
3007
|
+
const fallbackModelIds = unique(MODEL_CAPABILITIES.map((m) => getPublicModelId(m.id)));
|
|
3008
|
+
const pickable = models.length > 0 ? unique(models.filter((id) => supportedIds.has(id)).map((id) => getPublicModelId(id))) : fallbackModelIds;
|
|
3009
|
+
const finalPickable = pickable.length > 0 ? pickable : fallbackModelIds;
|
|
3010
|
+
if (models.length > 0) consola.info(`Available models:\n${models.map((id) => {
|
|
3011
|
+
return `- ${getPublicModelId(id)}${supportedIds.has(id) ? " (bridge-supported)" : ""}`;
|
|
3012
|
+
}).join("\n")}`);
|
|
2904
3013
|
else consola.warn("Could not fetch model list from upstream Copilot API");
|
|
2905
3014
|
consola.box([
|
|
2906
3015
|
`🌐 Usage viewer`,
|