tokenator 0.1.10__py3-none-any.whl → 0.1.12__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- tokenator/anthropic/client_anthropic.py +0 -1
- tokenator/base_wrapper.py +1 -1
- tokenator/openai/client_openai.py +9 -3
- tokenator-0.1.12.dist-info/METADATA +240 -0
- {tokenator-0.1.10.dist-info → tokenator-0.1.12.dist-info}/RECORD +7 -7
- {tokenator-0.1.10.dist-info → tokenator-0.1.12.dist-info}/WHEEL +1 -1
- tokenator-0.1.10.dist-info/METADATA +0 -127
- {tokenator-0.1.10.dist-info → tokenator-0.1.12.dist-info}/LICENSE +0 -0
@@ -71,7 +71,6 @@ def _create_usage_callback(execution_id, log_usage_fn):
|
|
71
71
|
usage_data.usage.prompt_tokens += chunk.message.usage.input_tokens
|
72
72
|
usage_data.usage.completion_tokens += chunk.message.usage.output_tokens
|
73
73
|
elif isinstance(chunk, RawMessageDeltaEvent):
|
74
|
-
usage_data.usage.prompt_tokens += chunk.usage.input_tokens
|
75
74
|
usage_data.usage.completion_tokens += chunk.usage.output_tokens
|
76
75
|
|
77
76
|
usage_data.usage.total_tokens = usage_data.usage.prompt_tokens + usage_data.usage.completion_tokens
|
tokenator/base_wrapper.py
CHANGED
@@ -14,7 +14,9 @@ logger = logging.getLogger(__name__)
|
|
14
14
|
|
15
15
|
|
16
16
|
class BaseOpenAIWrapper(BaseWrapper):
|
17
|
-
provider = "openai"
|
17
|
+
def __init__(self, client, db_path=None, provider: str = "openai"):
|
18
|
+
super().__init__(client, db_path)
|
19
|
+
self.provider = provider
|
18
20
|
|
19
21
|
def _process_response_usage(
|
20
22
|
self, response: ResponseType
|
@@ -134,6 +136,7 @@ class AsyncOpenAIWrapper(BaseOpenAIWrapper):
|
|
134
136
|
def tokenator_openai(
|
135
137
|
client: OpenAI,
|
136
138
|
db_path: Optional[str] = None,
|
139
|
+
provider: str = "openai",
|
137
140
|
) -> OpenAIWrapper: ...
|
138
141
|
|
139
142
|
|
@@ -141,23 +144,26 @@ def tokenator_openai(
|
|
141
144
|
def tokenator_openai(
|
142
145
|
client: AsyncOpenAI,
|
143
146
|
db_path: Optional[str] = None,
|
147
|
+
provider: str = "openai",
|
144
148
|
) -> AsyncOpenAIWrapper: ...
|
145
149
|
|
146
150
|
|
147
151
|
def tokenator_openai(
|
148
152
|
client: Union[OpenAI, AsyncOpenAI],
|
149
153
|
db_path: Optional[str] = None,
|
154
|
+
provider: str = "openai",
|
150
155
|
) -> Union[OpenAIWrapper, AsyncOpenAIWrapper]:
|
151
156
|
"""Create a token-tracking wrapper for an OpenAI client.
|
152
157
|
|
153
158
|
Args:
|
154
159
|
client: OpenAI or AsyncOpenAI client instance
|
155
160
|
db_path: Optional path to SQLite database for token tracking
|
161
|
+
provider: Provider name, defaults to "openai"
|
156
162
|
"""
|
157
163
|
if isinstance(client, OpenAI):
|
158
|
-
return OpenAIWrapper(client=client, db_path=db_path)
|
164
|
+
return OpenAIWrapper(client=client, db_path=db_path, provider=provider)
|
159
165
|
|
160
166
|
if isinstance(client, AsyncOpenAI):
|
161
|
-
return AsyncOpenAIWrapper(client=client, db_path=db_path)
|
167
|
+
return AsyncOpenAIWrapper(client=client, db_path=db_path, provider=provider)
|
162
168
|
|
163
169
|
raise ValueError("Client must be an instance of OpenAI or AsyncOpenAI")
|
@@ -0,0 +1,240 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: tokenator
|
3
|
+
Version: 0.1.12
|
4
|
+
Summary: Token usage tracking wrapper for LLMs
|
5
|
+
License: MIT
|
6
|
+
Author: Ujjwal Maheshwari
|
7
|
+
Author-email: your.email@example.com
|
8
|
+
Requires-Python: >=3.9,<4.0
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
16
|
+
Requires-Dist: alembic (>=1.13.0,<2.0.0)
|
17
|
+
Requires-Dist: anthropic (>=0.40.0,<0.41.0)
|
18
|
+
Requires-Dist: openai (>=1.57.0,<2.0.0)
|
19
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
20
|
+
Requires-Dist: sqlalchemy (>=2.0.0,<3.0.0)
|
21
|
+
Description-Content-Type: text/markdown
|
22
|
+
|
23
|
+
# Tokenator : Track and analyze LLM token usage and cost
|
24
|
+
|
25
|
+
Have you ever wondered about :
|
26
|
+
- How many tokens does your AI agent consume?
|
27
|
+
- How much does it cost to do run a complex AI workflow with multiple LLM providers?
|
28
|
+
- How much money/tokens did you spend today on developing with LLMs?
|
29
|
+
|
30
|
+
Afraid not, tokenator is here! With tokenator's easy to use API, you can start tracking LLM usage in a matter of minutes.
|
31
|
+
|
32
|
+
Get started with just 3 lines of code!
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
```bash
|
37
|
+
pip install tokenator
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
### OpenAI
|
43
|
+
|
44
|
+
```python
|
45
|
+
from openai import OpenAI
|
46
|
+
from tokenator import tokenator_openai
|
47
|
+
|
48
|
+
openai_client = OpenAI(api_key="your-api-key")
|
49
|
+
|
50
|
+
# Wrap it with Tokenator
|
51
|
+
client = tokenator_openai(openai_client)
|
52
|
+
|
53
|
+
# Use it exactly like the OpenAI client
|
54
|
+
response = client.chat.completions.create(
|
55
|
+
model="gpt-4o",
|
56
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
57
|
+
)
|
58
|
+
```
|
59
|
+
|
60
|
+
Works with AsyncOpenAI and `streaming=True` as well!
|
61
|
+
Note : When streaming, don't forget to add `stream_options={"include_usage": True}` to the `create()` call!
|
62
|
+
|
63
|
+
### Cost Analysis
|
64
|
+
|
65
|
+
```python
|
66
|
+
from tokenator import usage
|
67
|
+
|
68
|
+
# Get usage for different time periods
|
69
|
+
usage.last_hour()
|
70
|
+
usage.last_day()
|
71
|
+
usage.last_week()
|
72
|
+
usage.last_month()
|
73
|
+
|
74
|
+
# Custom date range
|
75
|
+
usage.between("2024-03-01", "2024-03-15")
|
76
|
+
|
77
|
+
# Get usage for different LLM providers
|
78
|
+
usage.last_day("openai")
|
79
|
+
usage.last_day("anthropic")
|
80
|
+
usage.last_day("google")
|
81
|
+
```
|
82
|
+
|
83
|
+
### Example `usage` object
|
84
|
+
|
85
|
+
```python
|
86
|
+
print(cost.last_hour().model_dump_json(indent=4))
|
87
|
+
```
|
88
|
+
|
89
|
+
```json
|
90
|
+
{
|
91
|
+
"total_cost": 0.0004,
|
92
|
+
"total_tokens": 79,
|
93
|
+
"prompt_tokens": 52,
|
94
|
+
"completion_tokens": 27,
|
95
|
+
"providers": [
|
96
|
+
{
|
97
|
+
"total_cost": 0.0004,
|
98
|
+
"total_tokens": 79,
|
99
|
+
"prompt_tokens": 52,
|
100
|
+
"completion_tokens": 27,
|
101
|
+
"provider": "openai",
|
102
|
+
"models": [
|
103
|
+
{
|
104
|
+
"total_cost": 0.0004,
|
105
|
+
"total_tokens": 79,
|
106
|
+
"prompt_tokens": 52,
|
107
|
+
"completion_tokens": 27,
|
108
|
+
"model": "gpt-4o-2024-08-06"
|
109
|
+
}
|
110
|
+
]
|
111
|
+
}
|
112
|
+
]
|
113
|
+
}
|
114
|
+
```
|
115
|
+
|
116
|
+
## Features
|
117
|
+
|
118
|
+
- Drop-in replacement for OpenAI, Anthropic client
|
119
|
+
- Automatic token usage tracking
|
120
|
+
- Cost analysis for different time periods
|
121
|
+
- SQLite storage with zero configuration
|
122
|
+
- Thread-safe operations
|
123
|
+
- Minimal memory footprint
|
124
|
+
- Minimal latency footprint
|
125
|
+
|
126
|
+
### Anthropic
|
127
|
+
|
128
|
+
```python
|
129
|
+
from anthropic import Anthropic, AsyncAnthropic
|
130
|
+
from tokenator import tokenator_anthropic
|
131
|
+
|
132
|
+
anthropic_client = AsyncAnthropic(api_key="your-api-key")
|
133
|
+
|
134
|
+
# Wrap it with Tokenator
|
135
|
+
client = tokenator_anthropic(anthropic_client)
|
136
|
+
|
137
|
+
# Use it exactly like the Anthropic client
|
138
|
+
response = await client.messages.create(
|
139
|
+
model="claude-3-5-haiku-20241022",
|
140
|
+
messages=[{"role": "user", "content": "hello how are you"}],
|
141
|
+
max_tokens=20,
|
142
|
+
)
|
143
|
+
|
144
|
+
print(response)
|
145
|
+
|
146
|
+
print(usage.last_execution().model_dump_json(indent=4))
|
147
|
+
"""
|
148
|
+
{
|
149
|
+
"total_cost": 0.0001,
|
150
|
+
"total_tokens": 23,
|
151
|
+
"prompt_tokens": 10,
|
152
|
+
"completion_tokens": 13,
|
153
|
+
"providers": [
|
154
|
+
{
|
155
|
+
"total_cost": 0.0001,
|
156
|
+
"total_tokens": 23,
|
157
|
+
"prompt_tokens": 10,
|
158
|
+
"completion_tokens": 13,
|
159
|
+
"provider": "anthropic",
|
160
|
+
"models": [
|
161
|
+
{
|
162
|
+
"total_cost": 0.0004,
|
163
|
+
"total_tokens": 79,
|
164
|
+
"prompt_tokens": 52,
|
165
|
+
"completion_tokens": 27,
|
166
|
+
"model": "claude-3-5-haiku-20241022"
|
167
|
+
}
|
168
|
+
]
|
169
|
+
}
|
170
|
+
]
|
171
|
+
}
|
172
|
+
"""
|
173
|
+
```
|
174
|
+
|
175
|
+
### xAI
|
176
|
+
|
177
|
+
You can use xAI models through the `openai` SDK and track usage using `provider` parameter in `tokenator`.
|
178
|
+
|
179
|
+
```python
|
180
|
+
from openai import OpenAI
|
181
|
+
from tokenator import tokenator_openai
|
182
|
+
|
183
|
+
xai_client = OpenAI(
|
184
|
+
api_key=os.getenv("XAI_API_KEY"),
|
185
|
+
base_url="https://api.x.ai/v1"
|
186
|
+
)
|
187
|
+
|
188
|
+
# Wrap it with Tokenator
|
189
|
+
client = tokenator_openai(client, db_path=temp_db, provider="xai")
|
190
|
+
|
191
|
+
# Use it exactly like the OpenAI client but with xAI models
|
192
|
+
response = client.chat.completions.create(
|
193
|
+
model="grok-2-latest",
|
194
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
195
|
+
)
|
196
|
+
|
197
|
+
print(response)
|
198
|
+
|
199
|
+
print(usage.last_execution())
|
200
|
+
```
|
201
|
+
|
202
|
+
### Other AI model providers through openai SDKs
|
203
|
+
|
204
|
+
Today, a variety of AI companies have made their APIs compatible to the `openai` SDK.
|
205
|
+
You can track usage of any such AI models using `tokenator`'s `provider` parameter.
|
206
|
+
|
207
|
+
For example, let's see how we can track usage of `perplexity` tokens.
|
208
|
+
|
209
|
+
```python
|
210
|
+
from openai import OpenAI
|
211
|
+
from tokenator import tokenator_openai
|
212
|
+
|
213
|
+
xai_client = OpenAI(
|
214
|
+
api_key=os.getenv("PERPLEXITY_API_KEY"),
|
215
|
+
base_url="https://api.perplexity.ai"
|
216
|
+
)
|
217
|
+
|
218
|
+
# Wrap it with Tokenator
|
219
|
+
client = tokenator_openai(client, db_path=temp_db, provider="perplexity")
|
220
|
+
|
221
|
+
# Use it exactly like the OpenAI client but with xAI models
|
222
|
+
response = client.chat.completions.create(
|
223
|
+
model="grok-2-latest",
|
224
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
225
|
+
)
|
226
|
+
|
227
|
+
print(response)
|
228
|
+
|
229
|
+
print(usage.last_execution())
|
230
|
+
|
231
|
+
print(usage.provider("perplexity"))
|
232
|
+
```
|
233
|
+
|
234
|
+
---
|
235
|
+
|
236
|
+
Most importantly, none of your data is ever sent to any server.
|
237
|
+
|
238
|
+
## License
|
239
|
+
|
240
|
+
MIT
|
@@ -1,19 +1,19 @@
|
|
1
1
|
tokenator/__init__.py,sha256=bIAPyGAvWreS2i_5tzxJEyX9JlZgAUNxzVk1iHNUhvU,593
|
2
|
-
tokenator/anthropic/client_anthropic.py,sha256=
|
2
|
+
tokenator/anthropic/client_anthropic.py,sha256=fnjWz_Kf8D0GUTudkZNeSmH9ueCGFLDSBDz1U8Jri3Y,5861
|
3
3
|
tokenator/anthropic/stream_interceptors.py,sha256=4VHC_-WkG3Pa10YizmFLrHcbz0Tm2MR_YB5-uohKp5A,5221
|
4
|
-
tokenator/base_wrapper.py,sha256=
|
4
|
+
tokenator/base_wrapper.py,sha256=IO344KWbRswQy4vG_pBxWPR7Wp7K-4mlgmS3SCYGep8,2467
|
5
5
|
tokenator/create_migrations.py,sha256=k9IHiGK21dLTA8MYNsuhO0-kUVIcMSViMFYtY4WU2Rw,730
|
6
6
|
tokenator/migrations/env.py,sha256=JoF5MJ4ae0wJW5kdBHuFlG3ZqeCCDvbMcU8fNA_a6hM,1396
|
7
7
|
tokenator/migrations/script.py.mako,sha256=nJL-tbLQE0Qy4P9S4r4ntNAcikPtoFUlvXe6xvm9ot8,635
|
8
8
|
tokenator/migrations/versions/f6f1f2437513_initial_migration.py,sha256=4cveHkwSxs-hxOPCm81YfvGZTkJJ2ClAFmyL98-1VCo,1910
|
9
9
|
tokenator/migrations.py,sha256=YAf9gZmDzAq36PWWXPtdUQoJFYPXtIDzflC79H6gcJg,1114
|
10
10
|
tokenator/models.py,sha256=MhYwCvmqposUNDRxFZNAVnzCqBTHxNL3Hp0MNFXM5ck,1201
|
11
|
-
tokenator/openai/client_openai.py,sha256=
|
11
|
+
tokenator/openai/client_openai.py,sha256=Ffa3ujLh5PuPe1W8KSISGH3NonZ_AC6ZpKhO6kTupTU,5996
|
12
12
|
tokenator/openai/stream_interceptors.py,sha256=ez1MnjRZW_rEalv2SIPAvrU9oMD6OJoD9vht-057fDM,5243
|
13
13
|
tokenator/schemas.py,sha256=Ye8hqZlrm3Gh2FyvOVX-hWCpKynWxS58QQRQMfDtIAQ,2114
|
14
14
|
tokenator/usage.py,sha256=eTWfcRrTLop-30FmwHpi7_GwCJxU6Qfji374hG1Qptw,8476
|
15
15
|
tokenator/utils.py,sha256=xg9l2GV1yJL1BlxKL1r8CboABWDslf3G5rGQEJSjFrE,1973
|
16
|
-
tokenator-0.1.
|
17
|
-
tokenator-0.1.
|
18
|
-
tokenator-0.1.
|
19
|
-
tokenator-0.1.
|
16
|
+
tokenator-0.1.12.dist-info/LICENSE,sha256=wdG-B6-ODk8RQ4jq5uXSn0w1UWTzCH_MMyvh7AwtGns,1074
|
17
|
+
tokenator-0.1.12.dist-info/METADATA,sha256=VdJVlwESY2_QbiterwI1lH9dng4r4WwWYd6MwXlT9V4,5969
|
18
|
+
tokenator-0.1.12.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
19
|
+
tokenator-0.1.12.dist-info/RECORD,,
|
@@ -1,127 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: tokenator
|
3
|
-
Version: 0.1.10
|
4
|
-
Summary: Token usage tracking wrapper for LLMs
|
5
|
-
License: MIT
|
6
|
-
Author: Ujjwal Maheshwari
|
7
|
-
Author-email: your.email@example.com
|
8
|
-
Requires-Python: >=3.9,<4.0
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
11
|
-
Classifier: Programming Language :: Python :: 3.9
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
16
|
-
Requires-Dist: alembic (>=1.13.0,<2.0.0)
|
17
|
-
Requires-Dist: anthropic (>=0.40.0,<0.41.0)
|
18
|
-
Requires-Dist: openai (>=1.57.0,<2.0.0)
|
19
|
-
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
20
|
-
Requires-Dist: sqlalchemy (>=2.0.0,<3.0.0)
|
21
|
-
Description-Content-Type: text/markdown
|
22
|
-
|
23
|
-
# Tokenator : Easiest way to track and analyze LLM token usage and cost
|
24
|
-
|
25
|
-
Have you ever wondered about :
|
26
|
-
- How many tokens does your AI agent consume?
|
27
|
-
- How much does it cost to do run a complex AI workflow with multiple LLM providers?
|
28
|
-
- How much money did I spent today on development?
|
29
|
-
|
30
|
-
Afraid not, tokenator is here! With tokenator's easy to use API, you can start tracking LLM usage in a matter of minutes.
|
31
|
-
|
32
|
-
Get started with just 3 lines of code!
|
33
|
-
|
34
|
-
## Installation
|
35
|
-
|
36
|
-
```bash
|
37
|
-
pip install tokenator
|
38
|
-
```
|
39
|
-
|
40
|
-
## Usage
|
41
|
-
|
42
|
-
### OpenAI
|
43
|
-
|
44
|
-
```python
|
45
|
-
from openai import OpenAI
|
46
|
-
from tokenator import tokenator_openai
|
47
|
-
|
48
|
-
openai_client = OpenAI(api_key="your-api-key")
|
49
|
-
|
50
|
-
# Wrap it with Tokenator
|
51
|
-
client = tokenator_openai(openai_client)
|
52
|
-
|
53
|
-
# Use it exactly like the OpenAI client
|
54
|
-
response = client.chat.completions.create(
|
55
|
-
model="gpt-4o",
|
56
|
-
messages=[{"role": "user", "content": "Hello!"}]
|
57
|
-
)
|
58
|
-
```
|
59
|
-
|
60
|
-
### Cost Analysis
|
61
|
-
|
62
|
-
```python
|
63
|
-
from tokenator import usage
|
64
|
-
|
65
|
-
# Get usage for different time periods
|
66
|
-
usage.last_hour()
|
67
|
-
usage.last_day()
|
68
|
-
usage.last_week()
|
69
|
-
usage.last_month()
|
70
|
-
|
71
|
-
# Custom date range
|
72
|
-
usage.between("2024-03-01", "2024-03-15")
|
73
|
-
|
74
|
-
# Get usage for different LLM providers
|
75
|
-
usage.last_day("openai")
|
76
|
-
usage.last_day("anthropic")
|
77
|
-
usage.last_day("google")
|
78
|
-
```
|
79
|
-
|
80
|
-
### Example `usage` object
|
81
|
-
|
82
|
-
```python
|
83
|
-
print(cost.last_hour().model_dump_json(indent=4))
|
84
|
-
```
|
85
|
-
|
86
|
-
```json
|
87
|
-
{
|
88
|
-
"total_cost": 0.0004,
|
89
|
-
"total_tokens": 79,
|
90
|
-
"prompt_tokens": 52,
|
91
|
-
"completion_tokens": 27,
|
92
|
-
"providers": [
|
93
|
-
{
|
94
|
-
"total_cost": 0.0004,
|
95
|
-
"total_tokens": 79,
|
96
|
-
"prompt_tokens": 52,
|
97
|
-
"completion_tokens": 27,
|
98
|
-
"provider": "openai",
|
99
|
-
"models": [
|
100
|
-
{
|
101
|
-
"total_cost": 0.0004,
|
102
|
-
"total_tokens": 79,
|
103
|
-
"prompt_tokens": 52,
|
104
|
-
"completion_tokens": 27,
|
105
|
-
"model": "gpt-4o-2024-08-06"
|
106
|
-
}
|
107
|
-
]
|
108
|
-
}
|
109
|
-
]
|
110
|
-
}
|
111
|
-
```
|
112
|
-
|
113
|
-
## Features
|
114
|
-
|
115
|
-
- Drop-in replacement for OpenAI, Anthropic client
|
116
|
-
- Automatic token usage tracking
|
117
|
-
- Cost analysis for different time periods
|
118
|
-
- SQLite storage with zero configuration
|
119
|
-
- Thread-safe operations
|
120
|
-
- Minimal memory footprint
|
121
|
-
- Minimal latency footprint
|
122
|
-
|
123
|
-
Most importantly, none of your data is ever sent to any server.
|
124
|
-
|
125
|
-
## License
|
126
|
-
|
127
|
-
MIT
|
File without changes
|