litellm-enterprise 0.1.29__tar.gz → 0.1.31__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/PKG-INFO +1 -1
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py +112 -7
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/common_utils/check_batch_cost.py +1 -1
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/hooks/managed_files.py +87 -10
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/pyproject.toml +2 -2
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/LICENSE.md +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/README.md +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/__init__.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/callback_controls.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/example_logging_api.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/llama_guard.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/llm_guard.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/pagerduty/pagerduty.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secret_detection.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/__init__.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/adafruit.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/adobe.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/age_secret_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/airtable_api_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/algolia_api_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/alibaba.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/asana.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/atlassian_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/authress_access_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/beamer_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/bitbucket.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/bittrex.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/clojars_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/codecov_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/coinbase_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/confluent.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/contentful_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/databricks_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/datadog_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/defined_networking_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/digitalocean.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/discord.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/doppler_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/droneci_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/dropbox.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/duffel_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/dynatrace_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/easypost.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/etsy_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/facebook_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/fastly_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/finicity.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/finnhub_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/flickr_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/flutterwave.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/frameio_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/freshbooks_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/gcp_api_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/github_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/gitlab.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/gitter_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/gocardless_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/grafana.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/hashicorp_tf_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/heroku_api_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/hubspot_api_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/huggingface.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/intercom_api_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/jfrog.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/jwt.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/kraken_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/kucoin.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/launchdarkly_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/linear.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/linkedin.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/lob.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/mailgun.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/mapbox_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/mattermost_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/messagebird.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/microsoft_teams_webhook.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/netlify_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/new_relic.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/nytimes_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/okta_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/openai_api_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/planetscale.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/postman_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/prefect_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/pulumi_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/pypi_upload_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/rapidapi_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/readme_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/rubygems_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/scalingo_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/sendbird.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/sendgrid_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/sendinblue_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/sentry_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/shippo_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/shopify.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/slack.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/snyk_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/squarespace_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/sumologic.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/telegram_bot_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/travisci_access_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/twitch_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/twitter.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/typeform_api_token.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/vault.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/yandex.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/secrets_plugins/zendesk_secret_key.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/send_emails/endpoints.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/send_emails/resend_email.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/send_emails/sendgrid_email.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/enterprise_callbacks/send_emails/smtp_email.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/integrations/custom_guardrail.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/litellm_core_utils/litellm_logging.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/__init__.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/audit_logging_endpoints.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/auth/__init__.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/auth/custom_sso_handler.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/auth/route_checks.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/auth/user_api_key_auth.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/common_utils/__init__.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/common_utils/check_responses_cost.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/enterprise_routes.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/management_endpoints/__init__.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/management_endpoints/internal_user_endpoints.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/management_endpoints/key_management_endpoints.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/proxy_server.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/readme.md +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/utils.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/vector_stores/endpoints.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/types/enterprise_callbacks/send_emails.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/types/proxy/audit_logging_endpoints.py +0 -0
- {litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/types/proxy/proxy_server.py +0 -0
|
@@ -30,8 +30,15 @@ from litellm.integrations.email_templates.user_invitation_email import (
|
|
|
30
30
|
from litellm.integrations.email_templates.templates import (
|
|
31
31
|
MAX_BUDGET_ALERT_EMAIL_TEMPLATE,
|
|
32
32
|
SOFT_BUDGET_ALERT_EMAIL_TEMPLATE,
|
|
33
|
+
TEAM_SOFT_BUDGET_ALERT_EMAIL_TEMPLATE,
|
|
34
|
+
)
|
|
35
|
+
from litellm.proxy._types import (
|
|
36
|
+
CallInfo,
|
|
37
|
+
InvitationNew,
|
|
38
|
+
Litellm_EntityType,
|
|
39
|
+
UserAPIKeyAuth,
|
|
40
|
+
WebhookEvent,
|
|
33
41
|
)
|
|
34
|
-
from litellm.proxy._types import CallInfo, InvitationNew, UserAPIKeyAuth, WebhookEvent
|
|
35
42
|
from litellm.secret_managers.main import get_secret_bool
|
|
36
43
|
from litellm.types.integrations.slack_alerting import LITELLM_LOGO_URL
|
|
37
44
|
from litellm.constants import (
|
|
@@ -217,6 +224,78 @@ class BaseEmailLogger(CustomLogger):
|
|
|
217
224
|
)
|
|
218
225
|
pass
|
|
219
226
|
|
|
227
|
+
async def send_team_soft_budget_alert_email(self, event: WebhookEvent):
|
|
228
|
+
"""
|
|
229
|
+
Send email to team members when team soft budget is crossed
|
|
230
|
+
Supports multiple recipients via alert_emails field from team metadata
|
|
231
|
+
"""
|
|
232
|
+
# Collect all recipient emails
|
|
233
|
+
recipient_emails: List[str] = []
|
|
234
|
+
|
|
235
|
+
# Add additional alert emails from team metadata.soft_budget_alert_emails
|
|
236
|
+
if hasattr(event, "alert_emails") and event.alert_emails:
|
|
237
|
+
for email in event.alert_emails:
|
|
238
|
+
if email and email not in recipient_emails: # Avoid duplicates
|
|
239
|
+
recipient_emails.append(email)
|
|
240
|
+
|
|
241
|
+
# If no recipients found, skip sending
|
|
242
|
+
if not recipient_emails:
|
|
243
|
+
verbose_proxy_logger.warning(
|
|
244
|
+
f"No recipient emails found for team soft budget alert. event={event.model_dump(exclude_none=True)}"
|
|
245
|
+
)
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
# Validate that we have at least one valid email address
|
|
249
|
+
first_recipient_email = recipient_emails[0]
|
|
250
|
+
if not first_recipient_email or not first_recipient_email.strip():
|
|
251
|
+
verbose_proxy_logger.warning(
|
|
252
|
+
f"Invalid recipient email found for team soft budget alert. event={event.model_dump(exclude_none=True)}"
|
|
253
|
+
)
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
verbose_proxy_logger.debug(
|
|
257
|
+
f"send_team_soft_budget_alert_email_event: {json.dumps(event.model_dump(exclude_none=True), indent=4, default=str)}"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Get email params using the first recipient email (for template formatting)
|
|
261
|
+
# For team alerts with alert_emails, we don't need user_id lookup since we already have email addresses
|
|
262
|
+
# Pass user_id=None to prevent _get_email_params from trying to look up email from a potentially None user_id
|
|
263
|
+
email_params = await self._get_email_params(
|
|
264
|
+
email_event=EmailEvent.soft_budget_crossed,
|
|
265
|
+
user_id=None, # Team alerts don't require user_id when alert_emails are provided
|
|
266
|
+
user_email=first_recipient_email,
|
|
267
|
+
event_message=event.event_message,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Format budget values
|
|
271
|
+
soft_budget_str = f"${event.soft_budget}" if event.soft_budget is not None else "N/A"
|
|
272
|
+
spend_str = f"${event.spend}" if event.spend is not None else "$0.00"
|
|
273
|
+
max_budget_info = ""
|
|
274
|
+
if event.max_budget is not None:
|
|
275
|
+
max_budget_info = f"<b>Maximum Budget:</b> ${event.max_budget} <br />"
|
|
276
|
+
|
|
277
|
+
# Use team alias or generic greeting
|
|
278
|
+
team_alias = event.team_alias or "Team"
|
|
279
|
+
|
|
280
|
+
email_html_content = TEAM_SOFT_BUDGET_ALERT_EMAIL_TEMPLATE.format(
|
|
281
|
+
email_logo_url=email_params.logo_url,
|
|
282
|
+
team_alias=team_alias,
|
|
283
|
+
soft_budget=soft_budget_str,
|
|
284
|
+
spend=spend_str,
|
|
285
|
+
max_budget_info=max_budget_info,
|
|
286
|
+
base_url=email_params.base_url,
|
|
287
|
+
email_support_contact=email_params.support_contact,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Send email to all recipients
|
|
291
|
+
await self.send_email(
|
|
292
|
+
from_email=self.DEFAULT_LITELLM_EMAIL,
|
|
293
|
+
to_email=recipient_emails,
|
|
294
|
+
subject=email_params.subject,
|
|
295
|
+
html_body=email_html_content,
|
|
296
|
+
)
|
|
297
|
+
pass
|
|
298
|
+
|
|
220
299
|
async def send_max_budget_alert_email(self, event: WebhookEvent):
|
|
221
300
|
"""
|
|
222
301
|
Send email to user when max budget alert threshold is reached
|
|
@@ -285,15 +364,36 @@ class BaseEmailLogger(CustomLogger):
|
|
|
285
364
|
# - Don't re-alert, if alert already sent
|
|
286
365
|
_cache: DualCache = self.internal_usage_cache
|
|
287
366
|
|
|
288
|
-
# percent of max_budget left to spend
|
|
289
|
-
if user_info.max_budget is None and user_info.soft_budget is None:
|
|
290
|
-
return
|
|
291
|
-
|
|
292
367
|
# For soft_budget alerts, check if we've already sent an alert
|
|
293
368
|
if type == "soft_budget":
|
|
369
|
+
# For team soft budget alerts, we only need team soft_budget to be set
|
|
370
|
+
# For other entity types, we need either max_budget or soft_budget
|
|
371
|
+
if user_info.event_group == Litellm_EntityType.TEAM:
|
|
372
|
+
if user_info.soft_budget is None:
|
|
373
|
+
return
|
|
374
|
+
# For team soft budget alerts, require alert_emails to be configured
|
|
375
|
+
# Team soft budget alerts are sent via metadata.soft_budget_alerting_emails
|
|
376
|
+
if user_info.alert_emails is None or len(user_info.alert_emails) == 0:
|
|
377
|
+
verbose_proxy_logger.debug(
|
|
378
|
+
"Skipping team soft budget email alert: no alert_emails configured",
|
|
379
|
+
)
|
|
380
|
+
return
|
|
381
|
+
else:
|
|
382
|
+
# For non-team alerts, require either max_budget or soft_budget
|
|
383
|
+
if user_info.max_budget is None and user_info.soft_budget is None:
|
|
384
|
+
return
|
|
294
385
|
if user_info.soft_budget is not None and user_info.spend >= user_info.soft_budget:
|
|
295
386
|
# Generate cache key based on event type and identifier
|
|
296
|
-
|
|
387
|
+
# Use appropriate ID based on event_group to ensure unique cache keys per entity type
|
|
388
|
+
if user_info.event_group == Litellm_EntityType.TEAM:
|
|
389
|
+
_id = user_info.team_id or "default_id"
|
|
390
|
+
elif user_info.event_group == Litellm_EntityType.ORGANIZATION:
|
|
391
|
+
_id = user_info.organization_id or "default_id"
|
|
392
|
+
elif user_info.event_group == Litellm_EntityType.USER:
|
|
393
|
+
_id = user_info.user_id or "default_id"
|
|
394
|
+
else:
|
|
395
|
+
# For KEY and other types, use token or user_id
|
|
396
|
+
_id = user_info.token or user_info.user_id or "default_id"
|
|
297
397
|
_cache_key = f"email_budget_alerts:soft_budget_crossed:{_id}"
|
|
298
398
|
|
|
299
399
|
# Check if we've already sent this alert
|
|
@@ -318,10 +418,15 @@ class BaseEmailLogger(CustomLogger):
|
|
|
318
418
|
projected_exceeded_date=user_info.projected_exceeded_date,
|
|
319
419
|
projected_spend=user_info.projected_spend,
|
|
320
420
|
event_group=user_info.event_group,
|
|
421
|
+
alert_emails=user_info.alert_emails,
|
|
321
422
|
)
|
|
322
423
|
|
|
323
424
|
try:
|
|
324
|
-
|
|
425
|
+
# Use team-specific function for team alerts, otherwise use standard function
|
|
426
|
+
if user_info.event_group == Litellm_EntityType.TEAM:
|
|
427
|
+
await self.send_team_soft_budget_alert_email(webhook_event)
|
|
428
|
+
else:
|
|
429
|
+
await self.send_soft_budget_alert_email(webhook_event)
|
|
325
430
|
|
|
326
431
|
# Cache the alert to prevent duplicate sends
|
|
327
432
|
await _cache.async_set_cache(
|
|
@@ -166,7 +166,11 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|
|
166
166
|
"updated_by": user_api_key_dict.user_id,
|
|
167
167
|
"status": file_object.status,
|
|
168
168
|
},
|
|
169
|
-
"update": {
|
|
169
|
+
"update": {
|
|
170
|
+
"file_object": file_object.model_dump_json(),
|
|
171
|
+
"status": file_object.status,
|
|
172
|
+
"updated_by": user_api_key_dict.user_id,
|
|
173
|
+
}, # FIX: Update status and file_object on every operation to keep state in sync
|
|
170
174
|
},
|
|
171
175
|
)
|
|
172
176
|
|
|
@@ -354,6 +358,31 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|
|
354
358
|
)
|
|
355
359
|
return False
|
|
356
360
|
|
|
361
|
+
async def check_file_ids_access(
|
|
362
|
+
self, file_ids: List[str], user_api_key_dict: UserAPIKeyAuth
|
|
363
|
+
) -> None:
|
|
364
|
+
"""
|
|
365
|
+
Check if the user has access to a list of file IDs.
|
|
366
|
+
Only checks managed (unified) file IDs.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
file_ids: List of file IDs to check access for
|
|
370
|
+
user_api_key_dict: User API key authentication details
|
|
371
|
+
|
|
372
|
+
Raises:
|
|
373
|
+
HTTPException: If user doesn't have access to any of the files
|
|
374
|
+
"""
|
|
375
|
+
for file_id in file_ids:
|
|
376
|
+
is_unified_file_id = _is_base64_encoded_unified_file_id(file_id)
|
|
377
|
+
if is_unified_file_id:
|
|
378
|
+
if not await self.can_user_call_unified_file_id(
|
|
379
|
+
file_id, user_api_key_dict
|
|
380
|
+
):
|
|
381
|
+
raise HTTPException(
|
|
382
|
+
status_code=403,
|
|
383
|
+
detail=f"User {user_api_key_dict.user_id} does not have access to the file {file_id}",
|
|
384
|
+
)
|
|
385
|
+
|
|
357
386
|
async def async_pre_call_hook( # noqa: PLR0915
|
|
358
387
|
self,
|
|
359
388
|
user_api_key_dict: UserAPIKeyAuth,
|
|
@@ -387,6 +416,9 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|
|
387
416
|
if messages:
|
|
388
417
|
file_ids = self.get_file_ids_from_messages(messages)
|
|
389
418
|
if file_ids:
|
|
419
|
+
# Check user has access to all managed files
|
|
420
|
+
await self.check_file_ids_access(file_ids, user_api_key_dict)
|
|
421
|
+
|
|
390
422
|
# Check if any files are stored in storage backends and need base64 conversion
|
|
391
423
|
# This is needed for Vertex AI/Gemini which requires base64 content
|
|
392
424
|
is_vertex_ai = model and ("vertex_ai" in model or "gemini" in model.lower())
|
|
@@ -402,15 +434,27 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|
|
402
434
|
)
|
|
403
435
|
data["model_file_id_mapping"] = model_file_id_mapping
|
|
404
436
|
elif call_type == CallTypes.aresponses.value or call_type == CallTypes.responses.value:
|
|
405
|
-
# Handle managed files in responses API input
|
|
437
|
+
# Handle managed files in responses API input and tools
|
|
438
|
+
file_ids = []
|
|
439
|
+
|
|
440
|
+
# Extract file IDs from input parameter
|
|
406
441
|
input_data = data.get("input")
|
|
407
442
|
if input_data:
|
|
408
|
-
file_ids
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
443
|
+
file_ids.extend(self.get_file_ids_from_responses_input(input_data))
|
|
444
|
+
|
|
445
|
+
# Extract file IDs from tools parameter (e.g., code_interpreter container)
|
|
446
|
+
tools = data.get("tools")
|
|
447
|
+
if tools:
|
|
448
|
+
file_ids.extend(self.get_file_ids_from_responses_tools(tools))
|
|
449
|
+
|
|
450
|
+
if file_ids:
|
|
451
|
+
# Check user has access to all managed files
|
|
452
|
+
await self.check_file_ids_access(file_ids, user_api_key_dict)
|
|
453
|
+
|
|
454
|
+
model_file_id_mapping = await self.get_model_file_id_mapping(
|
|
455
|
+
file_ids, user_api_key_dict.parent_otel_span
|
|
456
|
+
)
|
|
457
|
+
data["model_file_id_mapping"] = model_file_id_mapping
|
|
414
458
|
elif call_type == CallTypes.afile_content.value:
|
|
415
459
|
retrieve_file_id = cast(Optional[str], data.get("file_id"))
|
|
416
460
|
potential_file_id = (
|
|
@@ -460,8 +504,6 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|
|
460
504
|
if retrieve_object_id
|
|
461
505
|
else False
|
|
462
506
|
)
|
|
463
|
-
print(f"🔥potential_llm_object_id: {potential_llm_object_id}")
|
|
464
|
-
print(f"🔥retrieve_object_id: {retrieve_object_id}")
|
|
465
507
|
if potential_llm_object_id and retrieve_object_id:
|
|
466
508
|
## VALIDATE USER HAS ACCESS TO THE OBJECT ##
|
|
467
509
|
if not await self.can_user_call_unified_object_id(
|
|
@@ -614,6 +656,41 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|
|
614
656
|
|
|
615
657
|
return file_ids
|
|
616
658
|
|
|
659
|
+
def get_file_ids_from_responses_tools(
|
|
660
|
+
self, tools: List[Dict[str, Any]]
|
|
661
|
+
) -> List[str]:
|
|
662
|
+
"""
|
|
663
|
+
Gets file ids from responses API tools parameter.
|
|
664
|
+
|
|
665
|
+
The tools can contain code_interpreter with container.file_ids:
|
|
666
|
+
[
|
|
667
|
+
{
|
|
668
|
+
"type": "code_interpreter",
|
|
669
|
+
"container": {"type": "auto", "file_ids": ["file-123", "file-456"]}
|
|
670
|
+
}
|
|
671
|
+
]
|
|
672
|
+
"""
|
|
673
|
+
file_ids: List[str] = []
|
|
674
|
+
|
|
675
|
+
if not isinstance(tools, list):
|
|
676
|
+
return file_ids
|
|
677
|
+
|
|
678
|
+
for tool in tools:
|
|
679
|
+
if not isinstance(tool, dict):
|
|
680
|
+
continue
|
|
681
|
+
|
|
682
|
+
# Check for code_interpreter with container file_ids
|
|
683
|
+
if tool.get("type") == "code_interpreter":
|
|
684
|
+
container = tool.get("container")
|
|
685
|
+
if isinstance(container, dict):
|
|
686
|
+
container_file_ids = container.get("file_ids")
|
|
687
|
+
if isinstance(container_file_ids, list):
|
|
688
|
+
for file_id in container_file_ids:
|
|
689
|
+
if isinstance(file_id, str):
|
|
690
|
+
file_ids.append(file_id)
|
|
691
|
+
|
|
692
|
+
return file_ids
|
|
693
|
+
|
|
617
694
|
async def get_model_file_id_mapping(
|
|
618
695
|
self, file_ids: List[str], litellm_parent_otel_span: Span
|
|
619
696
|
) -> dict:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "litellm-enterprise"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.31"
|
|
4
4
|
description = "Package for LiteLLM Enterprise features"
|
|
5
5
|
authors = ["BerriAI"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -22,7 +22,7 @@ requires = ["poetry-core"]
|
|
|
22
22
|
build-backend = "poetry.core.masonry.api"
|
|
23
23
|
|
|
24
24
|
[tool.commitizen]
|
|
25
|
-
version = "0.1.
|
|
25
|
+
version = "0.1.31"
|
|
26
26
|
version_files = [
|
|
27
27
|
"pyproject.toml:version",
|
|
28
28
|
"../requirements.txt:litellm-enterprise==",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/auth/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{litellm_enterprise-0.1.29 → litellm_enterprise-0.1.31}/litellm_enterprise/proxy/proxy_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|