@roastcodes/ttdash 6.1.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/LICENSE +21 -0
- package/README.md +294 -0
- package/dist/assets/AutoImportModal-Dig6ASar.js +2 -0
- package/dist/assets/CustomTooltip-CPg9_T3c.js +1 -0
- package/dist/assets/DrillDownModal-C7O3X6oj.js +1 -0
- package/dist/assets/button-D7Ib8H7t.js +1 -0
- package/dist/assets/charts-vendor-CiBqdKXh.js +36 -0
- package/dist/assets/dialog-Cn1m7WhC.js +1 -0
- package/dist/assets/icons-vendor-DFoaijFJ.js +1 -0
- package/dist/assets/index-DWoj-vpZ.css +2 -0
- package/dist/assets/index-I9xzoyFJ.js +4 -0
- package/dist/assets/motion-vendor-BXI2L__C.js +1 -0
- package/dist/assets/react-vendor-0R1rd57Z.js +9 -0
- package/dist/assets/rolldown-runtime-COnpUsM8.js +1 -0
- package/dist/assets/ui-vendor-BGjRFQGY.js +45 -0
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +1 -0
- package/dist/index.html +26 -0
- package/package.json +95 -0
- package/server/report/charts.js +152 -0
- package/server/report/i18n.js +36 -0
- package/server/report/index.js +326 -0
- package/server/report/utils.js +674 -0
- package/server.js +1895 -0
- package/src/locales/de/common.json +996 -0
- package/src/locales/en/common.json +996 -0
- package/usage-normalizer.js +152 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
function toNumber(value) {
|
|
2
|
+
return Number.isFinite(value) ? value : Number(value) || 0
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function toStringValue(value) {
|
|
6
|
+
return typeof value === 'string' ? value : ''
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizeLegacyModelBreakdown(entry) {
|
|
10
|
+
return {
|
|
11
|
+
modelName: toStringValue(entry?.modelName),
|
|
12
|
+
inputTokens: toNumber(entry?.inputTokens),
|
|
13
|
+
outputTokens: toNumber(entry?.outputTokens),
|
|
14
|
+
cacheCreationTokens: toNumber(entry?.cacheCreationTokens),
|
|
15
|
+
cacheReadTokens: toNumber(entry?.cacheReadTokens),
|
|
16
|
+
thinkingTokens: toNumber(entry?.thinkingTokens),
|
|
17
|
+
cost: toNumber(entry?.cost),
|
|
18
|
+
requestCount: toNumber(entry?.requestCount),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function withDailyTotals(day) {
|
|
23
|
+
const totalTokens = toNumber(day.totalTokens) || (
|
|
24
|
+
toNumber(day.inputTokens) +
|
|
25
|
+
toNumber(day.outputTokens) +
|
|
26
|
+
toNumber(day.cacheCreationTokens) +
|
|
27
|
+
toNumber(day.cacheReadTokens) +
|
|
28
|
+
toNumber(day.thinkingTokens)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
date: toStringValue(day.date),
|
|
33
|
+
inputTokens: toNumber(day.inputTokens),
|
|
34
|
+
outputTokens: toNumber(day.outputTokens),
|
|
35
|
+
cacheCreationTokens: toNumber(day.cacheCreationTokens),
|
|
36
|
+
cacheReadTokens: toNumber(day.cacheReadTokens),
|
|
37
|
+
thinkingTokens: toNumber(day.thinkingTokens),
|
|
38
|
+
totalTokens,
|
|
39
|
+
totalCost: toNumber(day.totalCost),
|
|
40
|
+
requestCount: toNumber(day.requestCount),
|
|
41
|
+
modelsUsed: Array.isArray(day.modelsUsed) ? day.modelsUsed.filter((value) => typeof value === 'string') : [],
|
|
42
|
+
modelBreakdowns: Array.isArray(day.modelBreakdowns) ? day.modelBreakdowns.map(normalizeLegacyModelBreakdown) : [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeLegacyDay(entry) {
|
|
47
|
+
const modelBreakdowns = Array.isArray(entry?.modelBreakdowns)
|
|
48
|
+
? entry.modelBreakdowns.map(normalizeLegacyModelBreakdown)
|
|
49
|
+
: [];
|
|
50
|
+
|
|
51
|
+
return withDailyTotals({
|
|
52
|
+
date: entry?.date,
|
|
53
|
+
inputTokens: entry?.inputTokens,
|
|
54
|
+
outputTokens: entry?.outputTokens,
|
|
55
|
+
cacheCreationTokens: entry?.cacheCreationTokens,
|
|
56
|
+
cacheReadTokens: entry?.cacheReadTokens,
|
|
57
|
+
thinkingTokens: entry?.thinkingTokens,
|
|
58
|
+
totalTokens: entry?.totalTokens,
|
|
59
|
+
totalCost: entry?.totalCost,
|
|
60
|
+
requestCount: entry?.requestCount,
|
|
61
|
+
modelsUsed: Array.isArray(entry?.modelsUsed)
|
|
62
|
+
? entry.modelsUsed
|
|
63
|
+
: modelBreakdowns.map((item) => item.modelName),
|
|
64
|
+
modelBreakdowns,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeToktrackDay(entry) {
|
|
69
|
+
const models = entry?.models && typeof entry.models === 'object' && !Array.isArray(entry.models)
|
|
70
|
+
? entry.models
|
|
71
|
+
: {};
|
|
72
|
+
|
|
73
|
+
const modelBreakdowns = Object.entries(models).map(([modelName, modelData]) => ({
|
|
74
|
+
modelName,
|
|
75
|
+
inputTokens: toNumber(modelData?.input_tokens),
|
|
76
|
+
outputTokens: toNumber(modelData?.output_tokens),
|
|
77
|
+
cacheCreationTokens: toNumber(modelData?.cache_creation_tokens),
|
|
78
|
+
cacheReadTokens: toNumber(modelData?.cache_read_tokens),
|
|
79
|
+
thinkingTokens: toNumber(modelData?.thinking_tokens),
|
|
80
|
+
cost: toNumber(modelData?.cost_usd),
|
|
81
|
+
requestCount: toNumber(modelData?.count),
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
const requestCount = modelBreakdowns.reduce((sum, item) => sum + item.requestCount, 0);
|
|
85
|
+
|
|
86
|
+
return withDailyTotals({
|
|
87
|
+
date: entry?.date,
|
|
88
|
+
inputTokens: entry?.total_input_tokens,
|
|
89
|
+
outputTokens: entry?.total_output_tokens,
|
|
90
|
+
cacheCreationTokens: entry?.total_cache_creation_tokens,
|
|
91
|
+
cacheReadTokens: entry?.total_cache_read_tokens,
|
|
92
|
+
thinkingTokens: entry?.total_thinking_tokens,
|
|
93
|
+
totalCost: entry?.total_cost_usd,
|
|
94
|
+
requestCount,
|
|
95
|
+
modelsUsed: modelBreakdowns.map((item) => item.modelName),
|
|
96
|
+
modelBreakdowns,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function computeTotals(daily) {
|
|
101
|
+
return daily.reduce((totals, day) => ({
|
|
102
|
+
inputTokens: totals.inputTokens + day.inputTokens,
|
|
103
|
+
outputTokens: totals.outputTokens + day.outputTokens,
|
|
104
|
+
cacheCreationTokens: totals.cacheCreationTokens + day.cacheCreationTokens,
|
|
105
|
+
cacheReadTokens: totals.cacheReadTokens + day.cacheReadTokens,
|
|
106
|
+
thinkingTokens: totals.thinkingTokens + day.thinkingTokens,
|
|
107
|
+
totalCost: totals.totalCost + day.totalCost,
|
|
108
|
+
totalTokens: totals.totalTokens + day.totalTokens,
|
|
109
|
+
requestCount: totals.requestCount + day.requestCount,
|
|
110
|
+
}), {
|
|
111
|
+
inputTokens: 0,
|
|
112
|
+
outputTokens: 0,
|
|
113
|
+
cacheCreationTokens: 0,
|
|
114
|
+
cacheReadTokens: 0,
|
|
115
|
+
thinkingTokens: 0,
|
|
116
|
+
totalCost: 0,
|
|
117
|
+
totalTokens: 0,
|
|
118
|
+
requestCount: 0,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeIncomingData(payload) {
|
|
123
|
+
let daily;
|
|
124
|
+
|
|
125
|
+
if (Array.isArray(payload)) {
|
|
126
|
+
daily = payload.map(normalizeToktrackDay);
|
|
127
|
+
} else if (payload && typeof payload === 'object' && Array.isArray(payload.daily)) {
|
|
128
|
+
const looksLikeToktrack = payload.daily.some((item) => item && typeof item === 'object' && 'total_input_tokens' in item);
|
|
129
|
+
daily = looksLikeToktrack
|
|
130
|
+
? payload.daily.map(normalizeToktrackDay)
|
|
131
|
+
: payload.daily.map(normalizeLegacyDay);
|
|
132
|
+
} else {
|
|
133
|
+
throw new Error('Die JSON-Datei muss ein gültiges tägliches Nutzungsformat enthalten.');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const filtered = daily
|
|
137
|
+
.filter((item) => item.date)
|
|
138
|
+
.sort((a, b) => a.date.localeCompare(b.date));
|
|
139
|
+
|
|
140
|
+
if (filtered.length === 0) {
|
|
141
|
+
throw new Error('Keine Nutzungsdaten gefunden.');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
daily: filtered,
|
|
146
|
+
totals: computeTotals(filtered),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = {
|
|
151
|
+
normalizeIncomingData,
|
|
152
|
+
};
|