Skip to content

fix(evals): Update Judge Metrics Prompt - #1104

Merged
AkhileshNegi merged 18 commits into
mainfrom
fix/judge-metrics-prompt-update
Aug 3, 2026
Merged

fix(evals): Update Judge Metrics Prompt#1104
AkhileshNegi merged 18 commits into
mainfrom
fix/judge-metrics-prompt-update

Conversation

@Ayush8923

@Ayush8923 Ayush8923 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Migrates the v2 LLM-as-judge from a 0–1 float score to a 0–5 integer stepped scale and rewrites the three metric prompts as detailed 0–5 rubrics. LLMs score far more reliably on a small discrete integer scale than a continuous 0–1 float; the API response structure is unchanged, only the score range widens to 0–5.
  • Here is the updated judge prompt docs.

Checklist

Before submitting a pull request, please ensure that you mark these task.

  • Ran fastapi run --reload app/main.py or docker compose up in the repository root and test.
  • If you've fixed a bug or added code that is tested and has test cases.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e5f4a85-50a5-415a-be18-51ce1378d31e

📥 Commits

Reviewing files that changed from the base of the PR and between ff54e8f and 8a647fc.

📒 Files selected for processing (3)
  • backend/app/crud/evaluations/score.py
  • backend/app/tests/crud/evaluations/test_fast_judge.py
  • docs/wiki/modules/evaluations.md

📝 Walkthrough

Walkthrough

Evaluation judging now uses integer scores from 0 to 5. Validation, judge prompts, verdict thresholds, consistency thresholds, persistence fixtures, documentation, and tests were updated for the new scale.

Changes

Evaluation scoring

Layer / File(s) Summary
Scoring contract and judge instructions
backend/app/api/docs/evaluation/*, backend/app/crud/evaluations/judge.py, backend/app/crud/evaluations/score.py, backend/app/services/evaluations/prompt_improvement.py, docs/wiki/modules/evaluations.md
Metric validation and judge instructions now require integer scores from 0 to 5. Ground-truth, prompt-adherence, and knowledge-base rubrics use stepped integer criteria.
Judge parsing and fast evaluation coverage
backend/app/tests/crud/evaluations/test_judge.py, backend/app/tests/crud/evaluations/test_fast_judge.py, backend/app/tests/api/routes/test_improve_prompt_v2.py
Fixtures and assertions cover integer scores, fractional-score rejection, revised verdicts, weighted results, metric gating, and knowledge-base scoring.
Summary, verdict, and consistency behavior
backend/app/crud/evaluations/summary.py, backend/app/tests/crud/evaluations/test_overall_summary.py, backend/app/tests/crud/evaluations/test_run_ai_summary.py, backend/app/tests/crud/evaluations/test_score.py
Verdict boundaries use 2 and 4. Consistency thresholds use 0.5 and 1.0. Summary and boundary tests use the revised values.
Cached and resynced evaluation persistence
backend/app/tests/services/evaluations/test_evaluation_service_s3.py
S3 tests expect v2 overall scores and Needs Refinement verdicts in cached and resynced evaluations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: akhileshnegi

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the central judge-metric prompt update and matches the pull request changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/judge-metrics-prompt-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

OpenAPI changes   ⚪ No API surface changes

Note

This PR does not modify the API contract.

main1336453a · generated by oasdiff

@Ayush8923 Ayush8923 self-assigned this Aug 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
backend/app/crud/evaluations/judge.py (1)

249-253: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Centralize the score bounds.

Line 249 embeds 0 and 5 directly in validation. Define MIN_JUDGE_SCORE and MAX_JUDGE_SCORE and use them in validation and error text. This keeps the executable score contract in one place during future scale changes.

As per coding guidelines, do not use magic values; extract repeated literals into constants, enums, or settings.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/crud/evaluations/judge.py` around lines 249 - 253, Define
MIN_JUDGE_SCORE and MAX_JUDGE_SCORE near the judge score validation code, then
replace the hard-coded 0 and 5 in the condition with those constants. Update the
ValueError message to interpolate the same bounds, keeping the existing integer
conversion and validation behavior unchanged.

Source: Coding guidelines

backend/app/tests/crud/evaluations/test_run_ai_summary.py (1)

58-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a narrow summary-score fixture type.

list[dict] does not specify the required name, avg, and std fields. Define a TypedDict for this fixture and use it for _summary_scores and the adjacent _call parameter.

As per coding guidelines, “Use Python 3.11+ and provide narrow type hints for every function parameter and return value; do not use -> Any as a substitute for a specific annotation.”

Proposed fix
+class SummaryScore(TypedDict):
+    name: str
+    avg: float
+    std: float
+
-def _summary_scores() -> list[dict]:
+def _summary_scores() -> list[SummaryScore]:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/tests/crud/evaluations/test_run_ai_summary.py` around lines 58 -
64, Define a narrow TypedDict containing the required name, avg, and std fields,
then use it as the return type of _summary_scores and the corresponding _call
parameter type. Keep the existing fixture values and behavior unchanged, and
avoid broad dict or Any annotations.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/crud/evaluations/judge.py`:
- Line 154: Update the MetricScore.score annotation in
backend/app/crud/evaluations/judge.py at lines 154-154 from float to int,
matching its integer parsing contract. Also update the _both_metrics_response
score tuple annotations in backend/app/tests/crud/evaluations/test_fast_judge.py
at lines 172-173 from tuple[float, str] to tuple[int, str].
- Around line 249-253: Update the judge score validation around the score
parsing and normalization logic to reject boolean values, catch OverflowError
from float conversion, and validate math.isfinite(score) before int(score).
Ensure all invalid cases raise the documented ValueError so the row is isolated,
and add regression coverage for true, false, Infinity, and NaN.

---

Nitpick comments:
In `@backend/app/crud/evaluations/judge.py`:
- Around line 249-253: Define MIN_JUDGE_SCORE and MAX_JUDGE_SCORE near the judge
score validation code, then replace the hard-coded 0 and 5 in the condition with
those constants. Update the ValueError message to interpolate the same bounds,
keeping the existing integer conversion and validation behavior unchanged.

In `@backend/app/tests/crud/evaluations/test_run_ai_summary.py`:
- Around line 58-64: Define a narrow TypedDict containing the required name,
avg, and std fields, then use it as the return type of _summary_scores and the
corresponding _call parameter type. Keep the existing fixture values and
behavior unchanged, and avoid broad dict or Any annotations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc4ff613-9d92-43fe-8410-49431b605113

📥 Commits

Reviewing files that changed from the base of the PR and between 4b03a89 and ff54e8f.

📒 Files selected for processing (13)
  • backend/app/api/docs/evaluation/create_evaluation_v2.md
  • backend/app/api/docs/evaluation/improve_prompt_v2.md
  • backend/app/crud/evaluations/judge.py
  • backend/app/crud/evaluations/score.py
  • backend/app/crud/evaluations/summary.py
  • backend/app/services/evaluations/prompt_improvement.py
  • backend/app/tests/api/routes/test_improve_prompt_v2.py
  • backend/app/tests/crud/evaluations/test_fast_judge.py
  • backend/app/tests/crud/evaluations/test_judge.py
  • backend/app/tests/crud/evaluations/test_overall_summary.py
  • backend/app/tests/crud/evaluations/test_run_ai_summary.py
  • backend/app/tests/crud/evaluations/test_score.py
  • backend/app/tests/services/evaluations/test_evaluation_service_s3.py

@dataclass
class MetricScore:
"""One metric's outcome for a row: score in [0, 1] and its reasoning."""
"""One metric's outcome for a row: integer score in 0–5 and its reasoning."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files 'backend/app/**' | rg '(^backend/app/crud/evaluations/judge\.py$|backend/app/tests/crud/evaluations/test_fast_judge\.py$)' || true

echo
echo "judge.py outline:"
ast-grep outline backend/app/crud/evaluations/judge.py --view expanded || true

echo
echo "Relevant judge.py lines:"
sed -n '120,190p' backend/app/crud/evaluations/judge.py

echo
echo "Relevant test_fast_judge.py lines:"
sed -n '140,210p' backend/app/tests/crud/evaluations/test_fast_judge.py

echo
echo "Score annotation usages in judge.py:"
rg -n "MetricScore|\.score|ground_truth|prompt|tuple\[ (float|int) , str \]" backend/utils backend/app/crud/evaluations backend/app/tests/crud/evaluations || true

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 50387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "judge.py `_parse_metric_score`:"
sed -n '230,260p' backend/app/crud/evaluations/judge.py

echo
echo "test_fast_judge.py helper around _both_metrics_response:"
sed -n '160,184p' backend/app/tests/crud/evaluations/test_fast_judge.py

echo
echo "target annotations:"
python3 - <<'PY'
from pathlib import Path
for path in [
    Path("backend/app/crud/evaluations/judge.py"),
    Path("backend/app/tests/crud/evaluations/test_fast_judge.py"),
]:
    text = path.read_text()
    for needle in ["  score: float", "ground_truth: tuple[float, str]", "prompt: tuple[float, str]"]:
        idx = text.find(needle)
        print(f"{path}:{idx+1}:{needle}")
PY

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 2816


Use an integer contract for judge scores.

MetricScore.score is parsed as an integer in backend/app/crud/evaluations/judge.py#L237, but the dataclass still annotates it as float. Update that field to int and change _both_metrics_response score tuples in backend/app/tests/crud/evaluations/test_fast_judge.py#L172-173 from tuple[float, str] to tuple[int, str] so the contracts stay aligned.

📍 Affects 2 files
  • backend/app/crud/evaluations/judge.py#L154-L154 (this comment)
  • backend/app/tests/crud/evaluations/test_fast_judge.py#L172-L173
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/crud/evaluations/judge.py` at line 154, Update the
MetricScore.score annotation in backend/app/crud/evaluations/judge.py at lines
154-154 from float to int, matching its integer parsing contract. Also update
the _both_metrics_response score tuple annotations in
backend/app/tests/crud/evaluations/test_fast_judge.py at lines 172-173 from
tuple[float, str] to tuple[int, str].

Source: Coding guidelines

Comment on lines +249 to +253
if score != int(score) or not 0 <= score <= 5:
raise ValueError(
f"metric '{key.value}' score must be an integer 0-5: {raw.get('score')!r}"
)
score = int(score)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

python - <<'PY'
import json

for literal in ("true", "false", "Infinity", "NaN"):
    raw = json.loads(f'{{"score": {literal}}}')["score"]
    score = float(raw)
    try:
        normalized = int(score)
    except Exception as exc:
        normalized = type(exc).__name__
    print(literal, repr(raw), normalized)
PY

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 241


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'File candidates:\n'
fd -a 'judge\.py$' . | sed 's#^\./##'

printf '\nRelevant file outline:\n'
ast-grep outline backend/app/crud/evaluations/judge.py --view compact || true

printf '\nRelevant source lines 200-270:\n'
sed -n '200,270p' backend/app/crud/evaluations/judge.py | nl -ba -v200

printf '\nSearch for parse_score_usages/ValueError handling near this file:\n'
rg -n "parse_scores|ValueError|score must be an integer|metric '" backend/app/crud/evaluations backend/app -g '*.py' | head -200

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 632


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Relevant source lines 200-270:\n'
sed -n '200,270p' backend/app/crud/evaluations/judge.py | awk '{printf "%6d\t%s\n", NR+199, $0}'

printf '\nSearch for parse_scores/usages and related validation:\n'
rg -n "parse_scores|ValueError|score must be an integer|metric '" backend/app/crud/evaluations backend/app -g '*.py' | head -200

printf '\nRelevant tests around score validation:\n'
rg -n "score|ValueError|boolean|Infinity|NaN" backend/app/tests/crud/evaluations/test_judge.py backend/app/tests/crud/evaluations/test_fast_judge.py -g '*.py' | head -200

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 48273


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from typing import Any
from pathlib import Path
src = Path('backend/app/crud/evaluations/judge.py').read_text()
start = src.index('def _parse_metric_score')
end = src.index('\ndef _parse_judge_output', start)
code = src[start:end]
print(code)

print('\nBehavioral probes:')
for literal, raw_value in [('true', True), ('false', False), ('Infinity', 'Infinity'), ('NaN', 'NaN')]:
    print(f'>{literal!r}:')
    try:
        score = float(raw_value)
        print(f'float={score!r}')
        print(f'isfinite=bool={score != score if score != score else (score == score)}')
        if isinstance(score, float):
            try:
                normalized = int(score)
                print(f'int={normalized!r}')
            except Exception as exc:
                print(f'int_exception={type(exc).__name__}:{exc}')
        else:
            print('not a float')
    except Exception as exc:
        print(f'float_exception={type(exc).__name__}:{exc}')
PY

python3 - <<'PY'
from typing import Any
from pathlib import Path
src = Path('backend/app/crud/evaluations/judge.py').read_text()
start = src.index('def _parse_judge_output')
end = src.index('\ndef _judge', end)
code = src[start:end]
print(code)

# Inspect exact exception handling around _parse_judge_output using static source.
lines = code.splitlines()
for i, line in enumerate(lines, 1):
    if 'except' in line or '_parse_metric_score' in line or 'except ValueError' in line:
        print(f'{i:04d}: {line}')
PY

Repository: ProjectTech4DevAI/kaapi-backend

Length of output: 1595


Reject boolean and non-finite judge scores before normalization.

float() converts JSON true and false to 1.0 and 0.0, so malformed boolean scores can be stored as valid scores. JSON Infinity raises OverflowError from int(score), bypassing the documented ValueError path that isolates the row.

Reject booleans, check math.isfinite(score) before converting to int, and catch OverflowError from float(). Add regression tests for true, false, Infinity, and NaN.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/crud/evaluations/judge.py` around lines 249 - 253, Update the
judge score validation around the score parsing and normalization logic to
reject boolean values, catch OverflowError from float conversion, and validate
math.isfinite(score) before int(score). Ensure all invalid cases raise the
documented ValueError so the row is isolated, and add regression coverage for
true, false, Infinity, and NaN.

@Ayush8923
Ayush8923 requested a review from AkhileshNegi August 3, 2026 11:31
@Ayush8923
Ayush8923 removed the request for review from AkhileshNegi August 3, 2026 11:36
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.00000% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...kend/app/tests/crud/evaluations/test_fast_judge.py 88.46% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@Ayush8923
Ayush8923 requested a review from AkhileshNegi August 3, 2026 12:20
@AkhileshNegi
AkhileshNegi merged commit efaf42a into main Aug 3, 2026
2 checks passed
@AkhileshNegi
AkhileshNegi deleted the fix/judge-metrics-prompt-update branch August 3, 2026 13:05
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.4.0-main.8 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants