Skip to content

Commit 5b11b7f

Browse files
committed
attributes on chat completion
1 parent a29716b commit 5b11b7f

File tree

4 files changed

+84
-59
lines changed

4 files changed

+84
-59
lines changed

sentry_sdk/consts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ class SPANDATA:
417417
GEN_AI_REQUEST_MODEL = "gen_ai.request.model"
418418
"""
419419
The model identifier being used for the request.
420-
Example: "gpt-4-turbo-preview"
420+
Example: "gpt-4-turbo"
421421
"""
422422

423423
GEN_AI_REQUEST_PRESENCE_PENALTY = "gen_ai.request.presence_penalty"

sentry_sdk/integrations/openai.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from sentry_sdk.utils import (
1212
capture_internal_exceptions,
1313
event_from_exception,
14+
safe_serialize,
1415
)
1516

1617
from typing import TYPE_CHECKING
@@ -183,24 +184,50 @@ def _new_chat_completion_common(f, *args, **kwargs):
183184
)
184185
span.__enter__()
185186

187+
# Common attributes
188+
set_data_normalized(span, SPANDATA.GEN_AI_SYSTEM, "openai")
189+
set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MODEL, model)
190+
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "chat")
191+
set_data_normalized(span, SPANDATA.AI_STREAMING, streaming)
192+
193+
# Optional attributes
194+
max_tokens = kwargs.get("max_tokens")
195+
if max_tokens is not None:
196+
set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, max_tokens)
197+
198+
presence_penalty = kwargs.get("presence_penalty")
199+
if presence_penalty is not None:
200+
set_data_normalized(
201+
span, SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY, presence_penalty
202+
)
203+
204+
temperature = kwargs.get("temperature")
205+
if temperature is not None:
206+
set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_TEMPERATURE, temperature)
207+
208+
top_p = kwargs.get("top_p")
209+
if top_p is not None:
210+
set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_TOP_P, top_p)
211+
186212
res = yield f, args, kwargs
187213

188214
with capture_internal_exceptions():
189215
if should_send_default_pii() and integration.include_prompts:
190216
set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages)
191217

192-
set_data_normalized(span, SPANDATA.GEN_AI_SYSTEM, "openai")
193-
set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MODEL, model)
194-
set_data_normalized(span, SPANDATA.GEN_AI_OPERATION_NAME, "chat")
195-
set_data_normalized(span, SPANDATA.AI_STREAMING, streaming)
218+
if hasattr(res, "model"):
219+
set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_MODEL, res.model)
196220

197221
if hasattr(res, "choices"):
198222
if should_send_default_pii() and integration.include_prompts:
199-
set_data_normalized(
200-
span,
201-
SPANDATA.GEN_AI_RESPONSE_TEXT,
202-
list(map(lambda x: x.message, res.choices)),
203-
)
223+
response_text = [choice.message.dict() for choice in res.choices]
224+
if len(response_text) > 0:
225+
set_data_normalized(
226+
span,
227+
SPANDATA.GEN_AI_RESPONSE_TEXT,
228+
safe_serialize(response_text),
229+
)
230+
204231
_calculate_token_usage(messages, res, span, None, integration.count_tokens)
205232
span.__exit__(None, None, None)
206233
elif hasattr(res, "_iterator"):

sentry_sdk/integrations/openai_agents/utils.py

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import json
21
import sentry_sdk
32
from sentry_sdk.consts import SPANDATA
43
from sentry_sdk.integrations import DidNotEnable
54
from sentry_sdk.scope import should_send_default_pii
6-
from sentry_sdk.utils import event_from_exception
5+
from sentry_sdk.utils import event_from_exception, safe_serialize
76

87
from typing import TYPE_CHECKING
98

109
if TYPE_CHECKING:
1110
from typing import Any
1211
from typing import Callable
13-
from typing import Union
1412
from agents import Usage
1513

1614
try:
@@ -162,49 +160,3 @@ def _set_output_data(span, result):
162160
span.set_data(
163161
SPANDATA.GEN_AI_RESPONSE_TEXT, safe_serialize(output_messages["response"])
164162
)
165-
166-
167-
def safe_serialize(data):
168-
# type: (Any) -> str
169-
"""Safely serialize to a readable string."""
170-
171-
def serialize_item(item):
172-
# type: (Any) -> Union[str, dict[Any, Any], list[Any], tuple[Any, ...]]
173-
if callable(item):
174-
try:
175-
module = getattr(item, "__module__", None)
176-
qualname = getattr(item, "__qualname__", None)
177-
name = getattr(item, "__name__", "anonymous")
178-
179-
if module and qualname:
180-
full_path = f"{module}.{qualname}"
181-
elif module and name:
182-
full_path = f"{module}.{name}"
183-
else:
184-
full_path = name
185-
186-
return f"<function {full_path}>"
187-
except Exception:
188-
return f"<callable {type(item).__name__}>"
189-
elif isinstance(item, dict):
190-
return {k: serialize_item(v) for k, v in item.items()}
191-
elif isinstance(item, (list, tuple)):
192-
return [serialize_item(x) for x in item]
193-
elif hasattr(item, "__dict__"):
194-
try:
195-
attrs = {
196-
k: serialize_item(v)
197-
for k, v in vars(item).items()
198-
if not k.startswith("_")
199-
}
200-
return f"<{type(item).__name__} {attrs}>"
201-
except Exception:
202-
return repr(item)
203-
else:
204-
return item
205-
206-
try:
207-
serialized = serialize_item(data)
208-
return json.dumps(serialized, default=str)
209-
except Exception:
210-
return str(data)

sentry_sdk/utils.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,3 +1938,49 @@ def try_convert(convert_func, value):
19381938
return convert_func(value)
19391939
except Exception:
19401940
return None
1941+
1942+
1943+
def safe_serialize(data):
1944+
# type: (Any) -> str
1945+
"""Safely serialize to a readable string."""
1946+
1947+
def serialize_item(item):
1948+
# type: (Any) -> Union[str, dict[Any, Any], list[Any], tuple[Any, ...]]
1949+
if callable(item):
1950+
try:
1951+
module = getattr(item, "__module__", None)
1952+
qualname = getattr(item, "__qualname__", None)
1953+
name = getattr(item, "__name__", "anonymous")
1954+
1955+
if module and qualname:
1956+
full_path = f"{module}.{qualname}"
1957+
elif module and name:
1958+
full_path = f"{module}.{name}"
1959+
else:
1960+
full_path = name
1961+
1962+
return f"<function {full_path}>"
1963+
except Exception:
1964+
return f"<callable {type(item).__name__}>"
1965+
elif isinstance(item, dict):
1966+
return {k: serialize_item(v) for k, v in item.items()}
1967+
elif isinstance(item, (list, tuple)):
1968+
return [serialize_item(x) for x in item]
1969+
elif hasattr(item, "__dict__"):
1970+
try:
1971+
attrs = {
1972+
k: serialize_item(v)
1973+
for k, v in vars(item).items()
1974+
if not k.startswith("_")
1975+
}
1976+
return f"<{type(item).__name__} {attrs}>"
1977+
except Exception:
1978+
return repr(item)
1979+
else:
1980+
return item
1981+
1982+
try:
1983+
serialized = serialize_item(data)
1984+
return json.dumps(serialized, default=str)
1985+
except Exception:
1986+
return str(data)

0 commit comments

Comments
 (0)