diff --git a/docs/my-website/docs/proxy/dynamic_logging.md b/docs/my-website/docs/proxy/dynamic_logging.md
index 484c0c1eac..3bc9f72b03 100644
--- a/docs/my-website/docs/proxy/dynamic_logging.md
+++ b/docs/my-website/docs/proxy/dynamic_logging.md
@@ -7,7 +7,7 @@ import TabItem from '@theme/TabItem';
:::info
-This is an enterprise feature.
+✨ This is an enterprise feature.
[Get started with LiteLLM Enterprise](https://www.litellm.ai/enterprise)
@@ -20,74 +20,61 @@ LiteLLM's dynamic callback management enables teams to control logging behavior
You can disable callbacks by passing the `x-litellm-disable-callbacks` header with your requests, giving teams granular control over where their data is logged.
-## Quick Start
+## Getting Started: List and Disable Callbacks
-
-
+Managing callbacks is a two-step process:
+
+1. **First, list your active callbacks** to see what's currently enabled
+2. **Then, disable specific callbacks** as needed for your requests
+
+
+
+## 1. List Active Callbacks
+
+Start by viewing all currently enabled callbacks on your proxy to see what's available to disable.
+
+#### Request
```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Content-Type: application/json' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'x-litellm-disable-callbacks: langfuse' \
- --data '{
- "model": "claude-sonnet-4-20250514",
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ]
-}'
+curl -X 'GET' \
+ 'http://localhost:4000/callbacks/list' \
+ -H 'accept: application/json' \
+ -H 'x-litellm-api-key: sk-1234'
```
-
-
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Content-Type: application/json' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'x-litellm-disable-callbacks: langfuse,datadog' \
- --data '{
- "model": "claude-sonnet-4-20250514",
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ]
-}'
-```
-
-
-
-
-## 1. View Active Logging Callbacks
-
-Before disabling callbacks, you can view all currently enabled callbacks on your proxy.
-
-### Request
-
-```bash
-curl --location 'http://0.0.0.0:4000/callbacks/list' \
- --header 'Authorization: Bearer sk-1234'
-```
-
-### Response
+#### Response
```json
{
- "callbacks": [
- "langfuse",
- "datadog",
- "prometheus",
- "slack_alerting"
- ]
+ "success": [
+ "deployment_callback_on_success",
+ "sync_deployment_callback_on_success"
+ ],
+ "failure": [
+ "async_deployment_callback_on_failure",
+ "deployment_callback_on_failure"
+ ],
+ "success_and_failure": [
+ "langfuse",
+ "datadog"
+ ]
}
```
-## 2. Disable a Single Callback
+#### Response Fields
+
+The response contains three arrays that categorize your active callbacks:
+- **`success`** - Callbacks that only execute when requests complete successfully. These callbacks receive data from successful LLM responses.
+- **`failure`** - Callbacks that only execute when requests fail or encounter errors. These callbacks receive error information and failed request data.
+- **`success_and_failure`** - Callbacks that execute for both successful and failed requests. These are typically logging/observability tools that need to capture all request data regardless of outcome.
+
+---
+
+## 2. Disable Callbacks
+
+Now that you know which callbacks are active, you can selectively disable them using the `x-litellm-disable-callbacks` header. You can reference any callback name from the list response above.
+
+### Disable a Single Callback
Use the `x-litellm-disable-callbacks` header to disable specific callbacks for individual requests.
@@ -140,9 +127,9 @@ print(response)
-## 3. Disable Multiple Callbacks
+### Disable Multiple Callbacks
-You can disable multiple callbacks by providing a comma-separated list in the header.
+You can disable multiple callbacks by providing a comma-separated list in the header. Use any combination of callback names from your `/callbacks/list` response.
@@ -192,3 +179,36 @@ print(response)
+
+## Header Format and Case Sensitivity
+
+### Expected Header Format
+
+The `x-litellm-disable-callbacks` header accepts callback names in the following formats (use the exact names returned by `/callbacks/list`):
+
+- **Single callback**: `x-litellm-disable-callbacks: langfuse`
+- **Multiple callbacks**: `x-litellm-disable-callbacks: langfuse,datadog,prometheus`
+
+When specifying multiple callbacks, use comma-separated values without spaces around the commas.
+
+### Case Sensitivity
+
+**Callback name checks are case insensitive.** This means all of the following are equivalent:
+
+```bash
+# These are all equivalent
+x-litellm-disable-callbacks: langfuse
+x-litellm-disable-callbacks: LANGFUSE
+x-litellm-disable-callbacks: LangFuse
+x-litellm-disable-callbacks: langFUSE
+```
+
+This applies to both single and multiple callback specifications:
+
+```bash
+# Case insensitive for multiple callbacks
+x-litellm-disable-callbacks: LANGFUSE,datadog,PROMETHEUS
+x-litellm-disable-callbacks: langfuse,DATADOG,prometheus
+```
+
+
diff --git a/litellm/litellm_core_utils/logging_callback_manager.py b/litellm/litellm_core_utils/logging_callback_manager.py
index e1bddc6549..ab6c2ac765 100644
--- a/litellm/litellm_core_utils/logging_callback_manager.py
+++ b/litellm/litellm_core_utils/logging_callback_manager.py
@@ -4,6 +4,7 @@ import litellm
from litellm._logging import verbose_logger
from litellm.integrations.additional_logging_utils import AdditionalLoggingUtils
from litellm.integrations.custom_logger import CustomLogger
+from litellm.types.utils import CallbacksByType
class LoggingCallbackManager:
@@ -275,3 +276,65 @@ class LoggingCallbackManager:
isinstance(callback, callback_type)
for callback in self._get_all_callbacks()
)
+
+ def get_callbacks_by_type(self) -> CallbacksByType:
+ """
+ Get all active callbacks categorized by their type (success, failure, success_and_failure).
+
+ Returns:
+ CallbacksByType: Dict with keys 'success', 'failure', 'success_and_failure' containing lists of callback strings
+ """
+ # Get callback lists
+ success_callbacks = set(litellm.success_callback + litellm._async_success_callback)
+ failure_callbacks = set(litellm.failure_callback + litellm._async_failure_callback)
+ general_callbacks = set(litellm.callbacks)
+
+ # Get all unique callbacks
+ all_callbacks = success_callbacks | failure_callbacks | general_callbacks
+
+ result: CallbacksByType = CallbacksByType(
+ success=[],
+ failure=[],
+ success_and_failure=[]
+ )
+
+ for callback in all_callbacks:
+ callback_str = self._get_callback_string(callback)
+
+ is_in_success = callback in success_callbacks
+ is_in_failure = callback in failure_callbacks
+ is_in_general = callback in general_callbacks
+
+ if is_in_general or (is_in_success and is_in_failure):
+ result["success_and_failure"].append(callback_str)
+ elif is_in_success:
+ result["success"].append(callback_str)
+ elif is_in_failure:
+ result["failure"].append(callback_str)
+
+
+
+ # final de-duplication
+ result["success"] = list(set(result["success"]))
+ result["failure"] = list(set(result["failure"]))
+ result["success_and_failure"] = list(set(result["success_and_failure"]))
+
+ return result
+
+ def _get_callback_string(
+ self,
+ callback: Union[CustomLogger, Callable, str]
+ ) -> str:
+ from litellm.litellm_core_utils.custom_logger_registry import (
+ CustomLoggerRegistry,
+ )
+ """Convert a callback to its string representation"""
+ if isinstance(callback, str):
+ return callback
+ elif isinstance(callback, CustomLogger):
+ # Try to get the string representation from the registry
+ callback_str = CustomLoggerRegistry.get_callback_str_from_class_type(type(callback))
+ return callback_str if callback_str is not None else type(callback).__name__
+ elif callable(callback):
+ return getattr(callback, '__name__', str(callback))
+ return str(callback)
diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json
index 023fc3f935..8d4e5e276e 100644
--- a/litellm/model_prices_and_context_window_backup.json
+++ b/litellm/model_prices_and_context_window_backup.json
@@ -13648,10 +13648,10 @@
"output_cost_per_token": 8e-06,
"output_cost_per_reasoning_token": 3e-06,
"citation_cost_per_token": 2e-06,
- "search_queries_cost_per_query": {
- "search_queries_size_low": 0.005,
- "search_queries_size_medium": 0.005,
- "search_queries_size_high": 0.005
+ "search_context_cost_per_query": {
+ "search_context_size_low": 0.005,
+ "search_context_size_medium": 0.005,
+ "search_context_size_high": 0.005
},
"litellm_provider": "perplexity",
"mode": "chat",
diff --git a/litellm/proxy/management_endpoints/callback_management_endpoints.py b/litellm/proxy/management_endpoints/callback_management_endpoints.py
new file mode 100644
index 0000000000..3f611715e4
--- /dev/null
+++ b/litellm/proxy/management_endpoints/callback_management_endpoints.py
@@ -0,0 +1,27 @@
+"""
+Endpoints for managing callbacks
+"""
+from fastapi import APIRouter, Depends
+
+from litellm.litellm_core_utils.logging_callback_manager import CallbacksByType
+from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
+
+router = APIRouter()
+
+
+@router.get(
+ "/callbacks/list",
+ tags=["Logging Callbacks"],
+ dependencies=[Depends(user_api_key_auth)],
+ response_model=CallbacksByType,
+)
+async def list_callbacks():
+ """
+ View List of Active Logging Callbacks
+ """
+ from litellm import logging_callback_manager
+
+ # Get callbacks organized by type using the callback manager utility
+ callbacks_by_type = logging_callback_manager.get_callbacks_by_type()
+
+ return callbacks_by_type
\ No newline at end of file
diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml
index 4e7aa675f0..4550c1760a 100644
--- a/litellm/proxy/proxy_config.yaml
+++ b/litellm/proxy/proxy_config.yaml
@@ -15,4 +15,7 @@ mcp_servers:
general_settings:
store_model_in_db: true
- store_prompts_in_spend_logs: true
\ No newline at end of file
+ store_prompts_in_spend_logs: true
+
+litellm_settings:
+ callbacks: ["langfuse", "datadog"]
\ No newline at end of file
diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py
index 375833b7de..95173d1c09 100644
--- a/litellm/proxy/proxy_server.py
+++ b/litellm/proxy/proxy_server.py
@@ -231,6 +231,9 @@ from litellm.proxy.litellm_pre_call_utils import add_litellm_data_to_request
from litellm.proxy.management_endpoints.budget_management_endpoints import (
router as budget_management_router,
)
+from litellm.proxy.management_endpoints.callback_management_endpoints import (
+ router as callback_management_endpoints_router,
+)
from litellm.proxy.management_endpoints.common_utils import _user_has_admin_view
from litellm.proxy.management_endpoints.customer_endpoints import (
router as customer_router,
@@ -8595,6 +8598,7 @@ app.include_router(spend_management_router)
app.include_router(caching_router)
app.include_router(analytics_router)
app.include_router(guardrails_router)
+app.include_router(callback_management_endpoints_router)
app.include_router(debugging_endpoints_router)
app.include_router(ui_crud_endpoints_router)
app.include_router(openai_files_router)
diff --git a/litellm/types/utils.py b/litellm/types/utils.py
index 14be87704f..74a1422cdb 100644
--- a/litellm/types/utils.py
+++ b/litellm/types/utils.py
@@ -2489,3 +2489,8 @@ class DynamicPromptManagementParamLiteral(str, Enum):
@classmethod
def list_all_params(cls):
return [param.value for param in cls]
+
+class CallbacksByType(TypedDict):
+ success: List[str]
+ failure: List[str]
+ success_and_failure: List[str]
diff --git a/tests/test_litellm/proxy/management_endpoints/test_callback_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_callback_management_endpoints.py
new file mode 100644
index 0000000000..bb622304ef
--- /dev/null
+++ b/tests/test_litellm/proxy/management_endpoints/test_callback_management_endpoints.py
@@ -0,0 +1,202 @@
+import json
+import os
+import sys
+from datetime import datetime, timezone
+from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
+from fastapi.testclient import TestClient
+
+sys.path.insert(
+ 0, os.path.abspath("../../../..")
+) #
+
+from typing import cast
+
+import litellm
+from litellm.integrations.datadog.datadog import DataDogLogger
+from litellm.integrations.langfuse.langfuse import LangFuseLogger
+from litellm.proxy.management_endpoints.callback_management_endpoints import router
+from litellm.proxy.proxy_server import app
+
+
+class TestCallbackManagementEndpoints:
+ """Test suite for callback management endpoints"""
+
+ @pytest.fixture(autouse=True)
+ def setup_and_teardown(self):
+ """Setup and teardown for each test"""
+ # Reset callbacks before each test
+ litellm.success_callback = []
+ litellm.failure_callback = []
+ litellm._async_success_callback = []
+ litellm._async_failure_callback = []
+ litellm.callbacks = []
+
+ yield
+
+ # Clean up after each test
+ litellm.success_callback = []
+ litellm.failure_callback = []
+ litellm._async_success_callback = []
+ litellm._async_failure_callback = []
+ litellm.callbacks = []
+
+ def test_list_callbacks_no_active_callbacks(self):
+ """Test /callbacks/list endpoint with no active callbacks"""
+ # Setup test client
+ client = TestClient(app)
+
+ # Make request to list callbacks endpoint
+ response = client.get(
+ "/callbacks/list",
+ headers={"Authorization": "Bearer sk-1234"}
+ )
+
+ # Verify response
+ assert response.status_code == 200
+
+ response_data = response.json()
+ assert "success" in response_data
+ assert "failure" in response_data
+ assert "success_and_failure" in response_data
+
+ # All lists should be empty
+ assert response_data["success"] == []
+ assert response_data["failure"] == []
+ assert response_data["success_and_failure"] == []
+
+ @patch.dict(os.environ, {
+ "LANGFUSE_PUBLIC_KEY": "test_public_key",
+ "LANGFUSE_SECRET_KEY": "test_secret_key",
+ "LANGFUSE_HOST": "https://test.langfuse.com"
+ })
+ def test_list_callbacks_with_langfuse_logger(self):
+ """Test /callbacks/list endpoint with real Langfuse logger initialized"""
+ # Setup test client
+ client = TestClient(app)
+
+ # Initialize Langfuse logger and add to callbacks
+ with patch('litellm.integrations.langfuse.langfuse.Langfuse') as mock_langfuse:
+ # Mock the Langfuse client initialization
+ mock_langfuse_client = MagicMock()
+ mock_langfuse.return_value = mock_langfuse_client
+
+
+ # Add string representation to callback lists (this is how the system typically works)
+ litellm.success_callback.append("langfuse")
+ litellm._async_success_callback.append("langfuse")
+
+ # Make request to list callbacks endpoint
+ response = client.get(
+ "/callbacks/list",
+ headers={"Authorization": "Bearer sk-1234"}
+ )
+
+ # Verify response
+ assert response.status_code == 200
+
+ response_data = response.json()
+
+ # Verify langfuse appears in success callbacks
+ assert "langfuse" in response_data["success"]
+ assert response_data["failure"] == []
+ assert response_data["success_and_failure"] == []
+
+ # Verify the response structure is correct
+ assert isinstance(response_data["success"], list)
+ assert isinstance(response_data["failure"], list)
+ assert isinstance(response_data["success_and_failure"], list)
+
+ def test_list_callbacks_with_datadog_logger(self):
+ """Test /callbacks/list endpoint with DataDog logger configuration"""
+ # Setup test client
+ client = TestClient(app)
+
+ # Test with datadog callbacks added directly (without initializing the logger to avoid async issues)
+ # Add string representations to different callback types to test comprehensive categorization
+ litellm.success_callback.append("datadog")
+ litellm.failure_callback.append("datadog")
+ litellm.callbacks.append("datadog")
+
+ # Make request to list callbacks endpoint
+ response = client.get(
+ "/callbacks/list",
+ headers={"Authorization": "Bearer sk-1234"}
+ )
+
+ # Verify response
+ assert response.status_code == 200
+
+ response_data = response.json()
+
+ # Verify datadog appears in the correct categorization
+ # Since datadog is in both success and failure, it should appear in success_and_failure
+ assert "datadog" in response_data["success_and_failure"]
+
+ # The categorization logic should deduplicate properly
+ assert len([cb for cb in response_data["success"] if cb == "datadog"]) <= 1
+ assert len([cb for cb in response_data["failure"] if cb == "datadog"]) <= 1
+ assert len([cb for cb in response_data["success_and_failure"] if cb == "datadog"]) <= 1
+
+ # Verify the response structure is correct
+ assert isinstance(response_data["success"], list)
+ assert isinstance(response_data["failure"], list)
+ assert isinstance(response_data["success_and_failure"], list)
+
+ def test_list_callbacks_mixed_callback_types(self):
+ """Test /callbacks/list endpoint with mixed callback types (string and logger instances)"""
+ # Setup test client
+ client = TestClient(app)
+
+ # Setup mixed callbacks
+ litellm.success_callback.append("langfuse")
+ litellm.failure_callback.append("datadog")
+ litellm.callbacks.append("prometheus")
+
+ # Make request to list callbacks endpoint
+ response = client.get(
+ "/callbacks/list",
+ headers={"Authorization": "Bearer sk-1234"}
+ )
+
+ # Verify response
+ assert response.status_code == 200
+
+ response_data = response.json()
+
+ # Verify callbacks are properly categorized
+ assert "prometheus" in response_data["success_and_failure"] # callbacks list items go to success_and_failure
+ assert "langfuse" in response_data["success"]
+ assert "datadog" in response_data["failure"]
+
+ # Verify no duplicates
+ all_callbacks = (
+ response_data["success"] +
+ response_data["failure"] +
+ response_data["success_and_failure"]
+ )
+ assert len(set(all_callbacks)) == len(all_callbacks)
+
+
+ def test_list_callbacks_empty_response_structure(self):
+ """Test that response always has correct structure even with no callbacks"""
+ # Setup test client
+ client = TestClient(app)
+
+ # Make request to list callbacks endpoint
+ response = client.get(
+ "/callbacks/list",
+ headers={"Authorization": "Bearer sk-1234"}
+ )
+
+ # Verify response structure
+ assert response.status_code == 200
+ response_data = response.json()
+
+ # Verify all required keys are present
+ required_keys = ["success", "failure", "success_and_failure"]
+ for key in required_keys:
+ assert key in response_data
+ assert isinstance(response_data[key], list)
+