Skip to content

fix(litellm): [MLOS-182] select metadata keys to tag from litellm kwargs #14067

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

Merged
merged 4 commits into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ddtrace/llmobs/_integrations/litellm.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ def _llmobs_set_tags(

# use Open AI helpers since response format will match Open AI
if self.is_completion_operation(operation):
openai_set_meta_tags_from_completion(span, kwargs, response)
openai_set_meta_tags_from_completion(span, kwargs, response, integration_name="litellm")
else:
openai_set_meta_tags_from_chat(span, kwargs, response)
openai_set_meta_tags_from_chat(span, kwargs, response, integration_name="litellm")

# custom logic for updating metadata on litellm spans
self._update_litellm_metadata(span, kwargs, operation)
Expand Down
76 changes: 72 additions & 4 deletions ddtrace/llmobs/_integrations/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,57 @@
LITELLM_ROUTER_INSTANCE_KEY,
)

LITELLM_METADATA_CHAT_KEYS = (
"timeout",
"temperature",
"top_p",
"n",
"stream",
"stream_options",
"stop",
"max_completion_tokens",
"max_tokens",
"modalities",
"prediction",
"presence_penalty",
"frequency_penalty",
"logit_bias",
"user",
"response_format",
"seed",
"tool_choice",
"parallel_tool_calls",
"logprobs",
"top_logprobs",
"deployment_id",
"reasoning_effort",
"base_url",
"api_base",
"api_version",
"model_list",
)
LITELLM_METADATA_COMPLETION_KEYS = (
"best_of",
"echo",
"frequency_penalty",
"logit_bias",
"logprobs",
"max_tokens",
"n",
"presence_penalty",
"stop",
"stream",
"stream_options",
"suffix",
"temperature",
"top_p",
"user",
"api_base",
"api_version",
"model_list",
"custom_llm_provider",
)


def extract_model_name_google(instance, model_name_attr):
"""Extract the model name from the instance.
Expand Down Expand Up @@ -297,12 +348,14 @@ def get_messages_from_converse_content(role: str, content: List[Dict[str, Any]])
return messages


def openai_set_meta_tags_from_completion(span: Span, kwargs: Dict[str, Any], completions: Any) -> None:
def openai_set_meta_tags_from_completion(
span: Span, kwargs: Dict[str, Any], completions: Any, integration_name: str = "openai"
) -> None:
"""Extract prompt/response tags from a completion and set them as temporary "_ml_obs.meta.*" tags."""
prompt = kwargs.get("prompt", "")
if isinstance(prompt, str):
prompt = [prompt]
parameters = {k: v for k, v in kwargs.items() if k not in OPENAI_SKIPPED_COMPLETION_TAGS}
parameters = get_metadata_from_kwargs(kwargs, integration_name, "completion")
output_messages = [{"content": ""}]
if not span.error and completions:
choices = getattr(completions, "choices", completions)
Expand All @@ -316,7 +369,9 @@ def openai_set_meta_tags_from_completion(span: Span, kwargs: Dict[str, Any], com
)


def openai_set_meta_tags_from_chat(span: Span, kwargs: Dict[str, Any], messages: Optional[Any]) -> None:
def openai_set_meta_tags_from_chat(
span: Span, kwargs: Dict[str, Any], messages: Optional[Any], integration_name: str = "openai"
) -> None:
"""Extract prompt/response tags from a chat completion and set them as temporary "_ml_obs.meta.*" tags."""
input_messages = []
for m in kwargs.get("messages", []):
Expand All @@ -340,7 +395,7 @@ def openai_set_meta_tags_from_chat(span: Span, kwargs: Dict[str, Any], messages:
for tool_call in tool_calls
]
input_messages.append(processed_message)
parameters = {k: v for k, v in kwargs.items() if k not in OPENAI_SKIPPED_CHAT_TAGS}
parameters = get_metadata_from_kwargs(kwargs, integration_name, "chat")
span._set_ctx_items({INPUT_MESSAGES: input_messages, METADATA: parameters})

if span.error or not messages:
Expand Down Expand Up @@ -412,6 +467,19 @@ def openai_set_meta_tags_from_chat(span: Span, kwargs: Dict[str, Any], messages:
span._set_ctx_item(OUTPUT_MESSAGES, output_messages)


def get_metadata_from_kwargs(
kwargs: Dict[str, Any], integration_name: str = "openai", operation: str = "chat"
) -> Dict[str, Any]:
metadata = {}
if integration_name == "openai":
keys_to_skip = OPENAI_SKIPPED_CHAT_TAGS if operation == "chat" else OPENAI_SKIPPED_COMPLETION_TAGS
metadata = {k: v for k, v in kwargs.items() if k not in keys_to_skip}
elif integration_name == "litellm":
keys_to_include = LITELLM_METADATA_CHAT_KEYS if operation == "chat" else LITELLM_METADATA_COMPLETION_KEYS
metadata = {k: v for k, v in kwargs.items() if k in keys_to_include}
return metadata


def openai_get_input_messages_from_response_input(
messages: Optional[Union[str, List[Dict[str, Any]]]]
) -> List[Dict[str, Any]]:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fixes:
- |
litellm: This fix resolves an issue where potentially sensitive parameters were being tagged as metadata
on spans produced by the LiteLLM integration.
Loading