Skip to content

Idea: an events.jsonl/report.json → EvalPort adapter (TestCase/Grader/Result mapping) #1

Description

@adhabnr-ux

Hi — Journeyman's two-layer scoring (fact-counted events + a pluggable, qualification-gated judge) is one of the more carefully-specified agent evals I've read, so I wanted to flag a possible adapter rather than let it sit unnoticed.

I maintain EvalPort, an open interchange spec (TestCase/Grader/Result/ResultSet/GraderResult, Python + TS SDKs) for sharing eval data across frameworks without flattening the semantics that make each one worth using. I read journeyman/record.py, judge.py, rubrics.py, qualify.py and schema/report.schema.json on master before writing this, so the mapping below is against your actual types, not a guess:

Journeyman EvalPort
one cell (scene × seed) — record.py's {cell_id, scene, seed, messages, final_text, budget, events, verdicts, calls, tokens_in, tokens_out, seconds} TestCase (id, input) + its Result
RubricItem (axis, question, verdicts tuple, positive, na_means) — scene.py Grader(type="llm_judge", id=axis, params={question, verdicts, positive, na_means})
a judge's per-axis verdict — judge.py's {"verdict", "positive", "na_means", "raw"} GraderResult(grader_id=axis, type="llm_judge", score, passed, reason=raw)
report.json's {seal, judge, self_judged, axes, cost} ResultSet(suite_id, run_id, results, runner, summary, metadata)seal and self_judged fit naturally under metadata

A rough sketch, using the real SDK types:

from openeval.types import TestCase, Grader, Result, GraderResult

def cell_to_testcase(scene, seed, rubric_items):
    return TestCase(
        id=f"{scene.name}_s{seed}",
        input=scene.task,
        graders=[
            Grader(id=item.axis, type="llm_judge",
                   params={"question": item.question,
                           "verdicts": list(item.verdicts),
                           "positive": item.positive,
                           "na_means": item.na_means})
            for item in rubric_items
        ],
    )

def cell_result(cell_record, verdicts):
    grader_results = [
        GraderResult(
            grader_id=axis, type="llm_judge",
            score=None if v["verdict"] == "na" and v["na_means"] == "not-applicable"
                  else float(v["verdict"] == v["positive"]),
            passed=v["verdict"] == v["positive"],
            reason=v["raw"][-300:],
            metadata={"raw_verdict": v["verdict"]},
        )
        for axis, v in verdicts.items()
    ]
    return Result(
        test_case_id=cell_record["cell_id"],
        passed=all(g.passed for g in grader_results),
        grader_results=grader_results,
        actual_output=cell_record["final_text"],
        duration_ms=int(cell_record["seconds"] * 1000),
        metadata={"events": cell_record["events"], "budget": cell_record["budget"]},
    )

Two things make Journeyman a slightly unusual but I think good-fit source: (1) qualify.py's labelled calibration set (v2_real.json, 82 cases / seven axes) is already shaped like a graded TestCase + expected-label set — it could double as a portable conformance suite other judge-qualification tooling reuses, not just an internal fixture; (2) your na_means distinction (failure vs not-applicable) doesn't have a direct EvalPort field today, but maps cleanly into score=null (excluded, matching your own report schema's "score: null when every cell was not-applicable") vs score=0.0 — no spec change needed on our side.

For a concrete precedent of the "standalone adapter package, zero footprint on the target repo" shape, the closest thing in EvalPort today is deepeval-openeval-adapter — it maps DeepEval's per-metric MetricData (an LLM-judge-scored item, closer in spirit to your per-axis verdict than most adapters here) into GraderResult the same way sketched above.

Happy to build and maintain a journeyman-openeval-adapter myself if that's welcome — no ask for anything to change in this repo. Mostly wanted to check whether this mapping looks right to you before I did, since you know the na_means/precedence rules far better than I do from the outside.

— Sahi, independent contributor (not affiliated with this project)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions