Skip to content

⚡️ Speed up function make_cfapi_request by 22% in PR #1195 (liniting_issues)#1197

Closed
codeflash-ai[bot] wants to merge 1 commit intoliniting_issuesfrom
codeflash/optimize-pr1195-2026-01-29T17.19.39
Closed

⚡️ Speed up function make_cfapi_request by 22% in PR #1195 (liniting_issues)#1197
codeflash-ai[bot] wants to merge 1 commit intoliniting_issuesfrom
codeflash/optimize-pr1195-2026-01-29T17.19.39

Conversation

@codeflash-ai
Copy link
Contributor

@codeflash-ai codeflash-ai bot commented Jan 29, 2026

⚡️ This pull request contains optimizations for PR #1195

If you approve this dependent PR, these changes will be merged into the original PR branch liniting_issues.

This PR will be automatically closed if the original PR is merged.


📄 22% (0.22x) speedup for make_cfapi_request in codeflash/api/cfapi.py

⏱️ Runtime : 9.11 milliseconds 7.48 milliseconds (best of 16 runs)

📝 Explanation and details

The optimized code achieves a 21% runtime improvement by avoiding expensive JSON serialization when the payload contains only JSON-native Python types (strings, numbers, booleans, None, lists, tuples, and dicts with string keys).

Key optimization:

The code introduces a new _is_json_native() helper function that performs a fast iterative check using a deque-based stack to determine if a payload consists entirely of JSON-native types. When true, the code uses requests.post(json=payload) instead of manually calling json.dumps() with pydantic_encoder.

Why this is faster:

The line profiler shows that json.dumps(payload, indent=None, default=pydantic_encoder) originally consumed 8.5% of total function time. The pydantic_encoder is designed to handle complex types like datetime objects, but when the payload is already JSON-native, this custom serialization is unnecessary overhead. By detecting JSON-native payloads upfront (which takes 14.4% of time but is still worthwhile), we can bypass the pydantic_encoder entirely and let requests' built-in JSON handling work directly - this is more efficient as requests uses the faster standard json encoder path.

Impact based on function references:

Looking at the function references, make_cfapi_request is called extensively throughout the codebase in critical paths:

  • create_pr(), suggest_changes(), and create_staging() - these are called during PR creation workflows and send large payloads with optimization metadata
  • get_blocklisted_functions() and is_function_being_optimized_again() - called during optimization discovery, potentially in loops
  • Multiple telemetry/tracking functions like mark_optimization_success() and add_code_context_hash()

Most of these callers pass payloads containing simple dictionaries with strings, integers, and booleans (e.g., {"owner": owner, "repo": repo, "pr_number": pr_number}). The test results confirm this - 5 out of 6 test cases benefit from the optimization, with only test_post_payload_with_complex_types_serializes_via_pydantic_encoder needing the pydantic_encoder fallback (for datetime serialization).

Trade-off:

The optimization adds an upfront type-checking cost (14.4% of time), but this is offset by the significant savings from avoiding pydantic_encoder serialization in the common case. The net result is a 21% overall runtime improvement, which compounds across the many API calls made during a typical Codeflash workflow.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 7 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 92.3%
🌀 Click to see Generated Regression Tests
import datetime

# imports
import json
from unittest.mock import patch

import requests

# function to test
from codeflash.api.cfapi import make_cfapi_request


def test_get_request_success_includes_authorization_and_params():
    # Ensure GET requests include the Authorization header and pass through params correctly.
    # Patch the external API key resolver so the function uses a predictable API key.
    with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", return_value="cf-testkey"):
        # Prepare a simple successful Response object that requests.get will return.
        resp = requests.Response()
        resp.status_code = 200
        # JSON body so response.json() works
        resp._content = b'{"status":"ok"}'

        # Patch requests.get (imported inside the module under test) to return our Response.
        with patch("codeflash.api.cfapi.requests.get", return_value=resp) as mock_get:
            params = {"q": "search", "n": 1}
            # Call the function under test.
            codeflash_output = make_cfapi_request("/ping", "GET", params=params)
            result = codeflash_output

            # Validate that requests.get was called once.
            mock_get.assert_called_once()

            # Inspect the kwargs used to call requests.get to verify headers and params.
            called_kwargs = mock_get.call_args[1]

            headers = called_kwargs["headers"]


def test_http_error_with_json_error_field_logs_and_returns_response():
    # When the API responds with an HTTP error and a JSON body with "error", the function
    # should catch the HTTPError, log an error (unless suppressed), and return the Response.
    with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", return_value="cf-key"):
        resp = requests.Response()
        resp.status_code = 400
        # JSON body with an "error" key
        resp._content = b'{"error":"problem occurred"}'

        # Patch requests.get to return this Response. Calling raise_for_status will raise.
        with patch("codeflash.api.cfapi.requests.get", return_value=resp):
            # Patch the logger used in the module so we can assert it was called.
            with patch("codeflash.api.cfapi.logger") as mock_logger:
                codeflash_output = make_cfapi_request("/bad", "GET")
                returned = codeflash_output
                # And the last error message string should include basic identifying text.
                last_args = mock_logger.error.call_args[0][0]


def test_http_error_with_non_json_body_uses_text_and_respects_suppress_errors():
    # When response.json() raises or body is non-JSON, the function should use response.text for message.
    with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", return_value="cf-key"):
        resp = requests.Response()
        resp.status_code = 500
        resp._content = b"plain text internal server error"  # not valid JSON

        # Ensure response.json raises to simulate unparseable content.
        def raise_value_error():
            raise ValueError("No JSON")

        resp.json = raise_value_error  # type: ignore[assignment]

        with patch("codeflash.api.cfapi.requests.get", return_value=resp):
            # Patch logger to observe calls
            with patch("codeflash.api.cfapi.logger") as mock_logger:
                # suppress_errors=True should prevent logger.error from being called.
                codeflash_output = make_cfapi_request("/fail", "GET", suppress_errors=True)
                returned = codeflash_output

                # Now with suppress_errors False, logger.error should be called and include the text body.
                codeflash_output = make_cfapi_request("/fail", "GET", suppress_errors=False)
                returned2 = codeflash_output
                msg = mock_logger.error.call_args[0][0]


def test_explicit_api_key_prevents_env_lookup_from_being_called():
    # If api_key is provided explicitly, get_codeflash_api_key should not be invoked.
    # Patch get_codeflash_api_key to raise if called to ensure it isn't used.
    with patch(
        "codeflash.code_utils.env_utils.get_codeflash_api_key", side_effect=RuntimeError("should not be called")
    ):
        resp = requests.Response()
        resp.status_code = 200
        resp._content = b'{"ok":true}'

        with patch("codeflash.api.cfapi.requests.get", return_value=resp) as mock_get:
            # Providing api_key explicitly should avoid calling the patched get_codeflash_api_key.
            codeflash_output = make_cfapi_request("/ok", "GET", api_key="cf-explicit")
            result = codeflash_output

            # Validate Authorization header contains our explicit key.
            called_kwargs = mock_get.call_args[1]


def test_large_params_dict_is_forwarded_correctly():
    # Build a large params dictionary (500 entries) to exercise handling of many params.
    large_params = {f"key_{i}": i for i in range(500)}

    with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", return_value="cf-large"):
        resp = requests.Response()
        resp.status_code = 200
        resp._content = b'{"ok": true}'

        with patch("codeflash.api.cfapi.requests.get", return_value=resp) as mock_get:
            codeflash_output = make_cfapi_request("/big", "GET", params=large_params)
            result = codeflash_output

            # Ensure the params dict was forwarded unchanged.
            called_kwargs = mock_get.call_args[1]


def test_post_payload_with_complex_types_serializes_via_pydantic_encoder():
    # Ensure complex types like datetime are serialized via pydantic_encoder when posting.
    payload = {"created_at": datetime.datetime(2021, 5, 4, 12, 30, 45), "count": 42}

    with patch("codeflash.api.cfapi.requests.post") as mock_post:
        resp = requests.Response()
        resp.status_code = 200
        resp._content = b'{"ok": true}'
        mock_post.return_value = resp

        # Provide an api_key via environment patch to isolate behavior.
        with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", return_value="cf-datetime"):
            codeflash_output = make_cfapi_request("/complex", "POST", payload=payload)
            result = codeflash_output

            # Check that the posted data is JSON and includes an ISO-like datetime string for created_at.
            called_kwargs = mock_post.call_args[1]
            posted_data = called_kwargs["data"]
            # The JSON must load back to an object where created_at is present (as string).
            loaded = json.loads(posted_data)

            # Authorization header must be set with the env key
            headers = called_kwargs["headers"]


# 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-pr1195-2026-01-29T17.19.39 and push.

Codeflash

The optimized code achieves a **21% runtime improvement** by avoiding expensive JSON serialization when the payload contains only JSON-native Python types (strings, numbers, booleans, None, lists, tuples, and dicts with string keys).

**Key optimization:**

The code introduces a new `_is_json_native()` helper function that performs a fast iterative check using a deque-based stack to determine if a payload consists entirely of JSON-native types. When true, the code uses `requests.post(json=payload)` instead of manually calling `json.dumps()` with `pydantic_encoder`.

**Why this is faster:**

The line profiler shows that `json.dumps(payload, indent=None, default=pydantic_encoder)` originally consumed **8.5% of total function time**. The `pydantic_encoder` is designed to handle complex types like datetime objects, but when the payload is already JSON-native, this custom serialization is unnecessary overhead. By detecting JSON-native payloads upfront (which takes **14.4% of time** but is still worthwhile), we can bypass the pydantic_encoder entirely and let requests' built-in JSON handling work directly - this is more efficient as requests uses the faster standard json encoder path.

**Impact based on function references:**

Looking at the function references, `make_cfapi_request` is called extensively throughout the codebase in critical paths:
- `create_pr()`, `suggest_changes()`, and `create_staging()` - these are called during PR creation workflows and send large payloads with optimization metadata
- `get_blocklisted_functions()` and `is_function_being_optimized_again()` - called during optimization discovery, potentially in loops
- Multiple telemetry/tracking functions like `mark_optimization_success()` and `add_code_context_hash()`

Most of these callers pass payloads containing simple dictionaries with strings, integers, and booleans (e.g., `{"owner": owner, "repo": repo, "pr_number": pr_number}`). The test results confirm this - **5 out of 6 test cases benefit** from the optimization, with only `test_post_payload_with_complex_types_serializes_via_pydantic_encoder` needing the pydantic_encoder fallback (for datetime serialization).

**Trade-off:**

The optimization adds an upfront type-checking cost (14.4% of time), but this is offset by the significant savings from avoiding pydantic_encoder serialization in the common case. The net result is a 21% overall runtime improvement, which compounds across the many API calls made during a typical Codeflash workflow.
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Jan 29, 2026
@KRRT7 KRRT7 closed this Jan 29, 2026
@KRRT7 KRRT7 deleted the codeflash/optimize-pr1195-2026-01-29T17.19.39 branch January 29, 2026 17:27
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.

1 participant