Skip to content

Commit b39eb90

Browse files
committed
Add stats method, add main function
1 parent 99f0fd1 commit b39eb90

1 file changed

Lines changed: 121 additions & 27 deletions

File tree

llms_wrapper/cost_logger.py

Lines changed: 121 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import socket
1111
import getpass
1212
import datetime
13+
import argparse
1314

1415

1516
def get_username() -> str:
@@ -526,33 +527,126 @@ def get_output_tokens(self) -> int:
526527
except Exception as e:
527528
raise Exception(f"Failed to get output_tokens sum: {e}") from e
528529

530+
def stats(self) -> dict:
531+
"""
532+
Generate summary statistics over the entire table.
533+
Includes rows with NULL values using 'NULL' as the key.
534+
535+
Returns:
536+
Dictionary with three top-level keys:
537+
- 'by_user': {user: {modelalias: cost, ...}, ...}
538+
- 'by_project': {project: {modelalias: cost, ...}, ...}
539+
- 'by_user_project': {"user / project": {modelalias: cost, ...}, ...}
540+
541+
NULL values are represented as 'NULL' in the dictionary keys.
542+
543+
Raises:
544+
Exception: If query fails
545+
"""
546+
try:
547+
conn = sqlite3.connect(self.db_path, timeout=5.0)
548+
try:
549+
result = {
550+
'by_user': {},
551+
'by_project': {},
552+
'by_user_project': {}
553+
}
554+
555+
# Get cost by user and modelalias
556+
cursor = conn.execute('''
557+
SELECT user, modelalias, SUM(cost) as total_cost
558+
FROM logs
559+
GROUP BY user, modelalias
560+
ORDER BY user, modelalias
561+
''')
562+
563+
for row in cursor:
564+
user, modelalias, cost = row
565+
user_key = user if user is not None else 'NULL'
566+
if user_key not in result['by_user']:
567+
result['by_user'][user_key] = {}
568+
alias_key = modelalias if modelalias is not None else 'NULL'
569+
result['by_user'][user_key][alias_key] = float(cost) if cost else 0.0
570+
571+
# Get cost by project and modelalias
572+
cursor = conn.execute('''
573+
SELECT project, modelalias, SUM(cost) as total_cost
574+
FROM logs
575+
GROUP BY project, modelalias
576+
ORDER BY project, modelalias
577+
''')
578+
579+
for row in cursor:
580+
project, modelalias, cost = row
581+
project_key = project if project is not None else 'NULL'
582+
if project_key not in result['by_project']:
583+
result['by_project'][project_key] = {}
584+
alias_key = modelalias if modelalias is not None else 'NULL'
585+
result['by_project'][project_key][alias_key] = float(cost) if cost else 0.0
586+
587+
# Get cost by user/project combination and modelalias
588+
cursor = conn.execute('''
589+
SELECT user, project, modelalias, SUM(cost) as total_cost
590+
FROM logs
591+
GROUP BY user, project, modelalias
592+
ORDER BY user, project, modelalias
593+
''')
594+
595+
for row in cursor:
596+
user, project, modelalias, cost = row
597+
user_key = user if user is not None else 'NULL'
598+
project_key = project if project is not None else 'NULL'
599+
key = f"{user_key} / {project_key}"
600+
if key not in result['by_user_project']:
601+
result['by_user_project'][key] = {}
602+
alias_key = modelalias if modelalias is not None else 'NULL'
603+
result['by_user_project'][key][alias_key] = float(cost) if cost else 0.0
604+
605+
return result
606+
607+
finally:
608+
conn.close()
609+
except Exception as e:
610+
raise Exception(f"Failed to generate stats: {e}") from e
611+
612+
def get_args():
613+
parser = argparse.ArgumentParser(description='Show costs')
614+
parser.add_argument('file', type=str, help='Cost database file')
615+
args = parser.parse_args()
616+
argsconfig = {}
617+
argsconfig.update(vars(args))
618+
return argsconfig
619+
620+
621+
def main():
622+
config = get_args()
623+
costs = Log2Sqlite(config['file'])
624+
all_stats = costs.stats()
625+
print("Cost by User:")
626+
627+
def fflt(value: float):
628+
return f"{value:.8f}".rstrip('0').rstrip('.')
629+
630+
for user, aliases in all_stats['by_user'].items():
631+
total = fflt(sum(aliases.values()))
632+
print(f" {user}: ${total}")
633+
for alias, cost in aliases.items():
634+
cost = fflt(cost)
635+
print(f" {alias}: ${cost}")
636+
637+
# Print project costs
638+
print("\nCost by Project:")
639+
for project, aliases in all_stats['by_project'].items():
640+
total = fflt(sum(aliases.values()))
641+
print(f" {project}: ${total}")
642+
643+
# Print user/project combinations
644+
print("\nCost by User/Project:")
645+
for combo, aliases in all_stats['by_user_project'].items():
646+
total = fflt(sum(aliases.values()))
647+
print(f" {combo}: ${total}")
648+
529649

530650
# Example usage
531651
if __name__ == '__main__':
532-
# Create logger with defaults
533-
logger = Log2Sqlite('api_usage.db', project='my_project', user='alice')
534-
535-
# Log some entries
536-
logger.log({
537-
'model': 'gpt-4',
538-
'task': 'summarization',
539-
'cost': 0.05,
540-
'input_tokens': 1000,
541-
'output_tokens': 200,
542-
'datetime': '2024-02-06 10:30:00'
543-
})
544-
545-
logger.log({
546-
'model': 'gpt-3.5-turbo',
547-
'task': 'chat',
548-
'cost': 0.01,
549-
'input_tokens': 500,
550-
'output_tokens': 100,
551-
'datetime': '2024-02-06 11:00:00'
552-
})
553-
554-
# Get aggregations (only for project='my_project', user='alice')
555-
print(f"Total cost: ${logger.get_cost():.4f}")
556-
print(f"Total input tokens: {logger.get_input_tokens()}")
557-
print(f"Total output tokens: {logger.get_output_tokens()}")
558-
print(f"Get all: {logger.get()}")
652+
main()

0 commit comments

Comments
 (0)