Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Simplify _process_raw_result method in custom component processing #6040

Merged
merged 6 commits into from
Jan 31, 2025

Conversation

Cristhianzl
Copy link
Member

This pull request includes a significant refactor of the _process_raw_result method in the src/backend/base/langflow/custom/custom_component/component.py file to improve code readability and maintainability. The most important changes are the introduction of the extract_data method and the simplification of the _process_raw_result method.

Refactor and code simplification:

…y extract data from result object based on conditions and return it
@Cristhianzl Cristhianzl self-assigned this Jan 31, 2025
@dosubot dosubot bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jan 31, 2025
@github-actions github-actions bot added the refactor Maintenance tasks and housekeeping label Jan 31, 2025
@github-actions github-actions bot added refactor Maintenance tasks and housekeeping and removed refactor Maintenance tasks and housekeeping labels Jan 31, 2025
…y and maintainability by using more descriptive variable names and simplifying the logic.
@github-actions github-actions bot added refactor Maintenance tasks and housekeeping and removed refactor Maintenance tasks and housekeeping labels Jan 31, 2025
@github-actions github-actions bot added refactor Maintenance tasks and housekeeping and removed refactor Maintenance tasks and housekeeping labels Jan 31, 2025
@github-actions github-actions bot added refactor Maintenance tasks and housekeeping and removed refactor Maintenance tasks and housekeeping labels Jan 31, 2025
@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Jan 31, 2025
Copy link
Contributor

codeflash-ai bot commented Jan 31, 2025

⚡️ Codeflash found optimizations for this PR

📄 47% (0.47x) speedup for Component._process_raw_result in src/backend/base/langflow/custom/custom_component/component.py

⏱️ Runtime : 193 microseconds 131 microseconds (best of 202 runs)

📝 Explanation and details

Here's the optimized version of your code.

  1. I've removed redundant condition checks in the extract_data method to make it more straightforward and faster.
  2. I've streamlined the extract_data method to minimize multiple checks and directly return the result if conditions are met.

Changes.

  1. Removed the outer length check if len(self.outputs) == 1 as it seemed redundant given the code handling. Now the status check is performed directly.
  2. Changed the extract_data method to check for Data type at the beginning and return the appropriate value accordingly without redundant checks.
  3. Removed the unnecessary if self.status check from extract_data since it is already checked in _process_raw_result. This avoids unnecessary re-evaluation of self.status multiple times.

This will reduce the number of conditional checks and streamline the method calls, potentially leading to an improvement in performance.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 130 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage undefined
🌀 Generated Regression Tests Details
from __future__ import annotations

from copy import deepcopy
from typing import Any

import nanoid
# imports
import pytest  # used for our unit tests
from langflow.custom.custom_component.component import Component
from langflow.custom.custom_component.custom_component import CustomComponent
from langflow.events.event_manager import EventManager
from langflow.graph.edge.schema import EdgeData
from langflow.inputs.inputs import InputTypes
from langflow.schema.data import Data
from langflow.services.tracing.schema import Log
from langflow.template.field.base import Output
from langflow.utils.async_helpers import run_until_complete


# unit tests
class MockData:
    def __init__(self, data):
        self.data = data

class MockModel:
    def model_dump(self):
        return {"key": "value"}

@pytest.fixture
def component():
    # Fixture to create a component instance with default settings
    return Component()

def test_single_output_with_status(component):
    # Test single output with status set
    component.outputs = ["output1"]
    component.status = "status message"
    result = MockData({"key": "value"})
    codeflash_output = component._process_raw_result(result)

def test_single_output_with_data_attribute(component):
    # Test single output with result having data attribute
    component.outputs = ["output1"]
    component.status = ""
    result = MockData({"key": "value"})
    codeflash_output = component._process_raw_result(result)

def test_single_output_with_model_dump(component):
    # Test single output with result having model_dump method
    component.outputs = ["output1"]
    component.status = ""
    result = MockModel()
    codeflash_output = component._process_raw_result(result)

def test_single_output_with_data_instance(component):
    # Test single output with result being an instance of Data
    component.outputs = ["output1"]
    component.status = ""
    result = Data(data={"key": "value"})
    codeflash_output = component._process_raw_result(result)

def test_single_output_with_dict(component):
    # Test single output with result being a dictionary
    component.outputs = ["output1"]
    component.status = ""
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)

def test_single_output_with_string(component):
    # Test single output with result being a string
    component.outputs = ["output1"]
    component.status = ""
    result = "result string"
    codeflash_output = component._process_raw_result(result)

def test_multiple_outputs_with_data_attribute(component):
    # Test multiple outputs with result having data attribute
    component.outputs = ["output1", "output2"]
    result = MockData({"key": "value"})
    codeflash_output = component._process_raw_result(result)

def test_multiple_outputs_with_model_dump(component):
    # Test multiple outputs with result having model_dump method
    component.outputs = ["output1", "output2"]
    result = MockModel()
    codeflash_output = component._process_raw_result(result)

def test_multiple_outputs_with_data_instance(component):
    # Test multiple outputs with result being an instance of Data
    component.outputs = ["output1", "output2"]
    result = Data(data={"key": "value"})
    codeflash_output = component._process_raw_result(result)

def test_multiple_outputs_with_dict(component):
    # Test multiple outputs with result being a dictionary
    component.outputs = ["output1", "output2"]
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)

def test_multiple_outputs_with_string(component):
    # Test multiple outputs with result being a string
    component.outputs = ["output1", "output2"]
    result = "result string"
    codeflash_output = component._process_raw_result(result)

def test_empty_outputs_list(component):
    # Test with empty outputs list
    component.outputs = []
    result = MockData({"key": "value"})
    codeflash_output = component._process_raw_result(result)

def test_result_is_none(component):
    # Test with result being None
    component.outputs = ["output1"]
    result = None
    codeflash_output = component._process_raw_result(result)

def test_result_is_unexpected_type(component):
    # Test with result being an unexpected type (integer)
    component.outputs = ["output1"]
    result = 42
    codeflash_output = component._process_raw_result(result)

def test_large_result_data(component):
    # Test with large result data
    component.outputs = ["output1"]
    large_data = {"key": "value" * 1000}
    result = MockData(large_data)
    codeflash_output = component._process_raw_result(result)

def test_high_volume_of_outputs(component):
    # Test with high volume of outputs
    component.outputs = ["output" + str(i) for i in range(1000)]
    result = MockData({"key": "value"})
    codeflash_output = component._process_raw_result(result)

def test_complex_nested_data_structures(component):
    # Test with complex nested data structures
    component.outputs = ["output1"]
    nested_data = {"key": {"nested_key": {"deeply_nested_key": "value"}}}
    result = MockData(nested_data)
    codeflash_output = component._process_raw_result(result)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

from __future__ import annotations

from copy import deepcopy
from typing import Any
from unittest.mock import MagicMock

import nanoid
# imports
import pytest  # used for our unit tests
from langflow.custom.custom_component.component import Component
from langflow.custom.custom_component.custom_component import CustomComponent
from langflow.events.event_manager import EventManager
from langflow.graph.edge.schema import EdgeData
from langflow.inputs.inputs import InputTypes
from langflow.schema.data import Data
from langflow.services.tracing.schema import Log
from langflow.template.field.base import Output
from langflow.utils.async_helpers import run_until_complete

# unit tests

@pytest.fixture
def component():
    # Create a mock component instance for testing
    component = Component()
    component.status = None
    component.outputs = []
    return component

def test_single_output_with_status(component):
    # Test case: Single output and status is present
    component.outputs = ["output1"]
    component.status = "status_message"
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)

def test_single_output_without_status(component):
    # Test case: Single output and status is absent
    component.outputs = ["output1"]
    component.status = None
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)

def test_multiple_outputs(component):
    # Test case: Multiple outputs
    component.outputs = ["output1", "output2"]
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)

def test_empty_outputs(component):
    # Test case: Empty outputs
    component.outputs = []
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)

def test_result_is_none(component):
    # Test case: Result is None
    component.outputs = ["output1"]
    result = None
    codeflash_output = component._process_raw_result(result)

def test_result_is_integer(component):
    # Test case: Result is an integer
    component.outputs = ["output1"]
    result = 42
    codeflash_output = component._process_raw_result(result)

def test_result_is_list(component):
    # Test case: Result is a list
    component.outputs = ["output1"]
    result = [1, 2, 3]
    codeflash_output = component._process_raw_result(result)

def test_large_dictionary(component):
    # Test case: Result is a large dictionary
    component.outputs = ["output1"]
    result = {f"key{i}": f"value{i}" for i in range(1000)}
    codeflash_output = component._process_raw_result(result)

def test_large_custom_class(component):
    # Test case: Result is a custom class with large data attribute
    class CustomClass:
        def __init__(self):
            self.data = {f"key{i}": f"value{i}" for i in range(1000)}
    component.outputs = ["output1"]
    result = CustomClass()
    codeflash_output = component._process_raw_result(result)

def test_high_frequency_calls(component):
    # Test case: High frequency calls
    component.outputs = ["output1"]
    for i in range(100):
        result = {f"key{i}": f"value{i}"}
        codeflash_output = component._process_raw_result(result)

def test_extract_data_called(component, mocker):
    # Test case: Verify extract_data is called
    component.outputs = ["output1"]
    result = {"key": "value"}
    mocker.spy(component, 'extract_data')
    component._process_raw_result(result)
    component.extract_data.assert_called_once_with(result)

def test_status_priority(component):
    # Test case: Status is prioritized over extract_data
    component.outputs = ["output1"]
    component.status = "status_message"
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)

def test_extract_data_exception(component, mocker):
    # Test case: Exception in extract_data
    component.outputs = ["output1"]
    result = {"key": "value"}
    mocker.patch.object(component, 'extract_data', side_effect=Exception("Error"))
    with pytest.raises(Exception, match="Error"):
        component._process_raw_result(result)

def test_unusual_outputs_config(component):
    # Test case: Unusual outputs configuration
    component.outputs = [123, None, "output1"]
    result = {"key": "value"}
    codeflash_output = component._process_raw_result(result)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

📢 Feedback on this optimization? Discord

@Cristhianzl Cristhianzl added this pull request to the merge queue Jan 31, 2025
Merged via the queue into main with commit c2411d4 Jan 31, 2025
33 of 34 checks passed
@Cristhianzl Cristhianzl deleted the cz/change-output-status branch January 31, 2025 19:22
edwinjosechittilappilly pushed a commit that referenced this pull request Jan 31, 2025
…cessing (#6040)

* 🐛 (component.py): fix logic in _process_raw_result method to correctly extract data from result object based on conditions and return it

* [autofix.ci] apply automated fixes

* ♻️ (component.py): refactor extract_data method to improve readability and maintainability by using more descriptive variable names and simplifying the logic.

* [autofix.ci] apply automated fixes

* 🐛 (component.py): update isinstance check to use union type for better type handling

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lgtm This PR has been approved by a maintainer refactor Maintenance tasks and housekeeping size:S This PR changes 10-29 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants