def _call_feedback_function(
    feedback_name: str,
    feedback_function: Callable[
        [Any], Union[float, Tuple[float, Dict[str, Any]]]
    ],
    higher_is_better: bool,
    feedback_aggregator: Optional[Callable[[List[float]], float]],
    kwarg_inputs: Dict[str, FeedbackFunctionInput],
    record_root_attributes: Dict[str, Any],
    record_root_resource_attributes: Dict[str, Any],
    span_group: Optional[str] = None,
) -> None:
    """Call feedback function.

    Args:
        feedback_name: Name of the feedback function.
        feedback_function: Function to compute feedback.
        higher_is_better: Whether higher values are better.
        feedback_aggregator: Aggregator function to combine feedback scores.
        kwarg_inputs: kwarg inputs to feedback function.
        record_root_attributes: Span attributes of record root.
        record_root_resource_attributes: Resource attributes of record root.
        span_group: Span group of the invocation.
    """
    app_name, app_version, app_id, run_name = _get_app_and_run_info(
        record_root_attributes, record_root_resource_attributes
    )
    # TODO(otel, dhuang): need to use these when getting the object entity!
    # database_name = record_root_attributes[
    #    f"snow.{BASE_SCOPE}.database.name"
    # ]
    # schema_name = record_root_attributes[
    #    f"snow.{BASE_SCOPE}.schema.name"
    # ]
    input_id = record_root_attributes[SpanAttributes.INPUT_ID]
    target_record_id = record_root_attributes[SpanAttributes.RECORD_ID]
    context_manager = OtelFeedbackComputationRecordingContext(
        app_name=app_name,
        app_version=app_version,
        app_id=app_id,
        run_name=run_name,
        input_id=input_id,
        target_record_id=target_record_id,
        feedback_name=feedback_name,
    )
    with context_manager as eval_root_span:
        try:
            if span_group is not None:
                eval_root_span.set_attribute(
                    f"{SpanAttributes.EVAL_ROOT.SPAN_GROUP}",
                    span_group,
                )
            eval_root_span.set_attribute(
                SpanAttributes.EVAL_ROOT.HIGHER_IS_BETTER, higher_is_better
            )
            expanded_kwargs_inputs = [{}]
            aggregate = False
            for k, v in kwarg_inputs.items():
                if v.span_id is not None:
                    eval_root_span.set_attribute(
                        f"{SpanAttributes.EVAL_ROOT.ARGS_SPAN_ID}.{k}",
                        v.span_id,
                    )
                if v.span_attribute is not None:
                    eval_root_span.set_attribute(
                        f"{SpanAttributes.EVAL_ROOT.ARGS_SPAN_ATTRIBUTE}.{k}",
                        v.span_attribute,
                    )
                if isinstance(v.value, list) and not v.collect_list:
                    aggregate = True
                    new_expanded_kwargs_inputs = []
                    for curr in expanded_kwargs_inputs:
                        for val in v.value:
                            curr[k] = val
                            new_expanded_kwargs_inputs.append(curr.copy())
                    expanded_kwargs_inputs = new_expanded_kwargs_inputs
                else:
                    for curr in expanded_kwargs_inputs:
                        curr[k] = v.value
            res = []
            for i, curr in enumerate(expanded_kwargs_inputs):
                res.append(
                    _call_feedback_function_under_eval_span(
                        feedback_function,
                        curr,
                        eval_root_span,
                        is_only_child=len(expanded_kwargs_inputs) == 1,
                        eval_child_idx=i,
                    )
                )
            if aggregate:
                if feedback_aggregator is not None:
                    res = feedback_aggregator(res)
                else:
                    res = sum(res) / len(res) if res else 0.0
            else:
                res = res[0]
            eval_root_span.set_attribute(SpanAttributes.EVAL_ROOT.SCORE, res)
        except Exception as e:
            eval_root_span.set_attribute(SpanAttributes.EVAL_ROOT.ERROR, str(e))
            raise e
Call feedback function.

    Args:
        feedback_name: Name of the feedback function.
        feedback_function: Function to compute feedback.
        higher_is_better: Whether higher values are better.
        feedback_aggregator: Aggregator function to combine feedback scores.
        kwarg_inputs: kwarg inputs to feedback function.
        record_root_attributes: Span attributes of record root.
        record_root_resource_attributes: Resource attributes of record root.
        span_group: Span group of the invocation.
    
src/feedback/trulens/feedback/computer.py