prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add e2e tests for pipeline evaluation for 2.x
  Test eval for 2.x with tiny dataset, check dataframes, isolated, integrated eval (migrate existing integration tests to e2e tests for 2.x)
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `haystack/evaluation/eval_run_result.py`
  Content:
  ```
  1 from abc import ABC, abstractmethod
  2 from copy import deepcopy
  3 from typing import Any, Dict, List
  4 from warnings import warn
  5 
  6 from pandas import DataFrame
  7 from pandas import concat as pd_concat
  8 
  9 
  10 class BaseEvaluationRunResult(ABC):
  11     """
  12     Represents the results of an evaluation run.
  13     """
  14 
  15     @abstractmethod
  16     def to_pandas(self) -> "DataFrame":
  17         """
  18         Creates a Pandas DataFrame containing the scores of each metric for every input sample.
  19 
  20         :returns:
  21             Pandas DataFrame with the scores.
  22         """
  23 
  24     @abstractmethod
  25     def score_report(self) -> "DataFrame":
  26         """
  27         Transforms the results into a Pandas DataFrame with the aggregated scores for each metric.
  28 
  29         :returns:
  30             Pandas DataFrame with the aggregated scores.
  31         """
  32 
  33     @abstractmethod
  34     def comparative_individual_scores_report(self, other: "BaseEvaluationRunResult") -> "DataFrame":
  35         """
  36         Creates a Pandas DataFrame with the scores for each metric in the results of two different evaluation runs.
  37 
  38         The inputs to both evaluation runs is assumed to be the same.
  39 
  40         :param other:
  41             Results of another evaluation run to compare with.
  42         :returns:
  43             Pandas DataFrame with the score comparison.
  44         """
  45 
  46 
  47 class EvaluationRunResult(BaseEvaluationRunResult):
  48     """
  49     Contains the inputs and the outputs of an evaluation pipeline and provides methods to inspect them.
  50     """
  51 
  52     def __init__(self, run_name: str, inputs: Dict[str, List[Any]], results: Dict[str, Dict[str, Any]]):
  53         """
  54         Initialize a new evaluation run result.
  55 
  56         :param run_name:
  57             Name of the evaluation run.
  58         :param inputs:
  59             Dictionary containing the inputs used for the run.
  60             Each key is the name of the input and its value is
  61             a list of input values. The length of the lists should
  62             be the same.
  63         :param results:
  64             Dictionary containing the results of the evaluators
  65             used in the evaluation pipeline. Each key is the name
  66             of the metric and its value is dictionary with the following
  67             keys:
  68                 - 'score': The aggregated score for the metric.
  69                 - 'individual_scores': A list of scores for each input sample.
  70         """
  71         self.run_name = run_name
  72         self.inputs = deepcopy(inputs)
  73         self.results = deepcopy(results)
  74 
  75         if len(inputs) == 0:
  76             raise ValueError("No inputs provided.")
  77         if len({len(l) for l in inputs.values()}) != 1:
  78             raise ValueError("Lengths of the inputs should be the same.")
  79 
  80         expected_len = len(next(iter(inputs.values())))
  81 
  82         for metric, outputs in results.items():
  83             if "score" not in outputs:
  84                 raise ValueError(f"Aggregate score missing for {metric}.")
  85             if "individual_scores" not in outputs:
  86                 raise ValueError(f"Individual scores missing for {metric}.")
  87 
  88             if len(outputs["individual_scores"]) != expected_len:
  89                 raise ValueError(
  90                     f"Length of individual scores for '{metric}' should be the same as the inputs. "
  91                     f"Got {len(outputs['individual_scores'])} but expected {expected_len}."
  92                 )
  93 
  94     def score_report(self) -> DataFrame:  # noqa: D102
  95         results = {k: v["score"] for k, v in self.results.items()}
  96         return DataFrame.from_dict(results, orient="index", columns=["score"])
  97 
  98     def to_pandas(self) -> DataFrame:  # noqa: D102
  99         inputs_columns = list(self.inputs.keys())
  100         inputs_values = list(self.inputs.values())
  101         inputs_values = list(map(list, zip(*inputs_values)))  # transpose the values
  102         df_inputs = DataFrame(inputs_values, columns=inputs_columns)
  103 
  104         scores_columns = list(self.results.keys())
  105         scores_values = [v["individual_scores"] for v in self.results.values()]
  106         scores_values = list(map(list, zip(*scores_values)))  # transpose the values
  107         df_scores = DataFrame(scores_values, columns=scores_columns)
  108 
  109         return df_inputs.join(df_scores)
  110 
  111     def comparative_individual_scores_report(self, other: "BaseEvaluationRunResult") -> DataFrame:  # noqa: D102
  112         if not isinstance(other, EvaluationRunResult):
  113             raise ValueError("Comparative scores can only be computed between EvaluationRunResults.")
  114 
  115         this_name = self.run_name
  116         other_name = other.run_name
  117         if this_name == other_name:
  118             warn(f"The run names of the two evaluation results are the same ('{this_name}')")
  119             this_name = f"{this_name}_first"
  120             other_name = f"{other_name}_second"
  121 
  122         if self.inputs != other.inputs:
  123             warn(f"The inputs to the two evaluation results differ; using the inputs of '{this_name}'.")
  124 
  125         pipe_a_df = self.to_pandas()
  126         pipe_b_df = other.to_pandas()
  127 
  128         ignore = list(self.inputs.keys())
  129         pipe_b_df.drop(columns=ignore, inplace=True, errors="ignore")
  130         pipe_b_df.columns = [f"{other_name}_{column}" for column in pipe_b_df.columns]  # type: ignore
  131         pipe_a_df.columns = [
  132             f"{this_name}_{col}" if col not in ignore else col for col in pipe_a_df.columns
  133         ]  # type: ignore
  134 
  135         results_df = pd_concat([pipe_a_df, pipe_b_df], axis=1)
  136         return results_df
  137
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/haystack/evaluation/eval_run_result.py b/haystack/evaluation/eval_run_result.py
  --- a/haystack/evaluation/eval_run_result.py
  +++ b/haystack/evaluation/eval_run_result.py
  @@ -119,8 +119,8 @@
               this_name = f"{this_name}_first"
               other_name = f"{other_name}_second"
   
  -        if self.inputs != other.inputs:
  -            warn(f"The inputs to the two evaluation results differ; using the inputs of '{this_name}'.")
  +        if self.inputs.keys() != other.inputs.keys():
  +            warn(f"The input columns differ between the results; using the input columns of '{this_name}'.")
   
           pipe_a_df = self.to_pandas()
           pipe_b_df = other.to_pandas()
  
verification_info: {"golden_diff": "diff --git a/haystack/evaluation/eval_run_result.py b/haystack/evaluation/eval_run_result.py\n--- a/haystack/evaluation/eval_run_result.py\n+++ b/haystack/evaluation/eval_run_result.py\n@@ -119,8 +119,8 @@\n             this_name = f\"{this_name}_first\"\n             other_name = f\"{other_name}_second\"\n \n-        if self.inputs != other.inputs:\n-            warn(f\"The inputs to the two evaluation results differ; using the inputs of '{this_name}'.\")\n+        if self.inputs.keys() != other.inputs.keys():\n+            warn(f\"The input columns differ between the results; using the input columns of '{this_name}'.\")\n \n         pipe_a_df = self.to_pandas()\n         pipe_b_df = other.to_pandas()\n", "issue": "Add e2e tests for pipeline evaluation for 2.x\nTest eval for 2.x with tiny dataset, check dataframes, isolated, integrated eval (migrate existing integration tests to e2e tests for 2.x)\n", "before_files": [{"content": "from abc import ABC, abstractmethod\nfrom copy import deepcopy\nfrom typing import Any, Dict, List\nfrom warnings import warn\n\nfrom pandas import DataFrame\nfrom pandas import concat as pd_concat\n\n\nclass BaseEvaluationRunResult(ABC):\n    \"\"\"\n    Represents the results of an evaluation run.\n    \"\"\"\n\n    @abstractmethod\n    def to_pandas(self) -> \"DataFrame\":\n        \"\"\"\n        Creates a Pandas DataFrame containing the scores of each metric for every input sample.\n\n        :returns:\n            Pandas DataFrame with the scores.\n        \"\"\"\n\n    @abstractmethod\n    def score_report(self) -> \"DataFrame\":\n        \"\"\"\n        Transforms the results into a Pandas DataFrame with the aggregated scores for each metric.\n\n        :returns:\n            Pandas DataFrame with the aggregated scores.\n        \"\"\"\n\n    @abstractmethod\n    def comparative_individual_scores_report(self, other: \"BaseEvaluationRunResult\") -> \"DataFrame\":\n        \"\"\"\n        Creates a Pandas DataFrame with the scores for each metric in the results of two different evaluation runs.\n\n        The inputs to both evaluation runs is assumed to be the same.\n\n        :param other:\n            Results of another evaluation run to compare with.\n        :returns:\n            Pandas DataFrame with the score comparison.\n        \"\"\"\n\n\nclass EvaluationRunResult(BaseEvaluationRunResult):\n    \"\"\"\n    Contains the inputs and the outputs of an evaluation pipeline and provides methods to inspect them.\n    \"\"\"\n\n    def __init__(self, run_name: str, inputs: Dict[str, List[Any]], results: Dict[str, Dict[str, Any]]):\n        \"\"\"\n        Initialize a new evaluation run result.\n\n        :param run_name:\n            Name of the evaluation run.\n        :param inputs:\n            Dictionary containing the inputs used for the run.\n            Each key is the name of the input and its value is\n            a list of input values. The length of the lists should\n            be the same.\n        :param results:\n            Dictionary containing the results of the evaluators\n            used in the evaluation pipeline. Each key is the name\n            of the metric and its value is dictionary with the following\n            keys:\n                - 'score': The aggregated score for the metric.\n                - 'individual_scores': A list of scores for each input sample.\n        \"\"\"\n        self.run_name = run_name\n        self.inputs = deepcopy(inputs)\n        self.results = deepcopy(results)\n\n        if len(inputs) == 0:\n            raise ValueError(\"No inputs provided.\")\n        if len({len(l) for l in inputs.values()}) != 1:\n            raise ValueError(\"Lengths of the inputs should be the same.\")\n\n        expected_len = len(next(iter(inputs.values())))\n\n        for metric, outputs in results.items():\n            if \"score\" not in outputs:\n                raise ValueError(f\"Aggregate score missing for {metric}.\")\n            if \"individual_scores\" not in outputs:\n                raise ValueError(f\"Individual scores missing for {metric}.\")\n\n            if len(outputs[\"individual_scores\"]) != expected_len:\n                raise ValueError(\n                    f\"Length of individual scores for '{metric}' should be the same as the inputs. \"\n                    f\"Got {len(outputs['individual_scores'])} but expected {expected_len}.\"\n                )\n\n    def score_report(self) -> DataFrame:  # noqa: D102\n        results = {k: v[\"score\"] for k, v in self.results.items()}\n        return DataFrame.from_dict(results, orient=\"index\", columns=[\"score\"])\n\n    def to_pandas(self) -> DataFrame:  # noqa: D102\n        inputs_columns = list(self.inputs.keys())\n        inputs_values = list(self.inputs.values())\n        inputs_values = list(map(list, zip(*inputs_values)))  # transpose the values\n        df_inputs = DataFrame(inputs_values, columns=inputs_columns)\n\n        scores_columns = list(self.results.keys())\n        scores_values = [v[\"individual_scores\"] for v in self.results.values()]\n        scores_values = list(map(list, zip(*scores_values)))  # transpose the values\n        df_scores = DataFrame(scores_values, columns=scores_columns)\n\n        return df_inputs.join(df_scores)\n\n    def comparative_individual_scores_report(self, other: \"BaseEvaluationRunResult\") -> DataFrame:  # noqa: D102\n        if not isinstance(other, EvaluationRunResult):\n            raise ValueError(\"Comparative scores can only be computed between EvaluationRunResults.\")\n\n        this_name = self.run_name\n        other_name = other.run_name\n        if this_name == other_name:\n            warn(f\"The run names of the two evaluation results are the same ('{this_name}')\")\n            this_name = f\"{this_name}_first\"\n            other_name = f\"{other_name}_second\"\n\n        if self.inputs != other.inputs:\n            warn(f\"The inputs to the two evaluation results differ; using the inputs of '{this_name}'.\")\n\n        pipe_a_df = self.to_pandas()\n        pipe_b_df = other.to_pandas()\n\n        ignore = list(self.inputs.keys())\n        pipe_b_df.drop(columns=ignore, inplace=True, errors=\"ignore\")\n        pipe_b_df.columns = [f\"{other_name}_{column}\" for column in pipe_b_df.columns]  # type: ignore\n        pipe_a_df.columns = [\n            f\"{this_name}_{col}\" if col not in ignore else col for col in pipe_a_df.columns\n        ]  # type: ignore\n\n        results_df = pd_concat([pipe_a_df, pipe_b_df], axis=1)\n        return results_df\n", "path": "haystack/evaluation/eval_run_result.py"}], "after_files": [{"content": "from abc import ABC, abstractmethod\nfrom copy import deepcopy\nfrom typing import Any, Dict, List\nfrom warnings import warn\n\nfrom pandas import DataFrame\nfrom pandas import concat as pd_concat\n\n\nclass BaseEvaluationRunResult(ABC):\n    \"\"\"\n    Represents the results of an evaluation run.\n    \"\"\"\n\n    @abstractmethod\n    def to_pandas(self) -> \"DataFrame\":\n        \"\"\"\n        Creates a Pandas DataFrame containing the scores of each metric for every input sample.\n\n        :returns:\n            Pandas DataFrame with the scores.\n        \"\"\"\n\n    @abstractmethod\n    def score_report(self) -> \"DataFrame\":\n        \"\"\"\n        Transforms the results into a Pandas DataFrame with the aggregated scores for each metric.\n\n        :returns:\n            Pandas DataFrame with the aggregated scores.\n        \"\"\"\n\n    @abstractmethod\n    def comparative_individual_scores_report(self, other: \"BaseEvaluationRunResult\") -> \"DataFrame\":\n        \"\"\"\n        Creates a Pandas DataFrame with the scores for each metric in the results of two different evaluation runs.\n\n        The inputs to both evaluation runs is assumed to be the same.\n\n        :param other:\n            Results of another evaluation run to compare with.\n        :returns:\n            Pandas DataFrame with the score comparison.\n        \"\"\"\n\n\nclass EvaluationRunResult(BaseEvaluationRunResult):\n    \"\"\"\n    Contains the inputs and the outputs of an evaluation pipeline and provides methods to inspect them.\n    \"\"\"\n\n    def __init__(self, run_name: str, inputs: Dict[str, List[Any]], results: Dict[str, Dict[str, Any]]):\n        \"\"\"\n        Initialize a new evaluation run result.\n\n        :param run_name:\n            Name of the evaluation run.\n        :param inputs:\n            Dictionary containing the inputs used for the run.\n            Each key is the name of the input and its value is\n            a list of input values. The length of the lists should\n            be the same.\n        :param results:\n            Dictionary containing the results of the evaluators\n            used in the evaluation pipeline. Each key is the name\n            of the metric and its value is dictionary with the following\n            keys:\n                - 'score': The aggregated score for the metric.\n                - 'individual_scores': A list of scores for each input sample.\n        \"\"\"\n        self.run_name = run_name\n        self.inputs = deepcopy(inputs)\n        self.results = deepcopy(results)\n\n        if len(inputs) == 0:\n            raise ValueError(\"No inputs provided.\")\n        if len({len(l) for l in inputs.values()}) != 1:\n            raise ValueError(\"Lengths of the inputs should be the same.\")\n\n        expected_len = len(next(iter(inputs.values())))\n\n        for metric, outputs in results.items():\n            if \"score\" not in outputs:\n                raise ValueError(f\"Aggregate score missing for {metric}.\")\n            if \"individual_scores\" not in outputs:\n                raise ValueError(f\"Individual scores missing for {metric}.\")\n\n            if len(outputs[\"individual_scores\"]) != expected_len:\n                raise ValueError(\n                    f\"Length of individual scores for '{metric}' should be the same as the inputs. \"\n                    f\"Got {len(outputs['individual_scores'])} but expected {expected_len}.\"\n                )\n\n    def score_report(self) -> DataFrame:  # noqa: D102\n        results = {k: v[\"score\"] for k, v in self.results.items()}\n        return DataFrame.from_dict(results, orient=\"index\", columns=[\"score\"])\n\n    def to_pandas(self) -> DataFrame:  # noqa: D102\n        inputs_columns = list(self.inputs.keys())\n        inputs_values = list(self.inputs.values())\n        inputs_values = list(map(list, zip(*inputs_values)))  # transpose the values\n        df_inputs = DataFrame(inputs_values, columns=inputs_columns)\n\n        scores_columns = list(self.results.keys())\n        scores_values = [v[\"individual_scores\"] for v in self.results.values()]\n        scores_values = list(map(list, zip(*scores_values)))  # transpose the values\n        df_scores = DataFrame(scores_values, columns=scores_columns)\n\n        return df_inputs.join(df_scores)\n\n    def comparative_individual_scores_report(self, other: \"BaseEvaluationRunResult\") -> DataFrame:  # noqa: D102\n        if not isinstance(other, EvaluationRunResult):\n            raise ValueError(\"Comparative scores can only be computed between EvaluationRunResults.\")\n\n        this_name = self.run_name\n        other_name = other.run_name\n        if this_name == other_name:\n            warn(f\"The run names of the two evaluation results are the same ('{this_name}')\")\n            this_name = f\"{this_name}_first\"\n            other_name = f\"{other_name}_second\"\n\n        if self.inputs.keys() != other.inputs.keys():\n            warn(f\"The input columns differ between the results; using the input columns of '{this_name}'.\")\n\n        pipe_a_df = self.to_pandas()\n        pipe_b_df = other.to_pandas()\n\n        ignore = list(self.inputs.keys())\n        pipe_b_df.drop(columns=ignore, inplace=True, errors=\"ignore\")\n        pipe_b_df.columns = [f\"{other_name}_{column}\" for column in pipe_b_df.columns]  # type: ignore\n        pipe_a_df.columns = [\n            f\"{this_name}_{col}\" if col not in ignore else col for col in pipe_a_df.columns\n        ]  # type: ignore\n\n        results_df = pd_concat([pipe_a_df, pipe_b_df], axis=1)\n        return results_df\n", "path": "haystack/evaluation/eval_run_result.py"}]}