all_eval_results.setdefault(str(example_id), {}).update(
                            {"feedback": v}
                        )
                elif isinstance(callback, LangChainTracer):
                    run = callback.latest_run
                    execution_time = (
                        (run.end_time - run.start_time).total_seconds()
                        if run and run.end_time
                        else None
                    )
                    run_id = str(run.id) if run else None
                    all_eval_results.setdefault(str(callback.example_id), {}).update(
                        {
                            "execution_time": execution_time,
                            "run_id": run_id,
                        }
                    )
        return cast(Dict[str, _RowResult], all_eval_results)
    def _collect_test_results(
        self,
        batch_results: List[Union[dict, str, LLMResult, ChatResult]],
    ) -> TestResult:
        wait_for_all_evaluators()
        all_eval_results = self._collect_metrics()
        results = self._merge_test_outputs(batch_results, all_eval_results)
        return TestResult(
            project_name=self.project.name,
            results=results,
        )
    def finish(self, batch_results: list, verbose: bool = False) -> TestResult:
        results = self._collect_test_results(batch_results)
        if verbose:
            try:
                agg_feedback = results.get_aggregate_feedback()
                _display_aggregate_results(agg_feedback)
            except Exception as e:
                logger.debug(f"Failed to print aggregate feedback: {repr(e)}")
        try:
            # Closing the project permits name changing and metric optimizations
            self.client.update_project(self.project.id, end_time=datetime.utcnow())
        except Exception as e:
            logger.debug(f"Failed to close project: {repr(e)}")
        return results
    @classmethod
    def prepare(