-
Notifications
You must be signed in to change notification settings - Fork 193
Added AAI-Omniscience Benchmark #1161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 44 commits
194964c
8d6458e
4d058c9
707ed78
e1adc1c
443ccb0
84246e4
1a74320
7d0069d
cdb9a10
33e4dde
11d2b78
57fcdff
3183367
813d792
76683b1
08fc0b7
49e937d
16c0d7b
0b87a57
07956c2
1259a26
3f0d6cd
d6cc9d1
723a1fc
4843d71
6e599e6
18ce92d
e0e107a
48bf2b0
55ac14a
858ad2b
9529381
9a30970
32160a9
d08edbe
10492a4
6ac32db
86dc189
e805cd8
46235f4
20a2c84
838d182
d1c2fbf
774aa1b
9efc5c5
019a8f9
29ffea6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||||||
| # Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026 instead of 2025
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026, should be 2025
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copyright year is 2026 but should be 2025 |
||||||||||||||
| # | ||||||||||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||
| # you may not use this file except in compliance with the License. | ||||||||||||||
| # You may obtain a copy of the License at | ||||||||||||||
| # | ||||||||||||||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||
| # | ||||||||||||||
| # Unless required by applicable law or agreed to in writing, software | ||||||||||||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||
| # See the License for the specific language governing permissions and | ||||||||||||||
| # limitations under the License. | ||||||||||||||
|
|
||||||||||||||
| # settings that define how evaluation should be done by default (all can be changed from cmdline) | ||||||||||||||
| DATASET_GROUP = "math" | ||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this the right group? |
||||||||||||||
| METRICS_TYPE = "omniscience" | ||||||||||||||
| GENERATION_ARGS = "++prompt_config=eval/aai/omni" | ||||||||||||||
| EVAL_SPLIT = "text" | ||||||||||||||
|
|
||||||||||||||
| JUDGE_PIPELINE_ARGS = { | ||||||||||||||
| "model": "gemini-2.5-flash-preview-09-2025", | ||||||||||||||
| "server_type": "gemini", | ||||||||||||||
| "server_address": "https://generativelanguage.googleapis.com", | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
+21
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Short answer — as of January 13, 2026:
Sources: Google blog & Gemini changelog / Vertex AI docs and recent coverage. [1][2][3][4] References Replace with stable GA model version. The model 🤖 Prompt for AI Agents |
||||||||||||||
| JUDGE_ARGS = "++prompt_config=judge/aa-omni-judge ++generation_key=judgement ++add_generation_stats=False" | ||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,80 @@ | ||||||||||||||||||
| # Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026 instead of 2025
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026, should be 2025
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copyright year is 2026 but should be 2025 |
||||||||||||||||||
| # | ||||||||||||||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||||||
| # you may not use this file except in compliance with the License. | ||||||||||||||||||
| # You may obtain a copy of the License at | ||||||||||||||||||
| # | ||||||||||||||||||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||
| # | ||||||||||||||||||
| # Unless required by applicable law or agreed to in writing, software | ||||||||||||||||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||||||
| # See the License for the specific language governing permissions and | ||||||||||||||||||
| # limitations under the License. | ||||||||||||||||||
|
|
||||||||||||||||||
| import argparse | ||||||||||||||||||
| import json | ||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||
|
|
||||||||||||||||||
| from datasets import load_dataset | ||||||||||||||||||
| from tqdm import tqdm | ||||||||||||||||||
|
|
||||||||||||||||||
| TOPIC_TO_SPLIT_MAP = { | ||||||||||||||||||
| "Humanities and Social Sciences": "humanities", | ||||||||||||||||||
| "Health": "health", | ||||||||||||||||||
| "Software Engineering": "swe", | ||||||||||||||||||
| "Science Engineering and Mathematics": "stem", | ||||||||||||||||||
| "Law": "law", | ||||||||||||||||||
| "Finance": "finance", | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def parse_args() -> argparse.Namespace: | ||||||||||||||||||
| parser = argparse.ArgumentParser() | ||||||||||||||||||
| parser.add_argument( | ||||||||||||||||||
| "-s", | ||||||||||||||||||
| "--splits", | ||||||||||||||||||
| default=["text", "humanities", "health", "swe", "stem", "law", "finance"], | ||||||||||||||||||
| nargs="+", | ||||||||||||||||||
| choices=["text", "humanities", "health", "swe", "stem", "law", "finance"], | ||||||||||||||||||
| ) | ||||||||||||||||||
| return parser.parse_args() | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def format_entry(entry) -> dict: | ||||||||||||||||||
| return { | ||||||||||||||||||
| "id": entry["question_id"], | ||||||||||||||||||
| "domain": entry["domain"], | ||||||||||||||||||
| "topic": entry["topic"], | ||||||||||||||||||
| "question": entry["question"], | ||||||||||||||||||
| "expected_answer": entry["answer"], | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def write_jsonl(data: list[dict], path: str): | ||||||||||||||||||
| with open(path, "w", encoding="utf-8") as f: | ||||||||||||||||||
| for d in data: | ||||||||||||||||||
| f.write(json.dumps(d) + "\n") | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| if __name__ == "__main__": | ||||||||||||||||||
| args = parse_args() | ||||||||||||||||||
|
|
||||||||||||||||||
| dataset = load_dataset("ArtificialAnalysis/AA-Omniscience-Public", split="train") | ||||||||||||||||||
| jsonl_data = [format_entry(d) for d in dataset] | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment on lines
+63
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Variable
Comment on lines
+63
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style:
Suggested change
|
||||||||||||||||||
| output_dir = Path(__file__).absolute().parent | ||||||||||||||||||
|
|
||||||||||||||||||
| split_set = set(args.splits) | ||||||||||||||||||
| splits = { | ||||||||||||||||||
| "text": dataset, | ||||||||||||||||||
| **{ | ||||||||||||||||||
| TOPIC_TO_SPLIT_MAP.get(t, str(t).lower()): dataset.filter(lambda x: x["domain"] == t) | ||||||||||||||||||
| for t in dataset.unique("domain") | ||||||||||||||||||
| }, | ||||||||||||||||||
| } | ||||||||||||||||||
| splits = {k: v for k, v in splits.items() if k in split_set} | ||||||||||||||||||
|
|
||||||||||||||||||
| for split, data in tqdm(splits.items(), total=len(splits)): | ||||||||||||||||||
| output_file = output_dir / f"{split}.jsonl" | ||||||||||||||||||
| formatted_data = [format_entry(entry) for entry in data] | ||||||||||||||||||
| write_jsonl(formatted_data, output_file) | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,142 @@ | ||||||||||||||||||||||||||||||
| # Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026 instead of 2025
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copyright year is 2026, should be 2025
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copyright year is 2026 but should be 2025 |
||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||||||||||||||||||
| # you may not use this file except in compliance with the License. | ||||||||||||||||||||||||||||||
| # You may obtain a copy of the License at | ||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||
| # Unless required by applicable law or agreed to in writing, software | ||||||||||||||||||||||||||||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||||||||||||||||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||||||||||||||||||
| # See the License for the specific language governing permissions and | ||||||||||||||||||||||||||||||
| # limitations under the License. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| from collections import defaultdict | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| from nemo_skills.evaluation.metrics.math_metrics import BaseMetrics, as_int, as_percentage | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| class OmniMetrics(BaseMetrics): | ||||||||||||||||||||||||||||||
| def __init__(self, compute_no_answer: bool = True, answer_key: str = "generation"): | ||||||||||||||||||||||||||||||
| super().__init__(compute_no_answer=compute_no_answer) | ||||||||||||||||||||||||||||||
| self.answer_key = answer_key | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # use same RM code as MathMetrics | ||||||||||||||||||||||||||||||
| def _compute_reward_at_k(self, predictions: list[dict]): | ||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this function required for all datasets? Seems like math has it but most of the datasets don't
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's required, but I figured it might be helpful since a reward model could serve as a proxy for a judge model and may be useful for the downstream task. It's not critical to the benchmark itself though, so we can drop if need be. |
||||||||||||||||||||||||||||||
| score_dicts = [self._get_score_dict(pred) for pred in predictions] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| for k in range(1, len(predictions) + 1): | ||||||||||||||||||||||||||||||
| for score_method in score_dicts[0].keys(): | ||||||||||||||||||||||||||||||
|
Comment on lines
+29
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will crash with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
|
||||||||||||||||||||||||||||||
| # Get valid answers and their results for this field | ||||||||||||||||||||||||||||||
| valid_answers_and_results = [ | ||||||||||||||||||||||||||||||
| (elem[self.answer_key], correctness_dict[score_method], elem["reward_model_score"]) | ||||||||||||||||||||||||||||||
| for elem, correctness_dict in zip(predictions[:k], score_dicts[:k]) | ||||||||||||||||||||||||||||||
| if elem[self.answer_key] is not None | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # If no valid answers, it's incorrect | ||||||||||||||||||||||||||||||
| if not valid_answers_and_results: | ||||||||||||||||||||||||||||||
| is_correct = False | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment on lines
+36
to
+40
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. variable |
||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| is_correct_best = sorted(valid_answers_and_results, key=lambda x: x[2], reverse=True)[0][1] | ||||||||||||||||||||||||||||||
| self.eval_dict[f"rm_best@{k}"][score_method] += is_correct_best | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| answer_to_score_dict = defaultdict(float) | ||||||||||||||||||||||||||||||
| answer_to_correctness_dict = {} | ||||||||||||||||||||||||||||||
| for predicted_answer, is_correct, reward_score in valid_answers_and_results: | ||||||||||||||||||||||||||||||
| answer_to_score_dict[predicted_answer] += reward_score | ||||||||||||||||||||||||||||||
| answer_to_correctness_dict[predicted_answer] = is_correct | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| top_cum_reward_answer = sorted( | ||||||||||||||||||||||||||||||
| list(answer_to_score_dict.items()), key=lambda x: x[1], reverse=True | ||||||||||||||||||||||||||||||
| )[0][0] | ||||||||||||||||||||||||||||||
| is_correct_majority = answer_to_correctness_dict[top_cum_reward_answer] | ||||||||||||||||||||||||||||||
| self.eval_dict[f"rm_majority@{k}"][score_method] += is_correct_majority | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| no_answer = all(elem[self.answer_key] is None for elem in predictions[:k]) | ||||||||||||||||||||||||||||||
| self.eval_dict[f"rm_best@{k}"]["no_answer"] += no_answer | ||||||||||||||||||||||||||||||
| self.eval_dict[f"rm_majority@{k}"]["no_answer"] += no_answer | ||||||||||||||||||||||||||||||
|
Comment on lines
+26
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Multiple issues in
Suggested fix def _compute_reward_at_k(self, predictions: list[dict]):
+ if not predictions:
+ return
+
score_dicts = [self._get_score_dict(pred) for pred in predictions]
+ if not score_dicts or not score_dicts[0]:
+ return
for k in range(1, len(predictions) + 1):
for score_method in score_dicts[0].keys():
# Get valid answers and their results for this field
valid_answers_and_results = [
(elem[self.answer_key], correctness_dict[score_method], elem["reward_model_score"])
- for elem, correctness_dict in zip(predictions[:k], score_dicts[:k])
+ for elem, correctness_dict in zip(predictions[:k], score_dicts[:k], strict=True)
if elem[self.answer_key] is not None
]
# If no valid answers, it's incorrect
if not valid_answers_and_results:
- is_correct = False
+ pass # No valid answers to process
else:
is_correct_best = sorted(valid_answers_and_results, key=lambda x: x[2], reverse=True)[0][1]
self.eval_dict[f"rm_best@{k}"][score_method] += is_correct_best
answer_to_score_dict = defaultdict(float)
answer_to_correctness_dict = {}
- for predicted_answer, is_correct, reward_score in valid_answers_and_results:
+ for predicted_answer, correctness, reward_score in valid_answers_and_results:
answer_to_score_dict[predicted_answer] += reward_score
- answer_to_correctness_dict[predicted_answer] = is_correct
+ answer_to_correctness_dict[predicted_answer] = correctness🧰 Tools🪛 Ruff (0.14.11)34-34: Add explicit value for parameter (B905) |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def _get_score_dict(self, prediction: dict) -> dict[str, bool | int | float]: | ||||||||||||||||||||||||||||||
| correctness_dict = {} | ||||||||||||||||||||||||||||||
| if "judgement" in prediction: | ||||||||||||||||||||||||||||||
| judgement = prediction["judgement"].strip() | ||||||||||||||||||||||||||||||
| correctness_dict["judge_correct"] = int(judgement.lower() == "a") | ||||||||||||||||||||||||||||||
| correctness_dict["judge_incorrect"] = -int( | ||||||||||||||||||||||||||||||
| judgement.lower() == "b" | ||||||||||||||||||||||||||||||
| ) # negate incorrect so pass@k minimizes it | ||||||||||||||||||||||||||||||
| correctness_dict["judge_partially_correct"] = int(judgement.lower() == "c") | ||||||||||||||||||||||||||||||
| correctness_dict["judge_abstained"] = int(judgement.lower() == "d") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # keep counter of all entries where no answer is correct | ||||||||||||||||||||||||||||||
| correctness_dict["non_correct"] = -int(judgement.lower() in ["b", "c", "d"]) | ||||||||||||||||||||||||||||||
| return correctness_dict | ||||||||||||||||||||||||||||||
|
Comment on lines
+61
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's find the file and understand its structure
fd -p omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 108 🏁 Script executed: # Read the omni_metrics.py file to see the full context
wc -l nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 112 🏁 Script executed: # Read relevant sections of the file
head -150 nemo_skills/evaluation/metrics/omni_metrics.py | tail -100Repository: NVIDIA-NeMo/Skills Length of output: 4822 🏁 Script executed: # Look at the _compute_reward_at_k method around line 30
sed -n '20,50p' nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 1678 🏁 Script executed: # Check the full structure of the class and find base class
sed -n '1,100p' nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 4883 🏁 Script executed: # Search for _compute_pass_at_k in the file
grep -n "_compute_pass_at_k\|_compute_reward_at_k" nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 246 🏁 Script executed: # Find BaseMetrics class
fd -p math_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 108 🏁 Script executed: # Check the BaseMetrics class and _compute_pass_at_k method
cat nemo_skills/evaluation/metrics/math_metrics.py | head -200Repository: NVIDIA-NeMo/Skills Length of output: 6732 🏁 Script executed: # Find and read the base metrics class
fd -p base.py | grep evaluationRepository: NVIDIA-NeMo/Skills Length of output: 141 🏁 Script executed: # Read the base metrics file
cat nemo_skills/evaluation/metrics/base.py | head -250Repository: NVIDIA-NeMo/Skills Length of output: 10587 🏁 Script executed: # Search for _compute_pass_at_k implementation
grep -n "_compute_pass_at_k" nemo_skills/evaluation/metrics/base.pyRepository: NVIDIA-NeMo/Skills Length of output: 624 🏁 Script executed: # Read more of base.py to find _compute_pass_at_k
wc -l nemo_skills/evaluation/metrics/base.pyRepository: NVIDIA-NeMo/Skills Length of output: 104 🏁 Script executed: # Read the _compute_pass_at_k method from base.py
sed -n '200,350p' nemo_skills/evaluation/metrics/base.pyRepository: NVIDIA-NeMo/Skills Length of output: 6200 🏁 Script executed: # Read the _compute_pass_at_k method
sed -n '352,420p' nemo_skills/evaluation/metrics/base.pyRepository: NVIDIA-NeMo/Skills Length of output: 3433 🏁 Script executed: # Verify the get_metrics method and understand the issue better
sed -n '73,85p' nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 717 🏁 Script executed: # Check if there's any initialization or default values in eval_dict
grep -A 10 "def __init__" nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 597 🏁 Script executed: # Check the base class initialization
sed -n '25,35p' nemo_skills/evaluation/metrics/base.pyRepository: NVIDIA-NeMo/Skills Length of output: 525 🏁 Script executed: # Check the reset method to see how eval_dict is initialized
sed -n '435,445p' nemo_skills/evaluation/metrics/base.pyRepository: NVIDIA-NeMo/Skills Length of output: 591 🏁 Script executed: # Let's trace through exactly what happens - check how metrics are accessed
# Check if get_metrics handles missing keys gracefully
sed -n '35,55p' nemo_skills/evaluation/metrics/base.pyRepository: NVIDIA-NeMo/Skills Length of output: 1050 🏁 Script executed: # Let's verify the actual flow by checking OmniMetrics.update() completely
sed -n '94,102p' nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 448 🏁 Script executed: # Check what happens with defaultdict when accessing missing keys
python3 << 'EOF'
from collections import defaultdict
# This is how eval_dict is structured
eval_dict = defaultdict(lambda: defaultdict(float))
# Simulate what happens when no metrics are added
score_dicts = [{}] # Empty score dict
# This loop doesn't execute
for score_method in score_dicts[0].keys():
eval_dict[f"pass@1"]["method"] += 1
# Now try to access keys like get_metrics does
print("Keys in eval_dict:", dict(eval_dict))
print("Keys in eval_dict['pass@1']:", dict(eval_dict["pass@1"]))
# Check if accessing missing key raises error or returns default
try:
value = eval_dict["pass@1"]["judge_correct"]
print(f"Value for judge_correct: {value}")
except KeyError as e:
print(f"KeyError: {e}")
EOFRepository: NVIDIA-NeMo/Skills Length of output: 144 🏁 Script executed: # Let's test what happens in the actual OmniMetrics.get_metrics() flow
python3 << 'EOF'
from collections import defaultdict
# Simulate the flow
eval_dict = defaultdict(lambda: defaultdict(float))
# When no metrics are computed (empty score dicts), nothing is added
# Simulate calling get_metrics from base class
metrics_dict = {}
for agg_mode, agg_metric_dict in eval_dict.items():
metrics_dict[agg_mode] = {}
for metric_key, metric_value in agg_metric_dict.items():
if isinstance(metric_value, float):
metrics_dict[agg_mode][metric_key] = 100.0 * metric_value / 1 # assume total=1
print("After base get_metrics:", metrics_dict)
# Now OmniMetrics.get_metrics() tries to access keys
if metrics_dict: # Only if there are entries
for agg_method, agg_metric_dict in metrics_dict.items():
try:
correct = agg_metric_dict["judge_correct"]
print(f"Got judge_correct: {correct}")
except KeyError as e:
print(f"KeyError accessing judge_correct: {e}")
else:
print("metrics_dict is empty, so the loop won't even execute")
EOFRepository: NVIDIA-NeMo/Skills Length of output: 142 🏁 Script executed: # Let's check if there's always at least one agg_mode in eval_dict
grep -n "eval_dict\[" nemo_skills/evaluation/metrics/base.py | head -20Repository: NVIDIA-NeMo/Skills Length of output: 614 🏁 Script executed: # Let's trace through a realistic scenario - when _compute_pass_at_k is called with None predicted_answers
python3 << 'EOF'
from collections import defaultdict
# Simulate OmniMetrics scenario
eval_dict = defaultdict(lambda: defaultdict(float))
# Simulate _compute_pass_at_k with empty score_dicts[0] and predicted_answers=None
score_dicts = [{}] # Empty because no "judgement" field
predicted_answers = None
compute_no_answer = True
# The loop that depends on score_dicts[0]
for score_method in score_dicts[0].keys():
# This doesn't execute
eval_dict[f"pass@1"]["method"] += 1
pass
# The no_answer logic
for k in range(1, 2):
if predicted_answers is not None and compute_no_answer:
# This condition is False because predicted_answers is None
eval_dict[f"pass@{k}"]["no_answer"] += True
pass
# Check what's in eval_dict
print("eval_dict after _compute_pass_at_k:", dict(eval_dict))
print("Is eval_dict empty?", len(eval_dict) == 0)
# Now simulate get_metrics flow
metrics_dict = {}
for agg_mode, agg_metric_dict in eval_dict.items():
metrics_dict[agg_mode] = dict(agg_metric_dict)
print("metrics_dict from base get_metrics:", metrics_dict)
# Now OmniMetrics.get_metrics() tries to access
print("\nWould OmniMetrics.get_metrics() be called?")
if metrics_dict:
print(" Yes, and it would try to access judge_correct, judge_incorrect, etc.")
for agg_method, agg_metric_dict in metrics_dict.items():
try:
val = agg_metric_dict["judge_correct"]
print(f" Got {val}")
except KeyError as e:
print(f" KeyError: {e}")
else:
print(" No, metrics_dict is empty so the loop doesn't execute")
EOFRepository: NVIDIA-NeMo/Skills Length of output: 264 🏁 Script executed: # Test the actual problematic scenario - mixed predictions with and without "judgement"
python3 << 'EOF'
from collections import defaultdict
# Simulate mixed case - some predictions have judgement, others don't
def _get_score_dict_omni(prediction):
correctness_dict = {}
if "judgement" in prediction:
judgement = prediction["judgement"]
correctness_dict["judge_correct"] = int(judgement.lower() == "a")
correctness_dict["judge_incorrect"] = int(judgement.lower() == "b")
correctness_dict["judge_partially_correct"] = int(judgement.lower() == "c")
correctness_dict["judge_abstained"] = int(judgement.lower() == "d")
return correctness_dict
# Test scenario: some predictions have judgement, others don't
predictions = [
{"judgement": "A", "generation": "answer1"},
{"generation": "answer2"}, # Missing judgement
]
score_dicts = [_get_score_dict_omni(pred) for pred in predictions]
print("score_dicts:", score_dicts)
# Now try _compute_pass_at_k logic
try:
for score_method in score_dicts[0].keys():
print(f"Processing score_method: {score_method}")
# This is line 369 in _compute_pass_at_k
scores_list = [correctness_dict[score_method] for correctness_dict in score_dicts]
print(f" scores_list: {scores_list}")
except KeyError as e:
print(f"KeyError: {e}")
print(f"Trying to access '{e.args[0]}' in one of the score_dicts")
EOFRepository: NVIDIA-NeMo/Skills Length of output: 299 🏁 Script executed: # Check the update() method to see if there are any guards
sed -n '94,102p' nemo_skills/evaluation/metrics/omni_metrics.pyRepository: NVIDIA-NeMo/Skills Length of output: 448 🏁 Script executed: # Also check if OmniMetrics is used with consistent data
grep -r "OmniMetrics" nemo_skills/evaluation/ | grep -v ".pyc" | head -10Repository: NVIDIA-NeMo/Skills Length of output: 332 🏁 Script executed: # Check if there's any validation or guarantee that all predictions have consistent structure
grep -B 5 -A 10 "def _compute_pass_at_k" nemo_skills/evaluation/metrics/base.py | head -25Repository: NVIDIA-NeMo/Skills Length of output: 799 🏁 Script executed: # Test the exact error scenario again with the actual code
python3 << 'EOF'
# Reproduce the exact crash with line numbers from base.py
import math
from collections import defaultdict
# Simulate BaseMetrics._compute_pass_at_k
def _get_score_dict(prediction):
"""OmniMetrics version"""
correctness_dict = {}
if "judgement" in prediction:
judgement = prediction["judgement"]
correctness_dict["judge_correct"] = int(judgement.lower() == "a")
correctness_dict["judge_incorrect"] = int(judgement.lower() == "b")
correctness_dict["judge_partially_correct"] = int(judgement.lower() == "c")
correctness_dict["judge_abstained"] = int(judgement.lower() == "d")
return correctness_dict
def test_crash():
predictions = [
{"judgement": "A", "generation": "answer1"},
{"generation": "answer2"}, # Missing judgement - will cause crash
]
eval_dict = defaultdict(lambda: defaultdict(float))
score_dicts = [_get_score_dict(pred) for pred in predictions]
print(f"score_dicts = {score_dicts}")
print(f"score_dicts[0].keys() = {list(score_dicts[0].keys())}")
try:
# This is line 368 in base.py _compute_pass_at_k
for score_method in score_dicts[0].keys():
# This is line 369 in base.py
scores_list = [correctness_dict[score_method] for correctness_dict in score_dicts]
print(f"Success: {score_method} = {scores_list}")
except KeyError as e:
print(f"CRASH at line 369: KeyError accessing '{e.args[0]}' in score_dicts[1]")
return False
return True
test_crash()
EOFRepository: NVIDIA-NeMo/Skills Length of output: 355 KeyError crash when predictions have inconsistent "judgement" field. When some (but not all) predictions lack the scores_list = [correctness_dict[score_method] for correctness_dict in score_dicts]If Validate that all predictions in a batch contain the |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def get_metrics(self): | ||||||||||||||||||||||||||||||
| metrics = super().get_metrics() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| for agg_method, agg_metric_dict in metrics.items(): | ||||||||||||||||||||||||||||||
| correct, incorrect, part_correct, abstained = ( | ||||||||||||||||||||||||||||||
| agg_metric_dict["judge_correct"], | ||||||||||||||||||||||||||||||
| -agg_metric_dict["judge_incorrect"], # multiply negated judge_incorrect to get minimized incorrect pct | ||||||||||||||||||||||||||||||
| agg_metric_dict["judge_partially_correct"], | ||||||||||||||||||||||||||||||
| agg_metric_dict["judge_abstained"], | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| non_correct = -agg_metric_dict["non_correct"] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # convert pcts back to counts | ||||||||||||||||||||||||||||||
| if isinstance(correct, float): | ||||||||||||||||||||||||||||||
| correct *= self.total / 100 | ||||||||||||||||||||||||||||||
| if isinstance(incorrect, float): | ||||||||||||||||||||||||||||||
| incorrect *= self.total / 100 | ||||||||||||||||||||||||||||||
| if isinstance(part_correct, float): | ||||||||||||||||||||||||||||||
| part_correct *= self.total / 100 | ||||||||||||||||||||||||||||||
| if isinstance(abstained, float): | ||||||||||||||||||||||||||||||
| abstained *= self.total / 100 | ||||||||||||||||||||||||||||||
| if isinstance(non_correct, float): | ||||||||||||||||||||||||||||||
| non_correct *= self.total / 100 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # compute omni index between max correct and min incorrect (for pass@k) | ||||||||||||||||||||||||||||||
| metrics[agg_method]["judge_omni_index"] = 100 * (correct - incorrect) / self.total if self.total > 0 else 0 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # compute hallucination rate with min incorrect and min non_correct | ||||||||||||||||||||||||||||||
| metrics[agg_method]["judge_omni_hallucination"] = 100 * incorrect / non_correct if non_correct > 0 else 0 | ||||||||||||||||||||||||||||||
| return metrics | ||||||||||||||||||||||||||||||
|
Comment on lines
+76
to
+105
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential ZeroDivisionError in metric calculations. Two division operations can fail:
This will crash metrics computation in edge cases (empty data or perfect scores). Suggested fix with guards def get_metrics(self):
metrics = super().get_metrics()
for agg_method, agg_metric_dict in metrics.items():
correct, incorrect, part_correct, abstained = (
agg_metric_dict["judge_correct"],
agg_metric_dict["judge_incorrect"],
agg_metric_dict["judge_partially_correct"],
agg_metric_dict["judge_abstained"],
)
- metrics[agg_method]["judge_omni_index"] = (
- 100 * (correct - incorrect) / (correct + incorrect + part_correct + abstained)
- )
- metrics[agg_method]["judge_omni_hallucination"] = 100 * incorrect / (incorrect + part_correct + abstained)
+ total = correct + incorrect + part_correct + abstained
+ non_correct_total = incorrect + part_correct + abstained
+
+ metrics[agg_method]["judge_omni_index"] = (
+ 100 * (correct - incorrect) / total if total > 0 else 0.0
+ )
+ metrics[agg_method]["judge_omni_hallucination"] = (
+ 100 * incorrect / non_correct_total if non_correct_total > 0 else 0.0
+ )
return metrics |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def get_incorrect_sample(self, prediction: dict) -> dict: | ||||||||||||||||||||||||||||||
| copy_prediction = prediction.copy() | ||||||||||||||||||||||||||||||
| if "judgement" in prediction: | ||||||||||||||||||||||||||||||
| copy_prediction["judgement"] = "B" | ||||||||||||||||||||||||||||||
| copy_prediction["judge_correct"] = 0 | ||||||||||||||||||||||||||||||
| copy_prediction["judge_incorrect"] = -1 | ||||||||||||||||||||||||||||||
| copy_prediction["judge_partially_correct"] = 0 | ||||||||||||||||||||||||||||||
| copy_prediction["judge_abstained"] = 0 | ||||||||||||||||||||||||||||||
| copy_prediction["non_correct"] = -1 | ||||||||||||||||||||||||||||||
| return copy_prediction | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def update(self, predictions): | ||||||||||||||||||||||||||||||
| super().update(predictions) | ||||||||||||||||||||||||||||||
| self._compute_pass_at_k(predictions, None) | ||||||||||||||||||||||||||||||
| if "reward_model_score" in predictions[0]: | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
|
||||||||||||||||||||||||||||||
| self._compute_reward_at_k(predictions=predictions) | ||||||||||||||||||||||||||||||
|
Comment on lines
+118
to
+122
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IndexError risk with empty predictions. Line 99 accesses Suggested fix def update(self, predictions):
super().update(predictions)
+ if not predictions:
+ return
self._compute_pass_at_k(predictions, None)
if "reward_model_score" in predictions[0]:
self._compute_reward_at_k(predictions=predictions)🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # print the same evaluations/metrics as math but ignoring majority/rm since that doesn't really exist with omniscience | ||||||||||||||||||||||||||||||
| def evaluations_to_print(self): | ||||||||||||||||||||||||||||||
| return [ | ||||||||||||||||||||||||||||||
| f"pass@1[avg-of-{self.max_k}]", | ||||||||||||||||||||||||||||||
| f"pass@{self.max_k}", | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def metrics_to_print(self): | ||||||||||||||||||||||||||||||
| metrics_to_print = { | ||||||||||||||||||||||||||||||
| "num_entries": as_int, | ||||||||||||||||||||||||||||||
| "avg_tokens": as_int, | ||||||||||||||||||||||||||||||
| "gen_seconds": as_int, | ||||||||||||||||||||||||||||||
| "judge_correct": as_percentage, | ||||||||||||||||||||||||||||||
| "judge_omni_index": as_percentage, | ||||||||||||||||||||||||||||||
| "judge_omni_hallucination": as_percentage, | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| if self.compute_no_answer: | ||||||||||||||||||||||||||||||
| metrics_to_print["no_answer"] = as_percentage | ||||||||||||||||||||||||||||||
| return metrics_to_print | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # https://huggingface.co/datasets/ArtificialAnalysis/AA-Omniscience-Public | ||
|
|
||
| system: |- | ||
| You are answering questions about {domain}, and in particular {topic}. You will be given a question, answer with JUST the answer (no explanation). If you do not know the answer, or you need more context or tools to answer the question, be clear about this - it is better that you say this than get the wrong answer. | ||
|
|
||
| user: |- | ||
| {question} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copyright year is
2026(future year)