Repository navigation
fix(proxy): surface runtime-registered callbacks in UI Logging page #38974
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
24bd8a8
d99ab35
939a58f
beb5241
dfa0cf3
9aca4f6
0da275c
7c5660e
f2558b4
a75d5b6
12497f7
1a8e891
70c900b
8c461c4
f3b76f1
d63e655
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -268,7 +268,7 @@ def generate_feedback_box(): | |
| WEEKLY_SPEND_REPORT_JOB_ID, | ||
| ) | ||
| from litellm.exceptions import RejectedRequestError | ||
| from litellm.integrations.custom_guardrail import ModifyResponseException | ||
| from litellm.integrations.custom_guardrail import CustomGuardrail, ModifyResponseException | ||
| from litellm.integrations.custom_logger import CustomLogger | ||
| from litellm.integrations.SlackAlerting.slack_alerting import SlackAlerting | ||
| from litellm.litellm_core_utils.agentic_loop_settings import ( | ||
|
|
@@ -17550,6 +17550,66 @@ async def delete_callback( | |
| ) | ||
|
|
||
|
|
||
| def _normalize_callback_alias(callback_name: str) -> str: | ||
| callback_aliases: Final = ( | ||
| ("opentelemetry", "otel"), | ||
| ("s3_v2", "s3"), | ||
| ("aws_sqs", "sqs"), | ||
| ("custom_callback_api", "generic_api"), | ||
| ) | ||
| return next( | ||
| (canonical_name for alias, canonical_name in callback_aliases if alias == callback_name), | ||
| callback_name, | ||
| ) | ||
|
|
||
|
|
||
| def _callback_module_name(callback: CustomLogger | Callable[..., object]) -> str: | ||
| if inspect.ismethod(callback): | ||
| return callback.__func__.__module__ | ||
| if inspect.isfunction(callback): | ||
| return callback.__module__ | ||
| return type(callback).__module__ | ||
|
|
||
|
|
||
| def _is_litellm_internal_callback(callback_name: str, callback: CustomLogger | Callable[..., object]) -> bool: | ||
| from litellm.litellm_core_utils.custom_logger_registry import CustomLoggerRegistry | ||
|
|
||
| module_owner: Final = _callback_module_name(callback).partition(".")[0] | ||
| is_registered_integration: Final = callback_name in CustomLoggerRegistry.CALLBACK_CLASS_STR_TO_CLASS_TYPE | ||
| return not is_registered_integration and module_owner in ("litellm", "litellm_enterprise") | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
|
|
||
| def _is_instance_of_configured_callback( | ||
| callback_name: str, callback: CustomLogger | Callable[..., object], configured_classes: tuple[type, ...] | ||
| ) -> bool: | ||
| """Self-naming OTel-family instances (`arize`, `weave_otel`) match by name, so a configured `logfire` (a bare | ||
| `OpenTelemetry`) does not hide YAML-configured siblings of the same class.""" | ||
| from litellm.litellm_core_utils.custom_logger_registry import CustomLoggerRegistry | ||
|
|
||
| class_derived_name: Final = CustomLoggerRegistry.get_callback_str_from_class_type(type(callback)) | ||
| return isinstance(callback, configured_classes) and callback_name in (class_derived_name, type(callback).__name__) | ||
|
|
||
|
|
||
| def _hidden_runtime_callback_names(configured_callback_names: frozenset[str]) -> frozenset[str]: | ||
| from litellm.litellm_core_utils.custom_logger_registry import CustomLoggerRegistry | ||
|
|
||
| configured_classes: Final = tuple( | ||
| CustomLoggerRegistry.CALLBACK_CLASS_STR_TO_CLASS_TYPE[name] | ||
| for name in configured_callback_names | ||
| if name in CustomLoggerRegistry.CALLBACK_CLASS_STR_TO_CLASS_TYPE | ||
| ) | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| configured_modules: Final = frozenset(name.rsplit(".", 1)[0] for name in configured_callback_names if "." in name) | ||
| internal_callback_names: Final = frozenset({"cache", "vector_store_pre_call_hook"}) | ||
| return internal_callback_names | frozenset( | ||
| callback_name | ||
| for callback_name, callback in litellm.logging_callback_manager.get_callback_objects() | ||
|
greptile-apps[bot] marked this conversation as resolved.
|
||
| if isinstance(callback, CustomGuardrail) | ||
| or _is_litellm_internal_callback(callback_name, callback) | ||
| or _is_instance_of_configured_callback(callback_name, callback, configured_classes) | ||
| or _callback_module_name(callback) in configured_modules | ||
| ) | ||
|
|
||
|
|
||
| @router.get( | ||
| "/get/config/callbacks", | ||
| tags=["config.yaml"], | ||
|
|
@@ -17582,10 +17642,10 @@ async def get_config( | |
| # Normalize string callbacks to lists | ||
| def normalize_callback(callback): | ||
| if isinstance(callback, str): | ||
| return [callback] | ||
| elif callback is None: | ||
| return [] | ||
| return callback | ||
| return (callback,) | ||
| if callback is None: | ||
| return () | ||
| return tuple(callback) if isinstance(callback, (list, dict)) else () | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Dictionary callback deletion fails With dictionary-shaped settings, Was this helpful? React with 👍 or 👎 to provide feedback.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dict-shaped success_callback rows were already listed as editable on the base branch, and the delete route was list-only before this PR. Unchanged here, out of scope. |
||
|
|
||
| _success_callbacks = normalize_callback(_success_callbacks) | ||
| _failure_callbacks = normalize_callback(_failure_callbacks) | ||
|
|
@@ -17616,6 +17676,30 @@ def normalize_callback(callback): | |
| for _callback in _success_and_failure_callbacks: | ||
| _data_to_return.append(process_callback(_callback, "success_and_failure", environment_variables)) | ||
|
|
||
| configured_callback_names: Final = frozenset( | ||
| _normalize_callback_alias(callback) | ||
| for callback in (_success_callbacks + _failure_callbacks + _success_and_failure_callbacks) | ||
| ) | ||
|
devin-ai-integration[bot] marked this conversation as resolved.
|
||
| runtime_callbacks_by_type: Final = litellm.logging_callback_manager.get_callbacks_by_type() | ||
| hidden_callback_names: Final = _hidden_runtime_callback_names(configured_callback_names) | ||
| runtime_callback_rows: Final = tuple( | ||
| (_normalize_callback_alias(callback_name), callback_type) | ||
| for callback_type, callback_names in ( | ||
| ("success", runtime_callbacks_by_type["success"]), | ||
| ("failure", runtime_callbacks_by_type["failure"]), | ||
| ("success_and_failure", runtime_callbacks_by_type["success_and_failure"]), | ||
| ) | ||
| for callback_name in callback_names | ||
| if callback_name not in hidden_callback_names | ||
| ) | ||
| runtime_only_rows: Final = sorted( | ||
| frozenset(row for row in runtime_callback_rows if row[0] not in configured_callback_names) | ||
| ) | ||
| _data_to_return.extend( | ||
| dict(process_callback(callback_name, callback_type, environment_variables), read_only=True) | ||
| for callback_name, callback_type in runtime_only_rows | ||
| ) | ||
|
|
||
| _data_to_return = _apply_callback_role_gate(_data_to_return, is_full_admin) | ||
|
|
||
| # Check if slack alerting is on | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.