Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 43% (0.43x) speedup for _safe_get_request_query_params in litellm/proxy/common_utils/http_parsing_utils.py

⏱️ Runtime : 551 microseconds 385 microseconds (best of 392 runs)

📝 Explanation and details

The optimization achieves a 43% speedup by making two key changes to how query parameters are accessed and converted:

1. Replaced hasattr + attribute access with getattr

  • Original: if hasattr(request, "query_params"): return dict(request.query_params)
  • Optimized: qp = getattr(request, "query_params", None)
  • This eliminates duplicate attribute lookups - hasattr internally performs the same lookup as direct attribute access, so the original code was doing the work twice

2. Direct access to internal _dict attribute when available

  • For Starlette's QueryParams objects (common in FastAPI), the optimization checks if the internal _dict attribute exists and directly copies it: return qp_dict.copy()
  • This bypasses the expensive dict() constructor that would iterate through all key-value pairs
  • Falls back to dict(qp) for non-Starlette objects to maintain compatibility

Performance gains are most significant for:

  • Large query parameter sets: Tests with 1000+ parameters show 1770-1806% speedups, as the _dict shortcut avoids iterating through all parameters
  • Starlette QueryParams objects: 100-138% speedups on typical web request scenarios where the internal dictionary can be directly accessed
  • Edge cases with None values: 500-543% improvements by avoiding expensive exception handling in dict() constructor

The optimization maintains identical behavior and error handling while dramatically reducing computational overhead, especially for the common case of FastAPI/Starlette request objects.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 38 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest
from fastapi import Request
from litellm.proxy.common_utils.http_parsing_utils import \
    _safe_get_request_query_params
from starlette.datastructures import QueryParams

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

# Basic Test Cases

def test_none_request_returns_empty_dict():
    """Test that passing None returns an empty dict."""
    codeflash_output = _safe_get_request_query_params(None) # 377ns -> 367ns (2.72% faster)

def test_request_with_no_query_params_attribute_returns_empty_dict():
    """Test that passing an object without query_params attribute returns empty dict."""
    class Dummy:
        pass
    dummy = Dummy()
    codeflash_output = _safe_get_request_query_params(dummy) # 633ns -> 688ns (7.99% slower)

def test_request_with_empty_query_params():
    """Test that passing a Request with empty query params returns empty dict."""
    class DummyRequest:
        query_params = QueryParams({})
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 2.08μs -> 943ns (121% faster)

def test_request_with_single_query_param():
    """Test with a single query parameter."""
    class DummyRequest:
        query_params = QueryParams({"foo": "bar"})
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 2.23μs -> 958ns (133% faster)

def test_request_with_multiple_query_params():
    """Test with multiple query parameters."""
    class DummyRequest:
        query_params = QueryParams({"foo": "bar", "baz": "qux"})
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 2.22μs -> 931ns (138% faster)

# Edge Test Cases

def test_request_with_query_params_with_empty_key_and_value():
    """Test with empty string key and value."""
    class DummyRequest:
        query_params = QueryParams({"": ""})
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 1.97μs -> 933ns (112% faster)

def test_request_with_query_params_with_none_value():
    """Test with None value (QueryParams always returns str, so simulate)."""
    class DummyRequest:
        query_params = QueryParams({"foo": None})
    req = DummyRequest()
    # QueryParams will cast None to 'None' string
    codeflash_output = _safe_get_request_query_params(req) # 1.90μs -> 904ns (110% faster)

def test_request_with_query_params_with_special_characters():
    """Test with special characters in keys and values."""
    class DummyRequest:
        query_params = QueryParams({"spéciål": "välüe@!$"})
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 1.86μs -> 963ns (92.7% faster)

def test_request_with_query_params_with_duplicate_keys():
    """Test with duplicate keys (QueryParams keeps last value)."""
    class DummyRequest:
        query_params = QueryParams([("foo", "bar"), ("foo", "baz")])
    req = DummyRequest()
    # QueryParams returns the last value for duplicate keys
    codeflash_output = _safe_get_request_query_params(req) # 1.88μs -> 939ns (100% faster)

def test_request_with_query_params_as_non_dict_type():
    """Test with query_params as a non-dict type (simulate error)."""
    class DummyRequest:
        query_params = "not_a_query_params"
    req = DummyRequest()
    # dict("not_a_query_params") raises TypeError, should catch and return {}
    codeflash_output = _safe_get_request_query_params(req) # 6.14μs -> 6.11μs (0.590% faster)

def test_request_with_query_params_raises_exception():
    """Test if accessing query_params raises an exception."""
    class DummyRequest:
        @property
        def query_params(self):
            raise ValueError("Boom!")
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 3.38μs -> 3.56μs (4.94% slower)

def test_request_with_query_params_is_none():
    """Test with query_params attribute set to None."""
    class DummyRequest:
        query_params = None
    req = DummyRequest()
    # dict(None) raises TypeError, should catch and return {}
    codeflash_output = _safe_get_request_query_params(req) # 3.64μs -> 607ns (500% faster)

def test_request_with_query_params_is_empty_list():
    """Test with query_params attribute as empty list."""
    class DummyRequest:
        query_params = []
    req = DummyRequest()
    # dict([]) is valid and returns {}
    codeflash_output = _safe_get_request_query_params(req) # 969ns -> 981ns (1.22% slower)

def test_request_with_query_params_is_tuple_of_tuples():
    """Test with query_params as tuple of tuples."""
    class DummyRequest:
        query_params = (("foo", "bar"), ("baz", "qux"))
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 1.10μs -> 1.14μs (3.77% slower)

# Large Scale Test Cases

def test_large_number_of_query_params():
    """Test with a large number of query parameters (1000 keys)."""
    params = {f"key{i}": f"value{i}" for i in range(1000)}
    class DummyRequest:
        query_params = QueryParams(params)
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 80.2μs -> 4.29μs (1770% faster)

def test_large_number_of_duplicate_keys():
    """Test with a large number of duplicate keys (only last value should persist)."""
    # QueryParams keeps the last value for each key
    param_list = [(f"dupkey", f"value{i}") for i in range(1000)]
    class DummyRequest:
        query_params = QueryParams(param_list)
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 2.63μs -> 1.12μs (135% faster)

def test_large_number_of_special_char_keys_and_values():
    """Test with many keys/values containing special characters."""
    params = {f"k!@#{i}": f"v$%^&*{i}" for i in range(1000)}
    class DummyRequest:
        query_params = QueryParams(params)
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 78.7μs -> 4.13μs (1806% faster)

def test_large_query_params_as_tuple_of_tuples():
    """Test with large tuple of tuples for query_params."""
    param_list = [(f"key{i}", f"value{i}") for i in range(1000)]
    class DummyRequest:
        query_params = tuple(param_list)
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 43.1μs -> 42.1μs (2.40% faster)

def test_large_query_params_as_list_of_lists():
    """Test with large list of lists for query_params."""
    param_list = [[f"key{i}", f"value{i}"] for i in range(1000)]
    class DummyRequest:
        query_params = param_list
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 45.2μs -> 45.3μs (0.269% slower)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from typing import Dict, Optional

# imports
import pytest  # used for our unit tests
from litellm.proxy.common_utils.http_parsing_utils import \
    _safe_get_request_query_params


class DummyLogger:
    def __init__(self):
        self.logged = []
    def debug(self, msg):
        self.logged.append(msg)

# Patch verbose_proxy_logger for testing
verbose_proxy_logger = DummyLogger()
from litellm.proxy.common_utils.http_parsing_utils import \
    _safe_get_request_query_params

# ---- UNIT TESTS ----

# Basic Test Cases

def test_none_request_returns_empty_dict():
    # Test when request is None
    codeflash_output = _safe_get_request_query_params(None) # 391ns -> 390ns (0.256% faster)

def test_request_with_no_query_params_attribute_returns_empty_dict():
    # Test when request does not have query_params attribute
    class DummyRequest:
        pass
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 657ns -> 697ns (5.74% slower)

def test_request_with_empty_query_params_returns_empty_dict():
    # Test when query_params is empty
    class DummyRequest:
        query_params = {}
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 881ns -> 1.00μs (12.3% slower)

def test_request_with_single_query_param():
    # Test with one query param
    class DummyRequest:
        query_params = {'foo': 'bar'}
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 860ns -> 964ns (10.8% slower)

def test_request_with_multiple_query_params():
    # Test with multiple query params
    class DummyRequest:
        query_params = {'a': '1', 'b': '2', 'c': '3'}
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req) # 844ns -> 911ns (7.35% slower)

# Edge Test Cases

def test_query_params_is_not_dict_like():
    # Test when query_params is not dict-like (should raise TypeError and return {})
    class DummyRequest:
        query_params = 42  # not iterable
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 4.28μs -> 4.55μs (6.02% slower)

def test_query_params_is_list_of_tuples():
    # Test when query_params is a list of tuples (like Starlette's MultiDict.items())
    class DummyRequest:
        query_params = [('x', '1'), ('y', '2')]
    req = DummyRequest()
    # dict() can convert list of tuples to dict
    codeflash_output = _safe_get_request_query_params(req) # 1.13μs -> 1.02μs (10.4% faster)

def test_query_params_with_duplicate_keys():
    # Test when query_params has duplicate keys (simulate MultiDict)
    class DummyRequest:
        query_params = [('dup', '1'), ('dup', '2')]
    req = DummyRequest()
    # dict() will keep the last value for duplicate keys
    codeflash_output = _safe_get_request_query_params(req) # 1.02μs -> 1.02μs (0.196% faster)

def test_query_params_is_custom_mapping():
    # Test with a custom mapping object
    class CustomMapping:
        def __iter__(self):
            return iter(['foo', 'bar'])
        def __getitem__(self, key):
            return {'foo': 'baz', 'bar': 'qux'}[key]
        def keys(self):
            return ['foo', 'bar']
        def items(self):
            return [('foo', 'baz'), ('bar', 'qux')]
    class DummyRequest:
        query_params = CustomMapping()
    req = DummyRequest()
    # dict() uses __iter__ and __getitem__ if it's a mapping
    codeflash_output = _safe_get_request_query_params(req) # 2.28μs -> 2.31μs (1.04% slower)

def test_query_params_is_string():
    # Test when query_params is a string (should raise TypeError and return {})
    class DummyRequest:
        query_params = "notadict"
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 6.11μs -> 5.71μs (6.82% faster)

def test_query_params_is_none():
    # Test when query_params is None
    class DummyRequest:
        query_params = None
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 3.64μs -> 566ns (543% faster)

def test_request_is_not_object():
    # Test when request is an int (should return {})
    req = 123
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 541ns -> 601ns (9.98% slower)

def test_query_params_is_set():
    # Test when query_params is a set (should raise TypeError and return {})
    class DummyRequest:
        query_params = {'a', 'b', 'c'}
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 5.70μs -> 5.37μs (6.17% faster)

# Large Scale Test Cases

def test_query_params_large_dict():
    # Test with a large number of query params
    large_dict = {f'key{i}': f'value{i}' for i in range(1000)}
    class DummyRequest:
        query_params = large_dict
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 4.07μs -> 4.25μs (4.33% slower)

def test_query_params_large_list_of_tuples():
    # Test with a large list of tuples
    large_list = [(f'k{i}', f'v{i}') for i in range(1000)]
    class DummyRequest:
        query_params = large_list
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 44.9μs -> 43.8μs (2.50% faster)
    # dict() will keep the last value for duplicate keys
    expected = {f'k{i}': f'v{i}' for i in range(1000)}

def test_query_params_large_custom_mapping():
    # Test with a custom mapping with many keys
    class LargeCustomMapping:
        def __iter__(self):
            return iter([f'foo{i}' for i in range(1000)])
        def __getitem__(self, key):
            return f'bar{key[3:]}'
        def keys(self):
            return [f'foo{i}' for i in range(1000)]
        def items(self):
            return [(f'foo{i}', f'bar{i}') for i in range(1000)]
    class DummyRequest:
        query_params = LargeCustomMapping()
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 185μs -> 187μs (0.668% slower)
    expected = {f'foo{i}': f'bar{i}' for i in range(1000)}

# Special Case: Simulate FastAPI/Starlette QueryParams object
def test_query_params_starlette_queryparams_like():
    # Simulate Starlette's QueryParams object
    class StarletteQueryParams:
        def __init__(self, data):
            self._data = data
        def __iter__(self):
            return iter(self._data)
        def __getitem__(self, key):
            return self._data[key]
        def keys(self):
            return self._data.keys()
        def items(self):
            return self._data.items()
    class DummyRequest:
        query_params = StarletteQueryParams({'foo': 'bar', 'baz': 'qux'})
    req = DummyRequest()
    codeflash_output = _safe_get_request_query_params(req); result = codeflash_output # 2.72μs -> 2.87μs (5.36% slower)

# Ensure logger is not called for normal cases
def test_logger_not_called_for_normal_case():
    # Reset logger
    verbose_proxy_logger.logged.clear()
    class DummyRequest:
        query_params = {'x': '1'}
    req = DummyRequest()
    _safe_get_request_query_params(req) # 835ns -> 980ns (14.8% slower)

# Ensure logger is called for error cases
def test_logger_called_for_error_case():
    verbose_proxy_logger.logged.clear()
    class DummyRequest:
        query_params = 12345  # not iterable
    req = DummyRequest()
    _safe_get_request_query_params(req) # 4.25μs -> 4.29μs (0.886% slower)
# 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-_safe_get_request_query_params-mhdfc9ua and push.

Codeflash Static Badge

The optimization achieves a **43% speedup** by making two key changes to how query parameters are accessed and converted:

**1. Replaced `hasattr` + attribute access with `getattr`**
- Original: `if hasattr(request, "query_params"): return dict(request.query_params)`  
- Optimized: `qp = getattr(request, "query_params", None)`
- This eliminates duplicate attribute lookups - `hasattr` internally performs the same lookup as direct attribute access, so the original code was doing the work twice

**2. Direct access to internal `_dict` attribute when available**
- For Starlette's `QueryParams` objects (common in FastAPI), the optimization checks if the internal `_dict` attribute exists and directly copies it: `return qp_dict.copy()`
- This bypasses the expensive `dict()` constructor that would iterate through all key-value pairs
- Falls back to `dict(qp)` for non-Starlette objects to maintain compatibility

**Performance gains are most significant for:**
- **Large query parameter sets**: Tests with 1000+ parameters show 1770-1806% speedups, as the `_dict` shortcut avoids iterating through all parameters
- **Starlette QueryParams objects**: 100-138% speedups on typical web request scenarios where the internal dictionary can be directly accessed
- **Edge cases with None values**: 500-543% improvements by avoiding expensive exception handling in `dict()` constructor

The optimization maintains identical behavior and error handling while dramatically reducing computational overhead, especially for the common case of FastAPI/Starlette request objects.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 30, 2025 12:52
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 30, 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 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant