token-optimizer-opencode 1.0.8 → 1.0.14
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 +15 -23
- package/dist/continuity/matcher.d.ts +15 -0
- package/dist/continuity/matcher.d.ts.map +1 -1
- package/dist/continuity/matcher.js +30 -1
- package/dist/continuity/matcher.js.map +1 -1
- package/dist/continuity/restore.d.ts +8 -1
- package/dist/continuity/restore.d.ts.map +1 -1
- package/dist/continuity/restore.js +43 -1
- package/dist/continuity/restore.js.map +1 -1
- package/dist/continuity/resume-lean.d.ts +126 -0
- package/dist/continuity/resume-lean.d.ts.map +1 -0
- package/dist/continuity/resume-lean.js +437 -0
- package/dist/continuity/resume-lean.js.map +1 -0
- package/dist/dashboard/generator.d.ts.map +1 -1
- package/dist/dashboard/generator.js +232 -36
- package/dist/dashboard/generator.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -4
- package/dist/index.js.map +1 -1
- package/dist/nudges/fresh-session-nudge.d.ts +72 -0
- package/dist/nudges/fresh-session-nudge.d.ts.map +1 -0
- package/dist/nudges/fresh-session-nudge.js +190 -0
- package/dist/nudges/fresh-session-nudge.js.map +1 -0
- package/dist/nudges/verbosity-steer.d.ts +28 -0
- package/dist/nudges/verbosity-steer.d.ts.map +1 -0
- package/dist/nudges/verbosity-steer.js +61 -0
- package/dist/nudges/verbosity-steer.js.map +1 -0
- package/dist/pricing.d.ts +58 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +307 -0
- package/dist/pricing.js.map +1 -0
- package/dist/savings.baseline.test.d.ts +2 -0
- package/dist/savings.baseline.test.d.ts.map +1 -0
- package/dist/savings.baseline.test.js +100 -0
- package/dist/savings.baseline.test.js.map +1 -0
- package/dist/savings.d.ts +41 -3
- package/dist/savings.d.ts.map +1 -1
- package/dist/savings.js +296 -86
- package/dist/savings.js.map +1 -1
- package/dist/storage/trends.d.ts +61 -0
- package/dist/storage/trends.d.ts.map +1 -1
- package/dist/storage/trends.js +149 -0
- package/dist/storage/trends.js.map +1 -1
- package/dist/util/context-window.d.ts.map +1 -1
- package/dist/util/context-window.js +2 -1
- package/dist/util/context-window.js.map +1 -1
- package/dist/util/env.d.ts +2 -0
- package/dist/util/env.d.ts.map +1 -1
- package/dist/util/env.js +4 -0
- package/dist/util/env.js.map +1 -1
- package/package.json +1 -1
- package/dist/nudges/tool-call-warn.d.ts +0 -7
- package/dist/nudges/tool-call-warn.d.ts.map +0 -1
- package/dist/nudges/tool-call-warn.js +0 -20
- package/dist/nudges/tool-call-warn.js.map +0 -1
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal per-MTok rate card for OpenCode's realized-savings counterfactual.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode persists a pre-computed `cost_usd` per session (priced at its own
|
|
5
|
+
* model when recorded), so the ACTUAL arm needs no rate card. The
|
|
6
|
+
* COUNTERFACTUAL arm, however, reprices the same token VOLUME at a DIFFERENT
|
|
7
|
+
* (baseline) model mix, which requires a per-class rate card the stored cost
|
|
8
|
+
* cannot supply. This module is that card.
|
|
9
|
+
*
|
|
10
|
+
* Rates mirror openclaw/src/pricing.ts exactly (USD per token; verified
|
|
11
|
+
* May 30, 2026). cacheWrite = 5-minute-TTL rate (1.25x input); cacheWrite1h =
|
|
12
|
+
* 1-hour-TTL rate (2x input), only set for Claude models that support it.
|
|
13
|
+
*
|
|
14
|
+
* A "mix" is a {modelKey -> share} map (shares sum to ~1). price() blends the
|
|
15
|
+
* per-token cost across the mix; unpriced models fall back to a proxy rate.
|
|
16
|
+
*/
|
|
17
|
+
/** Default pricing (USD per token). Mirrors openclaw/src/pricing.ts. */
|
|
18
|
+
export const DEFAULT_PRICING = {
|
|
19
|
+
// Anthropic Claude (1M context for Fable/Opus/Sonnet as of March 13, 2026)
|
|
20
|
+
fable: { input: 10.0 / 1e6, output: 50.0 / 1e6, cacheRead: 1.0 / 1e6, cacheWrite: 12.5 / 1e6, cacheWrite1h: 20.0 / 1e6 },
|
|
21
|
+
opus: { input: 5.0 / 1e6, output: 25.0 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6, cacheWrite1h: 10.0 / 1e6 },
|
|
22
|
+
sonnet: { input: 3.0 / 1e6, output: 15.0 / 1e6, cacheRead: 0.3 / 1e6, cacheWrite: 3.75 / 1e6, cacheWrite1h: 6.0 / 1e6 },
|
|
23
|
+
haiku: { input: 1.0 / 1e6, output: 5.0 / 1e6, cacheRead: 0.1 / 1e6, cacheWrite: 1.25 / 1e6, cacheWrite1h: 2.0 / 1e6 },
|
|
24
|
+
// OpenAI GPT-5 family
|
|
25
|
+
"gpt-5.5-pro": { input: 30.0 / 1e6, output: 180.0 / 1e6, cacheRead: 30.0 / 1e6, cacheWrite: 0 },
|
|
26
|
+
"gpt-5.5": { input: 5.0 / 1e6, output: 30.0 / 1e6, cacheRead: 0.50 / 1e6, cacheWrite: 0 },
|
|
27
|
+
"gpt-5.4": { input: 2.5 / 1e6, output: 15.0 / 1e6, cacheRead: 0.25 / 1e6, cacheWrite: 0 },
|
|
28
|
+
"gpt-5.4-mini": { input: 0.75 / 1e6, output: 4.5 / 1e6, cacheRead: 0.075 / 1e6, cacheWrite: 0 },
|
|
29
|
+
"gpt-5.4-nano": { input: 0.20 / 1e6, output: 1.25 / 1e6, cacheRead: 0.02 / 1e6, cacheWrite: 0 },
|
|
30
|
+
"gpt-5.3-codex": { input: 1.75 / 1e6, output: 14.0 / 1e6, cacheRead: 0.175 / 1e6, cacheWrite: 0 },
|
|
31
|
+
"gpt-5.2-codex": { input: 1.75 / 1e6, output: 14.0 / 1e6, cacheRead: 0.175 / 1e6, cacheWrite: 0 },
|
|
32
|
+
"gpt-5.2": { input: 1.75 / 1e6, output: 14.0 / 1e6, cacheRead: 0.175 / 1e6, cacheWrite: 0 },
|
|
33
|
+
"gpt-5.1-codex-mini": { input: 0.25 / 1e6, output: 2.0 / 1e6, cacheRead: 0.025 / 1e6, cacheWrite: 0 },
|
|
34
|
+
"gpt-5.1-codex": { input: 1.25 / 1e6, output: 10.0 / 1e6, cacheRead: 0.125 / 1e6, cacheWrite: 0 },
|
|
35
|
+
"gpt-5.1": { input: 1.25 / 1e6, output: 10.0 / 1e6, cacheRead: 0.125 / 1e6, cacheWrite: 0 },
|
|
36
|
+
"gpt-5-codex": { input: 1.25 / 1e6, output: 10.0 / 1e6, cacheRead: 0.125 / 1e6, cacheWrite: 0 },
|
|
37
|
+
"gpt-5": { input: 1.25 / 1e6, output: 10.0 / 1e6, cacheRead: 0.125 / 1e6, cacheWrite: 0 },
|
|
38
|
+
"gpt-5-mini": { input: 0.25 / 1e6, output: 2.0 / 1e6, cacheRead: 0.025 / 1e6, cacheWrite: 0 },
|
|
39
|
+
"gpt-5-nano": { input: 0.05 / 1e6, output: 0.4 / 1e6, cacheRead: 0.005 / 1e6, cacheWrite: 0 },
|
|
40
|
+
// OpenAI GPT-4 family
|
|
41
|
+
"gpt-4.1": { input: 2.0 / 1e6, output: 8.0 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 0 },
|
|
42
|
+
"gpt-4.1-mini": { input: 0.4 / 1e6, output: 1.6 / 1e6, cacheRead: 0.1 / 1e6, cacheWrite: 0 },
|
|
43
|
+
"gpt-4.1-nano": { input: 0.1 / 1e6, output: 0.4 / 1e6, cacheRead: 0.025 / 1e6, cacheWrite: 0 },
|
|
44
|
+
"gpt-4o": { input: 2.5 / 1e6, output: 10.0 / 1e6, cacheRead: 1.25 / 1e6, cacheWrite: 0 },
|
|
45
|
+
"gpt-4o-mini": { input: 0.15 / 1e6, output: 0.6 / 1e6, cacheRead: 0.075 / 1e6, cacheWrite: 0 },
|
|
46
|
+
// OpenAI reasoning
|
|
47
|
+
"o3": { input: 2.0 / 1e6, output: 8.0 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 0 },
|
|
48
|
+
"o3-pro": { input: 20.0 / 1e6, output: 80.0 / 1e6, cacheRead: 5.0 / 1e6, cacheWrite: 0 },
|
|
49
|
+
"o3-mini": { input: 1.1 / 1e6, output: 4.4 / 1e6, cacheRead: 0.55 / 1e6, cacheWrite: 0 },
|
|
50
|
+
"o4-mini": { input: 1.10 / 1e6, output: 4.40 / 1e6, cacheRead: 0.275 / 1e6, cacheWrite: 0 },
|
|
51
|
+
// Google Gemini
|
|
52
|
+
"gemini-3.5-flash": { input: 1.5 / 1e6, output: 9.0 / 1e6, cacheRead: 0.15 / 1e6, cacheWrite: 0 },
|
|
53
|
+
"gemini-3.1-pro-preview": { input: 2.0 / 1e6, output: 12.0 / 1e6, cacheRead: 0.20 / 1e6, cacheWrite: 0 },
|
|
54
|
+
"gemini-3.1-flash-lite": { input: 0.25 / 1e6, output: 1.5 / 1e6, cacheRead: 0.025 / 1e6, cacheWrite: 0 },
|
|
55
|
+
"gemini-3-pro": { input: 2.0 / 1e6, output: 12.0 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
56
|
+
"gemini-3-flash": { input: 0.5 / 1e6, output: 3.0 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
57
|
+
"gemini-3.1-pro": { input: 2.0 / 1e6, output: 12.0 / 1e6, cacheRead: 0.20 / 1e6, cacheWrite: 0 },
|
|
58
|
+
"gemini-2.5-pro": { input: 1.25 / 1e6, output: 10.0 / 1e6, cacheRead: 0.125 / 1e6, cacheWrite: 0 },
|
|
59
|
+
"gemini-2.5-flash": { input: 0.3 / 1e6, output: 2.5 / 1e6, cacheRead: 0.03 / 1e6, cacheWrite: 0 },
|
|
60
|
+
"gemini-2.5-flash-lite": { input: 0.1 / 1e6, output: 0.4 / 1e6, cacheRead: 0.01 / 1e6, cacheWrite: 0 },
|
|
61
|
+
"gemini-2.0-flash": { input: 0.1 / 1e6, output: 0.4 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
62
|
+
"gemini-2.0-flash-lite": { input: 0.075 / 1e6, output: 0.3 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
63
|
+
"gemini-flash-lite": { input: 0.1 / 1e6, output: 0.4 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
64
|
+
// DeepSeek
|
|
65
|
+
"deepseek-v3": { input: 0.28 / 1e6, output: 0.42 / 1e6, cacheRead: 0.028 / 1e6, cacheWrite: 0 },
|
|
66
|
+
"deepseek-r1": { input: 0.55 / 1e6, output: 2.19 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
67
|
+
// Alibaba Qwen
|
|
68
|
+
"qwen3": { input: 0.30 / 1e6, output: 1.20 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
69
|
+
"qwen3-mini": { input: 0.08 / 1e6, output: 0.32 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
70
|
+
"qwen-coder": { input: 0.15 / 1e6, output: 0.60 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
71
|
+
// Moonshot Kimi
|
|
72
|
+
"kimi-k2.5": { input: 0.50 / 1e6, output: 2.00 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
73
|
+
// MiniMax
|
|
74
|
+
"minimax-2": { input: 0.30 / 1e6, output: 1.10 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
75
|
+
// Zhipu GLM
|
|
76
|
+
"glm-4.7": { input: 0.48 / 1e6, output: 0.96 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
77
|
+
"glm-4.7-flash": { input: 0.04 / 1e6, output: 0.04 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
78
|
+
// Xiaomi MiMo
|
|
79
|
+
"mimo-flash": { input: 0.20 / 1e6, output: 0.40 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
80
|
+
// Mistral
|
|
81
|
+
"mistral-large": { input: 0.5 / 1e6, output: 1.5 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
82
|
+
"mistral-small": { input: 0.10 / 1e6, output: 0.30 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
83
|
+
// xAI Grok
|
|
84
|
+
"grok-4": { input: 3.0 / 1e6, output: 15.0 / 1e6, cacheRead: 0, cacheWrite: 0 },
|
|
85
|
+
// Local models (free but track tokens)
|
|
86
|
+
"local": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
87
|
+
};
|
|
88
|
+
/** Price the proxy rate card uses when a model is unpriced. */
|
|
89
|
+
export const PROXY_MODEL = "sonnet";
|
|
90
|
+
const KNOWN_PROVIDER_PREFIXES = new Set([
|
|
91
|
+
"anthropic", "openai", "google", "gemini", "vertex", "bedrock",
|
|
92
|
+
"openrouter", "gateway", "litellm", "azure", "aws",
|
|
93
|
+
]);
|
|
94
|
+
function stripProviderPrefixes(modelId) {
|
|
95
|
+
let value = modelId.trim().toLowerCase();
|
|
96
|
+
while (true) {
|
|
97
|
+
const slash = value.indexOf("/");
|
|
98
|
+
const colon = value.indexOf(":");
|
|
99
|
+
if (slash === -1 && colon === -1)
|
|
100
|
+
return value;
|
|
101
|
+
const useSlash = slash !== -1 && (colon === -1 || slash < colon);
|
|
102
|
+
const idx = useSlash ? slash : colon;
|
|
103
|
+
const delimiter = useSlash ? "/" : ":";
|
|
104
|
+
const prefix = value.slice(0, idx);
|
|
105
|
+
const rest = value.slice(idx + 1);
|
|
106
|
+
if (!rest || !/[a-z]/.test(rest))
|
|
107
|
+
return value;
|
|
108
|
+
if (delimiter === "/" || KNOWN_PROVIDER_PREFIXES.has(prefix)) {
|
|
109
|
+
value = rest;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Normalize a model ID into a pricing key. Mirrors openclaw/src/pricing.ts.
|
|
117
|
+
* Handles provider prefixes (anthropic/claude-sonnet-4-6 -> sonnet) and version
|
|
118
|
+
* suffixes (gpt-5.2-2026-03 -> gpt-5.2). Returns lowercased raw on no match.
|
|
119
|
+
*/
|
|
120
|
+
export function normalizeModelName(modelId) {
|
|
121
|
+
if (!modelId || modelId.startsWith("<"))
|
|
122
|
+
return modelId || "unknown";
|
|
123
|
+
const m = stripProviderPrefixes(modelId);
|
|
124
|
+
if (m.includes("fable"))
|
|
125
|
+
return "fable";
|
|
126
|
+
if (m.includes("opus"))
|
|
127
|
+
return "opus";
|
|
128
|
+
if (m.includes("sonnet"))
|
|
129
|
+
return "sonnet";
|
|
130
|
+
if (m.includes("haiku"))
|
|
131
|
+
return "haiku";
|
|
132
|
+
if (m.includes("gpt-5.5-pro"))
|
|
133
|
+
return "gpt-5.5-pro";
|
|
134
|
+
if (m.includes("gpt-5.5"))
|
|
135
|
+
return "gpt-5.5";
|
|
136
|
+
if (m.includes("gpt-5.4") && m.includes("nano"))
|
|
137
|
+
return "gpt-5.4-nano";
|
|
138
|
+
if (m.includes("gpt-5.4") && m.includes("mini"))
|
|
139
|
+
return "gpt-5.4-mini";
|
|
140
|
+
if (m.includes("gpt-5.4"))
|
|
141
|
+
return "gpt-5.4";
|
|
142
|
+
if (m.includes("gpt-5.3") && m.includes("codex"))
|
|
143
|
+
return "gpt-5.3-codex";
|
|
144
|
+
if (m.includes("gpt-5.2") && m.includes("codex"))
|
|
145
|
+
return "gpt-5.2-codex";
|
|
146
|
+
if (m.includes("gpt-5.2"))
|
|
147
|
+
return "gpt-5.2";
|
|
148
|
+
if (m.includes("gpt-5.1") && m.includes("codex") && m.includes("mini"))
|
|
149
|
+
return "gpt-5.1-codex-mini";
|
|
150
|
+
if (m.includes("gpt-5.1") && m.includes("codex"))
|
|
151
|
+
return "gpt-5.1-codex";
|
|
152
|
+
if (m.includes("gpt-5.1"))
|
|
153
|
+
return "gpt-5.1";
|
|
154
|
+
if (m.includes("gpt-5") && m.includes("codex"))
|
|
155
|
+
return "gpt-5-codex";
|
|
156
|
+
if (m.includes("gpt-5") && m.includes("nano"))
|
|
157
|
+
return "gpt-5-nano";
|
|
158
|
+
if (m.includes("gpt-5") && m.includes("mini"))
|
|
159
|
+
return "gpt-5-mini";
|
|
160
|
+
if (m.includes("gpt-5"))
|
|
161
|
+
return "gpt-5";
|
|
162
|
+
if (m.includes("gpt-4.1") && m.includes("nano"))
|
|
163
|
+
return "gpt-4.1-nano";
|
|
164
|
+
if (m.includes("gpt-4.1") && m.includes("mini"))
|
|
165
|
+
return "gpt-4.1-mini";
|
|
166
|
+
if (m.includes("gpt-4.1"))
|
|
167
|
+
return "gpt-4.1";
|
|
168
|
+
if (m.includes("gpt-4o-mini"))
|
|
169
|
+
return "gpt-4o-mini";
|
|
170
|
+
if (m.includes("gpt-4o"))
|
|
171
|
+
return "gpt-4o";
|
|
172
|
+
if (m.includes("o4-mini"))
|
|
173
|
+
return "o4-mini";
|
|
174
|
+
if (m.includes("o3-mini"))
|
|
175
|
+
return "o3-mini";
|
|
176
|
+
if (m.includes("o3-pro"))
|
|
177
|
+
return "o3-pro";
|
|
178
|
+
if (m === "o3" || m.startsWith("o3-"))
|
|
179
|
+
return "o3";
|
|
180
|
+
if (m.includes("gemini") && m.includes("3.5") && m.includes("flash"))
|
|
181
|
+
return "gemini-3.5-flash";
|
|
182
|
+
if (m.includes("gemini") && m.includes("3.1") && m.includes("pro") && m.includes("preview"))
|
|
183
|
+
return "gemini-3.1-pro-preview";
|
|
184
|
+
if (m.includes("gemini") && m.includes("3.1") && m.includes("flash") && m.includes("lite"))
|
|
185
|
+
return "gemini-3.1-flash-lite";
|
|
186
|
+
if (m.includes("gemini") && m.includes("3.1") && m.includes("pro"))
|
|
187
|
+
return "gemini-3.1-pro";
|
|
188
|
+
if (m.includes("gemini") && m.includes("2.5") && m.includes("flash") && m.includes("lite"))
|
|
189
|
+
return "gemini-2.5-flash-lite";
|
|
190
|
+
if (m.includes("gemini") && m.includes("2.5") && m.includes("flash"))
|
|
191
|
+
return "gemini-2.5-flash";
|
|
192
|
+
if (m.includes("gemini") && m.includes("2.5") && m.includes("pro"))
|
|
193
|
+
return "gemini-2.5-pro";
|
|
194
|
+
if (m.includes("2.0") && m.includes("flash") && m.includes("lite"))
|
|
195
|
+
return "gemini-2.0-flash-lite";
|
|
196
|
+
if (m.includes("2.0") && m.includes("flash"))
|
|
197
|
+
return "gemini-2.0-flash";
|
|
198
|
+
if (m.includes("gemini-3") && m.includes("flash"))
|
|
199
|
+
return "gemini-3-flash";
|
|
200
|
+
if (m.includes("gemini-3") && m.includes("pro"))
|
|
201
|
+
return "gemini-3-pro";
|
|
202
|
+
if (m.includes("flash-lite") || m.includes("flash_lite"))
|
|
203
|
+
return "gemini-flash-lite";
|
|
204
|
+
if (m.includes("deepseek") && (m.includes("r1") || m.includes("reasoner")))
|
|
205
|
+
return "deepseek-r1";
|
|
206
|
+
if (m.includes("deepseek"))
|
|
207
|
+
return "deepseek-v3";
|
|
208
|
+
if (m.includes("qwen") && m.includes("coder"))
|
|
209
|
+
return "qwen-coder";
|
|
210
|
+
if (m.includes("qwen3") && m.includes("mini"))
|
|
211
|
+
return "qwen3-mini";
|
|
212
|
+
if (m.includes("qwen"))
|
|
213
|
+
return "qwen3";
|
|
214
|
+
if (m.includes("kimi") || m.includes("moonshot"))
|
|
215
|
+
return "kimi-k2.5";
|
|
216
|
+
if (m.includes("minimax"))
|
|
217
|
+
return "minimax-2";
|
|
218
|
+
if (m.includes("glm") && m.includes("flash"))
|
|
219
|
+
return "glm-4.7-flash";
|
|
220
|
+
if (m.includes("glm"))
|
|
221
|
+
return "glm-4.7";
|
|
222
|
+
if (m.includes("mimo"))
|
|
223
|
+
return "mimo-flash";
|
|
224
|
+
if (m.includes("mistral") && (m.includes("large") || m.includes("123")))
|
|
225
|
+
return "mistral-large";
|
|
226
|
+
if (m.includes("mistral") && m.includes("small"))
|
|
227
|
+
return "mistral-small";
|
|
228
|
+
if (m.includes("mistral"))
|
|
229
|
+
return "mistral-large";
|
|
230
|
+
if (m.includes("grok"))
|
|
231
|
+
return "grok-4";
|
|
232
|
+
if (m.includes("ollama") || m.includes("local") || m.includes("lmstudio"))
|
|
233
|
+
return "local";
|
|
234
|
+
return m;
|
|
235
|
+
}
|
|
236
|
+
/** Resolve the rate card for a model key, proxying unpriced models. */
|
|
237
|
+
function ratesFor(modelKey) {
|
|
238
|
+
const key = normalizeModelName(modelKey);
|
|
239
|
+
return DEFAULT_PRICING[key] ?? DEFAULT_PRICING[PROXY_MODEL];
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Per-token blended rate for one class ("input" | "output" | "cacheRead") over
|
|
243
|
+
* a model mix. Each model's rate is weighted by its share; unpriced models use
|
|
244
|
+
* the proxy. Empty mix -> proxy model's rate.
|
|
245
|
+
*/
|
|
246
|
+
function blendedRate(mix, klass) {
|
|
247
|
+
const items = Object.entries(mix).filter(([, s]) => s && s > 0);
|
|
248
|
+
if (items.length === 0)
|
|
249
|
+
return DEFAULT_PRICING[PROXY_MODEL][klass];
|
|
250
|
+
const tot = items.reduce((s, [, v]) => s + v, 0);
|
|
251
|
+
if (tot <= 0)
|
|
252
|
+
return DEFAULT_PRICING[PROXY_MODEL][klass];
|
|
253
|
+
let acc = 0;
|
|
254
|
+
for (const [model, share] of items)
|
|
255
|
+
acc += share * ratesFor(model)[klass];
|
|
256
|
+
return acc / tot;
|
|
257
|
+
}
|
|
258
|
+
/** Blended 5m + 1h cache-write rate over a mix (TTL-aware, like price_cw). */
|
|
259
|
+
function blendedCacheWriteRate(mix, cw5mShare, cw1hShare) {
|
|
260
|
+
// cw5mShare/cw1hShare are the fraction of cache-write tokens at each TTL.
|
|
261
|
+
const items = Object.entries(mix).filter(([, s]) => s && s > 0);
|
|
262
|
+
const score = (r) => cw5mShare * r.cacheWrite + cw1hShare * (r.cacheWrite1h ?? r.cacheWrite);
|
|
263
|
+
if (items.length === 0)
|
|
264
|
+
return score(DEFAULT_PRICING[PROXY_MODEL]);
|
|
265
|
+
const tot = items.reduce((s, [, v]) => s + v, 0);
|
|
266
|
+
if (tot <= 0)
|
|
267
|
+
return score(DEFAULT_PRICING[PROXY_MODEL]);
|
|
268
|
+
let acc = 0;
|
|
269
|
+
for (const [model, share] of items)
|
|
270
|
+
acc += share * score(ratesFor(model));
|
|
271
|
+
return acc / tot;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Price the fresh+cache_read POOL and OUTPUT at a model mix (NO cache-write).
|
|
275
|
+
* Linear in tokens, so aggregate window totals price the whole window directly.
|
|
276
|
+
* Mirrors measure.py's `price(fi, cr, out, shares)`.
|
|
277
|
+
*/
|
|
278
|
+
export function price(F, CR, O, mix) {
|
|
279
|
+
return (F * blendedRate(mix, "input") +
|
|
280
|
+
CR * blendedRate(mix, "cacheRead") +
|
|
281
|
+
O * blendedRate(mix, "output"));
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Price cache-write at a model mix, TTL-aware: 1h writes bill at 2x input, 5m
|
|
285
|
+
* at 1.25x. Cache-write IS a routing lever (billed at the writing model's
|
|
286
|
+
* rate), so each arm prices CW at its OWN mix. Mirrors measure.py's `price_cw`.
|
|
287
|
+
* OpenCode's session_log has no 5m/1h split, so all writes are treated as 5m
|
|
288
|
+
* (conservative) unless cw1h is supplied.
|
|
289
|
+
*/
|
|
290
|
+
export function price_cw(CW, mix, CW_5m, CW_1h) {
|
|
291
|
+
if (CW <= 0)
|
|
292
|
+
return 0;
|
|
293
|
+
const cw1h = CW_1h ?? 0;
|
|
294
|
+
const cw5m = CW_5m ?? CW - cw1h;
|
|
295
|
+
const cw5mShare = CW > 0 ? cw5m / CW : 1;
|
|
296
|
+
const cw1hShare = CW > 0 ? cw1h / CW : 0;
|
|
297
|
+
return CW * blendedCacheWriteRate(mix, cw5mShare, cw1hShare);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Cost of 1M fresh-input tokens at a mix. Used to reprice the compression
|
|
301
|
+
* add-back: baseline_input_rate / current_input_rate. Mirrors measure.py's
|
|
302
|
+
* `price(1_000_000, 0, 0, shares)`.
|
|
303
|
+
*/
|
|
304
|
+
export function inputRatePerMTok(mix) {
|
|
305
|
+
return price(1_000_000, 0, 0, mix);
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"savings.baseline.test.d.ts","sourceRoot":"","sources":["../src/savings.baseline.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baseline-savings stability tests (frozen-baseline port, v5.11.18).
|
|
3
|
+
*
|
|
4
|
+
* Proves the headline fix on the OpenCode engine: the pre-TO baseline
|
|
5
|
+
* ("old way / session") is a FROZEN, factual anchor that does NOT move when the
|
|
6
|
+
* current period's workload volume changes; "now / session" moves only with
|
|
7
|
+
* efficiency (model mix + cache reuse). Mirrors the Python suite.
|
|
8
|
+
*
|
|
9
|
+
* Run: bun test src/savings.baseline.test.ts
|
|
10
|
+
*/
|
|
11
|
+
import { test, expect } from "bun:test";
|
|
12
|
+
import { computeRealizedSavings } from "./savings.js";
|
|
13
|
+
const DAY = 86_400_000;
|
|
14
|
+
const T0 = Date.parse("2026-01-01T00:00:00Z"); // install day
|
|
15
|
+
const NOW = T0 + 200 * DAY; // well past the 31-day early window
|
|
16
|
+
function row(tsMs, model, fi, cr, cw, out) {
|
|
17
|
+
return {
|
|
18
|
+
created_at: Math.floor(tsMs / 1000),
|
|
19
|
+
model,
|
|
20
|
+
tokens_input: fi,
|
|
21
|
+
tokens_cache_read: cr,
|
|
22
|
+
tokens_cache_write: cw,
|
|
23
|
+
tokens_output: out,
|
|
24
|
+
cost_usd: 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// Fixed "before" (early-window) sessions: ~95% Opus, high cache reuse. This is the
|
|
28
|
+
// frozen baseline — identical in every scenario below. An anchor row at T0 fixes
|
|
29
|
+
// installTs (early window = [T0+1d, T0+31d)); the block sits densely inside it.
|
|
30
|
+
function beforeRows() {
|
|
31
|
+
const rows = [row(T0, "opus", 30_000, 13_500_000, 487_000, 51_000)]; // install anchor
|
|
32
|
+
for (let i = 0; i < 35; i++) {
|
|
33
|
+
const ts = T0 + 2 * DAY + i * 0.5 * DAY; // T0+2d .. ~T0+19.5d, all inside the window
|
|
34
|
+
const model = i < 33 ? "opus" : "sonnet"; // ~95% Opus by session
|
|
35
|
+
rows.push(row(ts, model, 30_000, 13_500_000, 487_000, 51_000));
|
|
36
|
+
}
|
|
37
|
+
return rows;
|
|
38
|
+
}
|
|
39
|
+
// `n` recent "after" sessions with a chosen per-session size, opus share, and cache hit.
|
|
40
|
+
// hit is set by the fi:cr split; size scales both while holding the ratio (so curHit is
|
|
41
|
+
// constant across sizes — isolating volume from efficiency).
|
|
42
|
+
// Default current cache-hit = the baseline cohort's native hit (13.5M/(13.5M+30k)),
|
|
43
|
+
// so the caching lever is ~0 and routing (Opus-share) is isolated. Drop it below this
|
|
44
|
+
// and a caching regression can legitimately cancel the routing win (tested separately).
|
|
45
|
+
const BASE_HIT = 13_500_000 / (13_500_000 + 30_000);
|
|
46
|
+
function afterRows(n, perInput, opusShare, hit = BASE_HIT) {
|
|
47
|
+
const rows = [];
|
|
48
|
+
const cr = perInput * hit;
|
|
49
|
+
const fi = perInput - cr;
|
|
50
|
+
const cw = perInput * 0.03;
|
|
51
|
+
const out = perInput * 0.01;
|
|
52
|
+
// Spread across the last ~24 days (inside the [NOW-30d, NOW] current window) so every
|
|
53
|
+
// after row counts regardless of n (n up to 120 at 0.2d spacing = 24d).
|
|
54
|
+
for (let i = 0; i < n; i++) {
|
|
55
|
+
const model = i < Math.round(n * opusShare) ? "opus" : "sonnet";
|
|
56
|
+
rows.push(row(NOW - (i + 1) * DAY * 0.2, model, fi, cr, cw, out));
|
|
57
|
+
}
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
function run(after) {
|
|
61
|
+
return computeRealizedSavings("/tmp/oc-baseline-test", 30, NOW, [...beforeRows(), ...after]);
|
|
62
|
+
}
|
|
63
|
+
test("baseline is frozen across different current volumes", () => {
|
|
64
|
+
const light = run(afterRows(40, 2_000_000, 0.56));
|
|
65
|
+
const heavy = run(afterRows(40, 10_000_000, 0.56));
|
|
66
|
+
expect(light.ready).toBe(true);
|
|
67
|
+
expect(heavy.ready).toBe(true);
|
|
68
|
+
// The old-way per-session baseline must not move when current volume 5x's.
|
|
69
|
+
expect(heavy.beforeCostPerSession).toBeCloseTo(light.beforeCostPerSession, 8);
|
|
70
|
+
// "now" depends on mix + cache-hit (held constant here), not volume -> also stable.
|
|
71
|
+
expect(heavy.afterCostPerSession).toBeCloseTo(light.afterCostPerSession, 8);
|
|
72
|
+
expect(heavy.savingsPerSession).toBeCloseTo(light.savingsPerSession, 8);
|
|
73
|
+
});
|
|
74
|
+
test("baseline frozen across session count; monthly scales with count", () => {
|
|
75
|
+
// opusShare 0.5 is exact at both counts (6/12 == 60/120), so the realized mix — and
|
|
76
|
+
// thus afterCps — is identical; only the count differs. (0.56 would round differently.)
|
|
77
|
+
const few = run(afterRows(12, 4_000_000, 0.5));
|
|
78
|
+
const many = run(afterRows(120, 4_000_000, 0.5));
|
|
79
|
+
expect(few.beforeCostPerSession).toBeCloseTo(many.beforeCostPerSession, 8);
|
|
80
|
+
expect(few.savingsPerSession).toBeCloseTo(many.savingsPerSession, 8);
|
|
81
|
+
// 10x the sessions -> ~10x the monthly transformation (per-session anchor is fixed).
|
|
82
|
+
const ratio = many.monthlySavingsUsd / Math.max(1e-9, few.monthlySavingsUsd);
|
|
83
|
+
expect(ratio).toBeGreaterThan(9);
|
|
84
|
+
expect(ratio).toBeLessThan(11);
|
|
85
|
+
});
|
|
86
|
+
test("now reacts to model mix; old way does not", () => {
|
|
87
|
+
const lean = run(afterRows(40, 4_000_000, 0.2));
|
|
88
|
+
const heavyOpus = run(afterRows(40, 4_000_000, 0.9));
|
|
89
|
+
expect(lean.beforeCostPerSession).toBeCloseTo(heavyOpus.beforeCostPerSession, 8);
|
|
90
|
+
// More Opus now -> costs more now -> less saved per session.
|
|
91
|
+
expect(heavyOpus.afterCostPerSession).toBeGreaterThan(lean.afterCostPerSession);
|
|
92
|
+
expect(heavyOpus.savingsPerSession).toBeLessThan(lean.savingsPerSession);
|
|
93
|
+
});
|
|
94
|
+
test("savings positive for a 95%-Opus baseline vs a 56%-Opus now", () => {
|
|
95
|
+
const r = run(afterRows(40, 4_000_000, 0.56));
|
|
96
|
+
expect(r.beforeCostPerSession).toBeGreaterThan(r.afterCostPerSession);
|
|
97
|
+
expect(r.afterCostPerSession).toBeGreaterThan(0);
|
|
98
|
+
expect(r.savingsPerSession).toBeGreaterThan(0);
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=savings.baseline.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"savings.baseline.test.js","sourceRoot":"","sources":["../src/savings.baseline.test.ts"],"names":[],"mappings":"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"}
|
package/dist/savings.d.ts
CHANGED
|
@@ -3,10 +3,39 @@ export interface SavingsBreakdownItem {
|
|
|
3
3
|
label: string;
|
|
4
4
|
monthlyUsd: number;
|
|
5
5
|
}
|
|
6
|
+
/** Progress toward a frozen baseline (displayed when ready=false). */
|
|
7
|
+
export interface BaselineBuilding {
|
|
8
|
+
/** Sessions collected in the early window so far. */
|
|
9
|
+
sessionsInWindow: number;
|
|
10
|
+
/** Sessions needed before the baseline locks in. */
|
|
11
|
+
sessionsNeeded: number;
|
|
12
|
+
/** Length of the early-window in days (matches BASELINE_EARLY_WINDOW_DAYS). */
|
|
13
|
+
earlyWindowDays: number;
|
|
14
|
+
/** Calendar days until the early window closes (0 once it has closed). */
|
|
15
|
+
daysLeft: number;
|
|
16
|
+
/** ISO date of the user's very first tracked session. */
|
|
17
|
+
firstDate: string;
|
|
18
|
+
}
|
|
6
19
|
export interface RealizedSavings {
|
|
7
20
|
ready: boolean;
|
|
8
21
|
status: string;
|
|
9
22
|
monthlySavingsUsd: number;
|
|
23
|
+
/** Monthly $ at the user's CURRENT model mix + cache pattern ("now" arm). */
|
|
24
|
+
actualMonthlyUsd: number;
|
|
25
|
+
/** Monthly $ at the user's BASELINE mix + cache pattern ("the old way" arm). */
|
|
26
|
+
counterfactualMonthlyUsd: number;
|
|
27
|
+
/** Transformation as a fraction: (counterfactual − actual) / counterfactual. */
|
|
28
|
+
transformationPct: number;
|
|
29
|
+
/**
|
|
30
|
+
* Metered compression floor — tokens TO removed from context, repriced to the
|
|
31
|
+
* baseline input mix. This is the PROVEN subset of compressionAddback and is
|
|
32
|
+
* NEVER summed into the transformation headline. Render it as a separate card.
|
|
33
|
+
*/
|
|
34
|
+
compressionMeasuredUsd: number;
|
|
35
|
+
/** Estimated verbosity-steer $ before the baseline-output reprice. */
|
|
36
|
+
verbosityMeasuredUsd: number;
|
|
37
|
+
/** Repriced verbosity-steer $ (estimated output reduction at baseline mix). */
|
|
38
|
+
verbosityTransformationUsd: number;
|
|
10
39
|
savingsPerSession: number;
|
|
11
40
|
beforeCostPerSession: number;
|
|
12
41
|
afterCostPerSession: number;
|
|
@@ -16,10 +45,19 @@ export interface RealizedSavings {
|
|
|
16
45
|
cumulativeSavedUsd: number;
|
|
17
46
|
installDate: string | null;
|
|
18
47
|
breakdown: SavingsBreakdownItem[];
|
|
48
|
+
/**
|
|
49
|
+
* Populated when ready=false and the user has at least one session. Gives the
|
|
50
|
+
* dashboard enough data to render a progress card + bar instead of a dead end.
|
|
51
|
+
* null when there are zero sessions (no installDate yet).
|
|
52
|
+
*/
|
|
53
|
+
baselineBuilding: BaselineBuilding | null;
|
|
19
54
|
}
|
|
20
55
|
/**
|
|
21
|
-
* Compute realized
|
|
22
|
-
*
|
|
56
|
+
* Compute realized savings via the current-volume counterfactual.
|
|
57
|
+
* `now` injectable for testing.
|
|
58
|
+
* `rowsOverride` bypasses the DB (raw session_log rows) for tests.
|
|
59
|
+
* `compressionOverride` injects the measured compression dollars when the DB
|
|
60
|
+
* is bypassed (rowsOverride present); default 0.
|
|
23
61
|
*/
|
|
24
|
-
export declare function computeRealizedSavings(dataDir: string, days?: number, now?: number, rowsOverride?: Array<Record<string, unknown
|
|
62
|
+
export declare function computeRealizedSavings(dataDir: string, days?: number, now?: number, rowsOverride?: Array<Record<string, unknown>>, compressionOverride?: number): RealizedSavings;
|
|
25
63
|
//# sourceMappingURL=savings.d.ts.map
|
package/dist/savings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"savings.d.ts","sourceRoot":"","sources":["../src/savings.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"savings.d.ts","sourceRoot":"","sources":["../src/savings.ts"],"names":[],"mappings":"AAoGA,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,eAAe,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,wBAAwB,EAAE,MAAM,CAAC;IACjC,gFAAgF;IAChF,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sEAAsE;IACtE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,+EAA+E;IAC/E,0BAA0B,EAAE,MAAM,CAAC;IACnC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC;;;;OAIG;IACH,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC3C;AA6BD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAW,EACjB,GAAG,GAAE,MAAmB,EACxB,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC7C,mBAAmB,CAAC,EAAE,MAAM,GAC3B,eAAe,CAuSjB"}
|