Skip to content

Commit 45643b6

Browse files
committed
fix(otel): don’t overwrite existing global tracer provider; reuse SDK provider if already set (prevents test context propagation from breaking)
1 parent d3ef004 commit 45643b6

File tree

342 files changed

+6214
-4603
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

342 files changed

+6214
-4603
lines changed

enterprise/litellm_enterprise/enterprise_callbacks/callback_controls.py

Lines changed: 80 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,53 +14,72 @@
1414
class EnterpriseCallbackControls:
1515
@staticmethod
1616
def is_callback_disabled_dynamically(
17-
callback: litellm.CALLBACK_TYPES,
18-
litellm_params: dict,
19-
standard_callback_dynamic_params: StandardCallbackDynamicParams
20-
) -> bool:
21-
"""
22-
Check if a callback is disabled via the x-litellm-disable-callbacks header or via `litellm_disabled_callbacks` in standard_callback_dynamic_params.
23-
24-
Args:
25-
callback: The callback to check (can be string, CustomLogger instance, or callable)
26-
litellm_params: Parameters containing proxy server request info
27-
28-
Returns:
29-
bool: True if the callback should be disabled, False otherwise
30-
"""
31-
from litellm.litellm_core_utils.custom_logger_registry import (
32-
CustomLoggerRegistry,
17+
callback: litellm.CALLBACK_TYPES,
18+
litellm_params: dict,
19+
standard_callback_dynamic_params: StandardCallbackDynamicParams,
20+
) -> bool:
21+
"""
22+
Check if a callback is disabled via the x-litellm-disable-callbacks header or via `litellm_disabled_callbacks` in standard_callback_dynamic_params.
23+
24+
Args:
25+
callback: The callback to check (can be string, CustomLogger instance, or callable)
26+
litellm_params: Parameters containing proxy server request info
27+
28+
Returns:
29+
bool: True if the callback should be disabled, False otherwise
30+
"""
31+
from litellm.litellm_core_utils.custom_logger_registry import (
32+
CustomLoggerRegistry,
33+
)
34+
35+
try:
36+
disabled_callbacks = EnterpriseCallbackControls.get_disabled_callbacks(
37+
litellm_params, standard_callback_dynamic_params
38+
)
39+
verbose_logger.debug(
40+
f"Dynamically disabled callbacks from {X_LITELLM_DISABLE_CALLBACKS}: {disabled_callbacks}"
3341
)
42+
verbose_logger.debug(
43+
f"Checking if {callback} is disabled via headers. Disable callbacks from headers: {disabled_callbacks}"
44+
)
45+
if disabled_callbacks is not None:
46+
#########################################################
47+
# premium user check
48+
#########################################################
49+
if not EnterpriseCallbackControls._premium_user_check():
50+
return False
51+
#########################################################
52+
if isinstance(callback, str):
53+
if callback.lower() in disabled_callbacks:
54+
verbose_logger.debug(
55+
f"Not logging to {callback} because it is disabled via {X_LITELLM_DISABLE_CALLBACKS}"
56+
)
57+
return True
58+
elif isinstance(callback, CustomLogger):
59+
# get the string name of the callback
60+
callback_str = (
61+
CustomLoggerRegistry.get_callback_str_from_class_type(
62+
callback.__class__
63+
)
64+
)
65+
if (
66+
callback_str is not None
67+
and callback_str.lower() in disabled_callbacks
68+
):
69+
verbose_logger.debug(
70+
f"Not logging to {callback_str} because it is disabled via {X_LITELLM_DISABLE_CALLBACKS}"
71+
)
72+
return True
73+
return False
74+
except Exception as e:
75+
verbose_logger.debug(f"Error checking disabled callbacks header: {str(e)}")
76+
return False
3477

35-
try:
36-
disabled_callbacks = EnterpriseCallbackControls.get_disabled_callbacks(litellm_params, standard_callback_dynamic_params)
37-
verbose_logger.debug(f"Dynamically disabled callbacks from {X_LITELLM_DISABLE_CALLBACKS}: {disabled_callbacks}")
38-
verbose_logger.debug(f"Checking if {callback} is disabled via headers. Disable callbacks from headers: {disabled_callbacks}")
39-
if disabled_callbacks is not None:
40-
#########################################################
41-
# premium user check
42-
#########################################################
43-
if not EnterpriseCallbackControls._premium_user_check():
44-
return False
45-
#########################################################
46-
if isinstance(callback, str):
47-
if callback.lower() in disabled_callbacks:
48-
verbose_logger.debug(f"Not logging to {callback} because it is disabled via {X_LITELLM_DISABLE_CALLBACKS}")
49-
return True
50-
elif isinstance(callback, CustomLogger):
51-
# get the string name of the callback
52-
callback_str = CustomLoggerRegistry.get_callback_str_from_class_type(callback.__class__)
53-
if callback_str is not None and callback_str.lower() in disabled_callbacks:
54-
verbose_logger.debug(f"Not logging to {callback_str} because it is disabled via {X_LITELLM_DISABLE_CALLBACKS}")
55-
return True
56-
return False
57-
except Exception as e:
58-
verbose_logger.debug(
59-
f"Error checking disabled callbacks header: {str(e)}"
60-
)
61-
return False
6278
@staticmethod
63-
def get_disabled_callbacks(litellm_params: dict, standard_callback_dynamic_params: StandardCallbackDynamicParams) -> Optional[List[str]]:
79+
def get_disabled_callbacks(
80+
litellm_params: dict,
81+
standard_callback_dynamic_params: StandardCallbackDynamicParams,
82+
) -> Optional[List[str]]:
6483
"""
6584
Get the disabled callbacks from the standard callback dynamic params.
6685
"""
@@ -71,22 +90,31 @@ def get_disabled_callbacks(litellm_params: dict, standard_callback_dynamic_param
7190
request_headers = get_proxy_server_request_headers(litellm_params)
7291
disabled_callbacks = request_headers.get(X_LITELLM_DISABLE_CALLBACKS, None)
7392
if disabled_callbacks is not None:
74-
disabled_callbacks = set([cb.strip().lower() for cb in disabled_callbacks.split(",")])
93+
disabled_callbacks = set(
94+
[cb.strip().lower() for cb in disabled_callbacks.split(",")]
95+
)
7596
return list(disabled_callbacks)
76-
7797

7898
#########################################################
7999
# check if disabled via request body
80100
#########################################################
81-
if standard_callback_dynamic_params.get("litellm_disabled_callbacks", None) is not None:
82-
return standard_callback_dynamic_params.get("litellm_disabled_callbacks", None)
83-
101+
if (
102+
standard_callback_dynamic_params.get("litellm_disabled_callbacks", None)
103+
is not None
104+
):
105+
return standard_callback_dynamic_params.get(
106+
"litellm_disabled_callbacks", None
107+
)
108+
84109
return None
85-
110+
86111
@staticmethod
87112
def _premium_user_check():
88113
from litellm.proxy.proxy_server import premium_user
114+
89115
if premium_user:
90116
return True
91-
verbose_logger.warning(f"Disabling callbacks using request headers is an enterprise feature. {CommonProxyErrors.not_premium_user.value}")
92-
return False
117+
verbose_logger.warning(
118+
f"Disabling callbacks using request headers is an enterprise feature. {CommonProxyErrors.not_premium_user.value}"
119+
)
120+
return False

enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,14 @@ async def _get_email_params(
131131
unused_custom_fields = []
132132

133133
# Function to safely get custom value or default
134-
def get_custom_or_default(custom_value: Optional[str], default_value: str, field_name: str) -> str:
135-
if custom_value is not None: # Only check premium if trying to use custom value
134+
def get_custom_or_default(
135+
custom_value: Optional[str], default_value: str, field_name: str
136+
) -> str:
137+
if (
138+
custom_value is not None
139+
): # Only check premium if trying to use custom value
136140
from litellm.proxy.proxy_server import premium_user
141+
137142
if premium_user is not True:
138143
unused_custom_fields.append(field_name)
139144
return default_value
@@ -142,27 +147,37 @@ def get_custom_or_default(custom_value: Optional[str], default_value: str, field
142147

143148
# Get parameters, falling back to defaults if custom values aren't allowed
144149
logo_url = get_custom_or_default(custom_logo, LITELLM_LOGO_URL, "logo URL")
145-
support_contact = get_custom_or_default(custom_support, self.DEFAULT_SUPPORT_EMAIL, "support contact")
146-
base_url = os.getenv("PROXY_BASE_URL", "http://0.0.0.0:4000") # Not a premium feature
147-
signature = get_custom_or_default(custom_signature, EMAIL_FOOTER, "email signature")
150+
support_contact = get_custom_or_default(
151+
custom_support, self.DEFAULT_SUPPORT_EMAIL, "support contact"
152+
)
153+
base_url = os.getenv(
154+
"PROXY_BASE_URL", "http://0.0.0.0:4000"
155+
) # Not a premium feature
156+
signature = get_custom_or_default(
157+
custom_signature, EMAIL_FOOTER, "email signature"
158+
)
148159

149160
# Get custom subject template based on email event type
150161
if email_event == EmailEvent.new_user_invitation:
151162
subject_template = get_custom_or_default(
152163
custom_subject_invitation,
153164
self.DEFAULT_SUBJECT_TEMPLATES[EmailEvent.new_user_invitation],
154-
"invitation subject template"
165+
"invitation subject template",
155166
)
156167
elif email_event == EmailEvent.virtual_key_created:
157168
subject_template = get_custom_or_default(
158169
custom_subject_key_created,
159170
self.DEFAULT_SUBJECT_TEMPLATES[EmailEvent.virtual_key_created],
160-
"key created subject template"
171+
"key created subject template",
161172
)
162173
else:
163174
subject_template = "LiteLLM: {event_message}"
164175

165-
subject = subject_template.format(event_message=event_message) if event_message else "LiteLLM Notification"
176+
subject = (
177+
subject_template.format(event_message=event_message)
178+
if event_message
179+
else "LiteLLM Notification"
180+
)
166181

167182
recipient_email: Optional[
168183
str
@@ -186,9 +201,7 @@ def get_custom_or_default(custom_value: Optional[str], default_value: str, field
186201
"This is an Enterprise feature. To use custom email fields, please upgrade to LiteLLM Enterprise. "
187202
"Schedule a meeting here: https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat"
188203
)
189-
verbose_proxy_logger.warning(
190-
f"{warning_msg}"
191-
)
204+
verbose_proxy_logger.warning(f"{warning_msg}")
192205

193206
return EmailParams(
194207
logo_url=logo_url,
@@ -235,76 +248,91 @@ async def _get_invitation_link(self, user_id: Optional[str], base_url: str) -> s
235248
if not user_id:
236249
verbose_proxy_logger.debug("No user_id provided for invitation link")
237250
return base_url
238-
251+
239252
if not await self._is_prisma_client_available():
240253
return base_url
241-
254+
242255
# Wait for any concurrent invitation creation to complete
243256
await self._wait_for_invitation_creation()
244-
257+
245258
# Get or create invitation
246259
invitation = await self._get_or_create_invitation(user_id)
247260
if not invitation:
248-
verbose_proxy_logger.warning(f"Failed to get/create invitation for user_id: {user_id}")
261+
verbose_proxy_logger.warning(
262+
f"Failed to get/create invitation for user_id: {user_id}"
263+
)
249264
return base_url
250-
265+
251266
return self._construct_invitation_link(invitation.id, base_url)
252267

253268
async def _is_prisma_client_available(self) -> bool:
254269
"""Check if Prisma client is available"""
255270
from litellm.proxy.proxy_server import prisma_client
256-
271+
257272
if prisma_client is None:
258-
verbose_proxy_logger.debug("Prisma client not found. Unable to lookup invitation")
273+
verbose_proxy_logger.debug(
274+
"Prisma client not found. Unable to lookup invitation"
275+
)
259276
return False
260277
return True
261278

262279
async def _wait_for_invitation_creation(self) -> None:
263280
"""
264281
Wait for any concurrent invitation creation to complete.
265-
282+
266283
The UI calls /invitation/new to generate the invitation link.
267284
We wait to ensure any pending invitation creation is completed.
268285
"""
269286
import asyncio
287+
270288
await asyncio.sleep(10)
271289

272290
async def _get_or_create_invitation(self, user_id: str):
273291
"""
274292
Get existing invitation or create a new one for the user
275-
293+
276294
Returns:
277295
Invitation object with id attribute, or None if failed
278296
"""
279297
from litellm.proxy.management_helpers.user_invitation import (
280298
create_invitation_for_user,
281299
)
282300
from litellm.proxy.proxy_server import prisma_client
283-
301+
284302
if prisma_client is None:
285-
verbose_proxy_logger.error("Prisma client is None in _get_or_create_invitation")
303+
verbose_proxy_logger.error(
304+
"Prisma client is None in _get_or_create_invitation"
305+
)
286306
return None
287-
307+
288308
try:
289309
# Try to get existing invitation
290-
existing_invitations = await prisma_client.db.litellm_invitationlink.find_many(
291-
where={"user_id": user_id},
292-
order={"created_at": "desc"},
310+
existing_invitations = (
311+
await prisma_client.db.litellm_invitationlink.find_many(
312+
where={"user_id": user_id},
313+
order={"created_at": "desc"},
314+
)
293315
)
294-
316+
295317
if existing_invitations and len(existing_invitations) > 0:
296-
verbose_proxy_logger.debug(f"Found existing invitation for user_id: {user_id}")
318+
verbose_proxy_logger.debug(
319+
f"Found existing invitation for user_id: {user_id}"
320+
)
297321
return existing_invitations[0]
298-
322+
299323
# Create new invitation if none exists
300-
verbose_proxy_logger.debug(f"Creating new invitation for user_id: {user_id}")
324+
verbose_proxy_logger.debug(
325+
f"Creating new invitation for user_id: {user_id}"
326+
)
301327
return await create_invitation_for_user(
302328
data=InvitationNew(user_id=user_id),
303329
user_api_key_dict=UserAPIKeyAuth(user_id=user_id),
304330
)
305-
331+
306332
except Exception as e:
307-
verbose_proxy_logger.error(f"Error getting/creating invitation for user_id {user_id}: {e}")
333+
verbose_proxy_logger.error(
334+
f"Error getting/creating invitation for user_id {user_id}: {e}"
335+
)
308336
return None
309337

310338
def _construct_invitation_link(self, invitation_id: str, base_url: str) -> str:

0 commit comments

Comments
 (0)