Skip to content

Commit c6e0608

Browse files
committed
feat: change default pricing fallback to openai/gpt-5 (then gpt-4o, then claude-3.5-sonnet); broaden token field keys; update README about fallback and tokens
1 parent ed5a4de commit c6e0608

3 files changed

Lines changed: 78 additions & 11 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ Examples:
3737
The CLI automatically fetches model pricing from the public OpenRouter models API and estimates cost based on input/output tokens per model.
3838

3939
- API: `https://openrouter.ai/api/v1/models`
40-
- If pricing is unavailable (offline or unknown model), cost shows as `$0.00` for those rows.
40+
- Fallback: when a model name is missing (`unknown`), cxusage attempts a fallback chain: `openai/gpt-5``openai/gpt-4o``anthropic/claude-3.5-sonnet`. The first available price is used.
41+
- Disable fallback with `--no-fallback`. With fallback disabled, unknown models show `$0.00`.
42+
43+
Notes about token counts: cxusage sums numeric fields commonly used for token counts (e.g., `input_tokens`, `output_tokens`, `promptTokenCount`, `completionTokenCount`, etc.). If older logs don’t include any of these fields, tokens will appear as zero even though events are counted. Use `--by model --debug` to inspect which model names are detected in your logs.
4144

4245
## Programmatic invocation
4346

dist/daily.js

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,28 @@ const path = __importStar(require("path"));
3939
const utils_1 = require("./utils");
4040
const pricing_1 = require("./pricing");
4141
const DEFAULT_ROOT = path.join(process.env.HOME || process.env.USERPROFILE || '', '.codex', 'sessions');
42-
const TOKEN_KEYS_IN = ["input_tokens", "prompt_tokens", "request_tokens"];
43-
const TOKEN_KEYS_OUT = ["output_tokens", "completion_tokens", "response_tokens"];
42+
const TOKEN_KEYS_IN = [
43+
// common
44+
"input_tokens", "prompt_tokens", "request_tokens",
45+
// count variants
46+
"input_token_count", "output_token_count", // sometimes misused
47+
"prompt_token_count", "completion_token_count",
48+
"inputTokenCount", "outputTokenCount",
49+
"promptTokenCount", "completionTokenCount",
50+
"num_input_tokens", "num_output_tokens",
51+
// shorthand variants
52+
"tokens_in", "tokensIn", "promptTokens",
53+
];
54+
const TOKEN_KEYS_OUT = [
55+
// common
56+
"output_tokens", "completion_tokens", "response_tokens",
57+
// count variants
58+
"output_token_count", "completion_token_count",
59+
"outputTokenCount", "completionTokenCount",
60+
"num_output_tokens",
61+
// shorthand variants
62+
"tokens_out", "tokensOut", "completionTokens",
63+
];
4464
const MODEL_KEYS = [
4565
"model",
4666
"model_name",
@@ -66,7 +86,11 @@ async function executeDaily(args) {
6686
const tz = args.tz;
6787
const by = args.by || 'day';
6888
const disableFallback = args.noFallback === true;
69-
const fallbackModel = 'claude-3.5-sonnet';
89+
const fallbackModels = [
90+
'openai/gpt-5', 'gpt-5',
91+
'openai/gpt-4o', 'gpt-4o',
92+
'anthropic/claude-3.5-sonnet', 'claude-3.5-sonnet'
93+
];
7094
const files = await (0, utils_1.listJsonlFiles)(root);
7195
const agg = new Map();
7296
const aggModel = new Map();
@@ -155,7 +179,11 @@ async function executeDaily(args) {
155179
for (const [m, inf] of mm.entries()) {
156180
let c = (0, pricing_1.estimateCostFor)(m, inf.in, inf.out, prices);
157181
if (!disableFallback && (!isFinite(c) || c === 0) && m === 'unknown') {
158-
c = (0, pricing_1.estimateCostFor)(fallbackModel, inf.in, inf.out, prices);
182+
for (const fb of fallbackModels) {
183+
c = (0, pricing_1.estimateCostFor)(fb, inf.in, inf.out, prices);
184+
if (c > 0)
185+
break;
186+
}
159187
}
160188
cost += c;
161189
}
@@ -191,7 +219,13 @@ async function executeDaily(args) {
191219
const tot = info.in + info.out;
192220
let cost = (0, pricing_1.estimateCostFor)(m, info.in, info.out, prices);
193221
if (!disableFallback && (!isFinite(cost) || cost === 0) && m === 'unknown') {
194-
cost = (0, pricing_1.estimateCostFor)(fallbackModel, info.in, info.out, prices);
222+
for (const fb of fallbackModels) {
223+
const c2 = (0, pricing_1.estimateCostFor)(fb, info.in, info.out, prices);
224+
if (c2 > 0) {
225+
cost = c2;
226+
break;
227+
}
228+
}
195229
}
196230
rows.push({
197231
date: curStr,

src/daily.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,28 @@ import { loadPricing, estimateCostFor } from './pricing'
55

66
const DEFAULT_ROOT = path.join(process.env.HOME || process.env.USERPROFILE || '', '.codex', 'sessions')
77

8-
const TOKEN_KEYS_IN = ["input_tokens", "prompt_tokens", "request_tokens"]
9-
const TOKEN_KEYS_OUT = ["output_tokens", "completion_tokens", "response_tokens"]
8+
const TOKEN_KEYS_IN = [
9+
// common
10+
"input_tokens", "prompt_tokens", "request_tokens",
11+
// count variants
12+
"input_token_count", "output_token_count", // sometimes misused
13+
"prompt_token_count", "completion_token_count",
14+
"inputTokenCount", "outputTokenCount",
15+
"promptTokenCount", "completionTokenCount",
16+
"num_input_tokens", "num_output_tokens",
17+
// shorthand variants
18+
"tokens_in", "tokensIn", "promptTokens",
19+
]
20+
const TOKEN_KEYS_OUT = [
21+
// common
22+
"output_tokens", "completion_tokens", "response_tokens",
23+
// count variants
24+
"output_token_count", "completion_token_count",
25+
"outputTokenCount", "completionTokenCount",
26+
"num_output_tokens",
27+
// shorthand variants
28+
"tokens_out", "tokensOut", "completionTokens",
29+
]
1030
const MODEL_KEYS = [
1131
"model",
1232
"model_name",
@@ -55,7 +75,11 @@ export async function executeDaily(args: DailyArgs): Promise<number> {
5575
const tz = args.tz
5676
const by = args.by || 'day'
5777
const disableFallback = (args as any).noFallback === true
58-
const fallbackModel = 'claude-3.5-sonnet'
78+
const fallbackModels = [
79+
'openai/gpt-5', 'gpt-5',
80+
'openai/gpt-4o', 'gpt-4o',
81+
'anthropic/claude-3.5-sonnet', 'claude-3.5-sonnet'
82+
]
5983

6084
const files = await listJsonlFiles(root)
6185

@@ -144,7 +168,10 @@ export async function executeDaily(args: DailyArgs): Promise<number> {
144168
for (const [m, inf] of mm.entries()) {
145169
let c = estimateCostFor(m, inf.in, inf.out, prices)
146170
if (!disableFallback && (!isFinite(c) || c === 0) && m === 'unknown') {
147-
c = estimateCostFor(fallbackModel, inf.in, inf.out, prices)
171+
for (const fb of fallbackModels) {
172+
c = estimateCostFor(fb, inf.in, inf.out, prices)
173+
if (c > 0) break
174+
}
148175
}
149176
cost += c
150177
}
@@ -176,7 +203,10 @@ export async function executeDaily(args: DailyArgs): Promise<number> {
176203
const tot = info.in + info.out
177204
let cost = estimateCostFor(m, info.in, info.out, prices)
178205
if (!disableFallback && (!isFinite(cost) || cost === 0) && m === 'unknown') {
179-
cost = estimateCostFor(fallbackModel, info.in, info.out, prices)
206+
for (const fb of fallbackModels) {
207+
const c2 = estimateCostFor(fb, info.in, info.out, prices)
208+
if (c2 > 0) { cost = c2; break }
209+
}
180210
}
181211
rows.push({
182212
date: curStr,

0 commit comments

Comments
 (0)