Skip to content

Commit 6036aa9

Browse files
authored
chore(openai): transition to allowlist for openai llmobs span metadata (#14100)
Using an allow list instead of the current deny list reduces the risk of potentially sensitive data being tagged in LLM Obs spans. This PR updates the Open AI LLM Obs integration to use an allow list when tagging metadata using request kwargs. There is a separate allow list for each of the Open AI endpoints based on the Open AI API reference: - [completions](https://platform.openai.com/docs/api-reference/completions/create) - [chat](https://platform.openai.com/docs/api-reference/chat/create) - [responses](https://platform.openai.com/docs/api-reference/responses/create) I included every kwarg except those that were in the old deny list or `metadata` which could contain information that we cannot verify is safe to tag on our spans. ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
1 parent f2a5f70 commit 6036aa9

File tree

1 file changed

+63
-29
lines changed

1 file changed

+63
-29
lines changed

ddtrace/llmobs/_integrations/utils.py

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from ddtrace.llmobs._constants import INPUT_MESSAGES
2020
from ddtrace.llmobs._constants import INPUT_TOKENS_METRIC_KEY
2121
from ddtrace.llmobs._constants import INPUT_VALUE
22-
from ddtrace.llmobs._constants import LITELLM_ROUTER_INSTANCE_KEY
2322
from ddtrace.llmobs._constants import METADATA
2423
from ddtrace.llmobs._constants import OAI_HANDOFF_TOOL_ARG
2524
from ddtrace.llmobs._constants import OUTPUT_MESSAGES
@@ -39,31 +38,71 @@
3938

4039
logger = get_logger(__name__)
4140

42-
OPENAI_SKIPPED_COMPLETION_TAGS = (
43-
"model",
44-
"prompt",
45-
"api_key",
46-
"user_api_key",
47-
"user_api_key_hash",
48-
LITELLM_ROUTER_INSTANCE_KEY,
41+
COMMON_METADATA_KEYS = (
42+
"stream",
43+
"temperature",
44+
"top_p",
45+
"user",
4946
)
50-
OPENAI_SKIPPED_CHAT_TAGS = (
51-
"model",
52-
"messages",
47+
OPENAI_METADATA_RESPONSE_KEYS = (
48+
"background",
49+
"include",
50+
"max_output_tokens",
51+
"max_tool_calls",
52+
"parallel_tool_calls",
53+
"previous_response_id",
54+
"prompt",
55+
"reasoning",
56+
"service_tier",
57+
"store",
58+
"text",
59+
"tool_choice",
5360
"tools",
54-
"functions",
55-
"api_key",
56-
"user_api_key",
57-
"user_api_key_hash",
58-
LITELLM_ROUTER_INSTANCE_KEY,
61+
"top_logprobs",
62+
"truncation",
63+
)
64+
OPENAI_METADATA_CHAT_KEYS = (
65+
"audio",
66+
"frequency_penalty",
67+
"function_call",
68+
"logit_bias",
69+
"logprobs",
70+
"max_completion_tokens",
71+
"max_tokens",
72+
"modalities",
73+
"n",
74+
"parallel_tool_calls",
75+
"prediction",
76+
"presence_penalty",
77+
"reasoning_effort",
78+
"response_format",
79+
"seed",
80+
"service_tier",
81+
"stop",
82+
"store",
83+
"stream_options",
84+
"tool_choice",
85+
"top_logprobs",
86+
"web_search_options",
87+
)
88+
OPENAI_METADATA_COMPLETION_KEYS = (
89+
"best_of",
90+
"echo",
91+
"frequency_penalty",
92+
"logit_bias",
93+
"logprobs",
94+
"max_tokens",
95+
"n",
96+
"presence_penalty",
97+
"seed",
98+
"stop",
99+
"stream_options",
100+
"suffix",
59101
)
60102

61103
LITELLM_METADATA_CHAT_KEYS = (
62104
"timeout",
63-
"temperature",
64-
"top_p",
65105
"n",
66-
"stream",
67106
"stream_options",
68107
"stop",
69108
"max_completion_tokens",
@@ -73,7 +112,6 @@
73112
"presence_penalty",
74113
"frequency_penalty",
75114
"logit_bias",
76-
"user",
77115
"response_format",
78116
"seed",
79117
"tool_choice",
@@ -97,12 +135,8 @@
97135
"n",
98136
"presence_penalty",
99137
"stop",
100-
"stream",
101138
"stream_options",
102139
"suffix",
103-
"temperature",
104-
"top_p",
105-
"user",
106140
"api_base",
107141
"api_version",
108142
"model_list",
@@ -471,12 +505,12 @@ def get_metadata_from_kwargs(
471505
kwargs: Dict[str, Any], integration_name: str = "openai", operation: str = "chat"
472506
) -> Dict[str, Any]:
473507
metadata = {}
508+
keys_to_include: Tuple[str, ...] = COMMON_METADATA_KEYS
474509
if integration_name == "openai":
475-
keys_to_skip = OPENAI_SKIPPED_CHAT_TAGS if operation == "chat" else OPENAI_SKIPPED_COMPLETION_TAGS
476-
metadata = {k: v for k, v in kwargs.items() if k not in keys_to_skip}
510+
keys_to_include += OPENAI_METADATA_CHAT_KEYS if operation == "chat" else OPENAI_METADATA_COMPLETION_KEYS
477511
elif integration_name == "litellm":
478-
keys_to_include = LITELLM_METADATA_CHAT_KEYS if operation == "chat" else LITELLM_METADATA_COMPLETION_KEYS
479-
metadata = {k: v for k, v in kwargs.items() if k in keys_to_include}
512+
keys_to_include += LITELLM_METADATA_CHAT_KEYS if operation == "chat" else LITELLM_METADATA_COMPLETION_KEYS
513+
metadata = {k: v for k, v in kwargs.items() if k in keys_to_include}
480514
return metadata
481515

482516

@@ -621,7 +655,7 @@ def openai_get_metadata_from_response(
621655
metadata = {}
622656

623657
if kwargs:
624-
metadata.update({k: v for k, v in kwargs.items() if k not in ("model", "input", "instructions")})
658+
metadata.update({k: v for k, v in kwargs.items() if k in OPENAI_METADATA_RESPONSE_KEYS + COMMON_METADATA_KEYS})
625659

626660
if not response:
627661
return metadata

0 commit comments

Comments
 (0)