Is your feature request related to a problem? Please describe.
AutoEval couples response generation to metric computation: it requires a LangChain chat model (langchain_llm) and calls it internally to generate responses and counterfactual responses. This is frustrating when:
- The generation stack isn't LangChain (direct provider SDKs, internal API gateways, batch/offline pipelines) — using AutoEval means wrapping that stack in a LangChain-compatible object just to reuse the orchestration.
- Responses already exist (e.g., collected from production). AutoEval accepts
responses=, but still needs a live LLM whenever prompts contain protected attribute words, because the counterfactual responses cannot be supplied through the public API.
- Generation and evaluation happen in different environments (e.g., generation behind a governed gateway, evaluation on a separate machine), and there is no clean hand-off between the two steps.
The underlying metric classes (ToxicityMetrics, StereotypeMetrics, CounterfactualMetrics) are already LLM-free — only the convenience orchestration forces the LangChain dependency.
Describe the solution you'd like
A SelfEval class in langfair.auto that splits the AutoEval workflow into two explicit phases, with generation delegated to the user:
- Prompt preparation (no LLM): the user provides
prompts; on construction, SelfEval runs the fairness-through-unawareness (FTU) check and creates counterfactual prompt variants for any protected attribute (gender, race) found. get_prompts() returns a flat, ordered list of every prompt requiring a response — each prompt repeated count times (default 25), originals first, then one block per counterfactual group. A prompt_manifest property documents the segment layout.
- Generation (user-side): the user generates exactly one response per element of
get_prompts(), in order, with any stack, inserting "Unable to get response" for failed generations.
- Metric computation (no LLM):
evaluate(responses=...) — synchronous, no await — validates the length/order contract (with a per-segment breakdown on mismatch) and computes the same toxicity, stereotype, and counterfactual metrics as AutoEval.evaluate, including print_results() / export_results().
Pre-existing responses should also be supported at construction (parity with AutoEval's responses= parameter), in which case get_prompts() returns only the counterfactual variants still requiring generation.
To keep the two classes in sync, the shared logic (FTU check, metric steps, results assembly, print/export) should be factored into a common base class, leaving AutoEval's public API unchanged. A demo notebook should illustrate the file-based hand-off (prompts.json → required_prompts.json → user-generated responses.json → metrics).
Describe alternatives you've considered
- Wrapping a custom stack in a LangChain
BaseChatModel to satisfy AutoEval: forces a LangChain dependency and async plumbing on users whose stacks are not LangChain-based, just to reuse orchestration.
- Using
AutoEval(responses=...): avoids generation for original prompts only; the counterfactual generation step still requires a live LLM whenever FTU is not satisfied.
- Calling the metric classes and
CounterfactualGenerator.parse_texts/create_prompts directly: possible today, but users must reimplement AutoEval's orchestration themselves (FTU handling, count expansion, group pairing, failure filtering, results assembly), which is error-prone and duplicated across teams.
Additional context
- Metric computation in AutoEval is already fully LLM-free; the LLM is only needed for generation, so there is a clean seam for this separation.
- The
count=25 default follows the established convention for toxicity evaluation (DecodingTrust, https://arxiv.org/abs/2306.11698; Gehman et al., 2020, https://aclanthology.org/2020.findings-emnlp.301/).
- An implementation of this proposal is available on branch
mc/selfeval (PR to follow).
Is your feature request related to a problem? Please describe.
AutoEvalcouples response generation to metric computation: it requires a LangChain chat model (langchain_llm) and calls it internally to generate responses and counterfactual responses. This is frustrating when:responses=, but still needs a live LLM whenever prompts contain protected attribute words, because the counterfactual responses cannot be supplied through the public API.The underlying metric classes (
ToxicityMetrics,StereotypeMetrics,CounterfactualMetrics) are already LLM-free — only the convenience orchestration forces the LangChain dependency.Describe the solution you'd like
A
SelfEvalclass inlangfair.autothat splits the AutoEval workflow into two explicit phases, with generation delegated to the user:prompts; on construction,SelfEvalruns the fairness-through-unawareness (FTU) check and creates counterfactual prompt variants for any protected attribute (gender, race) found.get_prompts()returns a flat, ordered list of every prompt requiring a response — each prompt repeatedcounttimes (default 25), originals first, then one block per counterfactual group. Aprompt_manifestproperty documents the segment layout.get_prompts(), in order, with any stack, inserting"Unable to get response"for failed generations.evaluate(responses=...)— synchronous, noawait— validates the length/order contract (with a per-segment breakdown on mismatch) and computes the same toxicity, stereotype, and counterfactual metrics asAutoEval.evaluate, includingprint_results()/export_results().Pre-existing responses should also be supported at construction (parity with AutoEval's
responses=parameter), in which caseget_prompts()returns only the counterfactual variants still requiring generation.To keep the two classes in sync, the shared logic (FTU check, metric steps, results assembly, print/export) should be factored into a common base class, leaving AutoEval's public API unchanged. A demo notebook should illustrate the file-based hand-off (
prompts.json→required_prompts.json→ user-generatedresponses.json→ metrics).Describe alternatives you've considered
BaseChatModelto satisfy AutoEval: forces a LangChain dependency and async plumbing on users whose stacks are not LangChain-based, just to reuse orchestration.AutoEval(responses=...): avoids generation for original prompts only; the counterfactual generation step still requires a live LLM whenever FTU is not satisfied.CounterfactualGenerator.parse_texts/create_promptsdirectly: possible today, but users must reimplement AutoEval's orchestration themselves (FTU handling,countexpansion, group pairing, failure filtering, results assembly), which is error-prone and duplicated across teams.Additional context
count=25default follows the established convention for toxicity evaluation (DecodingTrust, https://arxiv.org/abs/2306.11698; Gehman et al., 2020, https://aclanthology.org/2020.findings-emnlp.301/).mc/selfeval(PR to follow).