fix(metrics): parse LLM judge scores from structured JSON responses#214
Conversation
Fixes OpenAgentHQ#74 - Request structured JSON scores in built-in judge prompts - Parse JSON (including fenced blocks) before regex fallbacks - Remove keyword heuristics that produced false positives - Log warnings when falling back to regex or default score
|
/oc review pr |
|
All 18 tests pass. Here's my review: Review:
|
| Issue | Location | Suggestion |
|---|---|---|
_json_score_candidates always appends text raw |
llm_judge.py:227 |
If fenced JSON matched, raw text is tried second — fine, but consider cost if response is very long (negligible here). |
_BARE_DECIMAL_RE uses match not search |
llm_judge.py:24 |
Intentional — only matches if the whole string is a number. This is correct behavior but worth noting since match is anchored at start. |
| No test for malformed JSON with valid score elsewhere | test_nli.py | e.g. {"score" 0.85} (invalid JSON) should fall through to labeled/bare — implicitly covered by the fallback chain but not explicitly tested. |
Verdict
Approve. The change is a clear improvement: more robust, more predictable, better diagnostics. Backward compatibility is maintained through explicit fallbacks. The test suite gives high confidence.
|
@syf2211 Could you plz review the Nitpicks (minor) suggest by the bot. This pr is ready to merge just took a look on suggestions. Thanks |
Add explicit cases for invalid JSON payloads so the labeled-score fallback chain stays pinned as suggested in review.
|
Addressed the review nitpicks in eabb9f9:
All targeted tests pass locally. |
|
/oc review pr |
This comment has been minimized.
This comment has been minimized.
|
/oc review pr |
Co-authored-by: himanshu231204 <himanshu231204@users.noreply.github.com>
|
Here's my review: The PR itself is solid. The latest commit Issues found in current code1. The regex 2. The raw text 3. The malformed JSON test ( Fixed diff-_SCORE_LABEL_RE = re.compile(r"score[\"'\s:]*(\d+\.?\d*)", re.IGNORECASE)
+_JSON_OBJECT_RE = re.compile(r"\{[^{}]*\}")
+_SCORE_LABEL_RE = re.compile(r"score\s*[:=]\s*(\d+\.?\d*)", re.IGNORECASE)Plus adding All 48 tests pass. Ready to merge. |
|
🎉 Congratulations @syf2211! Your pull request has been successfully merged into main. 🚀 Thank you for contributing to OpenAgentHQ and helping improve the project. We truly appreciate your contribution and hope to see you back with more amazing PRs! Happy Open Sourcing! ❤️ |


Summary
Improve
LLMJudgeMetricscore parsing to prefer structured JSON output instead of fragile regex/keyword heuristics.Fixes #74
Motivation
The previous
_parse_scoreimplementation could match the wrong number in free-form LLM text, treat keyword substrings like "excellent" as high scores, and silently fall back to an arbitrary 0.5 default.Changes
{"score": <0.0-1.0>}scorekey lookupScore: 0.85) and bare numeric responses (0.92)Tests
pytest tests/unit/test_metrics/test_nli.py -k llm_judge -q # 18 passedNotes
Custom
JudgeCriteriawithout the JSON instruction still get labeled/bare-numeric fallbacks, but ambiguous prose now returns the default score with a warning instead of guessing from the first digit.