Skip to content

Commit ab83c50

Browse files
committed
add usage and cost logging
1 parent 79e7601 commit ab83c50

File tree

7 files changed

+1044
-10
lines changed

7 files changed

+1044
-10
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ The result of 47 + 23 is 70.
259259
- model: `claude-sonnet-4-20250514`
260260
- finish_reason: `stop`
261261
- usage:
262-
`Usage(completion_tokens=17, prompt_tokens=533, total_tokens=550, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`
262+
`Usage(completion_tokens=18, prompt_tokens=573, total_tokens=591, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`
263263

264264
</details>
265265

@@ -312,15 +312,18 @@ Now I’ll add the result (70) to the third number (59):
312312
'name': 'add_numbers',
313313
'content': '129'}
314314

315-
The answer is 129. So 47 + 23 + 59 = 129.
315+
The answer is **129**.
316+
317+
I calculated this by first adding 47 + 23 = 70, then adding 70 + 59 =
318+
129.
316319

317320
<details>
318321

319322
- id: `chatcmpl-xxx`
320323
- model: `claude-sonnet-4-20250514`
321324
- finish_reason: `stop`
322325
- usage:
323-
`Usage(completion_tokens=25, prompt_tokens=662, total_tokens=687, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`
326+
`Usage(completion_tokens=41, prompt_tokens=702, total_tokens=743, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=0)`
324327

325328
</details>
326329

cachy.jsonl

Lines changed: 7 additions & 0 deletions
Large diffs are not rendered by default.

lisette/_modidx.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,16 @@
5050
'lisette.core.patch_litellm': ('core.html#patch_litellm', 'lisette/core.py'),
5151
'lisette.core.random_tool_id': ('core.html#random_tool_id', 'lisette/core.py'),
5252
'lisette.core.remove_cache_ckpts': ('core.html#remove_cache_ckpts', 'lisette/core.py'),
53-
'lisette.core.stream_with_complete': ('core.html#stream_with_complete', 'lisette/core.py')}}}
53+
'lisette.core.stream_with_complete': ('core.html#stream_with_complete', 'lisette/core.py')},
54+
'lisette.usage': { 'lisette.usage.LisetteUsageLogger': ('usage.html#lisetteusagelogger', 'lisette/usage.py'),
55+
'lisette.usage.LisetteUsageLogger.__init__': ('usage.html#lisetteusagelogger.__init__', 'lisette/usage.py'),
56+
'lisette.usage.LisetteUsageLogger._log_usage': ( 'usage.html#lisetteusagelogger._log_usage',
57+
'lisette/usage.py'),
58+
'lisette.usage.LisetteUsageLogger.async_log_success_event': ( 'usage.html#lisetteusagelogger.async_log_success_event',
59+
'lisette/usage.py'),
60+
'lisette.usage.LisetteUsageLogger.log_success_event': ( 'usage.html#lisetteusagelogger.log_success_event',
61+
'lisette/usage.py'),
62+
'lisette.usage.LisetteUsageLogger.user_id_fn': ( 'usage.html#lisetteusagelogger.user_id_fn',
63+
'lisette/usage.py'),
64+
'lisette.usage.Usage': ('usage.html#usage', 'lisette/usage.py'),
65+
'lisette.usage.Usage.total_cost': ('usage.html#usage.total_cost', 'lisette/usage.py')}}}

lisette/usage.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Lisette usage and cost monitoring"""
2+
3+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_usage.ipynb.
4+
5+
# %% auto 0
6+
__all__ = ['Usage', 'LisetteUsageLogger']
7+
8+
# %% ../nbs/01_usage.ipynb
9+
from litellm.integrations.custom_logger import CustomLogger
10+
import time
11+
try:
12+
from fastlite import *
13+
from fastlite.core import dataclass
14+
except ImportError: raise ImportError("Please install `fastlite` to use sqlite based lisette usage logging.")
15+
16+
# %% ../nbs/01_usage.ipynb
17+
class Usage: id:int; timestamp:float; model:str; user_id:str; prompt_tokens:int; completion_tokens:int; total_tokens:int; cached_tokens:int; cache_creation_tokens:int; cache_read_tokens:int; web_search_requests:int; response_cost:int
18+
19+
# %% ../nbs/01_usage.ipynb
20+
class LisetteUsageLogger(CustomLogger):
21+
def __init__(self, db_path):
22+
self.db = Database(db_path)
23+
self.usage = self.db.create(Usage)
24+
25+
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): self._log_usage(response_obj, kwargs['response_cost'], start_time, end_time)
26+
def log_success_event(self, kwargs, response_obj, start_time, end_time): self._log_usage(response_obj, kwargs['response_cost'], start_time, end_time)
27+
def _log_usage(self, response_obj, response_cost, start_time, end_time):
28+
usage = response_obj.usage
29+
ptd = usage.prompt_tokens_details
30+
self.usage.insert(Usage(timestamp=time.time(), model=response_obj.model, user_id=self.user_id_fn(), prompt_tokens=usage.prompt_tokens, completion_tokens=usage.completion_tokens,
31+
total_tokens=usage.total_tokens, cached_tokens=ptd.cached_tokens if ptd else 0, cache_creation_tokens=usage.cache_creation_input_tokens,
32+
cache_read_tokens=usage.cache_read_input_tokens, web_search_requests=nested_idx(usage, 'server_tool_use', 'web_search_requests'), response_cost=response_cost))
33+
34+
def user_id_fn(self): raise NotImplementedError('Please implement `LisetteUsageLogger.user_id_fn` before initializing, e.g using fastcore.patch.')
35+
36+
# %% ../nbs/01_usage.ipynb
37+
@patch
38+
def total_cost(self:Usage, sc=0.01): return self.response_cost + sc * ifnone(self.web_search_requests, 0)

nbs/00_core.ipynb

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -773,12 +773,12 @@
773773
"- id: `chatcmpl-xxx`\n",
774774
"- model: `claude-sonnet-4-5-20250929`\n",
775775
"- finish_reason: `stop`\n",
776-
"- usage: `Usage(completion_tokens=5, prompt_tokens=3, total_tokens=8, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=2070, cache_read_input_tokens=0)`\n",
776+
"- usage: `Usage(completion_tokens=5, prompt_tokens=2073, total_tokens=2078, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=2070, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=2070)`\n",
777777
"\n",
778778
"</details>"
779779
],
780780
"text/plain": [
781-
"ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='1', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=5, prompt_tokens=3, total_tokens=8, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None), cache_creation_input_tokens=2070, cache_read_input_tokens=0))"
781+
"ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='1', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=5, prompt_tokens=2073, total_tokens=2078, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=2070, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=2070))"
782782
]
783783
},
784784
"execution_count": null,
@@ -816,12 +816,12 @@
816816
"- id: `chatcmpl-xxx`\n",
817817
"- model: `claude-sonnet-4-5-20250929`\n",
818818
"- finish_reason: `stop`\n",
819-
"- usage: `Usage(completion_tokens=5, prompt_tokens=2073, total_tokens=2078, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=2070, text_tokens=None, image_tokens=None), cache_creation_input_tokens=2074, cache_read_input_tokens=2070)`\n",
819+
"- usage: `Usage(completion_tokens=5, prompt_tokens=4147, total_tokens=4152, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=4144, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=4144)`\n",
820820
"\n",
821821
"</details>"
822822
],
823823
"text/plain": [
824-
"ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='2', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=5, prompt_tokens=2073, total_tokens=2078, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=2070, text_tokens=None, image_tokens=None), cache_creation_input_tokens=2074, cache_read_input_tokens=2070))"
824+
"ModelResponse(id='chatcmpl-xxx', created=1000000000, model='claude-sonnet-4-5-20250929', object='chat.completion', system_fingerprint=None, choices=[Choices(finish_reason='stop', index=0, message=Message(content='2', role='assistant', tool_calls=None, function_call=None, provider_specific_fields={'citations': None, 'thinking_blocks': None}))], usage=Usage(completion_tokens=5, prompt_tokens=4147, total_tokens=4152, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=4144, text_tokens=None, image_tokens=None), cache_creation_input_tokens=0, cache_read_input_tokens=4144))"
825825
]
826826
},
827827
"execution_count": null,
@@ -851,7 +851,7 @@
851851
{
852852
"data": {
853853
"text/plain": [
854-
"PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=2070, text_tokens=None, image_tokens=None)"
854+
"PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=4144, text_tokens=None, image_tokens=None)"
855855
]
856856
},
857857
"execution_count": null,
@@ -3833,7 +3833,14 @@
38333833
"text": [
38343834
"Otters are charismatic members of the weasel family found on every continent except Australia and Antarctica. There are 13 species in total, including sea otters and river otters.\n",
38353835
"\n",
3836-
"These aquatic mammals have elongated bodies, long tails, and soft, dense fur. In fact, otters have the densest fur of any animal—as many as a million hairs per square inch. Webbed feet and powerful tails make otters strong swimmers.\n",
3836+
"These aquatic mammals have elongated bodies, long tails, and soft, dense fur"
3837+
]
3838+
},
3839+
{
3840+
"name": "stdout",
3841+
"output_type": "stream",
3842+
"text": [
3843+
". In fact, otters have the densest fur of any animal—as many as a million hairs per square inch. Webbed feet and powerful tails make otters strong swimmers.\n",
38373844
"\n",
38383845
"All otters are expert hunters that eat fish, crustaceans, and other critters. Sea otters float on their backs, place a rock on their chest, then smash mollusks down on it until it breaks open. They're also known for being playful animals, engaging in activities like sliding into water on natural slides."
38393846
]

0 commit comments

Comments
 (0)