pennyrouter 0.2.26 → 0.2.27
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/package.json +1 -1
- package/src/harnesses/claude-code.js +20 -5
- package/src/launch.js +6 -5
- package/src/launch.test.js +2 -1
- package/src/session.js +1 -1
package/package.json
CHANGED
|
@@ -17,15 +17,18 @@ export const CLAUDE_CODE_MODELS = {
|
|
|
17
17
|
sonnet: "claude-sonnet-5[1m]",
|
|
18
18
|
opus: "claude-opus-5[1m]",
|
|
19
19
|
fable: "claude-fable-5[1m]",
|
|
20
|
-
custom: "Penny Custom", // -> dynamic (PennyRouter cost-routing)
|
|
21
20
|
};
|
|
22
21
|
const CLAUDE_CODE_MODEL_PRESENTATION = {
|
|
23
|
-
haiku: { name: "Haiku 4.5
|
|
24
|
-
sonnet: { name: "Sonnet 5
|
|
25
|
-
opus: { name: "Opus 5
|
|
26
|
-
fable: { name: "Fable 5
|
|
22
|
+
haiku: { name: "Haiku 4.5", description: "Fast, economical Claude model · 200K context" },
|
|
23
|
+
sonnet: { name: "Sonnet 5", description: "Balanced Claude model · 1M context" },
|
|
24
|
+
opus: { name: "Opus 5", description: "Most capable Claude model · 1M context" },
|
|
25
|
+
fable: { name: "Fable 5", description: "Claude model · 1M context" },
|
|
27
26
|
};
|
|
28
27
|
const LEGACY_PINNED_MODEL_IDS = {
|
|
28
|
+
"Haiku 4.5": CLAUDE_CODE_MODELS.haiku,
|
|
29
|
+
"Sonnet 5": CLAUDE_CODE_MODELS.sonnet,
|
|
30
|
+
"Opus 5": CLAUDE_CODE_MODELS.opus,
|
|
31
|
+
"Fable 5": CLAUDE_CODE_MODELS.fable,
|
|
29
32
|
"Haiku 4.5 (PennyRouter)": CLAUDE_CODE_MODELS.haiku,
|
|
30
33
|
"Sonnet 5 (PennyRouter)": CLAUDE_CODE_MODELS.sonnet,
|
|
31
34
|
"Opus 5 (PennyRouter)": CLAUDE_CODE_MODELS.opus,
|
|
@@ -86,6 +89,7 @@ export const claudeCodeHarness = {
|
|
|
86
89
|
// Anthropic API key. Clear any stale key so it doesn't take precedence.
|
|
87
90
|
current.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
88
91
|
delete current.env.ANTHROPIC_API_KEY;
|
|
92
|
+
current.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
|
|
89
93
|
// Claude Code displays these env values directly in its model picker; the
|
|
90
94
|
// gateway decodes the friendly labels into bundle/profile overrides.
|
|
91
95
|
current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
|
|
@@ -93,6 +97,9 @@ export const claudeCodeHarness = {
|
|
|
93
97
|
current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
|
|
94
98
|
current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
|
|
95
99
|
writeClaudeCodeModelPresentation(current.env);
|
|
100
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION;
|
|
101
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
|
|
102
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
|
|
96
103
|
// This is global, not per-model. Leave it unset so Claude Code uses each native id's
|
|
97
104
|
// real context limit instead of incorrectly giving Haiku a 1M window.
|
|
98
105
|
delete current.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
|
|
@@ -138,11 +145,15 @@ export const claudeCodeHarness = {
|
|
|
138
145
|
current.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
139
146
|
// Single credential only: no stale api key, no separate Anthropic bearer.
|
|
140
147
|
delete current.env.ANTHROPIC_API_KEY;
|
|
148
|
+
current.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
|
|
141
149
|
current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
|
|
142
150
|
current.env.ANTHROPIC_DEFAULT_SONNET_MODEL = CLAUDE_CODE_MODELS.sonnet;
|
|
143
151
|
current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
|
|
144
152
|
current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
|
|
145
153
|
writeClaudeCodeModelPresentation(current.env);
|
|
154
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION;
|
|
155
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
|
|
156
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
|
|
146
157
|
// This is global, not per-model. Leave it unset so Claude Code uses each native id's
|
|
147
158
|
// real context limit instead of incorrectly giving Haiku a 1M window.
|
|
148
159
|
delete current.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
|
|
@@ -184,11 +195,15 @@ export const claudeCodeHarness = {
|
|
|
184
195
|
// Authorization bearer token is forwarded by the gateway to Anthropic.
|
|
185
196
|
current.env.ANTHROPIC_API_KEY = pennyKey;
|
|
186
197
|
current.env.ANTHROPIC_AUTH_TOKEN = anthropicAuthToken;
|
|
198
|
+
current.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
|
|
187
199
|
current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
|
|
188
200
|
current.env.ANTHROPIC_DEFAULT_SONNET_MODEL = CLAUDE_CODE_MODELS.sonnet;
|
|
189
201
|
current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
|
|
190
202
|
current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
|
|
191
203
|
writeClaudeCodeModelPresentation(current.env);
|
|
204
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION;
|
|
205
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
|
|
206
|
+
delete current.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
|
|
192
207
|
// This is global, not per-model. Leave it unset so Claude Code uses each native id's
|
|
193
208
|
// real context limit instead of incorrectly giving Haiku a 1M window.
|
|
194
209
|
delete current.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
|
package/src/launch.js
CHANGED
|
@@ -433,16 +433,17 @@ export function buildPennyInvocation(harness, args, config) {
|
|
|
433
433
|
const models = item.models || {};
|
|
434
434
|
env.ANTHROPIC_BASE_URL = config.gateway_base_url;
|
|
435
435
|
env.ANTHROPIC_AUTH_TOKEN = config.api_key;
|
|
436
|
+
// Ask Claude Code to discover the gateway's model catalog at startup. The gateway
|
|
437
|
+
// advertises Penny-facing ids in /v1/models; this is separate from inference routing.
|
|
438
|
+
env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
|
|
436
439
|
delete env.ANTHROPIC_API_KEY;
|
|
437
440
|
if (models.haiku) env.ANTHROPIC_DEFAULT_HAIKU_MODEL = upgradeClaudeCodeModelValue(models.haiku);
|
|
438
441
|
if (models.sonnet) env.ANTHROPIC_DEFAULT_SONNET_MODEL = upgradeClaudeCodeModelValue(models.sonnet);
|
|
439
442
|
if (models.opus) env.ANTHROPIC_DEFAULT_OPUS_MODEL = upgradeClaudeCodeModelValue(models.opus);
|
|
440
443
|
if (models.fable) env.ANTHROPIC_DEFAULT_FABLE_MODEL = upgradeClaudeCodeModelValue(models.fable);
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (models.custom_description) env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION = models.custom_description;
|
|
445
|
-
}
|
|
444
|
+
delete env.ANTHROPIC_CUSTOM_MODEL_OPTION;
|
|
445
|
+
delete env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME;
|
|
446
|
+
delete env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION;
|
|
446
447
|
writeClaudeCodeModelPresentation(env);
|
|
447
448
|
// Per-model limits must come from Claude Code's recognized native model ids. This
|
|
448
449
|
// global override would incorrectly give Haiku the 1M window.
|
package/src/launch.test.js
CHANGED
|
@@ -103,12 +103,13 @@ async function main() {
|
|
|
103
103
|
assert.ok(!claude.args.includes("--plugin-dir"));
|
|
104
104
|
assert.equal(claude.env.ANTHROPIC_AUTH_TOKEN, "pr-super-secret");
|
|
105
105
|
assert.equal(claude.env.ANTHROPIC_BASE_URL, "https://gateway.example/");
|
|
106
|
+
assert.equal(claude.env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY, "1");
|
|
106
107
|
// 1M-capable slots carry the `[1m]` suffix; Haiku has no 1M variant and stays bare.
|
|
107
108
|
// Per-slot suffixes, not the global CLAUDE_CODE_MAX_CONTEXT_TOKENS override, which
|
|
108
109
|
// would wrongly hand Haiku a 1M window too.
|
|
109
110
|
assert.equal(claude.env.ANTHROPIC_DEFAULT_OPUS_MODEL, "claude-opus-5[1m]");
|
|
110
111
|
assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL, "claude-haiku-4-5-20251001");
|
|
111
|
-
assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME, "Haiku 4.5
|
|
112
|
+
assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME, "Haiku 4.5");
|
|
112
113
|
assert.equal(claude.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION, "Fast, economical Claude model · 200K context");
|
|
113
114
|
assert.equal(claude.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS, undefined);
|
|
114
115
|
|
package/src/session.js
CHANGED
|
@@ -97,7 +97,7 @@ export async function probeClaudeCodeGateway({ gatewayBaseUrl, apiKey }) {
|
|
|
97
97
|
"anthropic-beta": "compact-2026-01-12",
|
|
98
98
|
},
|
|
99
99
|
body: JSON.stringify({
|
|
100
|
-
model: "Sonnet 5
|
|
100
|
+
model: "Sonnet 5",
|
|
101
101
|
max_tokens: 1,
|
|
102
102
|
stream: false,
|
|
103
103
|
messages: [{ role: "user", content: "Reply OK." }],
|