feat(deepeval): support custom metric instances in evaluator - #3841
Open
ClaireXi99 wants to merge 2 commits into
Open
feat(deepeval): support custom metric instances in evaluator#3841ClaireXi99 wants to merge 2 commits into
ClaireXi99 wants to merge 2 commits into
Conversation
`DeepEvalEvaluator` now accepts an already initialized DeepEval metric, so any subclass of `deepeval.metrics.BaseMetric` can be used for evaluation. The inputs the component exposes are derived from the test case parameters the metric declares, and built-in metrics selected by name or `DeepEvalMetric` keep working exactly as before. A metric instance cannot be serialized, so `to_dict` raises for that case and the docstring points at the built-in metrics when a pipeline must survive a `to_dict`/`from_dict` round trip. Parameter discovery reads any collection of `SingleTurnParams`, not just `list`: DeepEval does not promise a concrete container. When the declaration cannot be read as params -- including the common case where a metric never declares it and the annotation leaks through as `typing.List[SingleTurnParams]` -- the built-in RAG inputs are assumed.
Contributor
|
Heads-up for maintainers This PR is from a fork and touches integrations whose integration tests require API keys. Affected integrations:
Please run the integration tests locally ( |
ClaireXi99
force-pushed
the
feat/deepeval-custom-metrics
branch
from
August 24, 2026 10:15
4387011 to
5d785cb
Compare
CI runs the unit tests against the lowest allowed direct dependencies, where deepeval is 2.9.0. Two things broke there: - `SingleTurnParams` does not exist before deepeval 4, where the enum was named `LLMTestCaseParams`. Import the new name and fall back to the old one, and have the tests take the enum from the module under test so both always agree. - `MetricData` requires `threshold` and `success` before deepeval 4 and makes them optional after, so the local measurement double passes them explicitly. Verified against deepeval 2.9.0 and 4.1.8: 33 passed, 5 skipped on both.
ClaireXi99
force-pushed
the
feat/deepeval-custom-metrics
branch
from
August 24, 2026 10:26
5d785cb to
fbb39f8
Compare
Contributor
Coverage report (deepeval)Click to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3838
What
DeepEvalEvaluatornow accepts an already initialized DeepEval metric, so any subclassof
deepeval.metrics.BaseMetriccan be used for evaluation:The inputs the component exposes are derived from the test case parameters the metric
declares, and results come back in the same
name/score/explanationshape as thebuilt-ins.
Backwards compatibility
Purely additive.
metricwidens fromstr | DeepEvalMetrictostr | DeepEvalMetric | BaseMetric; the built-in branch, its init validation, its inputvalidation and its serde are untouched. Existing tests still pass unchanged.
Two design points worth your call
1. Parameter discovery reads
_required_params. DeepEval exposes no public way toask a metric which test case params it needs, so this reads the private attribute. Two
details:
SingleTurnParamsis accepted, not justlist— DeepEval does notpromise a concrete container type, and silently discarding a
tuple/setdeclarationwould map the component's inputs to the RAG defaults without telling the user.
BaseMetriconly annotates_required_paramswithout assigning it, so on a metricthat never declares it
getattrreturns the typing aliastyping.List[SingleTurnParams]rather thanNone. That case — and anything else thatcannot be read as params — falls back to the built-in RAG inputs.
If you would rather not depend on a private attribute, the alternative is an explicit
constructor argument for the expected inputs. Happy to switch.
2. A metric instance cannot be serialized.
to_dictraises for that case, since aninstance carries runtime state that cannot be reliably reconstructed. The class docstring
points at the built-in metrics when a pipeline has to survive a
to_dict/from_dictround trip. Tell me if you would prefer a warning plus a degraded dict instead of a hard
error.
Also note this supports a single metric instance, matching the current DeepEval
evaluator shape.
RagasEvaluatortakes a list of metrics — say the word and I will alignthis one to that API.
Tests
integrations/deepeval/tests/test_evaluator.pygains coverage for input derivation(declared as list, declared as tuple, undeclared, unreadable, unsupported params), the
measure()call path, output shape,LLMTestCaseconversion, and passing a built-inmetric as an instance. Locally: 32 passed, 5 skipped (the skips need
OPENAI_API_KEY).ruff checkandruff format --checkare clean.Two notes so they don't surprise you in review:
CHANGELOG.md— it looks likeHaystackBotgenerates it viacliff.tomlduring the release workflow. Let me know if contributors are expected toadd an entry.
mypy srcreports 8 errors in this integration, but they are identical before andafter this change (only one line number shifts, because the docstring grew). They come
from
deepeval.metricsattribute resolution against the installed deepeval version, soI left them alone rather than widening the diff.