cctrack 0.1.0__tar.gz → 0.2.0__tar.gz
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.
- {cctrack-0.1.0 → cctrack-0.2.0}/PKG-INFO +9 -8
- {cctrack-0.1.0 → cctrack-0.2.0}/README.md +5 -4
- {cctrack-0.1.0 → cctrack-0.2.0}/pyproject.toml +4 -4
- {cctrack-0.1.0 → cctrack-0.2.0}/src/cctrack/__init__.py +6 -3
- {cctrack-0.1.0 → cctrack-0.2.0}/tests/test_cctrack.py +25 -4
- {cctrack-0.1.0 → cctrack-0.2.0}/.github/workflows/ci.yml +0 -0
- {cctrack-0.1.0 → cctrack-0.2.0}/.github/workflows/publish.yml +0 -0
- {cctrack-0.1.0 → cctrack-0.2.0}/.gitignore +0 -0
- {cctrack-0.1.0 → cctrack-0.2.0}/CLAUDE.md +0 -0
- {cctrack-0.1.0 → cctrack-0.2.0}/LICENSE +0 -0
- {cctrack-0.1.0 → cctrack-0.2.0}/src/cctrack/__main__.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cctrack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Claude Code cost tracker — scans JSONL logs and reports token usage + estimated cost
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
-
Project-URL: Issues, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/rappdw/cctrack
|
|
6
|
+
Project-URL: Repository, https://github.com/rappdw/cctrack
|
|
7
|
+
Project-URL: Issues, https://github.com/rappdw/cctrack/issues
|
|
8
8
|
Author: Dan Rapp
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
@@ -99,11 +99,12 @@ Prices per million tokens, from [Anthropic's pricing page](https://www.anthropic
|
|
|
99
99
|
|
|
100
100
|
| Model | Input | Output | Cache Read | Cache Write |
|
|
101
101
|
|---|---|---|---|---|
|
|
102
|
-
| Opus 4.6 / 4.5 | $5.00 | $25.00 | $0.50 | $6.25 |
|
|
102
|
+
| Opus 4.7 / 4.6 / 4.5 | $5.00 | $25.00 | $0.50 | $6.25 |
|
|
103
103
|
| Opus 4 / 4.1 | $15.00 | $75.00 | $1.50 | $18.75 |
|
|
104
|
-
| Sonnet 4.x | $3.00 | $15.00 | $0.30 | $3.75 |
|
|
105
|
-
| Haiku 4.
|
|
106
|
-
| Haiku 3.
|
|
104
|
+
| Sonnet 4.x / 3.7 | $3.00 | $15.00 | $0.30 | $3.75 |
|
|
105
|
+
| Haiku 4.x | $1.00 | $5.00 | $0.10 | $1.25 |
|
|
106
|
+
| Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
|
107
|
+
| Haiku 3 | $0.25 | $1.25 | $0.03 | $0.30 |
|
|
107
108
|
|
|
108
109
|
Unknown models fall back to Sonnet rates.
|
|
109
110
|
|
|
@@ -74,11 +74,12 @@ Prices per million tokens, from [Anthropic's pricing page](https://www.anthropic
|
|
|
74
74
|
|
|
75
75
|
| Model | Input | Output | Cache Read | Cache Write |
|
|
76
76
|
|---|---|---|---|---|
|
|
77
|
-
| Opus 4.6 / 4.5 | $5.00 | $25.00 | $0.50 | $6.25 |
|
|
77
|
+
| Opus 4.7 / 4.6 / 4.5 | $5.00 | $25.00 | $0.50 | $6.25 |
|
|
78
78
|
| Opus 4 / 4.1 | $15.00 | $75.00 | $1.50 | $18.75 |
|
|
79
|
-
| Sonnet 4.x | $3.00 | $15.00 | $0.30 | $3.75 |
|
|
80
|
-
| Haiku 4.
|
|
81
|
-
| Haiku 3.
|
|
79
|
+
| Sonnet 4.x / 3.7 | $3.00 | $15.00 | $0.30 | $3.75 |
|
|
80
|
+
| Haiku 4.x | $1.00 | $5.00 | $0.10 | $1.25 |
|
|
81
|
+
| Haiku 3.5 | $0.80 | $4.00 | $0.08 | $1.00 |
|
|
82
|
+
| Haiku 3 | $0.25 | $1.25 | $0.03 | $0.30 |
|
|
82
83
|
|
|
83
84
|
Unknown models fall back to Sonnet rates.
|
|
84
85
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cctrack"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Claude Code cost tracker — scans JSONL logs and reports token usage + estimated cost"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -31,6 +31,6 @@ classifiers = [
|
|
|
31
31
|
cctrack = "cctrack:main"
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
34
|
-
Homepage = "https://github.com/
|
|
35
|
-
Repository = "https://github.com/
|
|
36
|
-
Issues = "https://github.com/
|
|
34
|
+
Homepage = "https://github.com/rappdw/cctrack"
|
|
35
|
+
Repository = "https://github.com/rappdw/cctrack"
|
|
36
|
+
Issues = "https://github.com/rappdw/cctrack/issues"
|
|
@@ -25,15 +25,18 @@ from pathlib import Path
|
|
|
25
25
|
# Longer/more-specific prefixes MUST come first — "claude-opus-4-6" would
|
|
26
26
|
# otherwise match "claude-opus-4" and get the wrong rate.
|
|
27
27
|
RATES = [
|
|
28
|
+
{"family": "claude-opus-4-7", "input": 5.0, "output": 25.0, "cache_read": 0.50, "cache_write": 6.25},
|
|
28
29
|
{"family": "claude-opus-4-6", "input": 5.0, "output": 25.0, "cache_read": 0.50, "cache_write": 6.25},
|
|
29
30
|
{"family": "claude-opus-4-5", "input": 5.0, "output": 25.0, "cache_read": 0.50, "cache_write": 6.25},
|
|
30
31
|
{"family": "claude-opus-4", "input": 15.0, "output": 75.0, "cache_read": 1.50, "cache_write": 18.75},
|
|
31
32
|
{"family": "claude-sonnet-4", "input": 3.0, "output": 15.0, "cache_read": 0.30, "cache_write": 3.75},
|
|
32
|
-
{"family": "claude-
|
|
33
|
-
{"family": "claude-haiku-
|
|
33
|
+
{"family": "claude-sonnet-3", "input": 3.0, "output": 15.0, "cache_read": 0.30, "cache_write": 3.75},
|
|
34
|
+
{"family": "claude-haiku-4", "input": 1.0, "output": 5.0, "cache_read": 0.10, "cache_write": 1.25},
|
|
35
|
+
{"family": "claude-haiku-3-5", "input": 0.80, "output": 4.0, "cache_read": 0.08, "cache_write": 1.00},
|
|
36
|
+
{"family": "claude-haiku-3", "input": 0.25, "output": 1.25, "cache_read": 0.03, "cache_write": 0.30},
|
|
34
37
|
]
|
|
35
38
|
|
|
36
|
-
SONNET_RATES = RATES[
|
|
39
|
+
SONNET_RATES = RATES[4] # fallback
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
def get_rates(model: str) -> dict:
|
|
@@ -52,13 +52,20 @@ def write_jsonl(dir_path: str, filename: str, events: list[dict]) -> str:
|
|
|
52
52
|
|
|
53
53
|
# ── Rate card tests ─────────────────────────────────────────────────────
|
|
54
54
|
|
|
55
|
+
def test_get_rates_opus_4_7():
|
|
56
|
+
r = cctrack.get_rates("claude-opus-4-7-20260416")
|
|
57
|
+
assert r["family"] == "claude-opus-4-7"
|
|
58
|
+
assert r["input"] == 5.0
|
|
59
|
+
assert r["output"] == 25.0
|
|
60
|
+
assert r["cache_read"] == 0.50
|
|
61
|
+
assert r["cache_write"] == 6.25
|
|
62
|
+
|
|
63
|
+
|
|
55
64
|
def test_get_rates_opus_4_6():
|
|
56
65
|
r = cctrack.get_rates("claude-opus-4-6")
|
|
57
66
|
assert r["family"] == "claude-opus-4-6"
|
|
58
67
|
assert r["input"] == 5.0
|
|
59
68
|
assert r["output"] == 25.0
|
|
60
|
-
assert r["cache_read"] == 0.50
|
|
61
|
-
assert r["cache_write"] == 6.25
|
|
62
69
|
|
|
63
70
|
|
|
64
71
|
def test_get_rates_opus_4_5():
|
|
@@ -95,20 +102,34 @@ def test_get_rates_sonnet_4_6():
|
|
|
95
102
|
assert r["input"] == 3.0
|
|
96
103
|
|
|
97
104
|
|
|
105
|
+
def test_get_rates_sonnet_3_7():
|
|
106
|
+
"""Deprecated Sonnet 3.7 matches claude-sonnet-3 prefix."""
|
|
107
|
+
r = cctrack.get_rates("claude-sonnet-3-7-20250219")
|
|
108
|
+
assert r["family"] == "claude-sonnet-3"
|
|
109
|
+
assert r["input"] == 3.0
|
|
110
|
+
|
|
111
|
+
|
|
98
112
|
def test_get_rates_haiku_4_5():
|
|
99
113
|
r = cctrack.get_rates("claude-haiku-4-5-20251001")
|
|
100
|
-
assert r["family"] == "claude-haiku-4
|
|
114
|
+
assert r["family"] == "claude-haiku-4"
|
|
101
115
|
assert r["input"] == 1.0
|
|
102
116
|
assert r["output"] == 5.0
|
|
103
117
|
|
|
104
118
|
|
|
105
119
|
def test_get_rates_haiku_3_5():
|
|
106
120
|
r = cctrack.get_rates("claude-haiku-3-5-20241022")
|
|
107
|
-
assert r["family"] == "claude-haiku-3"
|
|
121
|
+
assert r["family"] == "claude-haiku-3-5"
|
|
108
122
|
assert r["input"] == 0.80
|
|
109
123
|
assert r["output"] == 4.0
|
|
110
124
|
|
|
111
125
|
|
|
126
|
+
def test_get_rates_haiku_3():
|
|
127
|
+
r = cctrack.get_rates("claude-haiku-3-20240307")
|
|
128
|
+
assert r["family"] == "claude-haiku-3"
|
|
129
|
+
assert r["input"] == 0.25
|
|
130
|
+
assert r["output"] == 1.25
|
|
131
|
+
|
|
132
|
+
|
|
112
133
|
def test_get_rates_unknown_falls_back_to_sonnet():
|
|
113
134
|
r = cctrack.get_rates("claude-mystery-99")
|
|
114
135
|
assert r["family"] == "claude-sonnet-4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|