codebuddy-stats 1.2.1 → 1.2.3
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/dist/index.js +71 -31
- package/dist/lib/pricing.js +71 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -421,7 +421,7 @@ function renderDaily(box, data, scrollOffset = 0, selectedIndex = 0, width, note
|
|
|
421
421
|
}
|
|
422
422
|
box.setContent(content);
|
|
423
423
|
}
|
|
424
|
-
// 渲染 Daily Detail
|
|
424
|
+
// 渲染 Daily Detail 视图(某一天的详细数据,按 project 分组显示所有 model 用量)
|
|
425
425
|
function renderDailyDetail(box, data, date, scrollOffset = 0, width, pageSize) {
|
|
426
426
|
const { dailySummary, dailyData } = data;
|
|
427
427
|
const daySummary = dailySummary[date];
|
|
@@ -430,57 +430,94 @@ function renderDailyDetail(box, data, date, scrollOffset = 0, width, pageSize) {
|
|
|
430
430
|
box.setContent(`{bold}${date}{/bold}\n\nNo data available for this date.`);
|
|
431
431
|
return;
|
|
432
432
|
}
|
|
433
|
-
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
433
|
+
const projectDetails = [];
|
|
434
|
+
for (const [projectName, models] of Object.entries(dayData)) {
|
|
435
|
+
const shortName = resolveProjectName(projectName, data.workspaceMappings);
|
|
436
|
+
const modelList = [];
|
|
437
|
+
let totalCost = 0;
|
|
438
|
+
let totalTokens = 0;
|
|
439
|
+
let totalRequests = 0;
|
|
440
|
+
for (const [modelId, stats] of Object.entries(models)) {
|
|
437
441
|
const s = stats;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
442
|
+
const cost = Number(s.cost ?? 0);
|
|
443
|
+
const tokens = Number(s.totalTokens ?? 0);
|
|
444
|
+
const requests = Number(s.requests ?? 0);
|
|
445
|
+
modelList.push({ id: modelId, cost, tokens, requests });
|
|
446
|
+
totalCost += cost;
|
|
447
|
+
totalTokens += tokens;
|
|
448
|
+
totalRequests += requests;
|
|
449
|
+
}
|
|
450
|
+
// 按 cost 降序排序 models
|
|
451
|
+
modelList.sort((a, b) => b.cost - a.cost);
|
|
452
|
+
projectDetails.push({
|
|
453
|
+
name: projectName,
|
|
454
|
+
shortName,
|
|
455
|
+
totalCost,
|
|
456
|
+
totalTokens,
|
|
457
|
+
totalRequests,
|
|
458
|
+
models: modelList,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
// 按 project 总 cost 降序排序
|
|
462
|
+
projectDetails.sort((a, b) => b.totalCost - a.totalCost);
|
|
463
|
+
const displayLines = [];
|
|
464
|
+
for (const project of projectDetails) {
|
|
465
|
+
displayLines.push({ type: 'project', project });
|
|
466
|
+
for (const model of project.models) {
|
|
467
|
+
displayLines.push({ type: 'model', model });
|
|
444
468
|
}
|
|
445
469
|
}
|
|
446
|
-
const sortedModels = Object.entries(modelStats).sort((a, b) => b[1].cost - a[1].cost);
|
|
447
470
|
// 根据宽度计算列宽
|
|
448
471
|
const availableWidth = width - 6; // padding
|
|
449
472
|
const fixedCols = 12 + 12 + 12; // Cost + Requests + Tokens
|
|
450
|
-
const
|
|
451
|
-
const totalWidth =
|
|
452
|
-
let content = `{bold}${date} - Model Usage Details{/bold}\n\n`;
|
|
473
|
+
const nameCol = Math.max(25, availableWidth - fixedCols);
|
|
474
|
+
const totalWidth = nameCol + fixedCols;
|
|
475
|
+
let content = `{bold}${date} - Project & Model Usage Details{/bold}\n\n`;
|
|
453
476
|
// 当天汇总
|
|
454
477
|
content += `{green-fg}Total cost:{/green-fg} ${formatCost(daySummary.cost)} `;
|
|
455
478
|
content += `{green-fg}Tokens:{/green-fg} ${formatTokens(daySummary.tokens)} `;
|
|
456
|
-
content += `{green-fg}Requests:{/green-fg} ${formatNumber(daySummary.requests)}
|
|
479
|
+
content += `{green-fg}Requests:{/green-fg} ${formatNumber(daySummary.requests)} `;
|
|
480
|
+
content += `{green-fg}Projects:{/green-fg} ${projectDetails.length}\n\n`;
|
|
457
481
|
content +=
|
|
458
482
|
'{underline}' +
|
|
459
|
-
'Model'.padEnd(
|
|
483
|
+
'Project / Model'.padEnd(nameCol) +
|
|
460
484
|
'~Cost'.padStart(12) +
|
|
461
485
|
'Requests'.padStart(12) +
|
|
462
486
|
'Tokens'.padStart(12) +
|
|
463
487
|
'{/underline}\n';
|
|
464
488
|
const safePageSize = Math.max(1, Math.floor(pageSize || 1));
|
|
465
|
-
const
|
|
466
|
-
for (const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
489
|
+
const visibleLines = displayLines.slice(scrollOffset, scrollOffset + safePageSize);
|
|
490
|
+
for (const line of visibleLines) {
|
|
491
|
+
if (line.type === 'project') {
|
|
492
|
+
const p = line.project;
|
|
493
|
+
content +=
|
|
494
|
+
'{cyan-fg}' +
|
|
495
|
+
truncate(p.shortName, nameCol - 1).padEnd(nameCol) +
|
|
496
|
+
formatCost(p.totalCost).padStart(12) +
|
|
497
|
+
formatNumber(p.totalRequests).padStart(12) +
|
|
498
|
+
formatTokens(p.totalTokens).padStart(12) +
|
|
499
|
+
'{/cyan-fg}\n';
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
const m = line.model;
|
|
503
|
+
content +=
|
|
504
|
+
(' ' + truncate(m.id, nameCol - 3)).padEnd(nameCol) +
|
|
505
|
+
formatCost(m.cost).padStart(12) +
|
|
506
|
+
formatNumber(m.requests).padStart(12) +
|
|
507
|
+
formatTokens(m.tokens).padStart(12) +
|
|
508
|
+
'\n';
|
|
509
|
+
}
|
|
473
510
|
}
|
|
474
511
|
content += '─'.repeat(totalWidth) + '\n';
|
|
475
512
|
content +=
|
|
476
513
|
'{bold}' +
|
|
477
|
-
`Total (${
|
|
514
|
+
`Total (${projectDetails.length} projects)`.padEnd(nameCol) +
|
|
478
515
|
formatCost(daySummary.cost).padStart(12) +
|
|
479
516
|
formatNumber(daySummary.requests).padStart(12) +
|
|
480
517
|
formatTokens(daySummary.tokens).padStart(12) +
|
|
481
518
|
'{/bold}\n';
|
|
482
|
-
if (
|
|
483
|
-
content += `\n{gray-fg}Showing ${scrollOffset + 1}-${Math.min(scrollOffset + safePageSize,
|
|
519
|
+
if (displayLines.length > safePageSize) {
|
|
520
|
+
content += `\n{gray-fg}Showing ${scrollOffset + 1}-${Math.min(scrollOffset + safePageSize, displayLines.length)} of ${displayLines.length} rows (↑↓ scroll, Esc back){/gray-fg}`;
|
|
484
521
|
}
|
|
485
522
|
else {
|
|
486
523
|
content += `\n{gray-fg}(Esc back to Daily list){/gray-fg}`;
|
|
@@ -784,11 +821,14 @@ async function main() {
|
|
|
784
821
|
}
|
|
785
822
|
if (currentTab === 3) {
|
|
786
823
|
if (dailyDetailDate) {
|
|
787
|
-
// 在 detail
|
|
824
|
+
// 在 detail 视图中滚动(计算总行数:project 数 + 每个 project 下的 model 数)
|
|
788
825
|
const dayData = data.dailyData[dailyDetailDate];
|
|
789
826
|
if (dayData) {
|
|
790
|
-
|
|
791
|
-
const
|
|
827
|
+
let totalLines = 0;
|
|
828
|
+
for (const models of Object.values(dayData)) {
|
|
829
|
+
totalLines += 1 + Object.keys(models).length; // 1 for project header + model count
|
|
830
|
+
}
|
|
831
|
+
const maxOffset = Math.max(0, totalLines - dailyDetailPageSize);
|
|
792
832
|
dailyDetailScrollOffset = Math.min(maxOffset, dailyDetailScrollOffset + 1);
|
|
793
833
|
}
|
|
794
834
|
}
|
package/dist/lib/pricing.js
CHANGED
|
@@ -2,25 +2,37 @@
|
|
|
2
2
|
function createPricing(inputPrice, cachedInputPrice, outputPrice, cacheWritePrice) {
|
|
3
3
|
return {
|
|
4
4
|
prompt: [{ limit: Number.POSITIVE_INFINITY, pricePerMTok: inputPrice }],
|
|
5
|
-
completion: [
|
|
6
|
-
|
|
5
|
+
completion: [
|
|
6
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: outputPrice },
|
|
7
|
+
],
|
|
8
|
+
cacheRead: [
|
|
9
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: cachedInputPrice },
|
|
10
|
+
],
|
|
7
11
|
cacheWrite: [
|
|
8
|
-
{
|
|
12
|
+
{
|
|
13
|
+
limit: Number.POSITIVE_INFINITY,
|
|
14
|
+
pricePerMTok: cacheWritePrice ?? inputPrice,
|
|
15
|
+
},
|
|
9
16
|
],
|
|
10
17
|
};
|
|
11
18
|
}
|
|
12
19
|
export const MODEL_PRICING = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// GPT 系列
|
|
21
|
+
"gpt-5.2": createPricing(1.75, 0.175, 14.0),
|
|
22
|
+
"gpt-5.1": createPricing(1.25, 0.125, 10.0),
|
|
23
|
+
"gpt-5": createPricing(1.25, 0.125, 10.0),
|
|
24
|
+
"gpt-5-mini": createPricing(0.25, 0.025, 2.0),
|
|
25
|
+
"gpt-5-nano": createPricing(0.05, 0.005, 0.4),
|
|
26
|
+
"gpt-5.1-chat-latest": createPricing(1.25, 0.125, 10.0),
|
|
27
|
+
"gpt-5-chat-latest": createPricing(1.25, 0.125, 10.0),
|
|
28
|
+
"gpt-5.1-codex": createPricing(1.25, 0.125, 10.0),
|
|
29
|
+
"gpt-5.1-codex-max": createPricing(1.25, 0.125, 10.0),
|
|
30
|
+
"gpt-5.1-codex-mini": createPricing(0.25, 0.025, 2.0),
|
|
31
|
+
"gpt-5-codex": createPricing(1.25, 0.125, 10.0),
|
|
32
|
+
// Claude 系列
|
|
33
|
+
"claude-opus-4.5": createPricing(5.0, 0.5, 25.0, 10.0),
|
|
34
|
+
"claude-haiku-4.5": createPricing(1.0, 0.1, 5.0, 1.25),
|
|
35
|
+
"claude-4.5": {
|
|
24
36
|
prompt: [
|
|
25
37
|
{ limit: 200_000, pricePerMTok: 3.0 },
|
|
26
38
|
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 6.0 },
|
|
@@ -38,7 +50,8 @@ export const MODEL_PRICING = {
|
|
|
38
50
|
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 12.0 },
|
|
39
51
|
],
|
|
40
52
|
},
|
|
41
|
-
|
|
53
|
+
// Gemini 系列
|
|
54
|
+
"gemini-3.0-pro": {
|
|
42
55
|
prompt: [
|
|
43
56
|
{ limit: 200_000, pricePerMTok: 2.0 },
|
|
44
57
|
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 4.0 },
|
|
@@ -56,7 +69,8 @@ export const MODEL_PRICING = {
|
|
|
56
69
|
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.4 },
|
|
57
70
|
],
|
|
58
71
|
},
|
|
59
|
-
|
|
72
|
+
"gemini-3.0-flash": createPricing(0.5, 0.05, 3.0),
|
|
73
|
+
"gemini-2.5-pro": {
|
|
60
74
|
prompt: [
|
|
61
75
|
{ limit: 200_000, pricePerMTok: 1.25 },
|
|
62
76
|
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 2.5 },
|
|
@@ -74,8 +88,48 @@ export const MODEL_PRICING = {
|
|
|
74
88
|
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.25 },
|
|
75
89
|
],
|
|
76
90
|
},
|
|
91
|
+
// GLM 系列 (价格从人民币转换: 1 USD = 7 CNY)
|
|
92
|
+
// 按上下文长度分段定价:[0,32K), [32K,200K)
|
|
93
|
+
"glm-4.7": {
|
|
94
|
+
prompt: [
|
|
95
|
+
{ limit: 32_000, pricePerMTok: 0.286 }, // 2元/M tokens
|
|
96
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.571 }, // 4元/M tokens
|
|
97
|
+
],
|
|
98
|
+
completion: [
|
|
99
|
+
{ limit: 32_000, pricePerMTok: 1.143 }, // 8元/M tokens
|
|
100
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 2.286 }, // 16元/M tokens
|
|
101
|
+
],
|
|
102
|
+
cacheRead: [
|
|
103
|
+
{ limit: 32_000, pricePerMTok: 0.057 }, // 0.4元/M tokens
|
|
104
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.114 }, // 0.8元/M tokens
|
|
105
|
+
],
|
|
106
|
+
cacheWrite: [
|
|
107
|
+
{ limit: 32_000, pricePerMTok: 0.286 }, // 2元/M tokens
|
|
108
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.571 }, // 4元/M tokens
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
"glm-4.6": {
|
|
112
|
+
prompt: [
|
|
113
|
+
{ limit: 32_000, pricePerMTok: 0.286 }, // 2元/M tokens
|
|
114
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.571 }, // 4元/M tokens
|
|
115
|
+
],
|
|
116
|
+
completion: [
|
|
117
|
+
{ limit: 32_000, pricePerMTok: 1.143 }, // 8元/M tokens
|
|
118
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 2.286 }, // 16元/M tokens
|
|
119
|
+
],
|
|
120
|
+
cacheRead: [
|
|
121
|
+
{ limit: 32_000, pricePerMTok: 0.057 }, // 0.4元/M tokens
|
|
122
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.114 }, // 0.8元/M tokens
|
|
123
|
+
],
|
|
124
|
+
cacheWrite: [
|
|
125
|
+
{ limit: 32_000, pricePerMTok: 0.286 }, // 2元/M tokens
|
|
126
|
+
{ limit: Number.POSITIVE_INFINITY, pricePerMTok: 0.571 }, // 4元/M tokens
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
// DeepSeek 系列
|
|
130
|
+
"deepseek-v3.1": createPricing(0.56, 0.056, 1.68),
|
|
77
131
|
};
|
|
78
|
-
export const DEFAULT_MODEL_ID =
|
|
132
|
+
export const DEFAULT_MODEL_ID = "gpt-5.1";
|
|
79
133
|
export function selectTierPrice(tokens, tiers) {
|
|
80
134
|
if (tokens <= 0)
|
|
81
135
|
return tiers[0]?.pricePerMTok ?? 0;
|