Skip to content

Conversation

codeflash-ai[bot]
Copy link

@codeflash-ai codeflash-ai bot commented Oct 2, 2025

📄 40% (0.40x) speedup for _get_input_attributes in sentry_sdk/tracing_utils.py

⏱️ Runtime : 543 microseconds 387 microseconds (best of 143 runs)

📝 Explanation and details

The optimization achieved a 40% speedup by eliminating inefficient dictionary lookups and unnecessary operations. The key improvements are:

  1. Removed Nested Function and Double Dictionary Lookups: The original code used a _set_from_key helper function that performed if key in mapping: followed by mapping[key], resulting in two hash table lookups. The optimized version directly checks if key in mapping: and then accesses mapping[key] inline, reducing function call overhead and maintaining the same lookup pattern but in a more streamlined way.

  2. Eliminated Unnecessary list() Call: Changed for key, value in list(kwargs.items()): to for key, value in kwargs.items():. Creating a list from the dict items is wasteful since we're just iterating once without modification, saving both memory allocation and iteration overhead.

  3. More Efficient Message Building: For prompt handling, the optimized version stores the result of setdefault() in a variable (messages = attributes.setdefault(...)) and reuses it, avoiding repeated setdefault() calls for the same key.

The performance gains are most significant for test cases with many kwargs (103% faster with 999 elements) and cases with unmapped keys (21-35% faster), showing that the dictionary lookup optimizations have the biggest impact when processing larger parameter sets. The optimization maintains identical behavior while reducing computational overhead through more efficient data structure access patterns.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 34 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest  # used for our unit tests
from sentry_sdk.tracing_utils import _get_input_attributes


# Mock constants for testing (since sentry_sdk.consts is not available)
class SPANDATA:
    GEN_AI_REQUEST_MODEL = "gen_ai_request_model"
    GEN_AI_AGENT_NAME = "gen_ai_agent_name"
    GEN_AI_REQUEST_MAX_TOKENS = "gen_ai_request_max_tokens"
    GEN_AI_REQUEST_FREQUENCY_PENALTY = "gen_ai_request_frequency_penalty"
    GEN_AI_REQUEST_PRESENCE_PENALTY = "gen_ai_request_presence_penalty"
    GEN_AI_REQUEST_TEMPERATURE = "gen_ai_request_temperature"
    GEN_AI_REQUEST_TOP_P = "gen_ai_request_top_p"
    GEN_AI_REQUEST_TOP_K = "gen_ai_request_top_k"
    GEN_AI_REQUEST_MESSAGES = "gen_ai_request_messages"
    GEN_AI_TOOL_INPUT = "gen_ai_tool_input"

class SPANTEMPLATE:
    AI_AGENT = "ai_agent"
    AI_TOOL = "ai_tool"
    AI_CHAT = "ai_chat"
    OTHER = "other"
from sentry_sdk.tracing_utils import _get_input_attributes

# ------------------- UNIT TESTS -------------------

# 1. BASIC TEST CASES

def test_basic_model_and_agent():
    # Test basic mapping for model and agent name
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT,
        False,
        (),
        {"model": "gpt-4", "agent_name": "assistant"}
    ); result = codeflash_output # 5.43μs -> 4.49μs (21.0% faster)


def test_basic_numeric_parameters():
    # Test numeric parameters mapping
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT,
        False,
        (),
        {"max_tokens": 256, "temperature": 0.7, "top_k": 5}
    ); result = codeflash_output # 7.53μs -> 6.85μs (9.88% faster)

def test_basic_ignore_wrong_types():
    # Should ignore values with wrong types
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_CHAT,
        False,
        (),
        {"max_tokens": "not_an_int", "temperature": "not_a_float"}
    ); result = codeflash_output # 5.68μs -> 4.98μs (14.1% faster)

def test_basic_other_template_returns_empty():
    # Should return empty dict for non-AI templates
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.OTHER,
        False,
        (),
        {"model": "gpt-4", "prompt": "Hello"}
    ); result = codeflash_output # 1.96μs -> 1.58μs (23.8% faster)

# 2. EDGE TEST CASES

def test_edge_empty_kwargs_and_args():
    # Should handle empty kwargs and args gracefully
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT,
        False,
        (),
        {}
    ); result = codeflash_output # 4.57μs -> 3.85μs (18.9% faster)

def test_edge_none_values():
    # Should ignore None values
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT,
        False,
        (),
        {"model": None, "max_tokens": None, "prompt": None}
    ); result = codeflash_output # 5.98μs -> 4.76μs (25.5% faster)


def test_edge_unmapped_keys():
    # Should ignore keys not in mapping
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT,
        False,
        (),
        {"foo": "bar", "baz": 123}
    ); result = codeflash_output # 7.03μs -> 5.79μs (21.3% faster)


def test_edge_tool_with_send_pii_false():
    # Should NOT include tool input when send_pii is False
    args = (1, 2)
    kwargs = {"model": "gpt-4"}
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_TOOL,
        False,
        args,
        kwargs
    ); result = codeflash_output # 7.13μs -> 6.11μs (16.6% faster)

def test_edge_prompt_not_string():
    # Should ignore prompt if not string
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_CHAT,
        False,
        (),
        {"prompt": 12345}
    ); result = codeflash_output # 5.45μs -> 4.67μs (16.7% faster)

def test_edge_system_prompt_not_string():
    # Should ignore system_prompt if not string
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_CHAT,
        False,
        (),
        {"system_prompt": 12345}
    ); result = codeflash_output # 5.58μs -> 4.27μs (30.5% faster)


def test_edge_safe_repr_broken_object():
    # Should handle objects with broken repr
    class BadRepr:
        def __repr__(self):
            raise Exception("broken")
    args = (BadRepr(),)
    kwargs = {}
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_TOOL,
        True,
        args,
        kwargs
    ); result = codeflash_output # 10.9μs -> 10.3μs (5.83% faster)

# 3. LARGE SCALE TEST CASES

def test_large_scale_many_kwargs():
    # Test with many kwargs (up to 999 elements)
    kwargs = {f"model_name": "gpt-4"}
    # Add 998 unmapped keys
    for i in range(998):
        kwargs[f"foo_{i}"] = i
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT,
        False,
        (),
        kwargs
    ); result = codeflash_output # 161μs -> 79.4μs (103% faster)


def test_large_scale_many_numeric_params():
    # Test with all numeric params set to valid types
    kwargs = {
        "max_tokens": 999,
        "frequency_penalty": 0.9,
        "presence_penalty": 0.8,
        "temperature": 0.7,
        "top_p": 0.6,
        "top_k": 999,
    }
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT,
        False,
        (),
        kwargs
    ); result = codeflash_output # 8.36μs -> 7.54μs (11.0% faster)



#------------------------------------------------
import pytest
from sentry_sdk.tracing_utils import _get_input_attributes


# Mocks for sentry_sdk.consts and sentry_sdk.utils
class SPANDATA:
    GEN_AI_REQUEST_MODEL = "gen_ai_request_model"
    GEN_AI_AGENT_NAME = "gen_ai_agent_name"
    GEN_AI_REQUEST_MAX_TOKENS = "gen_ai_request_max_tokens"
    GEN_AI_REQUEST_FREQUENCY_PENALTY = "gen_ai_request_frequency_penalty"
    GEN_AI_REQUEST_PRESENCE_PENALTY = "gen_ai_request_presence_penalty"
    GEN_AI_REQUEST_TEMPERATURE = "gen_ai_request_temperature"
    GEN_AI_REQUEST_TOP_P = "gen_ai_request_top_p"
    GEN_AI_REQUEST_TOP_K = "gen_ai_request_top_k"
    GEN_AI_REQUEST_MESSAGES = "gen_ai_request_messages"
    GEN_AI_TOOL_INPUT = "gen_ai_tool_input"

class SPANTEMPLATE:
    AI_AGENT = "ai_agent"
    AI_TOOL = "ai_tool"
    AI_CHAT = "ai_chat"
    OTHER = "other"
from sentry_sdk.tracing_utils import _get_input_attributes

# ------------------- UNIT TESTS -------------------

# 1. Basic Test Cases

def test_basic_model_and_agent():
    # Test basic extraction of model and agent
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {"model": "gpt-4", "agent": "assistant"}
    ); attrs = codeflash_output # 7.87μs -> 6.41μs (22.7% faster)


def test_basic_numerics():
    # Test extraction of numeric attributes
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {
            "max_tokens": 128,
            "temperature": 0.7,
            "top_k": 50
        }
    ); attrs = codeflash_output # 6.28μs -> 5.18μs (21.1% faster)

def test_basic_model_name_and_agent_name():
    # Test alternate keys for model and agent
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {"model_name": "gpt-3.5", "agent_name": "bot"}
    ); attrs = codeflash_output # 6.11μs -> 4.79μs (27.3% faster)

def test_basic_tool_input_with_pii():
    # Test that tool input is included when send_pii is True
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_TOOL, True, (1, 2), {"foo": "bar"}
    ); attrs = codeflash_output # 9.02μs -> 8.17μs (10.4% faster)

def test_basic_tool_input_without_pii():
    # Test that tool input is not included when send_pii is False
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_TOOL, False, (1, 2), {"foo": "bar"}
    ); attrs = codeflash_output # 5.12μs -> 4.02μs (27.5% faster)

def test_basic_non_ai_template():
    # Test that nothing is extracted for non-AI templates
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.OTHER, True, (), {"model": "gpt-4", "prompt": "hi"}
    ); attrs = codeflash_output # 2.00μs -> 1.64μs (22.6% faster)

# 2. Edge Test Cases

def test_edge_empty_kwargs_and_args():
    # Test with empty args and kwargs
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {}
    ); attrs = codeflash_output # 4.98μs -> 3.72μs (33.7% faster)

def test_edge_none_values():
    # Test with None values (should not be included)
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {
            "model": None,
            "agent": None,
            "max_tokens": None,
            "prompt": None,
            "system_prompt": None
        }
    ); attrs = codeflash_output # 6.82μs -> 5.08μs (34.3% faster)

def test_edge_wrong_types():
    # Test with wrong types for mapped keys
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {
            "model": 123,  # should be str
            "max_tokens": "128",  # should be int
            "temperature": "0.5",  # should be float
            "top_k": 3.7  # should be int
        }
    ); attrs = codeflash_output # 6.38μs -> 5.29μs (20.8% faster)

def test_edge_prompt_not_str():
    # Test prompt and system_prompt with non-string values
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {
            "prompt": 123,
            "system_prompt": ["not", "a", "string"]
        }
    ); attrs = codeflash_output # 5.81μs -> 4.41μs (31.9% faster)

def test_edge_duplicate_prompts():
    # Test multiple prompt and system_prompt keys
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_CHAT, False, (), {
            "prompt": "Hello",
            "system_prompt": "System",
            "prompt": "Hi again"
        }
    ); attrs = codeflash_output # 10.3μs -> 9.63μs (6.83% faster)
    # Python dict can't have duplicate keys, so only last value remains
    expected = [
        {"role": "user", "content": "Hi again"},
        {"role": "system", "content": "System"}
    ]

def test_edge_extra_unmapped_keys():
    # Test that unmapped keys are ignored
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {
            "foo": "bar",
            "baz": 123
        }
    ); attrs = codeflash_output # 5.51μs -> 4.08μs (35.0% faster)

def test_edge_partial_kwargs():
    # Test with some mapped and some unmapped keys
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), {
            "model": "gpt-4",
            "foo": "bar"
        }
    ); attrs = codeflash_output # 5.52μs -> 4.30μs (28.5% faster)

def test_edge_tool_input_repr_broken():
    # Test safe_repr fallback if repr fails
    class BadRepr:
        def __repr__(self):
            raise Exception("broken repr")
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_TOOL, True, (BadRepr(),), {"foo": BadRepr()}
    ); attrs = codeflash_output # 9.66μs -> 8.62μs (12.2% faster)

def test_edge_messages_repr_broken():
    # Test safe_repr fallback for messages
    class BadRepr:
        def __repr__(self):
            raise Exception("broken repr")
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_CHAT, False, (), {"prompt": BadRepr()}
    ); attrs = codeflash_output # 5.83μs -> 4.56μs (27.9% faster)

# 3. Large Scale Test Cases

def test_large_many_kwargs():
    # Test with many mapped keys (but <1000)
    kwargs = {
        "model": "gpt-4",
        "agent": "assistant",
        "max_tokens": 1024,
        "temperature": 0.9,
        "top_p": 0.95,
        "top_k": 100,
        "frequency_penalty": 0.1,
        "presence_penalty": 0.2
    }
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), kwargs
    ); attrs = codeflash_output # 7.55μs -> 6.08μs (24.2% faster)
    for key, value in kwargs.items():
        if key in ["model", "model_name"]:
            pass
        elif key in ["agent", "agent_name"]:
            pass
        elif key == "max_tokens":
            pass
        elif key == "temperature":
            pass
        elif key == "top_p":
            pass
        elif key == "top_k":
            pass
        elif key == "frequency_penalty":
            pass
        elif key == "presence_penalty":
            pass

def test_large_many_prompts():
    # Test with many prompts (<1000)
    kwargs = {"prompt": "Prompt %d" % i for i in range(10)}
    # Only last prompt will remain due to dict behavior
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_CHAT, False, (), kwargs
    ); attrs = codeflash_output # 9.13μs -> 7.95μs (14.9% faster)
    expected = [{"role": "user", "content": "Prompt 9"}]

def test_large_tool_input_repr():
    # Test tool input with large args/kwargs
    args = tuple(range(500))
    kwargs = {"k%d" % i: i for i in range(500)}
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_TOOL, True, args, kwargs
    ); attrs = codeflash_output # 161μs -> 123μs (31.2% faster)

def test_large_all_mapped_keys():
    # Test all mapped keys together
    kwargs = {
        "model": "gpt-4",
        "agent": "assistant",
        "max_tokens": 512,
        "frequency_penalty": 0.2,
        "presence_penalty": 0.3,
        "temperature": 0.8,
        "top_p": 0.85,
        "top_k": 42
    }
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_AGENT, False, (), kwargs
    ); attrs = codeflash_output # 8.16μs -> 5.92μs (37.8% faster)

def test_large_messages_repr():
    # Test with many messages (prompts and system_prompts)
    kwargs = {}
    for i in range(500):
        if i % 2 == 0:
            kwargs["prompt"] = f"Prompt {i}"
        else:
            kwargs["system_prompt"] = f"System {i}"
    # Only last prompt and system_prompt will remain
    codeflash_output = _get_input_attributes(
        SPANTEMPLATE.AI_CHAT, False, (), kwargs
    ); attrs = codeflash_output # 10.2μs -> 9.03μs (12.8% faster)
    expected = [
        {"role": "user", "content": "Prompt 498"},
        {"role": "system", "content": "System 499"}
    ]
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-_get_input_attributes-mg9o212u and push.

Codeflash

The optimization achieved a 40% speedup by eliminating inefficient dictionary lookups and unnecessary operations. The key improvements are:

1. **Removed Nested Function and Double Dictionary Lookups**: The original code used a `_set_from_key` helper function that performed `if key in mapping:` followed by `mapping[key]`, resulting in two hash table lookups. The optimized version directly checks `if key in mapping:` and then accesses `mapping[key]` inline, reducing function call overhead and maintaining the same lookup pattern but in a more streamlined way.

2. **Eliminated Unnecessary `list()` Call**: Changed `for key, value in list(kwargs.items()):` to `for key, value in kwargs.items():`. Creating a list from the dict items is wasteful since we're just iterating once without modification, saving both memory allocation and iteration overhead.

3. **More Efficient Message Building**: For prompt handling, the optimized version stores the result of `setdefault()` in a variable (`messages = attributes.setdefault(...)`) and reuses it, avoiding repeated `setdefault()` calls for the same key.

The performance gains are most significant for test cases with many kwargs (103% faster with 999 elements) and cases with unmapped keys (21-35% faster), showing that the dictionary lookup optimizations have the biggest impact when processing larger parameter sets. The optimization maintains identical behavior while reducing computational overhead through more efficient data structure access patterns.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 2, 2025 17:05
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants