From 88cd97eac65d8d8c67b3400c1fc4cf3414380f26 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 3 Sep 2025 10:14:11 +0200 Subject: [PATCH 01/11] Rename attributes --- sentry_sdk/consts.py | 1 + sentry_sdk/integrations/huggingface_hub.py | 38 +++++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index d7a0603a10..6c82cffc90 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -794,6 +794,7 @@ class OP: GEN_AI_CHAT = "gen_ai.chat" GEN_AI_EMBEDDINGS = "gen_ai.embeddings" GEN_AI_EXECUTE_TOOL = "gen_ai.execute_tool" + GEN_AI_GENERATE_TEXT = "gen_ai.generate_text" GEN_AI_HANDOFF = "gen_ai.handoff" GEN_AI_PIPELINE = "gen_ai.pipeline" GEN_AI_INVOKE_AGENT = "gen_ai.invoke_agent" diff --git a/sentry_sdk/integrations/huggingface_hub.py b/sentry_sdk/integrations/huggingface_hub.py index 2dfcb5925a..22099c5559 100644 --- a/sentry_sdk/integrations/huggingface_hub.py +++ b/sentry_sdk/integrations/huggingface_hub.py @@ -1,15 +1,14 @@ from functools import wraps -from sentry_sdk import consts -from sentry_sdk.ai.monitoring import record_token_usage -from sentry_sdk.ai.utils import set_data_normalized -from sentry_sdk.consts import SPANDATA from typing import Any, Iterable, Callable import sentry_sdk -from sentry_sdk.scope import should_send_default_pii +from sentry_sdk.ai.monitoring import record_token_usage +from sentry_sdk.ai.utils import set_data_normalized +from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations import DidNotEnable, Integration +from sentry_sdk.scope import should_send_default_pii from sentry_sdk.utils import ( capture_internal_exceptions, event_from_exception, @@ -34,6 +33,8 @@ def __init__(self, include_prompts=True): @staticmethod def setup_once(): # type: () -> None + + # Other tasks that can be called: https://huggingface.co/docs/huggingface_hub/guides/inference#supported-providers-and-tasks huggingface_hub.inference._client.InferenceClient.text_generation = ( _wrap_text_generation( huggingface_hub.inference._client.InferenceClient.text_generation @@ -70,15 +71,22 @@ def new_text_generation(*args, **kwargs): # invalid call, let it return error return f(*args, **kwargs) - model = kwargs.get("model") + client = args[0] + model = client.model or kwargs.get("model") or "" streaming = kwargs.get("stream") span = sentry_sdk.start_span( - op=consts.OP.HUGGINGFACE_HUB_CHAT_COMPLETIONS_CREATE, - name="Text Generation", + op=OP.GEN_AI_GENERATE_TEXT, + name=f"generate_text {model}", origin=HuggingfaceHubIntegration.origin, ) span.__enter__() + + span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "generate_text") + if model: + span.set_data(SPANDATA.GEN_AI_REQUEST_MODEL, model) + span.set_data(SPANDATA.GEN_AI_SYSTEM, "TODO!!!!!") + try: res = f(*args, **kwargs) except Exception as e: @@ -88,16 +96,15 @@ def new_text_generation(*args, **kwargs): with capture_internal_exceptions(): if should_send_default_pii() and integration.include_prompts: - set_data_normalized(span, SPANDATA.AI_INPUT_MESSAGES, prompt) + set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MESSAGES, prompt) - set_data_normalized(span, SPANDATA.AI_MODEL_ID, model) - set_data_normalized(span, SPANDATA.AI_STREAMING, streaming) + span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, streaming) if isinstance(res, str): if should_send_default_pii() and integration.include_prompts: set_data_normalized( span, - SPANDATA.AI_RESPONSES, + SPANDATA.GEN_AI_RESPONSE_TEXT, [res], ) span.__exit__(None, None, None) @@ -107,7 +114,7 @@ def new_text_generation(*args, **kwargs): if should_send_default_pii() and integration.include_prompts: set_data_normalized( span, - SPANDATA.AI_RESPONSES, + SPANDATA.GEN_AI_RESPONSE_TEXT, [res.generated_text], ) if res.details is not None and res.details.generated_tokens > 0: @@ -120,7 +127,6 @@ def new_text_generation(*args, **kwargs): if not isinstance(res, Iterable): # we only know how to deal with strings and iterables, ignore - set_data_normalized(span, "unknown_response", True) span.__exit__(None, None, None) return res @@ -145,7 +151,7 @@ def new_details_iterator(): and integration.include_prompts ): set_data_normalized( - span, SPANDATA.AI_RESPONSES, "".join(data_buf) + span, SPANDATA.GEN_AI_RESPONSE_TEXT, "".join(data_buf) ) if tokens_used > 0: record_token_usage( @@ -172,7 +178,7 @@ def new_iterator(): and integration.include_prompts ): set_data_normalized( - span, SPANDATA.AI_RESPONSES, "".join(data_buf) + span, SPANDATA.GEN_AI_RESPONSE_TEXT, "".join(data_buf) ) span.__exit__(None, None, None) From 54164cd1462c0cf713211ef37a6edc1b40953c0f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 3 Sep 2025 10:56:38 +0200 Subject: [PATCH 02/11] text generation done. --- sentry_sdk/integrations/huggingface_hub.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry_sdk/integrations/huggingface_hub.py b/sentry_sdk/integrations/huggingface_hub.py index 22099c5559..a4ad16fdcb 100644 --- a/sentry_sdk/integrations/huggingface_hub.py +++ b/sentry_sdk/integrations/huggingface_hub.py @@ -85,7 +85,6 @@ def new_text_generation(*args, **kwargs): span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "generate_text") if model: span.set_data(SPANDATA.GEN_AI_REQUEST_MODEL, model) - span.set_data(SPANDATA.GEN_AI_SYSTEM, "TODO!!!!!") try: res = f(*args, **kwargs) From 05ef7e338ff5b11ee4beb23dc3451ab248ce6c1f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 3 Sep 2025 12:25:13 +0200 Subject: [PATCH 03/11] First version of supporting chat-completion --- sentry_sdk/integrations/huggingface_hub.py | 54 ++++++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/sentry_sdk/integrations/huggingface_hub.py b/sentry_sdk/integrations/huggingface_hub.py index a4ad16fdcb..1c399e6082 100644 --- a/sentry_sdk/integrations/huggingface_hub.py +++ b/sentry_sdk/integrations/huggingface_hub.py @@ -17,7 +17,7 @@ try: import huggingface_hub.inference._client - from huggingface_hub import ChatCompletionStreamOutput, TextGenerationOutput + from huggingface_hub import ChatCompletionOutput, TextGenerationOutput except ImportError: raise DidNotEnable("Huggingface not installed") @@ -40,6 +40,11 @@ def setup_once(): huggingface_hub.inference._client.InferenceClient.text_generation ) ) + huggingface_hub.inference._client.InferenceClient.chat_completion = ( + _wrap_text_generation( + huggingface_hub.inference._client.InferenceClient.chat_completion + ) + ) def _capture_exception(exc): @@ -63,12 +68,14 @@ def new_text_generation(*args, **kwargs): if "prompt" in kwargs: prompt = kwargs["prompt"] + elif "messages" in kwargs: + prompt = kwargs["messages"] elif len(args) >= 2: kwargs["prompt"] = args[1] prompt = kwargs["prompt"] args = (args[0],) + args[2:] else: - # invalid call, let it return error + # invalid call, dont instrument, let it return error return f(*args, **kwargs) client = args[0] @@ -95,7 +102,9 @@ def new_text_generation(*args, **kwargs): with capture_internal_exceptions(): if should_send_default_pii() and integration.include_prompts: - set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MESSAGES, prompt) + set_data_normalized( + span, SPANDATA.GEN_AI_REQUEST_MESSAGES, prompt, unpack=False + ) span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, streaming) @@ -104,17 +113,20 @@ def new_text_generation(*args, **kwargs): set_data_normalized( span, SPANDATA.GEN_AI_RESPONSE_TEXT, - [res], + res, ) span.__exit__(None, None, None) return res if isinstance(res, TextGenerationOutput): if should_send_default_pii() and integration.include_prompts: + import ipdb + + ipdb.set_trace() set_data_normalized( span, SPANDATA.GEN_AI_RESPONSE_TEXT, - [res.generated_text], + res.generated_text, ) if res.details is not None and res.details.generated_tokens > 0: record_token_usage( @@ -124,15 +136,35 @@ def new_text_generation(*args, **kwargs): span.__exit__(None, None, None) return res + if isinstance(res, ChatCompletionOutput): + if should_send_default_pii() and integration.include_prompts: + text_response = "".join( + [x.get("message", {}).get("content") for x in res.choices] + ) + set_data_normalized( + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + text_response, + ) + if hasattr(res, "usage") and res.usage is not None: + record_token_usage( + span, + input_tokens=res.usage.prompt_tokens, + output_tokens=res.usage.completion_tokens, + total_tokens=res.usage.total_tokens, + ) + span.__exit__(None, None, None) + return res + if not isinstance(res, Iterable): # we only know how to deal with strings and iterables, ignore span.__exit__(None, None, None) return res if kwargs.get("details", False): - # res is Iterable[TextGenerationStreamOutput] + def new_details_iterator(): - # type: () -> Iterable[ChatCompletionStreamOutput] + # type: () -> Iterable[Any] with capture_internal_exceptions(): tokens_used = 0 data_buf: list[str] = [] @@ -150,7 +182,9 @@ def new_details_iterator(): and integration.include_prompts ): set_data_normalized( - span, SPANDATA.GEN_AI_RESPONSE_TEXT, "".join(data_buf) + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + "".join(data_buf), ) if tokens_used > 0: record_token_usage( @@ -177,7 +211,9 @@ def new_iterator(): and integration.include_prompts ): set_data_normalized( - span, SPANDATA.GEN_AI_RESPONSE_TEXT, "".join(data_buf) + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + "".join(data_buf), ) span.__exit__(None, None, None) From d43d17fae203c2642589121ded2f40f42672b2a7 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 3 Sep 2025 12:42:12 +0200 Subject: [PATCH 04/11] Cleanup --- sentry_sdk/integrations/huggingface_hub.py | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/sentry_sdk/integrations/huggingface_hub.py b/sentry_sdk/integrations/huggingface_hub.py index 1c399e6082..d8ae2a2285 100644 --- a/sentry_sdk/integrations/huggingface_hub.py +++ b/sentry_sdk/integrations/huggingface_hub.py @@ -36,13 +36,15 @@ def setup_once(): # Other tasks that can be called: https://huggingface.co/docs/huggingface_hub/guides/inference#supported-providers-and-tasks huggingface_hub.inference._client.InferenceClient.text_generation = ( - _wrap_text_generation( - huggingface_hub.inference._client.InferenceClient.text_generation + _wrap_huggingface_task( + huggingface_hub.inference._client.InferenceClient.text_generation, + OP.GEN_AI_GENERATE_TEXT, ) ) huggingface_hub.inference._client.InferenceClient.chat_completion = ( - _wrap_text_generation( - huggingface_hub.inference._client.InferenceClient.chat_completion + _wrap_huggingface_task( + huggingface_hub.inference._client.InferenceClient.chat_completion, + OP.GEN_AI_CHAT, ) ) @@ -57,8 +59,8 @@ def _capture_exception(exc): sentry_sdk.capture_event(event, hint=hint) -def _wrap_text_generation(f): - # type: (Callable[..., Any]) -> Callable[..., Any] +def _wrap_huggingface_task(f, op): + # type: (Callable[..., Any], str) -> Callable[..., Any] @wraps(f) def new_text_generation(*args, **kwargs): # type: (*Any, **Any) -> Any @@ -81,21 +83,23 @@ def new_text_generation(*args, **kwargs): client = args[0] model = client.model or kwargs.get("model") or "" streaming = kwargs.get("stream") + operation_name = op.split(".")[-1] span = sentry_sdk.start_span( - op=OP.GEN_AI_GENERATE_TEXT, - name=f"generate_text {model}", + op=op, + name=f"{operation_name} {model}", origin=HuggingfaceHubIntegration.origin, ) span.__enter__() - span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "generate_text") + span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, operation_name) if model: span.set_data(SPANDATA.GEN_AI_REQUEST_MODEL, model) try: res = f(*args, **kwargs) except Exception as e: + span.set_status("error") _capture_exception(e) span.__exit__(None, None, None) raise e from None @@ -120,9 +124,6 @@ def new_text_generation(*args, **kwargs): if isinstance(res, TextGenerationOutput): if should_send_default_pii() and integration.include_prompts: - import ipdb - - ipdb.set_trace() set_data_normalized( span, SPANDATA.GEN_AI_RESPONSE_TEXT, From 27c851dd627c6b5916d1c9cbefcaa8bd8309f2a8 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 3 Sep 2025 12:47:26 +0200 Subject: [PATCH 05/11] better format of dict in span data --- sentry_sdk/ai/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/ai/utils.py b/sentry_sdk/ai/utils.py index cf52cba6e8..d6e5293a68 100644 --- a/sentry_sdk/ai/utils.py +++ b/sentry_sdk/ai/utils.py @@ -1,3 +1,5 @@ +import json + from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -33,4 +35,4 @@ def set_data_normalized(span, key, value, unpack=True): if isinstance(normalized, (int, float, bool, str)): span.set_data(key, normalized) else: - span.set_data(key, str(normalized)) + span.set_data(key, json.dumps(normalized)) From ade94106f6c4b2365dcd7348340b43088759f60f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 3 Sep 2025 13:44:48 +0200 Subject: [PATCH 06/11] fix test --- tests/integrations/huggingface_hub/test_huggingface_hub.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integrations/huggingface_hub/test_huggingface_hub.py b/tests/integrations/huggingface_hub/test_huggingface_hub.py index df0c6c6d76..8a50dd0fe2 100644 --- a/tests/integrations/huggingface_hub/test_huggingface_hub.py +++ b/tests/integrations/huggingface_hub/test_huggingface_hub.py @@ -68,7 +68,7 @@ def test_nonstreaming_chat_completion( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.chat_completions.create.huggingface_hub" + assert span["op"] == "gen_ai.generate_text" if send_default_pii and include_prompts: assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES] @@ -127,7 +127,7 @@ def test_streaming_chat_completion( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.chat_completions.create.huggingface_hub" + assert span["op"] == "gen_ai.generate_text" if send_default_pii and include_prompts: assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES] From afa687cfc08e6bba52a1b481d954e2a53db15084 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 3 Sep 2025 15:04:38 +0200 Subject: [PATCH 07/11] attributes for huggingface requests/responses --- sentry_sdk/integrations/huggingface_hub.py | 115 +++++++++++++++------ 1 file changed, 84 insertions(+), 31 deletions(-) diff --git a/sentry_sdk/integrations/huggingface_hub.py b/sentry_sdk/integrations/huggingface_hub.py index d8ae2a2285..5a5cbe61d6 100644 --- a/sentry_sdk/integrations/huggingface_hub.py +++ b/sentry_sdk/integrations/huggingface_hub.py @@ -62,7 +62,7 @@ def _capture_exception(exc): def _wrap_huggingface_task(f, op): # type: (Callable[..., Any], str) -> Callable[..., Any] @wraps(f) - def new_text_generation(*args, **kwargs): + def new_huggingface_task(*args, **kwargs): # type: (*Any, **Any) -> Any integration = sentry_sdk.get_client().get_integration(HuggingfaceHubIntegration) if integration is None: @@ -82,7 +82,6 @@ def new_text_generation(*args, **kwargs): client = args[0] model = client.model or kwargs.get("model") or "" - streaming = kwargs.get("stream") operation_name = op.split(".")[-1] span = sentry_sdk.start_span( @@ -93,9 +92,29 @@ def new_text_generation(*args, **kwargs): span.__enter__() span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, operation_name) + if model: span.set_data(SPANDATA.GEN_AI_REQUEST_MODEL, model) + # Input attributes + attribute_mapping = { + "tools": SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS, + "frequency_penalty": SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY, + "max_tokens": SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, + "presence_penalty": SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY, + "temperature": SPANDATA.GEN_AI_REQUEST_TEMPERATURE, + "top_p": SPANDATA.GEN_AI_REQUEST_TOP_P, + "top_k": SPANDATA.GEN_AI_REQUEST_TOP_K, + "stream": SPANDATA.GEN_AI_RESPONSE_STREAMING, + } + for attribute, span_attribute in attribute_mapping.items(): + value = kwargs.get(attribute, None) + if value is not None: + if isinstance(value, (int, float, bool, str)): + span.set_data(span_attribute, value) + else: + set_data_normalized(span, span_attribute, value, unpack=False) + try: res = f(*args, **kwargs) except Exception as e: @@ -105,30 +124,56 @@ def new_text_generation(*args, **kwargs): raise e from None with capture_internal_exceptions(): + # Output attributes + if hasattr(res, "model"): + model = res.model + if model: + span.set_data(SPANDATA.GEN_AI_RESPONSE_MODEL, model) + + if hasattr(res, "details") and res.details is not None: + finish_reason = getattr(res.details, "finish_reason", None) + if finish_reason: + span.set_data( + SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reason + ) + + try: + tool_calls = res.choices[0].message.tool_calls + except Exception: + tool_calls = [] + + if len(tool_calls) > 0: + set_data_normalized( + span, + SPANDATA.GEN_AI_RESPONSE_TOOL_CALLS, + tool_calls, + unpack=False, + ) + if should_send_default_pii() and integration.include_prompts: set_data_normalized( span, SPANDATA.GEN_AI_REQUEST_MESSAGES, prompt, unpack=False ) - span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, streaming) - if isinstance(res, str): if should_send_default_pii() and integration.include_prompts: - set_data_normalized( - span, - SPANDATA.GEN_AI_RESPONSE_TEXT, - res, - ) + if res: + set_data_normalized( + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + res, + ) span.__exit__(None, None, None) return res if isinstance(res, TextGenerationOutput): if should_send_default_pii() and integration.include_prompts: - set_data_normalized( - span, - SPANDATA.GEN_AI_RESPONSE_TEXT, - res.generated_text, - ) + if res.generated_text: + set_data_normalized( + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + res.generated_text, + ) if res.details is not None and res.details.generated_tokens > 0: record_token_usage( span, @@ -140,13 +185,17 @@ def new_text_generation(*args, **kwargs): if isinstance(res, ChatCompletionOutput): if should_send_default_pii() and integration.include_prompts: text_response = "".join( - [x.get("message", {}).get("content") for x in res.choices] - ) - set_data_normalized( - span, - SPANDATA.GEN_AI_RESPONSE_TEXT, - text_response, + [ + x.get("message", {}).get("content", None) or "" + for x in res.choices + ] ) + if text_response: + set_data_normalized( + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + text_response, + ) if hasattr(res, "usage") and res.usage is not None: record_token_usage( span, @@ -182,11 +231,13 @@ def new_details_iterator(): and should_send_default_pii() and integration.include_prompts ): - set_data_normalized( - span, - SPANDATA.GEN_AI_RESPONSE_TEXT, - "".join(data_buf), - ) + text_response = "".join(data_buf) + if text_response: + set_data_normalized( + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + text_response, + ) if tokens_used > 0: record_token_usage( span, @@ -211,13 +262,15 @@ def new_iterator(): and should_send_default_pii() and integration.include_prompts ): - set_data_normalized( - span, - SPANDATA.GEN_AI_RESPONSE_TEXT, - "".join(data_buf), - ) + text_response = "".join(data_buf) + if text_response: + set_data_normalized( + span, + SPANDATA.GEN_AI_RESPONSE_TEXT, + text_response, + ) span.__exit__(None, None, None) return new_iterator() - return new_text_generation + return new_huggingface_task From 8b439a33da14514045e83c3069d1bbbce6d35d6e Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 4 Sep 2025 08:31:06 +0200 Subject: [PATCH 08/11] cleanup --- sentry_sdk/integrations/huggingface_hub.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry_sdk/integrations/huggingface_hub.py b/sentry_sdk/integrations/huggingface_hub.py index 5a5cbe61d6..9e7f6ff2d5 100644 --- a/sentry_sdk/integrations/huggingface_hub.py +++ b/sentry_sdk/integrations/huggingface_hub.py @@ -207,7 +207,6 @@ def new_huggingface_task(*args, **kwargs): return res if not isinstance(res, Iterable): - # we only know how to deal with strings and iterables, ignore span.__exit__(None, None, None) return res From 4ca5442dc106e330bdd88f2ab882af32eec9e792 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 4 Sep 2025 14:27:28 +0200 Subject: [PATCH 09/11] updated tests --- .../huggingface_hub/test_huggingface_hub.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/integrations/huggingface_hub/test_huggingface_hub.py b/tests/integrations/huggingface_hub/test_huggingface_hub.py index 8a50dd0fe2..897c4bb223 100644 --- a/tests/integrations/huggingface_hub/test_huggingface_hub.py +++ b/tests/integrations/huggingface_hub/test_huggingface_hub.py @@ -71,11 +71,11 @@ def test_nonstreaming_chat_completion( assert span["op"] == "gen_ai.generate_text" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES] - assert "the model response" in span["data"][SPANDATA.AI_RESPONSES] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] + assert "the model response" in span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] else: - assert SPANDATA.AI_INPUT_MESSAGES not in span["data"] - assert SPANDATA.AI_RESPONSES not in span["data"] + assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"] + assert SPANDATA.GEN_AI_RESPONSE_TEXT not in span["data"] if details_arg: assert span["data"]["gen_ai.usage.total_tokens"] == 10 @@ -130,11 +130,11 @@ def test_streaming_chat_completion( assert span["op"] == "gen_ai.generate_text" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES] - assert "the model response" in span["data"][SPANDATA.AI_RESPONSES] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] + assert "the model response" in span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] else: - assert SPANDATA.AI_INPUT_MESSAGES not in span["data"] - assert SPANDATA.AI_RESPONSES not in span["data"] + assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"] + assert SPANDATA.GEN_AI_RESPONSE_TEXT not in span["data"] if details_arg: assert span["data"]["gen_ai.usage.total_tokens"] == 10 From 9b1d23a48a4104c7de9d24f3260eecfc73881519 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 4 Sep 2025 14:31:05 +0200 Subject: [PATCH 10/11] better tests --- .../huggingface_hub/test_huggingface_hub.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/integrations/huggingface_hub/test_huggingface_hub.py b/tests/integrations/huggingface_hub/test_huggingface_hub.py index 897c4bb223..a9dc450168 100644 --- a/tests/integrations/huggingface_hub/test_huggingface_hub.py +++ b/tests/integrations/huggingface_hub/test_huggingface_hub.py @@ -8,7 +8,6 @@ from huggingface_hub.errors import OverloadedError from sentry_sdk import start_transaction -from sentry_sdk.consts import SPANDATA from sentry_sdk.integrations.huggingface_hub import HuggingfaceHubIntegration @@ -71,11 +70,11 @@ def test_nonstreaming_chat_completion( assert span["op"] == "gen_ai.generate_text" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] - assert "the model response" in span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] + assert "hello" in span["data"]["gen_ai.request.messages"] + assert "the model response" in span["data"]["gen_ai.response.text"] else: - assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"] - assert SPANDATA.GEN_AI_RESPONSE_TEXT not in span["data"] + assert "gen_ai.request.messages" not in span["data"] + assert "gen_ai.response.text" not in span["data"] if details_arg: assert span["data"]["gen_ai.usage.total_tokens"] == 10 @@ -130,11 +129,11 @@ def test_streaming_chat_completion( assert span["op"] == "gen_ai.generate_text" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] - assert "the model response" in span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] + assert "hello" in span["data"]["gen_ai.request.messages"] + assert "the model response" in span["data"]["gen_ai.response.text"] else: - assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"] - assert SPANDATA.GEN_AI_RESPONSE_TEXT not in span["data"] + assert "gen_ai.request.messages" not in span["data"] + assert "gen_ai.response.text" not in span["data"] if details_arg: assert span["data"]["gen_ai.usage.total_tokens"] == 10 From 8451b6d996d9778c8185f3a99dd3f9507deca489 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 4 Sep 2025 14:41:25 +0200 Subject: [PATCH 11/11] do this in separate pr --- sentry_sdk/ai/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sentry_sdk/ai/utils.py b/sentry_sdk/ai/utils.py index d6e5293a68..cf52cba6e8 100644 --- a/sentry_sdk/ai/utils.py +++ b/sentry_sdk/ai/utils.py @@ -1,5 +1,3 @@ -import json - from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -35,4 +33,4 @@ def set_data_normalized(span, key, value, unpack=True): if isinstance(normalized, (int, float, bool, str)): span.set_data(key, normalized) else: - span.set_data(key, json.dumps(normalized)) + span.set_data(key, str(normalized))