Skip to content

Migrating from Feedback to Metric

The Metric class is the new unified API for evaluation metrics in TruLens. It replaces both Feedback and MetricConfig with a cleaner, more explicit interface.

Why Migrate?

  • Clearer API: Explicit selectors dict instead of chained .on_*() methods
  • Better OTel integration: Native support for OpenTelemetry span selection
  • Unified interface: One class instead of separate Feedback and MetricConfig
  • Same functionality: All existing features are preserved

Quick Comparison

Before (Feedback)

from trulens.core import Feedback
from trulens.providers.openai import OpenAI

provider = OpenAI()

f_relevance = (
    Feedback(provider.relevance, name="Answer Relevance")
    .on_input()
    .on_output()
)

f_groundedness = (
    Feedback(provider.groundedness_measure_with_cot_reasons, name="Groundedness")
    .on_context(collect_list=True)
    .on_output()
    .on_input()
)

f_context_relevance = (
    Feedback(provider.context_relevance, name="Context Relevance")
    .on_input()
    .on_context(collect_list=False)
    .aggregate(np.mean)
)

After (Metric)

from trulens.core import Metric, Selector
from trulens.providers.openai import OpenAI
import numpy as np

provider = OpenAI()

f_relevance = Metric(
    implementation=provider.relevance,
    name="Answer Relevance",
    selectors={
        "prompt": Selector.select_record_input(),
        "response": Selector.select_record_output(),
    },
)

f_groundedness = Metric(
    implementation=provider.groundedness_measure_with_cot_reasons,
    name="Groundedness",
    selectors={
        "source": Selector.select_context(collect_list=True),
        "statement": Selector.select_record_output(),
        "question": Selector.select_record_input(),
    },
)

f_context_relevance = Metric(
    implementation=provider.context_relevance,
    name="Context Relevance",
    selectors={
        "question": Selector.select_record_input(),
        "context": Selector.select_context(collect_list=False),
    },
    agg=np.mean,
)

Selector Reference

The Selector class provides static methods for common selections:

Method Description
Selector.select_record_input() The input to the traced application
Selector.select_record_output() The output from the traced application
Selector.select_context(collect_list=True) Retrieved contexts from RAG

Custom Selectors

For advanced use cases, create custom selectors by matching span attributes:

from trulens.core import Selector
from trulens.otel.semconv.trace import SpanAttributes

# Select by span type
retrieval_query = Selector(
    span_type=SpanAttributes.SpanType.RETRIEVAL,
    function_attribute="query",
)

# Select by function name
custom_output = Selector(
    function_name="my_module.my_function",
    function_attribute="return",
)

# Select specific span attribute
generation_output = Selector(
    span_type=SpanAttributes.SpanType.GENERATION,
    span_attribute=SpanAttributes.GENERATION.OUTPUT,
)

Parameter Mapping

Feedback Metric Notes
imp=fn implementation=fn Renamed for clarity
.on_input() selectors={"param": Selector.select_record_input()} Explicit selector
.on_output() selectors={"param": Selector.select_record_output()} Explicit selector
.on_context() selectors={"param": Selector.select_context()} Explicit selector
.aggregate(fn) agg=fn Same functionality
name="..." name="..." Unchanged
examples=[...] examples=[...] Unchanged
criteria="..." criteria="..." Unchanged

New Fields

The Metric class adds optional metadata fields:

Metric(
    implementation=my_fn,
    name="My Metric",
    metric_type="custom",  # NEW: Implementation identifier
    description="Measures X quality",  # NEW: Human-readable description
    selectors={...},
)

Migrating from MetricConfig

If you were using MetricConfig for custom client-side metrics:

Before (MetricConfig)

from trulens.core.feedback.custom_metric import MetricConfig

config = MetricConfig(
    metric_name="word_count",
    metric_implementation=lambda text: len(text.split()),
    selectors={"text": Selector.select_record_output()},
)
feedback = config.create_feedback_definition()

After (Metric)

from trulens.core import Metric, Selector

metric = Metric(
    name="word_count",
    implementation=lambda text: len(text.split()),
    selectors={"text": Selector.select_record_output()},
)

Backward Compatibility

The old Feedback class still works and will emit a deprecation warning:

# This still works but shows a deprecation warning
from trulens.core import Feedback
f = Feedback(provider.relevance).on_input().on_output()

The chained .on_*() methods also work on Metric for gradual migration, but the selectors={} style is recommended for new code.

Type Checking Notes

The Feedback class is implemented as a subclass of Metric:

class Feedback(Metric):
    ...

This means:

Check Result
isinstance(metric_obj, Metric) True
isinstance(feedback_obj, Metric) True
isinstance(feedback_obj, Feedback) True
isinstance(metric_obj, Feedback) False

For downstream integrators: If your code performs type checks with isinstance(obj, Feedback), you should migrate to isinstance(obj, Metric) before the Feedback alias is removed. After removal:

  • isinstance(obj, Metric) will continue to work βœ…
  • isinstance(obj, Feedback) will raise NameError ❌
# Before (will break when Feedback is removed)
if isinstance(obj, Feedback):
    ...

# After (recommended)
if isinstance(obj, Metric):
    ...

Timeline

  • Future release: Feedback and MetricConfig will be removed

We recommend migrating to Metric for all new code.