From 6d1c183a12be39a12926666611d2a258220b8d9a Mon Sep 17 00:00:00 2001 From: Taruj Goyal Date: Thu, 16 Apr 2026 16:39:53 -0700 Subject: [PATCH] fix(responses): propagate metadata to callbacks on Responses API path `responses()` accepts `metadata` as a named parameter, consuming it from `**kwargs`. Downstream handlers read `kwargs.get("litellm_metadata")` which never contains the caller-provided metadata. This causes custom callback fields (e.g. caller_service for cost attribution) to be null in success/failure callbacks for all Responses API calls. When the Router is involved, it creates its own `litellm_metadata` dict with `model_group` before calling `litellm.aresponses()`. The fix merges caller-provided `metadata` into the existing `litellm_metadata` via `setdefault` so Router keys are preserved while caller fields are added. Fixes #15466 --- litellm/responses/main.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/litellm/responses/main.py b/litellm/responses/main.py index b6dc5afb944..4be24542615 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -940,6 +940,17 @@ def responses( local_vars = locals() try: + # metadata is consumed by the function signature and is not in **kwargs. + # Merge it into litellm_metadata so downstream handlers and callbacks + # can access caller-provided fields (e.g. Langfuse tags, session IDs). + if metadata is not None: + existing = kwargs.get("litellm_metadata") + if existing is None: + kwargs["litellm_metadata"] = metadata + elif isinstance(existing, dict): + for k, v in metadata.items(): + existing.setdefault(k, v) + litellm_logging_obj: LiteLLMLoggingObj = kwargs.get("litellm_logging_obj") # type: ignore litellm_call_id: Optional[str] = kwargs.get("litellm_call_id", None) _is_async = kwargs.pop("aresponses", False) is True