import configparser import os import time from typing import Dict, Final, List, Optional CONFIG_FILE_PATH_DEFAULT: Final[str] = "~/.opik.config" def create_uuid7(): ns = time.time_ns() last = [0, 0, 0, 0] # Simple uuid7 implementation sixteen_secs = 16_000_000_000 t1, rest1 = divmod(ns, sixteen_secs) t2, rest2 = divmod(rest1 << 16, sixteen_secs) t3, _ = divmod(rest2 << 12, sixteen_secs) t3 |= 7 << 12 # Put uuid version in top 4 bits, which are 0 in t3 # The next two bytes are an int (t4) with two bits for # the variant 2 and a 14 bit sequence counter which increments # if the time is unchanged. if t1 == last[0] and t2 == last[1] and t3 == last[2]: # Stop the seq counter wrapping past 0x3FFF. # This won't happen in practice, but if it does, # uuids after the 16383rd with that same timestamp # will not longer be correctly ordered but # are still unique due to the 6 random bytes. if last[3] < 0x3FFF: last[3] += 1 else: last[:] = (t1, t2, t3, 0) t4 = (2 << 14) | last[3] # Put variant 0b10 in top two bits # Six random bytes for the lower part of the uuid rand = os.urandom(6) return f"{t1:>08x}-{t2:>04x}-{t3:>04x}-{t4:>04x}-{rand.hex()}" def _read_opik_config_file() -> Dict[str, str]: config_path = os.path.expanduser(CONFIG_FILE_PATH_DEFAULT) config = configparser.ConfigParser() config.read(config_path) config_values = { section: dict(config.items(section)) for section in config.sections() } if "opik" in config_values: return config_values["opik"] return {} def _get_env_variable(key: str) -> Optional[str]: env_prefix = "opik_" return os.getenv((env_prefix + key).upper(), None) def get_opik_config_variable( key: str, user_value: Optional[str] = None, default_value: Optional[str] = None ) -> Optional[str]: """ Get the configuration value of a variable, order priority is: 1. user provided value 2. environment variable 3. Opik configuration file 4. default value """ # Return user provided value if it is not None if user_value is not None: return user_value # Return environment variable if it is not None env_value = _get_env_variable(key) if env_value is not None: return env_value # Return value from Opik configuration file if it is not None config_values = _read_opik_config_file() if key in config_values: return config_values[key] # Return default value if it is not None return default_value def create_usage_object(usage): usage_dict = {} if usage.completion_tokens is not None: usage_dict["completion_tokens"] = usage.completion_tokens if usage.prompt_tokens is not None: usage_dict["prompt_tokens"] = usage.prompt_tokens if usage.total_tokens is not None: usage_dict["total_tokens"] = usage.total_tokens return usage_dict def _remove_nulls(x): x_ = {k: v for k, v in x.items() if v is not None} return x_ def get_traces_and_spans_from_payload(payload: List): traces = [_remove_nulls(x) for x in payload if "type" not in x] spans = [_remove_nulls(x) for x in payload if "type" in x] return traces, spans