token-tracker-ai 1.0.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/README.md +89 -0
- package/bin/cli.js +2 -0
- package/dashboard/assets/index-BS0HTjXP.css +1 -0
- package/dashboard/assets/index-C_ZJr76A.js +107 -0
- package/dashboard/assets/index-Dac7psic.js +107 -0
- package/dashboard/index.html +13 -0
- package/package.json +29 -0
- package/src/index.js +93 -0
- package/src/server.js +65 -0
- package/token_logs.json +13 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="he" dir="rtl">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>מעקב עלויות טוקנים</title>
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-Dac7psic.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BS0HTjXP.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "token-tracker-ai",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Real-time token cost tracker for AI applications. See exactly how much each step costs across Gemini, GPT, and Claude.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"token-tracker-ai": "./bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/server.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai",
|
|
15
|
+
"tokens",
|
|
16
|
+
"cost",
|
|
17
|
+
"gemini",
|
|
18
|
+
"openai",
|
|
19
|
+
"claude",
|
|
20
|
+
"analytics",
|
|
21
|
+
"dashboard"
|
|
22
|
+
],
|
|
23
|
+
"author": "tomer",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"cors": "^2.8.5",
|
|
27
|
+
"express": "^4.18.2"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const SERVER_URL = "http://localhost:3001";
|
|
2
|
+
|
|
3
|
+
const STEP_NAMES = {
|
|
4
|
+
classifyProduct: "סיווג מוצר",
|
|
5
|
+
checkExemption: "בדיקת פטור",
|
|
6
|
+
generateRegistration: "יצירת רישום",
|
|
7
|
+
generateForm206: "יצירת טופס 206",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const MODEL_PRICES = {
|
|
11
|
+
"gemini-2.5-flash": { input: 0.15, output: 0.60 },
|
|
12
|
+
"gemini-1.5-pro": { input: 1.25, output: 5.00 },
|
|
13
|
+
"gpt-4o": { input: 2.50, output: 10.00 },
|
|
14
|
+
"gpt-4o-mini": { input: 0.15, output: 0.60 },
|
|
15
|
+
"claude-3-5-sonnet":{ input: 3.00, output: 15.00 },
|
|
16
|
+
"claude-3-haiku": { input: 0.25, output: 1.25 },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* עטוף כל קריאה למודל בינה מלאכותית כדי לעקוב אחרי עלויות הטוקנים.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} stepName - שם השלב (לדוגמה: "יצירת דוח", "ניתוח טקסט")
|
|
23
|
+
* @param {Function} apiFn - פונקציה שמבצעת את הקריאה למודל ומחזירה את התשובה
|
|
24
|
+
* @param {string} model - שם המודל (לדוגמה: "gemini-2.5-flash")
|
|
25
|
+
* @returns התשובה המקורית מהמודל
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const result = await trackCall("ניתוח חוזה", () => callGemini(...), "gemini-2.5-flash");
|
|
29
|
+
*/
|
|
30
|
+
export async function trackCall(stepName, apiFn, model = "gemini-2.5-flash") {
|
|
31
|
+
const response = await apiFn();
|
|
32
|
+
|
|
33
|
+
const usage = extractUsage(response);
|
|
34
|
+
if (usage) {
|
|
35
|
+
await sendLog(stepName, model, usage.inputTokens, usage.outputTokens);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return response;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function extractUsage(response) {
|
|
42
|
+
// תמיכה בגמיני
|
|
43
|
+
if (response?.usageMetadata) {
|
|
44
|
+
return {
|
|
45
|
+
inputTokens: response.usageMetadata.promptTokenCount ?? 0,
|
|
46
|
+
outputTokens: response.usageMetadata.candidatesTokenCount ?? 0,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// תמיכה באופן אי
|
|
51
|
+
if (response?.usage) {
|
|
52
|
+
return {
|
|
53
|
+
inputTokens: response.usage.prompt_tokens ?? 0,
|
|
54
|
+
outputTokens: response.usage.completion_tokens ?? 0,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// תמיכה בקלוד
|
|
59
|
+
if (response?.usage?.input_tokens !== undefined) {
|
|
60
|
+
return {
|
|
61
|
+
inputTokens: response.usage.input_tokens ?? 0,
|
|
62
|
+
outputTokens: response.usage.output_tokens ?? 0,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function sendLog(stepName, model, inputTokens, outputTokens) {
|
|
70
|
+
const prices = MODEL_PRICES[model] ?? { input: 0, output: 0 };
|
|
71
|
+
const costUSD = (inputTokens / 1_000_000) * prices.input + (outputTokens / 1_000_000) * prices.output;
|
|
72
|
+
|
|
73
|
+
const entry = {
|
|
74
|
+
timestamp: new Date().toISOString(),
|
|
75
|
+
step: stepName,
|
|
76
|
+
stepName: STEP_NAMES[stepName] ?? stepName,
|
|
77
|
+
model,
|
|
78
|
+
inputTokens,
|
|
79
|
+
outputTokens,
|
|
80
|
+
totalTokens: inputTokens + outputTokens,
|
|
81
|
+
costUSD: parseFloat(costUSD.toFixed(6)),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await fetch(`${SERVER_URL}/api/log`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "Content-Type": "application/json" },
|
|
88
|
+
body: JSON.stringify(entry),
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
console.warn("[token-tracker-ai] השרת לא פועל - הנתונים לא נשמרו. הרץ: npm start");
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/server.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const DATA_FILE = path.join(__dirname, "..", "token_logs.json");
|
|
9
|
+
const DASHBOARD_DIR = path.join(__dirname, "..", "dashboard");
|
|
10
|
+
const PORT = 3001;
|
|
11
|
+
|
|
12
|
+
const app = express();
|
|
13
|
+
app.use(cors());
|
|
14
|
+
app.use(express.json());
|
|
15
|
+
|
|
16
|
+
// הגשת לוח הבקרה
|
|
17
|
+
app.use(express.static(DASHBOARD_DIR));
|
|
18
|
+
|
|
19
|
+
function readLogs() {
|
|
20
|
+
if (!fs.existsSync(DATA_FILE)) return [];
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(fs.readFileSync(DATA_FILE, "utf-8"));
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function writeLogs(logs) {
|
|
29
|
+
fs.writeFileSync(DATA_FILE, JSON.stringify(logs, null, 2), "utf-8");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
app.post("/api/log", (req, res) => {
|
|
33
|
+
const entry = req.body;
|
|
34
|
+
if (!entry || !entry.step) {
|
|
35
|
+
return res.status(400).json({ error: "נתונים חסרים" });
|
|
36
|
+
}
|
|
37
|
+
const logs = readLogs();
|
|
38
|
+
logs.push({ ...entry, id: Date.now() });
|
|
39
|
+
writeLogs(logs);
|
|
40
|
+
res.json({ ok: true });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
app.get("/api/logs", (req, res) => {
|
|
44
|
+
res.json(readLogs());
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
app.delete("/api/logs", (req, res) => {
|
|
48
|
+
writeLogs([]);
|
|
49
|
+
res.json({ ok: true });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
app.get("*", (_req, res) => {
|
|
53
|
+
res.sendFile(path.join(DASHBOARD_DIR, "index.html"));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
app.listen(PORT, () => {
|
|
57
|
+
console.log(`
|
|
58
|
+
╔══════════════════════════════════════╗
|
|
59
|
+
║ token-tracker-ai פועל! ║
|
|
60
|
+
╠══════════════════════════════════════╣
|
|
61
|
+
║ פתח לוח בקרה: ║
|
|
62
|
+
║ 👉 http://localhost:${PORT} ║
|
|
63
|
+
╚══════════════════════════════════════╝
|
|
64
|
+
`);
|
|
65
|
+
});
|
package/token_logs.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"timestamp": "2026-04-05T09:11:40.006Z",
|
|
4
|
+
"step": "generateForm206",
|
|
5
|
+
"stepName": "יצירת טופס 206",
|
|
6
|
+
"model": "gemini-2.5-flash",
|
|
7
|
+
"inputTokens": 7309,
|
|
8
|
+
"outputTokens": 3883,
|
|
9
|
+
"totalTokens": 11192,
|
|
10
|
+
"costUSD": 0.003426,
|
|
11
|
+
"id": 1775380300096
|
|
12
|
+
}
|
|
13
|
+
]
|