forked from phoenix/litellm-mirror
(refactor) remove berrispendLogger - unused logging integration (#6363)
* fix remove berrispendLogger * remove unused clickhouse logger
This commit is contained in:
parent
a0c5fee61d
commit
7a5f997fc9
8 changed files with 3 additions and 583 deletions
|
@ -1,72 +0,0 @@
|
||||||
import clickhouse_connect
|
|
||||||
import datetime as datetime
|
|
||||||
import os
|
|
||||||
|
|
||||||
client = clickhouse_connect.get_client(
|
|
||||||
host=os.getenv("CLICKHOUSE_HOST"),
|
|
||||||
port=int(os.getenv("CLICKHOUSE_PORT")),
|
|
||||||
username=os.getenv("CLICKHOUSE_USERNAME"),
|
|
||||||
password=os.getenv("CLICKHOUSE_PASSWORD"),
|
|
||||||
)
|
|
||||||
import clickhouse_connect
|
|
||||||
|
|
||||||
row1 = [
|
|
||||||
"ishaan", # request_id
|
|
||||||
"GET", # call_type
|
|
||||||
"api_key_123", # api_key
|
|
||||||
50.00, # spend
|
|
||||||
1000, # total_tokens
|
|
||||||
800, # prompt_tokens
|
|
||||||
200, # completion_tokens
|
|
||||||
datetime.datetime.now(), # startTime (replace with the actual timestamp)
|
|
||||||
datetime.datetime.now(), # endTime (replace with the actual timestamp)
|
|
||||||
"gpt-3.5", # model
|
|
||||||
"user123", # user
|
|
||||||
'{"key": "value"}', # metadata (replace with valid JSON)
|
|
||||||
"True", # cache_hit
|
|
||||||
"cache_key_123", # cache_key
|
|
||||||
"tag1,tag2", # request_tags
|
|
||||||
]
|
|
||||||
|
|
||||||
row2 = [
|
|
||||||
"jaffer", # request_id
|
|
||||||
"POST", # call_type
|
|
||||||
"api_key_456", # api_key
|
|
||||||
30.50, # spend
|
|
||||||
800, # total_tokens
|
|
||||||
600, # prompt_tokens
|
|
||||||
200, # completion_tokens
|
|
||||||
datetime.datetime.now(), # startTime (replace with the actual timestamp)
|
|
||||||
datetime.datetime.now(), # endTime (replace with the actual timestamp)
|
|
||||||
"gpt-4.0", # model
|
|
||||||
"user456", # user
|
|
||||||
'{"key": "value"}', # metadata (replace with valid JSON)
|
|
||||||
"False", # cache_hit
|
|
||||||
"cache_key_789", # cache_key
|
|
||||||
"tag3,tag4", # request_tags
|
|
||||||
]
|
|
||||||
|
|
||||||
data = [row1, row2]
|
|
||||||
resp = client.insert(
|
|
||||||
"spend_logs",
|
|
||||||
data,
|
|
||||||
column_names=[
|
|
||||||
"request_id",
|
|
||||||
"call_type",
|
|
||||||
"api_key",
|
|
||||||
"spend",
|
|
||||||
"total_tokens",
|
|
||||||
"prompt_tokens",
|
|
||||||
"completion_tokens",
|
|
||||||
"startTime",
|
|
||||||
"endTime",
|
|
||||||
"model",
|
|
||||||
"user",
|
|
||||||
"metadata",
|
|
||||||
"cache_hit",
|
|
||||||
"cache_key",
|
|
||||||
"request_tags",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
print(resp)
|
|
|
@ -1,39 +0,0 @@
|
||||||
# insert data into clickhouse
|
|
||||||
# response = client.command(
|
|
||||||
# """
|
|
||||||
# CREATE TEMPORARY TABLE temp_spend_logs AS (
|
|
||||||
# SELECT
|
|
||||||
# generateUUIDv4() AS request_id,
|
|
||||||
# arrayElement(['TypeA', 'TypeB', 'TypeC'], rand() % 3 + 1) AS call_type,
|
|
||||||
# 'ishaan' as api_key,
|
|
||||||
# rand() * 1000 AS spend,
|
|
||||||
# rand() * 100 AS total_tokens,
|
|
||||||
# rand() * 50 AS prompt_tokens,
|
|
||||||
# rand() * 50 AS completion_tokens,
|
|
||||||
# toDate('2024-02-01') + toIntervalDay(rand()%27) AS startTime,
|
|
||||||
# now() AS endTime,
|
|
||||||
# arrayElement(['azure/gpt-4', 'gpt-3.5', 'vertexai/gemini-pro', 'mistral/mistral-small', 'ollama/llama2'], rand() % 3 + 1) AS model,
|
|
||||||
# 'ishaan-insert-rand' as user,
|
|
||||||
# 'data' as metadata,
|
|
||||||
# 'true'AS cache_hit,
|
|
||||||
# 'ishaan' as cache_key,
|
|
||||||
# '{"tag1": "value1", "tag2": "value2"}' AS request_tags
|
|
||||||
# FROM numbers(1, 1000000)
|
|
||||||
# );
|
|
||||||
# """
|
|
||||||
# )
|
|
||||||
|
|
||||||
# client.command(
|
|
||||||
# """
|
|
||||||
# -- Insert data into spend_logs table
|
|
||||||
# INSERT INTO spend_logs
|
|
||||||
# SELECT * FROM temp_spend_logs;
|
|
||||||
# """
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
# client.command(
|
|
||||||
# """
|
|
||||||
# DROP TABLE IF EXISTS temp_spend_logs;
|
|
||||||
# """
|
|
||||||
# )
|
|
|
@ -777,7 +777,6 @@ general_settings:
|
||||||
| public_routes | List[str] | (Enterprise Feature) Control list of public routes |
|
| public_routes | List[str] | (Enterprise Feature) Control list of public routes |
|
||||||
| alert_types | List[str] | Control list of alert types to send to slack (Doc on alert types)[./alerting.md] |
|
| alert_types | List[str] | Control list of alert types to send to slack (Doc on alert types)[./alerting.md] |
|
||||||
| enforced_params | List[str] | (Enterprise Feature) List of params that must be included in all requests to the proxy |
|
| enforced_params | List[str] | (Enterprise Feature) List of params that must be included in all requests to the proxy |
|
||||||
| enable_oauth2_auth | boolean | (Enterprise Feature) If true, enables oauth2.0 authentication |
|
|
||||||
| use_x_forwarded_for | str | If true, uses the X-Forwarded-For header to get the client IP address |
|
| use_x_forwarded_for | str | If true, uses the X-Forwarded-For header to get the client IP address |
|
||||||
| service_account_settings | List[Dict[str, Any]] | Set `service_account_settings` if you want to create settings that only apply to service account keys (Doc on service accounts)[./service_accounts.md] |
|
| service_account_settings | List[Dict[str, Any]] | Set `service_account_settings` if you want to create settings that only apply to service account keys (Doc on service accounts)[./service_accounts.md] |
|
||||||
| image_generation_model | str | The default model to use for image generation - ignores model set in request |
|
| image_generation_model | str | The default model to use for image generation - ignores model set in request |
|
||||||
|
@ -898,10 +897,6 @@ router_settings:
|
||||||
| BRAINTRUST_API_KEY | API key for Braintrust integration
|
| BRAINTRUST_API_KEY | API key for Braintrust integration
|
||||||
| CIRCLE_OIDC_TOKEN | OpenID Connect token for CircleCI
|
| CIRCLE_OIDC_TOKEN | OpenID Connect token for CircleCI
|
||||||
| CIRCLE_OIDC_TOKEN_V2 | Version 2 of the OpenID Connect token for CircleCI
|
| CIRCLE_OIDC_TOKEN_V2 | Version 2 of the OpenID Connect token for CircleCI
|
||||||
| CLICKHOUSE_HOST | Host for ClickHouse database
|
|
||||||
| CLICKHOUSE_PASSWORD | Password for ClickHouse authentication
|
|
||||||
| CLICKHOUSE_PORT | Port for ClickHouse database connection
|
|
||||||
| CLICKHOUSE_USERNAME | Username for ClickHouse authentication
|
|
||||||
| CONFIG_FILE_PATH | File path for configuration file
|
| CONFIG_FILE_PATH | File path for configuration file
|
||||||
| CUSTOM_TIKTOKEN_CACHE_DIR | Custom directory for Tiktoken cache
|
| CUSTOM_TIKTOKEN_CACHE_DIR | Custom directory for Tiktoken cache
|
||||||
| DATABASE_HOST | Hostname for the database server
|
| DATABASE_HOST | Hostname for the database server
|
||||||
|
|
|
@ -84,21 +84,3 @@ model_cost = {
|
||||||
"output_cost_per_token": 0.000015,
|
"output_cost_per_token": 0.000015,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BerriSpendLogger:
|
|
||||||
# Class variables or attributes
|
|
||||||
def __init__(self):
|
|
||||||
# Instance variables
|
|
||||||
self.account_id = os.getenv("BERRISPEND_ACCOUNT_ID")
|
|
||||||
|
|
||||||
def price_calculator(self, model, response_obj, start_time, end_time):
|
|
||||||
return
|
|
||||||
|
|
||||||
def log_event(
|
|
||||||
self, model, messages, response_obj, start_time, end_time, print_verbose
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
This integration is not implemented yet.
|
|
||||||
"""
|
|
||||||
return
|
|
||||||
|
|
|
@ -1,334 +0,0 @@
|
||||||
# callback to make a request to an API endpoint
|
|
||||||
|
|
||||||
#### What this does ####
|
|
||||||
# On success, logs events to Promptlayer
|
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import traceback
|
|
||||||
from typing import Literal, Optional, Union
|
|
||||||
|
|
||||||
import dotenv
|
|
||||||
import requests
|
|
||||||
|
|
||||||
import litellm
|
|
||||||
from litellm._logging import verbose_logger
|
|
||||||
from litellm.caching.caching import DualCache
|
|
||||||
from litellm.proxy._types import UserAPIKeyAuth
|
|
||||||
from litellm.types.utils import StandardLoggingPayload
|
|
||||||
|
|
||||||
#### What this does ####
|
|
||||||
# On success + failure, log events to Supabase
|
|
||||||
|
|
||||||
|
|
||||||
def create_client():
|
|
||||||
try:
|
|
||||||
import clickhouse_connect
|
|
||||||
|
|
||||||
port = os.getenv("CLICKHOUSE_PORT")
|
|
||||||
clickhouse_host = os.getenv("CLICKHOUSE_HOST")
|
|
||||||
if clickhouse_host is not None:
|
|
||||||
verbose_logger.debug("setting up clickhouse")
|
|
||||||
|
|
||||||
port = os.getenv("CLICKHOUSE_PORT")
|
|
||||||
if port is not None and isinstance(port, str):
|
|
||||||
port = int(port)
|
|
||||||
|
|
||||||
host: Optional[str] = os.getenv("CLICKHOUSE_HOST")
|
|
||||||
if host is None:
|
|
||||||
raise ValueError("CLICKHOUSE_HOST is not set")
|
|
||||||
|
|
||||||
username: Optional[str] = os.getenv("CLICKHOUSE_USERNAME")
|
|
||||||
if username is None:
|
|
||||||
raise ValueError("CLICKHOUSE_USERNAME is not set")
|
|
||||||
|
|
||||||
password: Optional[str] = os.getenv("CLICKHOUSE_PASSWORD")
|
|
||||||
if password is None:
|
|
||||||
raise ValueError("CLICKHOUSE_PASSWORD is not set")
|
|
||||||
if port is None:
|
|
||||||
raise ValueError("CLICKHOUSE_PORT is not set")
|
|
||||||
|
|
||||||
client = clickhouse_connect.get_client(
|
|
||||||
host=host,
|
|
||||||
port=port,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
)
|
|
||||||
return client
|
|
||||||
else:
|
|
||||||
raise Exception("Clickhouse: Clickhouse host not set")
|
|
||||||
except Exception as e:
|
|
||||||
raise ValueError(f"Clickhouse: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def build_daily_metrics():
|
|
||||||
click_house_client = create_client()
|
|
||||||
|
|
||||||
# get daily spend
|
|
||||||
daily_spend = click_house_client.query_df(
|
|
||||||
"""
|
|
||||||
SELECT sumMerge(DailySpend) as daily_spend, day FROM daily_aggregated_spend GROUP BY day
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
# get daily spend per model
|
|
||||||
daily_spend_per_model = click_house_client.query_df(
|
|
||||||
"""
|
|
||||||
SELECT sumMerge(DailySpend) as daily_spend, day, model FROM daily_aggregated_spend_per_model GROUP BY day, model
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
new_df = daily_spend_per_model.to_dict(orient="records")
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
df = pd.DataFrame(new_df)
|
|
||||||
# Group by 'day' and create a dictionary for each group
|
|
||||||
result_dict = {}
|
|
||||||
for day, group in df.groupby("day"):
|
|
||||||
models = group["model"].tolist()
|
|
||||||
spend = group["daily_spend"].tolist()
|
|
||||||
spend_per_model = {model: spend for model, spend in zip(models, spend)}
|
|
||||||
result_dict[day] = spend_per_model
|
|
||||||
|
|
||||||
# Display the resulting dictionary
|
|
||||||
|
|
||||||
# get daily spend per API key
|
|
||||||
daily_spend_per_api_key = click_house_client.query_df(
|
|
||||||
"""
|
|
||||||
SELECT
|
|
||||||
daily_spend,
|
|
||||||
day,
|
|
||||||
api_key
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
sumMerge(DailySpend) as daily_spend,
|
|
||||||
day,
|
|
||||||
api_key,
|
|
||||||
RANK() OVER (PARTITION BY day ORDER BY sumMerge(DailySpend) DESC) as spend_rank
|
|
||||||
FROM
|
|
||||||
daily_aggregated_spend_per_api_key
|
|
||||||
GROUP BY
|
|
||||||
day,
|
|
||||||
api_key
|
|
||||||
) AS ranked_api_keys
|
|
||||||
WHERE
|
|
||||||
spend_rank <= 5
|
|
||||||
AND day IS NOT NULL
|
|
||||||
ORDER BY
|
|
||||||
day,
|
|
||||||
daily_spend DESC
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
new_df = daily_spend_per_api_key.to_dict(orient="records")
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
df = pd.DataFrame(new_df)
|
|
||||||
# Group by 'day' and create a dictionary for each group
|
|
||||||
api_key_result_dict = {}
|
|
||||||
for day, group in df.groupby("day"):
|
|
||||||
api_keys = group["api_key"].tolist()
|
|
||||||
spend = group["daily_spend"].tolist()
|
|
||||||
spend_per_api_key = {api_key: spend for api_key, spend in zip(api_keys, spend)}
|
|
||||||
api_key_result_dict[day] = spend_per_api_key
|
|
||||||
|
|
||||||
# Display the resulting dictionary
|
|
||||||
|
|
||||||
# Calculate total spend across all days
|
|
||||||
total_spend = daily_spend["daily_spend"].sum()
|
|
||||||
|
|
||||||
# Identify top models and top API keys with the highest spend across all days
|
|
||||||
top_models = {}
|
|
||||||
top_api_keys = {}
|
|
||||||
|
|
||||||
for day, spend_per_model in result_dict.items():
|
|
||||||
for model, model_spend in spend_per_model.items():
|
|
||||||
if model not in top_models or model_spend > top_models[model]:
|
|
||||||
top_models[model] = model_spend
|
|
||||||
|
|
||||||
for day, spend_per_api_key in api_key_result_dict.items():
|
|
||||||
for api_key, api_key_spend in spend_per_api_key.items():
|
|
||||||
if api_key not in top_api_keys or api_key_spend > top_api_keys[api_key]:
|
|
||||||
top_api_keys[api_key] = api_key_spend
|
|
||||||
|
|
||||||
# for each day in daily spend, look up the day in result_dict and api_key_result_dict
|
|
||||||
# Assuming daily_spend DataFrame has 'day' column
|
|
||||||
result = []
|
|
||||||
for index, row in daily_spend.iterrows():
|
|
||||||
day = row["day"]
|
|
||||||
data_day = row.to_dict()
|
|
||||||
|
|
||||||
# Look up in result_dict
|
|
||||||
if day in result_dict:
|
|
||||||
spend_per_model = result_dict[day]
|
|
||||||
# Assuming there is a column named 'model' in daily_spend
|
|
||||||
data_day["spend_per_model"] = spend_per_model # Assign 0 if model not found
|
|
||||||
|
|
||||||
# Look up in api_key_result_dict
|
|
||||||
if day in api_key_result_dict:
|
|
||||||
spend_per_api_key = api_key_result_dict[day]
|
|
||||||
# Assuming there is a column named 'api_key' in daily_spend
|
|
||||||
data_day["spend_per_api_key"] = spend_per_api_key
|
|
||||||
|
|
||||||
result.append(data_day)
|
|
||||||
|
|
||||||
data_to_return = {}
|
|
||||||
data_to_return["daily_spend"] = result
|
|
||||||
|
|
||||||
data_to_return["total_spend"] = total_spend
|
|
||||||
data_to_return["top_models"] = top_models
|
|
||||||
data_to_return["top_api_keys"] = top_api_keys
|
|
||||||
return data_to_return
|
|
||||||
|
|
||||||
|
|
||||||
# build_daily_metrics()
|
|
||||||
|
|
||||||
|
|
||||||
def _start_clickhouse():
|
|
||||||
import clickhouse_connect
|
|
||||||
|
|
||||||
port = os.getenv("CLICKHOUSE_PORT")
|
|
||||||
clickhouse_host = os.getenv("CLICKHOUSE_HOST")
|
|
||||||
if clickhouse_host is not None:
|
|
||||||
verbose_logger.debug("setting up clickhouse")
|
|
||||||
if port is not None and isinstance(port, str):
|
|
||||||
port = int(port)
|
|
||||||
|
|
||||||
port = os.getenv("CLICKHOUSE_PORT")
|
|
||||||
if port is not None and isinstance(port, str):
|
|
||||||
port = int(port)
|
|
||||||
|
|
||||||
host: Optional[str] = os.getenv("CLICKHOUSE_HOST")
|
|
||||||
if host is None:
|
|
||||||
raise ValueError("CLICKHOUSE_HOST is not set")
|
|
||||||
|
|
||||||
username: Optional[str] = os.getenv("CLICKHOUSE_USERNAME")
|
|
||||||
if username is None:
|
|
||||||
raise ValueError("CLICKHOUSE_USERNAME is not set")
|
|
||||||
|
|
||||||
password: Optional[str] = os.getenv("CLICKHOUSE_PASSWORD")
|
|
||||||
if password is None:
|
|
||||||
raise ValueError("CLICKHOUSE_PASSWORD is not set")
|
|
||||||
if port is None:
|
|
||||||
raise ValueError("CLICKHOUSE_PORT is not set")
|
|
||||||
|
|
||||||
client = clickhouse_connect.get_client(
|
|
||||||
host=host,
|
|
||||||
port=port,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
)
|
|
||||||
# view all tables in DB
|
|
||||||
response = client.query("SHOW TABLES")
|
|
||||||
verbose_logger.debug(
|
|
||||||
f"checking if litellm spend logs exists, all tables={response.result_rows}"
|
|
||||||
)
|
|
||||||
# all tables is returned like this: all tables = [('new_table',), ('spend_logs',)]
|
|
||||||
# check if spend_logs in all tables
|
|
||||||
table_names = [all_tables[0] for all_tables in response.result_rows]
|
|
||||||
|
|
||||||
if "spend_logs" not in table_names:
|
|
||||||
verbose_logger.debug(
|
|
||||||
"Clickhouse: spend logs table does not exist... creating it"
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.command(
|
|
||||||
"""
|
|
||||||
CREATE TABLE default.spend_logs
|
|
||||||
(
|
|
||||||
`request_id` String,
|
|
||||||
`call_type` String,
|
|
||||||
`api_key` String,
|
|
||||||
`spend` Float64,
|
|
||||||
`total_tokens` Int256,
|
|
||||||
`prompt_tokens` Int256,
|
|
||||||
`completion_tokens` Int256,
|
|
||||||
`startTime` DateTime,
|
|
||||||
`endTime` DateTime,
|
|
||||||
`model` String,
|
|
||||||
`user` String,
|
|
||||||
`metadata` String,
|
|
||||||
`cache_hit` String,
|
|
||||||
`cache_key` String,
|
|
||||||
`request_tags` String
|
|
||||||
)
|
|
||||||
ENGINE = MergeTree
|
|
||||||
ORDER BY tuple();
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# check if spend logs exist, if it does then return the schema
|
|
||||||
response = client.query("DESCRIBE default.spend_logs")
|
|
||||||
verbose_logger.debug(f"spend logs schema ={response.result_rows}")
|
|
||||||
|
|
||||||
|
|
||||||
class ClickhouseLogger:
|
|
||||||
# Class variables or attributes
|
|
||||||
def __init__(self, endpoint=None, headers=None):
|
|
||||||
import clickhouse_connect
|
|
||||||
|
|
||||||
_start_clickhouse()
|
|
||||||
|
|
||||||
verbose_logger.debug(
|
|
||||||
f"ClickhouseLogger init, host {os.getenv('CLICKHOUSE_HOST')}, port {os.getenv('CLICKHOUSE_PORT')}, username {os.getenv('CLICKHOUSE_USERNAME')}"
|
|
||||||
)
|
|
||||||
|
|
||||||
port = os.getenv("CLICKHOUSE_PORT")
|
|
||||||
if port is not None and isinstance(port, str):
|
|
||||||
port = int(port)
|
|
||||||
|
|
||||||
host: Optional[str] = os.getenv("CLICKHOUSE_HOST")
|
|
||||||
if host is None:
|
|
||||||
raise ValueError("CLICKHOUSE_HOST is not set")
|
|
||||||
|
|
||||||
username: Optional[str] = os.getenv("CLICKHOUSE_USERNAME")
|
|
||||||
if username is None:
|
|
||||||
raise ValueError("CLICKHOUSE_USERNAME is not set")
|
|
||||||
|
|
||||||
password: Optional[str] = os.getenv("CLICKHOUSE_PASSWORD")
|
|
||||||
if password is None:
|
|
||||||
raise ValueError("CLICKHOUSE_PASSWORD is not set")
|
|
||||||
if port is None:
|
|
||||||
raise ValueError("CLICKHOUSE_PORT is not set")
|
|
||||||
|
|
||||||
client = clickhouse_connect.get_client(
|
|
||||||
host=host,
|
|
||||||
port=port,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
)
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
# This is sync, because we run this in a separate thread. Running in a sepearate thread ensures it will never block an LLM API call
|
|
||||||
# Experience with s3, Langfuse shows that async logging events are complicated and can block LLM calls
|
|
||||||
def log_event(
|
|
||||||
self, kwargs, response_obj, start_time, end_time, user_id, print_verbose
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
verbose_logger.debug(
|
|
||||||
f"ClickhouseLogger Logging - Enters logging function for model {kwargs}"
|
|
||||||
)
|
|
||||||
# follows the same params as langfuse.py
|
|
||||||
|
|
||||||
payload: Optional[StandardLoggingPayload] = kwargs.get(
|
|
||||||
"standard_logging_object"
|
|
||||||
)
|
|
||||||
if payload is None:
|
|
||||||
return
|
|
||||||
# Build the initial payload
|
|
||||||
|
|
||||||
verbose_logger.debug(f"\nClickhouse Logger - Logging payload = {payload}")
|
|
||||||
|
|
||||||
# just get the payload items in one array and payload keys in 2nd array
|
|
||||||
values = []
|
|
||||||
keys = []
|
|
||||||
for key, value in payload.items():
|
|
||||||
keys.append(key)
|
|
||||||
values.append(value)
|
|
||||||
data = [values]
|
|
||||||
|
|
||||||
response = self.client.insert("default.spend_logs", data, column_names=keys)
|
|
||||||
|
|
||||||
# make request to endpoint with payload
|
|
||||||
verbose_logger.debug(f"Clickhouse Logger - final response = {response}")
|
|
||||||
except Exception as e:
|
|
||||||
verbose_logger.debug(f"Clickhouse - {str(e)}\n{traceback.format_exc()}")
|
|
||||||
pass
|
|
|
@ -61,9 +61,7 @@ from litellm.utils import (
|
||||||
from ..integrations.aispend import AISpendLogger
|
from ..integrations.aispend import AISpendLogger
|
||||||
from ..integrations.argilla import ArgillaLogger
|
from ..integrations.argilla import ArgillaLogger
|
||||||
from ..integrations.athina import AthinaLogger
|
from ..integrations.athina import AthinaLogger
|
||||||
from ..integrations.berrispend import BerriSpendLogger
|
|
||||||
from ..integrations.braintrust_logging import BraintrustLogger
|
from ..integrations.braintrust_logging import BraintrustLogger
|
||||||
from ..integrations.clickhouse import ClickhouseLogger
|
|
||||||
from ..integrations.datadog.datadog import DataDogLogger
|
from ..integrations.datadog.datadog import DataDogLogger
|
||||||
from ..integrations.dynamodb import DyanmoDBLogger
|
from ..integrations.dynamodb import DyanmoDBLogger
|
||||||
from ..integrations.galileo import GalileoObserve
|
from ..integrations.galileo import GalileoObserve
|
||||||
|
@ -122,13 +120,10 @@ prometheusLogger = None
|
||||||
dynamoLogger = None
|
dynamoLogger = None
|
||||||
s3Logger = None
|
s3Logger = None
|
||||||
genericAPILogger = None
|
genericAPILogger = None
|
||||||
clickHouseLogger = None
|
|
||||||
greenscaleLogger = None
|
greenscaleLogger = None
|
||||||
lunaryLogger = None
|
lunaryLogger = None
|
||||||
aispendLogger = None
|
aispendLogger = None
|
||||||
berrispendLogger = None
|
|
||||||
supabaseClient = None
|
supabaseClient = None
|
||||||
liteDebuggerClient = None
|
|
||||||
callback_list: Optional[List[str]] = []
|
callback_list: Optional[List[str]] = []
|
||||||
user_logger_fn = None
|
user_logger_fn = None
|
||||||
additional_details: Optional[Dict[str, str]] = {}
|
additional_details: Optional[Dict[str, str]] = {}
|
||||||
|
@ -191,7 +186,7 @@ in_memory_dynamic_logger_cache = DynamicLoggingCache()
|
||||||
|
|
||||||
|
|
||||||
class Logging:
|
class Logging:
|
||||||
global supabaseClient, liteDebuggerClient, promptLayerLogger, weightsBiasesLogger, logfireLogger, capture_exception, add_breadcrumb, lunaryLogger, logfireLogger, prometheusLogger, slack_app
|
global supabaseClient, promptLayerLogger, weightsBiasesLogger, logfireLogger, capture_exception, add_breadcrumb, lunaryLogger, logfireLogger, prometheusLogger, slack_app
|
||||||
custom_pricing: bool = False
|
custom_pricing: bool = False
|
||||||
stream_options = None
|
stream_options = None
|
||||||
|
|
||||||
|
@ -970,22 +965,6 @@ class Logging:
|
||||||
):
|
):
|
||||||
print_verbose("no-log request, skipping logging")
|
print_verbose("no-log request, skipping logging")
|
||||||
continue
|
continue
|
||||||
if callback == "lite_debugger" and liteDebuggerClient is not None:
|
|
||||||
print_verbose("reaches lite_debugger for logging!")
|
|
||||||
print_verbose(f"liteDebuggerClient: {liteDebuggerClient}")
|
|
||||||
print_verbose(
|
|
||||||
f"liteDebuggerClient details function {self.call_type} and stream set to {self.stream}"
|
|
||||||
)
|
|
||||||
liteDebuggerClient.log_event(
|
|
||||||
end_user=kwargs.get("user", "default"),
|
|
||||||
response_obj=result,
|
|
||||||
start_time=start_time,
|
|
||||||
end_time=end_time,
|
|
||||||
litellm_call_id=self.litellm_call_id,
|
|
||||||
print_verbose=print_verbose,
|
|
||||||
call_type=self.call_type,
|
|
||||||
stream=self.stream,
|
|
||||||
)
|
|
||||||
if callback == "promptlayer" and promptLayerLogger is not None:
|
if callback == "promptlayer" and promptLayerLogger is not None:
|
||||||
print_verbose("reaches promptlayer for logging!")
|
print_verbose("reaches promptlayer for logging!")
|
||||||
promptLayerLogger.log_event(
|
promptLayerLogger.log_event(
|
||||||
|
@ -1248,37 +1227,6 @@ class Logging:
|
||||||
user_id=kwargs.get("user", None),
|
user_id=kwargs.get("user", None),
|
||||||
print_verbose=print_verbose,
|
print_verbose=print_verbose,
|
||||||
)
|
)
|
||||||
if callback == "clickhouse":
|
|
||||||
global clickHouseLogger
|
|
||||||
verbose_logger.debug("reaches clickhouse for success logging!")
|
|
||||||
kwargs = {}
|
|
||||||
for k, v in self.model_call_details.items():
|
|
||||||
if (
|
|
||||||
k != "original_response"
|
|
||||||
): # copy.deepcopy raises errors as this could be a coroutine
|
|
||||||
kwargs[k] = v
|
|
||||||
# this only logs streaming once, complete_streaming_response exists i.e when stream ends
|
|
||||||
if self.stream:
|
|
||||||
verbose_logger.debug(
|
|
||||||
f"is complete_streaming_response in kwargs: {kwargs.get('complete_streaming_response', None)}"
|
|
||||||
)
|
|
||||||
if complete_streaming_response is None:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
print_verbose(
|
|
||||||
"reaches clickhouse for streaming logging!"
|
|
||||||
)
|
|
||||||
result = kwargs["complete_streaming_response"]
|
|
||||||
if clickHouseLogger is None:
|
|
||||||
clickHouseLogger = ClickhouseLogger()
|
|
||||||
clickHouseLogger.log_event(
|
|
||||||
kwargs=kwargs,
|
|
||||||
response_obj=result,
|
|
||||||
start_time=start_time,
|
|
||||||
end_time=end_time,
|
|
||||||
user_id=kwargs.get("user", None),
|
|
||||||
print_verbose=print_verbose,
|
|
||||||
)
|
|
||||||
if callback == "greenscale" and greenscaleLogger is not None:
|
if callback == "greenscale" and greenscaleLogger is not None:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for k, v in self.model_call_details.items():
|
for k, v in self.model_call_details.items():
|
||||||
|
@ -1874,9 +1822,7 @@ class Logging:
|
||||||
)
|
)
|
||||||
for callback in callbacks:
|
for callback in callbacks:
|
||||||
try:
|
try:
|
||||||
if callback == "lite_debugger" and liteDebuggerClient is not None:
|
if callback == "lunary" and lunaryLogger is not None:
|
||||||
pass
|
|
||||||
elif callback == "lunary" and lunaryLogger is not None:
|
|
||||||
print_verbose("reaches lunary for logging error!")
|
print_verbose("reaches lunary for logging error!")
|
||||||
|
|
||||||
model = self.model
|
model = self.model
|
||||||
|
@ -2195,7 +2141,7 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
|
||||||
"""
|
"""
|
||||||
Globally sets the callback client
|
Globally sets the callback client
|
||||||
"""
|
"""
|
||||||
global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel, traceloopLogger, athinaLogger, heliconeLogger, aispendLogger, berrispendLogger, supabaseClient, liteDebuggerClient, lunaryLogger, promptLayerLogger, langFuseLogger, customLogger, weightsBiasesLogger, logfireLogger, dynamoLogger, s3Logger, dataDogLogger, prometheusLogger, greenscaleLogger, openMeterLogger
|
global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel, traceloopLogger, athinaLogger, heliconeLogger, aispendLogger, supabaseClient, lunaryLogger, promptLayerLogger, langFuseLogger, customLogger, weightsBiasesLogger, logfireLogger, dynamoLogger, s3Logger, dataDogLogger, prometheusLogger, greenscaleLogger, openMeterLogger
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for callback in callback_list:
|
for callback in callback_list:
|
||||||
|
@ -2277,24 +2223,12 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
|
||||||
logfireLogger = LogfireLogger()
|
logfireLogger = LogfireLogger()
|
||||||
elif callback == "aispend":
|
elif callback == "aispend":
|
||||||
aispendLogger = AISpendLogger()
|
aispendLogger = AISpendLogger()
|
||||||
elif callback == "berrispend":
|
|
||||||
berrispendLogger = BerriSpendLogger()
|
|
||||||
elif callback == "supabase":
|
elif callback == "supabase":
|
||||||
print_verbose("instantiating supabase")
|
print_verbose("instantiating supabase")
|
||||||
supabaseClient = Supabase()
|
supabaseClient = Supabase()
|
||||||
elif callback == "greenscale":
|
elif callback == "greenscale":
|
||||||
greenscaleLogger = GreenscaleLogger()
|
greenscaleLogger = GreenscaleLogger()
|
||||||
print_verbose("Initialized Greenscale Logger")
|
print_verbose("Initialized Greenscale Logger")
|
||||||
elif callback == "lite_debugger":
|
|
||||||
print_verbose("instantiating lite_debugger")
|
|
||||||
if function_id:
|
|
||||||
liteDebuggerClient = LiteDebugger(email=function_id)
|
|
||||||
elif litellm.token:
|
|
||||||
liteDebuggerClient = LiteDebugger(email=litellm.token)
|
|
||||||
elif litellm.email:
|
|
||||||
liteDebuggerClient = LiteDebugger(email=litellm.email)
|
|
||||||
else:
|
|
||||||
liteDebuggerClient = LiteDebugger(email=str(uuid.uuid4()))
|
|
||||||
elif callable(callback):
|
elif callable(callback):
|
||||||
customLogger = CustomLogger()
|
customLogger = CustomLogger()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -213,13 +213,10 @@ prometheusLogger = None
|
||||||
dynamoLogger = None
|
dynamoLogger = None
|
||||||
s3Logger = None
|
s3Logger = None
|
||||||
genericAPILogger = None
|
genericAPILogger = None
|
||||||
clickHouseLogger = None
|
|
||||||
greenscaleLogger = None
|
greenscaleLogger = None
|
||||||
lunaryLogger = None
|
lunaryLogger = None
|
||||||
aispendLogger = None
|
aispendLogger = None
|
||||||
berrispendLogger = None
|
|
||||||
supabaseClient = None
|
supabaseClient = None
|
||||||
liteDebuggerClient = None
|
|
||||||
callback_list: Optional[List[str]] = []
|
callback_list: Optional[List[str]] = []
|
||||||
user_logger_fn = None
|
user_logger_fn = None
|
||||||
additional_details: Optional[Dict[str, str]] = {}
|
additional_details: Optional[Dict[str, str]] = {}
|
||||||
|
@ -609,7 +606,6 @@ def function_setup( # noqa: PLR0915
|
||||||
|
|
||||||
|
|
||||||
def client(original_function): # noqa: PLR0915
|
def client(original_function): # noqa: PLR0915
|
||||||
global liteDebuggerClient
|
|
||||||
rules_obj = Rules()
|
rules_obj = Rules()
|
||||||
|
|
||||||
def check_coroutine(value) -> bool:
|
def check_coroutine(value) -> bool:
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import io, asyncio
|
|
||||||
|
|
||||||
# import logging
|
|
||||||
# logging.basicConfig(level=logging.DEBUG)
|
|
||||||
sys.path.insert(0, os.path.abspath("../.."))
|
|
||||||
print("Modified sys.path:", sys.path)
|
|
||||||
|
|
||||||
|
|
||||||
from litellm import completion
|
|
||||||
import litellm
|
|
||||||
from litellm._logging import verbose_logger
|
|
||||||
import logging
|
|
||||||
|
|
||||||
litellm.num_retries = 3
|
|
||||||
|
|
||||||
import time, random
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
@pytest.mark.skip(reason="beta test - this is a new feature")
|
|
||||||
async def test_custom_api_logging():
|
|
||||||
try:
|
|
||||||
litellm.success_callback = ["clickhouse"]
|
|
||||||
litellm.set_verbose = True
|
|
||||||
verbose_logger.setLevel(logging.DEBUG)
|
|
||||||
await litellm.acompletion(
|
|
||||||
model="gpt-3.5-turbo",
|
|
||||||
messages=[{"role": "user", "content": f"This is a test"}],
|
|
||||||
max_tokens=10,
|
|
||||||
temperature=0.7,
|
|
||||||
user="ishaan-2",
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
pytest.fail(f"An exception occurred - {e}")
|
|
||||||
finally:
|
|
||||||
# post, close log file and verify
|
|
||||||
# Reset stdout to the original value
|
|
||||||
print("Passed!")
|
|
Loading…
Add table
Add a link
Reference in a new issue