diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index 01f72e2887..fdd62ae489 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -372,6 +372,12 @@ class SPANDATA: Example: "chat" """ + GEN_AI_RESPONSE_MODEL = "gen_ai.response.model" + """ + Exact model identifier used to generate the response + Example: gpt-4o-mini-2024-07-18 + """ + GEN_AI_RESPONSE_TEXT = "gen_ai.response.text" """ The model's response text messages. @@ -649,6 +655,7 @@ class OP: FUNCTION_AWS = "function.aws" FUNCTION_GCP = "function.gcp" GEN_AI_CHAT = "gen_ai.chat" + GEN_AI_EMBEDDINGS = "gen_ai.embeddings" GEN_AI_EXECUTE_TOOL = "gen_ai.execute_tool" GEN_AI_HANDOFF = "gen_ai.handoff" GEN_AI_INVOKE_AGENT = "gen_ai.invoke_agent" @@ -674,8 +681,6 @@ class OP: MIDDLEWARE_STARLITE = "middleware.starlite" MIDDLEWARE_STARLITE_RECEIVE = "middleware.starlite.receive" MIDDLEWARE_STARLITE_SEND = "middleware.starlite.send" - OPENAI_CHAT_COMPLETIONS_CREATE = "ai.chat_completions.create.openai" - OPENAI_EMBEDDINGS_CREATE = "ai.embeddings.create.openai" HUGGINGFACE_HUB_CHAT_COMPLETIONS_CREATE = ( "ai.chat_completions.create.huggingface_hub" ) diff --git a/sentry_sdk/integrations/openai.py b/sentry_sdk/integrations/openai.py index e95753f6e1..5ab4d0b163 100644 --- a/sentry_sdk/integrations/openai.py +++ b/sentry_sdk/integrations/openai.py @@ -136,8 +136,8 @@ def _new_chat_completion_common(f, *args, **kwargs): streaming = kwargs.get("stream") span = sentry_sdk.start_span( - op=consts.OP.OPENAI_CHAT_COMPLETIONS_CREATE, - name="Chat Completion", + op=consts.OP.GEN_AI_CHAT, + name=f"{consts.OP.GEN_AI_CHAT} {model}", origin=OpenAIIntegration.origin, ) span.__enter__() @@ -146,16 +146,16 @@ def _new_chat_completion_common(f, *args, **kwargs): with capture_internal_exceptions(): if should_send_default_pii() and integration.include_prompts: - set_data_normalized(span, SPANDATA.AI_INPUT_MESSAGES, messages) + set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MESSAGES, messages) - set_data_normalized(span, SPANDATA.AI_MODEL_ID, model) + set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MODEL, model) set_data_normalized(span, SPANDATA.AI_STREAMING, streaming) if hasattr(res, "choices"): if should_send_default_pii() and integration.include_prompts: set_data_normalized( span, - SPANDATA.AI_RESPONSES, + SPANDATA.GEN_AI_RESPONSE_TEXT, list(map(lambda x: x.message, res.choices)), ) _calculate_chat_completion_usage( @@ -189,7 +189,7 @@ def new_iterator(): ) if should_send_default_pii() and integration.include_prompts: set_data_normalized( - span, SPANDATA.AI_RESPONSES, all_responses + span, SPANDATA.GEN_AI_RESPONSE_TEXT, all_responses ) _calculate_chat_completion_usage( messages, @@ -222,7 +222,7 @@ async def new_iterator_async(): ) if should_send_default_pii() and integration.include_prompts: set_data_normalized( - span, SPANDATA.AI_RESPONSES, all_responses + span, SPANDATA.GEN_AI_RESPONSE_TEXT, all_responses ) _calculate_chat_completion_usage( messages, @@ -320,24 +320,30 @@ def _new_embeddings_create_common(f, *args, **kwargs): if integration is None: return f(*args, **kwargs) + model = kwargs.get("model") + with sentry_sdk.start_span( - op=consts.OP.OPENAI_EMBEDDINGS_CREATE, - description="OpenAI Embedding Creation", + op=consts.OP.GEN_AI_EMBEDDINGS, + name=f"{consts.OP.GEN_AI_EMBEDDINGS} {model}", origin=OpenAIIntegration.origin, ) as span: if "input" in kwargs and ( should_send_default_pii() and integration.include_prompts ): if isinstance(kwargs["input"], str): - set_data_normalized(span, SPANDATA.AI_INPUT_MESSAGES, [kwargs["input"]]) + set_data_normalized( + span, SPANDATA.GEN_AI_REQUEST_MESSAGES, [kwargs["input"]] + ) elif ( isinstance(kwargs["input"], list) and len(kwargs["input"]) > 0 and isinstance(kwargs["input"][0], str) ): - set_data_normalized(span, SPANDATA.AI_INPUT_MESSAGES, kwargs["input"]) + set_data_normalized( + span, SPANDATA.GEN_AI_REQUEST_MESSAGES, kwargs["input"] + ) if "model" in kwargs: - set_data_normalized(span, SPANDATA.AI_MODEL_ID, kwargs["model"]) + set_data_normalized(span, SPANDATA.GEN_AI_REQUEST_MODEL, kwargs["model"]) response = yield f, args, kwargs diff --git a/tests/integrations/openai/test_openai.py b/tests/integrations/openai/test_openai.py index 3fdc138f39..ca8c7178b9 100644 --- a/tests/integrations/openai/test_openai.py +++ b/tests/integrations/openai/test_openai.py @@ -81,14 +81,17 @@ def test_nonstreaming_chat_completion( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.chat_completions.create.openai" + assert span["op"] == "gen_ai.chat" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES]["content"] - assert "the model response" in span["data"][SPANDATA.AI_RESPONSES]["content"] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]["content"] + assert ( + "the model response" + in span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT]["content"] + ) 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"] assert span["measurements"]["ai_completion_tokens_used"]["value"] == 10 assert span["measurements"]["ai_prompt_tokens_used"]["value"] == 20 @@ -123,14 +126,17 @@ async def test_nonstreaming_chat_completion_async( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.chat_completions.create.openai" + assert span["op"] == "gen_ai.chat" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES]["content"] - assert "the model response" in span["data"][SPANDATA.AI_RESPONSES]["content"] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]["content"] + assert ( + "the model response" + in span["data"][SPANDATA.GEN_AI_RESPONSE_TEXT]["content"] + ) 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"] assert span["measurements"]["ai_completion_tokens_used"]["value"] == 10 assert span["measurements"]["ai_prompt_tokens_used"]["value"] == 20 @@ -216,14 +222,14 @@ def test_streaming_chat_completion( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.chat_completions.create.openai" + assert span["op"] == "gen_ai.chat" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES]["content"] - assert "hello world" in span["data"][SPANDATA.AI_RESPONSES] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]["content"] + assert "hello world" 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"] try: import tiktoken # type: ignore # noqa # pylint: disable=unused-import @@ -312,14 +318,14 @@ async def test_streaming_chat_completion_async( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.chat_completions.create.openai" + assert span["op"] == "gen_ai.chat" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES]["content"] - assert "hello world" in span["data"][SPANDATA.AI_RESPONSES] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES]["content"] + assert "hello world" 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"] try: import tiktoken # type: ignore # noqa # pylint: disable=unused-import @@ -403,11 +409,11 @@ def test_embeddings_create( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.embeddings.create.openai" + assert span["op"] == "gen_ai.embeddings" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] else: - assert SPANDATA.AI_INPUT_MESSAGES not in span["data"] + assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"] assert span["measurements"]["ai_prompt_tokens_used"]["value"] == 20 assert span["measurements"]["ai_total_tokens_used"]["value"] == 30 @@ -451,11 +457,11 @@ async def test_embeddings_create_async( tx = events[0] assert tx["type"] == "transaction" span = tx["spans"][0] - assert span["op"] == "ai.embeddings.create.openai" + assert span["op"] == "gen_ai.embeddings" if send_default_pii and include_prompts: - assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES] + assert "hello" in span["data"][SPANDATA.GEN_AI_REQUEST_MESSAGES] else: - assert SPANDATA.AI_INPUT_MESSAGES not in span["data"] + assert SPANDATA.GEN_AI_REQUEST_MESSAGES not in span["data"] assert span["measurements"]["ai_prompt_tokens_used"]["value"] == 20 assert span["measurements"]["ai_total_tokens_used"]["value"] == 30