diff --git a/sentry_sdk/integrations/pydantic_ai/__init__.py b/sentry_sdk/integrations/pydantic_ai/__init__.py index 517e88d64f..5b9dcc0717 100644 --- a/sentry_sdk/integrations/pydantic_ai/__init__.py +++ b/sentry_sdk/integrations/pydantic_ai/__init__.py @@ -172,19 +172,14 @@ class PydanticAIIntegration(Integration): identifier = "pydantic_ai" origin = f"auto.ai.{identifier}" - def __init__( - self, include_prompts: bool = True, handled_tool_call_exceptions: bool = True - ) -> None: + def __init__(self, handled_tool_call_exceptions: bool = True) -> None: """ Initialize the Pydantic AI integration. Args: - include_prompts: Whether to include prompts and messages in span data. - Requires send_default_pii=True. Defaults to True. handled_tool_exceptions: Capture tool call exceptions that Pydantic AI internally prevents from bubbling up. """ - self.include_prompts = include_prompts self.handled_tool_call_exceptions = handled_tool_call_exceptions @staticmethod diff --git a/sentry_sdk/integrations/pydantic_ai/utils.py b/sentry_sdk/integrations/pydantic_ai/utils.py index 7e1c06d0a5..a5b873fa51 100644 --- a/sentry_sdk/integrations/pydantic_ai/utils.py +++ b/sentry_sdk/integrations/pydantic_ai/utils.py @@ -19,33 +19,12 @@ from pydantic_ai.settings import ModelSettings -def _should_send_prompts_legacy() -> bool: - """ - Check if prompts should be sent to Sentry based on the deprecated - ``send_default_pii`` option and the ``include_prompts`` integration setting. - - TODO: Remove this once `send_default_pii` is deprecated. - """ - if not should_send_default_pii(): - return False - - from . import PydanticAIIntegration - - # Get the integration instance from the client - integration = sentry_sdk.get_client().get_integration(PydanticAIIntegration) - - if integration is None: - return False - - return getattr(integration, "include_prompts", False) - - def _should_send_inputs() -> bool: client = sentry_sdk.get_client() if has_data_collection_enabled(client.options): return bool(client.options["data_collection"]["gen_ai"]["inputs"]) - return _should_send_prompts_legacy() + return should_send_default_pii() def _should_send_outputs() -> bool: @@ -53,7 +32,7 @@ def _should_send_outputs() -> bool: if has_data_collection_enabled(client.options): return bool(client.options["data_collection"]["gen_ai"]["outputs"]) - return _should_send_prompts_legacy() + return should_send_default_pii() def _set_agent_data(span: "Span", agent: "Optional[Agent]") -> None: diff --git a/tests/integrations/pydantic_ai/test_pydantic_ai.py b/tests/integrations/pydantic_ai/test_pydantic_ai.py index a85d061c76..7254a00e93 100644 --- a/tests/integrations/pydantic_ai/test_pydantic_ai.py +++ b/tests/integrations/pydantic_ai/test_pydantic_ai.py @@ -635,19 +635,13 @@ async def test_model_settings( @pytest.mark.asyncio @pytest.mark.parametrize( - "send_default_pii, include_prompts", - [ - (True, True), - (True, False), - (False, True), - (False, False), - ], + "send_default_pii", + [True, False], ) async def test_system_prompt_attribute( sentry_init, capture_items, send_default_pii, - include_prompts, ): """ Test that system prompts are included as the first message. @@ -659,7 +653,7 @@ async def test_system_prompt_attribute( ) sentry_init( - integrations=[PydanticAIIntegration(include_prompts=include_prompts)], + integrations=[PydanticAIIntegration()], traces_sample_rate=1.0, send_default_pii=send_default_pii, ) @@ -678,7 +672,7 @@ async def test_system_prompt_attribute( chat_span = chat_spans[0] - if send_default_pii and include_prompts: + if send_default_pii: system_instructions = chat_span["attributes"][ SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS ] @@ -918,149 +912,6 @@ async def test_gen_ai_system( assert chat_span["attributes"][SPANDATA.GEN_AI_PROVIDER_NAME] == "test" -@pytest.mark.asyncio -async def test_include_prompts_false( - sentry_init, - capture_items, - get_test_agent, -): - """ - Test that prompts are not captured when include_prompts=False. - """ - sentry_init( - integrations=[PydanticAIIntegration(include_prompts=False)], - traces_sample_rate=1.0, - send_default_pii=True, # Even with PII enabled, prompts should not be captured - ) - - test_agent = get_test_agent() - items = capture_items("span") - - await test_agent.run("Sensitive prompt") - - sentry_sdk.flush() - spans = [item.payload for item in items] - - # Find child spans (invoke_agent is the transaction, not a child span) - chat_spans = [ - s for s in spans if s["attributes"].get("sentry.op", "") == "gen_ai.chat" - ] - - # Verify that messages and response text are not captured - for span in chat_spans: - assert "gen_ai.request.messages" not in span["attributes"] - assert "gen_ai.response.text" not in span["attributes"] - - -@pytest.mark.asyncio -async def test_include_prompts_true( - sentry_init, - capture_items, - get_test_agent, -): - """ - Test that prompts are captured when include_prompts=True (default). - """ - sentry_init( - integrations=[PydanticAIIntegration(include_prompts=True)], - traces_sample_rate=1.0, - send_default_pii=True, - ) - - test_agent = get_test_agent() - items = capture_items("span") - - await test_agent.run("Test prompt") - - sentry_sdk.flush() - spans = [item.payload for item in items] - - # Find child spans (invoke_agent is the transaction, not a child span) - chat_spans = [ - s for s in spans if s["attributes"].get("sentry.op", "") == "gen_ai.chat" - ] - - # Verify that messages are captured in chat spans - assert len(chat_spans) == 1 - assert "gen_ai.request.messages" in chat_spans[0]["attributes"] - - -@pytest.mark.asyncio -async def test_include_prompts_false_with_tools( - sentry_init, - capture_items, - get_test_agent, -): - """ - Test that tool input/output are not captured when include_prompts=False. - """ - sentry_init( - integrations=[PydanticAIIntegration(include_prompts=False)], - traces_sample_rate=1.0, - send_default_pii=True, - ) - - test_agent = get_test_agent() - - @test_agent.tool_plain - def test_tool(value: int) -> int: - """A test tool.""" - return value * 2 - - items = capture_items("span") - - await test_agent.run("Use the test tool with value 5") - - sentry_sdk.flush() - spans = [item.payload for item in items] - - # Find tool spans - tool_spans = [ - s - for s in spans - if s["attributes"].get("sentry.op", "") == "gen_ai.execute_tool" - ] - - # If tool was executed, verify input/output are not captured - for tool_span in tool_spans: - assert "gen_ai.tool.input" not in tool_span["attributes"] - assert "gen_ai.tool.output" not in tool_span["attributes"] - - -@pytest.mark.asyncio -async def test_include_prompts_requires_pii( - sentry_init, - capture_items, - get_test_agent, -): - """ - Test that include_prompts requires send_default_pii=True. - """ - sentry_init( - integrations=[PydanticAIIntegration(include_prompts=True)], - traces_sample_rate=1.0, - send_default_pii=False, # PII disabled - ) - - test_agent = get_test_agent() - items = capture_items("span") - - await test_agent.run("Test prompt") - - sentry_sdk.flush() - spans = [item.payload for item in items] - - # Find child spans (invoke_agent is the transaction, not a child span) - chat_spans = [ - s for s in spans if s["attributes"].get("sentry.op", "") == "gen_ai.chat" - ] - - # Even with include_prompts=True, if PII is disabled, messages should not be captured - for span in chat_spans: - assert "gen_ai.request.messages" not in span["attributes"] - assert "gen_ai.response.text" not in span["attributes"] - - @pytest.mark.asyncio async def test_context_cleanup_after_run(sentry_init, get_test_agent): """ @@ -1253,19 +1104,13 @@ async def test_invoke_agent_with_list_user_prompt( @pytest.mark.asyncio @pytest.mark.parametrize( - "send_default_pii, include_prompts", - [ - (True, True), - (True, False), - (False, True), - (False, False), - ], + "send_default_pii", + [True, False], ) async def test_invoke_agent_with_instructions( sentry_init, capture_items, send_default_pii, - include_prompts, ): """ Test that invoke_agent span handles instructions correctly. @@ -1283,7 +1128,7 @@ async def test_invoke_agent_with_instructions( instructions_separator = "\n\n" if PYDANTIC_AI_VERSION >= (2, 36) else "\n" sentry_init( - integrations=[PydanticAIIntegration(include_prompts=include_prompts)], + integrations=[PydanticAIIntegration()], traces_sample_rate=1.0, send_default_pii=send_default_pii, ) @@ -1302,7 +1147,7 @@ async def test_invoke_agent_with_instructions( chat_span = chat_spans[0] - if send_default_pii and include_prompts: + if send_default_pii: system_instructions = chat_span["attributes"][ SPANDATA.GEN_AI_SYSTEM_INSTRUCTIONS ] @@ -2020,7 +1865,7 @@ async def test_set_input_messages_without_prompts( import sentry_sdk sentry_init( - integrations=[PydanticAIIntegration(include_prompts=False)], + integrations=[PydanticAIIntegration()], traces_sample_rate=1.0, send_default_pii=True, ) @@ -2116,7 +1961,7 @@ async def test_should_send_prompts_without_pii( ) sentry_init( - integrations=[PydanticAIIntegration(include_prompts=True)], + integrations=[PydanticAIIntegration()], traces_sample_rate=1.0, send_default_pii=False, # PII disabled, ) @@ -2250,7 +2095,7 @@ async def test_execute_tool_span_without_prompts( ) sentry_init( - integrations=[PydanticAIIntegration(include_prompts=False)], + integrations=[PydanticAIIntegration()], traces_sample_rate=1.0, send_default_pii=True, ) @@ -2865,20 +2710,18 @@ def _spans_by_op(items): @pytest.mark.parametrize( - "data_collection,send_default_pii,include_prompts,expect_inputs,expect_available_tools", + "data_collection,send_default_pii,expect_inputs,expect_available_tools", [ pytest.param( {"gen_ai": {"inputs": True}}, False, - False, True, True, - id="gen-ai-inputs-enabled-overrides-pii-and-include-prompts-disabled", + id="gen-ai-inputs-enabled-overrides-pii", ), pytest.param( {"gen_ai": {"inputs": False}}, True, - True, False, False, id="gen-ai-inputs-disabled-overrides-pii-enabled", @@ -2886,7 +2729,6 @@ def _spans_by_op(items): pytest.param( {}, False, - False, True, True, id="gen-ai-omitted-defaults-to-enabled", @@ -2894,7 +2736,6 @@ def _spans_by_op(items): pytest.param( {"gen_ai": {"outputs": False}}, False, - False, True, True, id="gen-ai-outputs-disabled-does-not-affect-inputs", @@ -2904,21 +2745,11 @@ def _spans_by_op(items): True, True, True, - True, - id="no-data-collection-pii-and-include-prompts-enabled-collects", - ), - pytest.param( - None, - True, - False, - False, - True, - id="no-data-collection-include-prompts-disabled", + id="no-data-collection-pii", ), pytest.param( None, False, - True, False, True, id="no-data-collection-pii-disabled", @@ -2932,12 +2763,11 @@ async def test_data_collection_gen_ai_inputs_gates_request_messages_tool_inputs_ get_test_agent, data_collection, send_default_pii, - include_prompts, expect_inputs, expect_available_tools, ): init_kwargs = { - "integrations": [PydanticAIIntegration(include_prompts=include_prompts)], + "integrations": [PydanticAIIntegration()], "traces_sample_rate": 1.0, "send_default_pii": send_default_pii, } @@ -2997,33 +2827,29 @@ def add_numbers(a: int, b: int) -> int: @pytest.mark.parametrize( - "data_collection,send_default_pii,include_prompts,expect_outputs", + "data_collection,send_default_pii,expect_outputs", [ pytest.param( {"gen_ai": {"outputs": True}}, False, - False, True, - id="gen-ai-outputs-enabled-overrides-pii-and-include-prompts-disabled", + id="gen-ai-outputs-enabled-overrides-pii", ), pytest.param( {"gen_ai": {"outputs": False}}, True, - True, False, id="gen-ai-outputs-disabled-overrides-pii-enabled", ), pytest.param( {}, False, - False, True, id="gen-ai-omitted-defaults-to-enabled", ), pytest.param( {"gen_ai": {"inputs": False}}, False, - False, True, id="gen-ai-inputs-disabled-does-not-affect-outputs", ), @@ -3031,21 +2857,12 @@ def add_numbers(a: int, b: int) -> int: None, True, True, - True, - id="no-data-collection-pii-and-include-prompts-enabled-collects", + id="no-data-collection-pii", ), pytest.param( None, - True, False, False, - id="no-data-collection-include-prompts-disabled", - ), - pytest.param( - None, - False, - True, - False, id="no-data-collection-pii-disabled", ), ], @@ -3057,11 +2874,10 @@ async def test_data_collection_gen_ai_outputs_gates_response_text_and_tool_outpu get_test_agent, data_collection, send_default_pii, - include_prompts, expect_outputs, ): init_kwargs = { - "integrations": [PydanticAIIntegration(include_prompts=include_prompts)], + "integrations": [PydanticAIIntegration()], "traces_sample_rate": 1.0, "send_default_pii": send_default_pii, }