Repository navigation
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes incorrect sample totals when importing eval logs without a results block.
Changes:
- Counts recorded samples when results are absent.
- Preserves result-provided counts when present.
- Adds converter and PostgreSQL integration coverage.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
hawk/hawk/core/importer/eval/converter.py |
Adds fallback sample counting. |
hawk/tests/core/importer/eval/test_converter.py |
Tests count selection behavior. |
hawk/tests/core/importer/eval/test_writer_postgres.py |
Tests persisted counts and sample rows. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
legion-implementer Bot
pushed a commit
to sjawhar/hawk
that referenced
this pull request
Sep 3, 2026
…from the actual parsed file Addresses METR#1426 Copilot review findings: - completed_samples was set to the full recorded-sample count, which reports every recorded sample as completed even when some errored. Derive it from summaries marked completed (mirroring eval_status._is_completed) that do not carry an error, per the Eval.completed_samples contract ("samples completed without error"). - the no-results sample count was read from the persisted location (location_override for S3 imports), not the file EvalConverter is actually parsing -- defeating the S3 download optimization and risking a stale/replaced-object mismatch. Read from EvalConverter.eval_source via a new sample_source parameter instead. Omp-Session: 01a05ad7-ca0d-7000-a3a3-7187810518dd
legion-implementer
Bot
force-pushed
the
fix/eval-log-importer-sample-count-without-results
branch
from
September 3, 2026 02:48
d33a102 to
f41804d
Compare
legion-implementer Bot
pushed a commit
to sjawhar/hawk
that referenced
this pull request
Sep 5, 2026
…from the actual parsed file Addresses METR#1426 Copilot review findings: - completed_samples was set to the full recorded-sample count, which reports every recorded sample as completed even when some errored. Derive it from summaries marked completed (mirroring eval_status._is_completed) that do not carry an error, per the Eval.completed_samples contract ("samples completed without error"). - the no-results sample count was read from the persisted location (location_override for S3 imports), not the file EvalConverter is actually parsing -- defeating the S3 download optimization and risking a stale/replaced-object mismatch. Read from EvalConverter.eval_source via a new sample_source parameter instead. Omp-Session: 01a05ad7-ca0d-7000-a3a3-7187810518dd
legion-implementer
Bot
force-pushed
the
fix/eval-log-importer-sample-count-without-results
branch
from
September 5, 2026 16:14
f41804d to
5420f38
Compare
legion-implementer Bot
pushed a commit
to sjawhar/hawk
that referenced
this pull request
Sep 8, 2026
…from the actual parsed file Addresses METR#1426 Copilot review findings: - completed_samples was set to the full recorded-sample count, which reports every recorded sample as completed even when some errored. Derive it from summaries marked completed (mirroring eval_status._is_completed) that do not carry an error, per the Eval.completed_samples contract ("samples completed without error"). - the no-results sample count was read from the persisted location (location_override for S3 imports), not the file EvalConverter is actually parsing -- defeating the S3 download optimization and risking a stale/replaced-object mismatch. Read from EvalConverter.eval_source via a new sample_source parameter instead. Omp-Session: 01a05ad7-ca0d-7000-a3a3-7187810518dd
legion-implementer
Bot
force-pushed
the
fix/eval-log-importer-sample-count-without-results
branch
from
September 8, 2026 20:18
5420f38 to
7160d9f
Compare
An eval log without a `results` block (e.g. one written before scoring finished, or a hand-repaired eval that never got one back) persisted total_samples=0 and completed_samples=0 on the eval row, even though the importer went on to write every sample row from the log. The warehouse row and its own sample rows silently disagreed, and 'hawk list evals' showed 0/0 for evals that genuinely held hundreds of samples. When results is present its numbers are still trusted verbatim (this can legitimately differ from the raw sample count, e.g. a resumed run). When it is absent, count the samples the importer will actually write (the same sample_summaries the write loop iterates) instead of persisting 0.
…from the actual parsed file Addresses METR#1426 Copilot review findings: - completed_samples was set to the full recorded-sample count, which reports every recorded sample as completed even when some errored. Derive it from summaries marked completed (mirroring eval_status._is_completed) that do not carry an error, per the Eval.completed_samples contract ("samples completed without error"). - the no-results sample count was read from the persisted location (location_override for S3 imports), not the file EvalConverter is actually parsing -- defeating the S3 download optimization and risking a stale/replaced-object mismatch. Read from EvalConverter.eval_source via a new sample_source parameter instead. Omp-Session: 01a05ad7-ca0d-7000-a3a3-7187810518dd
sjawhar-agent
Bot
force-pushed
the
fix/eval-log-importer-sample-count-without-results
branch
from
September 24, 2026 04:49
7160d9f to
40dbd23
Compare
Resolve the two conflicts in hawk/core/importer/eval/converter.py: - keep main's _model_event_names helper next to this branch's _is_sample_completed/_count_recorded_samples helpers; - EvalConverter.parse_eval_log passes both this branch's sample_source and main's new file_metadata to build_eval_rec_from_log. The test files merged without conflicts. Omp-Session: 01a081d4-2514-7000-b8e2-ff8548776146
Live sample import added two paths that build the eval record of a running eval on every poll: the header-only parse for buffer syncs (parse_eval_log_header_only) and the per-file import of an eval whose status is still "started". A running eval has no results block yet, so the no-results fallback in build_eval_rec_from_log re-read every recorded sample summary on each of those polls, including on the header-only path, which exists to skip per-sample work. Count only for a finished log, and only when the caller passes sample_source, the file it is actually parsing. EvalConverter.parse_eval_log passes it, so a finished results-less import still gets its real counts; a running eval and the header-only parse keep 0, which the import after the eval finishes overwrites. Counting also no longer falls back to eval_source, the persisted location. Red on the merge parent, green with this change: test_header_only_does_not_count_samples_when_results_absent (read_log_sample_summaries called once by the header-only parse) and test_converter_leaves_running_eval_counts_to_the_final_import (total_samples 4 for a running eval). Omp-Session: 01a081d4-2514-7000-b8e2-ff8548776146
rasmusfaber
requested changes
Oct 5, 2026
| for summary in sample_summaries | ||
| if _is_sample_completed(summary) and not summary.error | ||
| ) | ||
| return len(sample_summaries), completed_samples |
Contributor
There was a problem hiding this comment.
total_samples is documented to be the planned count of samples. This instead stores the recorded count.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
When an eval log has no
resultsblock, the importer wrotetotal_samples=0andcompleted_samples=0on theevalrow while still writing every sample row, sohawk list evalsshowed0/0for finished evals whose samples were all imported. This PR counts the written samples instead when a finished log has noresultsblock.Approach
A present
resultsblock is still trusted; it can legitimately differ from the file (a resumed run, say). Without one, the importer counts the log's recorded sample summaries, the list its sample loop iterates.completed_samplescredits only samples marked complete (or withcompleted_aton older logs) and without anerror.The count reads the file being parsed, not
location_override. For an S3 import that is the downloaded copy, so a replaced object cannot make the count disagree with the rows written.Live polls do not count: a running eval has no
resultsyet and is parsed on every poll (the header-only buffer-sync parse and the per-file import), so counting would re-read every sample summary each time. Its row keeps 0 until the import after it finishes.Updating the row after the sample loop instead would leave it wrong until the loop ends; counting up front keeps it right from the first write.
Risks
0/0are not backfilled; a forced re-import rewrites them.started(a run killed before it finished) still shows0/0.Testing & validation
New converter tests cover a present
resultsblock winning, counting without one, errored samples, counting from the parsed file rather thanlocation_override, and live polls not counting. A Postgres test checks that the persisted counts equal thesamplerows written.uv run pytest tests/core/importer/eval -n auto: 414 passed.Verified the change works (commands / manual steps described above)
Added or updated tests where it makes sense
Code quality
pre-commit run --all-filespasses (ruff, basedpyright/mypy, eslint/prettier/tsc, shellcheck — what CI's Lint job runs)Before merging