Mark grader content-filter refusals as unscored #10
Conversation
Anthropic's content filter can non-deterministically block the LLM judge, returning stop_reason=content_filter with an empty completion. That response previously fell through to a silent INCORRECT, turning a transient grader failure into a false negative. Detect refusals (content_filter or empty completion), retry up to MAX_GRADER_ATTEMPTS, and on persistent failure return Score.unscored so the sample is excluded from accuracy/stderr and recorded as unscored rather than counted wrong. Applies to all judge-graded tags via _judge_score.
| A ``content_filter`` stop reason or an empty completion means moderation | ||
| blocked the grader (or it returned nothing), not that the answer is wrong. | ||
| """ | ||
| if result.stop_reason == "content_filter": |
There was a problem hiding this comment.
There was a problem hiding this comment.
thats so interesting that the content filter blocked the grading stage but not the actual question answering stage ahah
There was a problem hiding this comment.
I guess the different provider have different content filters: the model under test is gpt 5.2, whereas the judge is Sonnet 4.5
ItsTania
left a comment
There was a problem hiding this comment.
LGTM - the comments below are just a record of my thoughts (esp as I'm thinking about the question of 'what is the best way to deal with errors not to do with the solver" which is relevant to a few evals rn)
| import textwrap | ||
|
|
||
|
|
||
| def test_package_imports_without_optional_labbench2_dependency() -> None: |
There was a problem hiding this comment.
Not relevant to this PR - but I wonder if this optional dependency is a weakness of the template... it seems like it added more confusion than necessary for most people who would only implement one eval per repo...
There was a problem hiding this comment.
If there is only a single eval in the repo, users can make the deps required. Just add them in the main dependencies list here.
I put LB2's deps as optional to test the template setup for the multi-eval per repo case
| calls = _patch_grader( | ||
| monkeypatch, | ||
| [ | ||
| ("", "content_filter"), |
There was a problem hiding this comment.
Should we also try and handle other refusal patterns (maybe in a future PR?)
https://developers.openai.com/api/docs/guides/structured-outputs
| ("", "content_filter"), | |
| ("", "content_filter"), # the anthropic API does model refusal by ... |
There was a problem hiding this comment.
Another thing that could be added to Inspect AI as to support LLM grader
(handling refusals)
| name="evaluation_result", json_schema=json_schema(EvaluationResult) | ||
| ) | ||
|
|
||
| async def call_grader_with_retry(prompt: str) -> ModelOutput | None: |
There was a problem hiding this comment.
This is the first time I've seen the Score.unscored option - v cool
I like that it enables future scoring and really highlights that the issue is with the scoring (and make re-scoring easier https://inspect.aisi.org.uk/scoring-workflow.html)
The pattern I previously knew of is to raise an error and rely on --retry-on-error (https://inspect.aisi.org.uk/options.html#errors-and-limits). The benefit is the retry is built in, but the ^ re-scoring options aren't (get logged as errors after x retries))
I wonder if there is a neat fix combining the two in inspect ai! Esp as LLM judges are getting more popular as scoring options.
This PR contains
Description
Validation
Automated checks
make check
make test
Manual checks