Skip to content

Commit

Permalink
feat: thread-safe logger.
Browse files Browse the repository at this point in the history
  • Loading branch information
drudilorenzo committed May 28, 2024
1 parent 9293f88 commit 8c0ae6e
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 5 deletions.
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ Models supported:
-------------------
* The response generation is totally up to the user. The library support every model which response contains the fields **usage.prompt_tokens** and **usage.total_tokens** (e.g. chat completions, embeddings, etc.).

Multithreading:
---------------
* Be aware that the classic cost logger is not thread-safe.
* If you want to use it in a multithreading environment, you should use the **thread-safe** version of the logger: **OpenAICostLogger_Singleton**. The interface is the same as the classic logger. This will prevent multiple threads from writing to the same file at the same time.

Note:
-----
* Every cost is specified per **million tokens**.
Expand Down
6 changes: 2 additions & 4 deletions changes_proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Change:
- we can just infer it from `response.model`
- removes possible problems with choosing the right enum or forgetting to change it while changing the model for experiment

2. allow for experiment/subexperiment stats
2. allow for experiment/subexperiment stats

3. ✅ cost tracker handles completion creation - Merged

Expand Down Expand Up @@ -45,6 +45,4 @@ Change:
- change strftime format to `strftime("%Y-%m-%d_%H:%M:%S")`, makes it more readable
- we could possibly infer the datetime and do plots with datetime instead of str
6. ⌛ web ui for stats viz
7. WIP
6. ⌛ web ui for stats viz
1 change: 1 addition & 0 deletions openai_cost_logger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
from .constants import MODELS_COST, DEFAULT_LOG_PATH
from .openai_cost_logger_viz import OpenAICostLoggerViz
from .openai_cost_logger_utils import OpenAICostLoggerUtils
from .openai_cost_logger_singleton import OpenAICostLogger_Singleton
print('imported openai-cost-logger')
66 changes: 66 additions & 0 deletions openai_cost_logger/openai_cost_logger_singleton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import threading

from openai_cost_logger.constants import DEFAULT_LOG_PATH
from openai_cost_logger.openai_cost_logger import OpenAICostLogger


""" Metaclass for creating singletons."""
class Singleton(type):
_instance = None
_lock = threading.Lock()


def __call__(cls, *args, **kwargs):
if not cls._instance:
with cls._lock:
if not cls._instance:
# We have not every built an instance before. Build one now.
instance = super().__call__(*args, **kwargs)
cls._instance = instance
else:
instance = cls._instance
return instance


"""Singleton class for the OpenAICostLogger class."""
class OpenAICostLogger_Singleton(metaclass=Singleton):
def __init__(self, experiment_name: str, cost_upperbound: float, log_folder: str = DEFAULT_LOG_PATH):
"""Initializes the OpenAICostLogger_Singleton class.
Args:
experiment_name (str): the name of the experiment.
log_folder (str): the folder where the logs will be stored.
cost_upperbound (float): the upperbound of the cost.
"""
self.__cost_logger = OpenAICostLogger(
experiment_name=experiment_name,
cost_upperbound=cost_upperbound,
log_folder=log_folder
)
self.lock = threading.Lock() # Lock to ensure thread safety when updating the cost logger.


def update_cost(self, response: dict, input_cost: float, output_cost: float = 0):
"""Updates the cost logger with the response, input cost, and output cost.
Args:
response (dict): the response from the model.
input_cost (float): the cost of the input per million tokens.
output_cost (float, optional): the cost of the output per million tokens.. Defaults to 0.
"""
with self.lock:
self.__cost_logger.update_cost(
response=response,
input_cost=input_cost,
output_cost=output_cost
)


def get_current_cost(self) -> float:
"""Returns the current cost of the experiment.
Returns:
float: the current cost of the experiment.
"""
with self.lock:
return self.__cost_logger.get_current_cost()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
with open('README.rst') as f:
long_description = f.read()

version_number = '0.4.1'
version_number = '0.5.0'

setup(
name='openai-cost-logger',
Expand Down

0 comments on commit 8c0ae6e

Please sign in to comment.