Eval Datasets
Load JSONL task sets and export passing runs as fine-tuning data
Eval Datasets loads a task set from JSONL, then turns the runs that scored well into a fine-tuning file โ closing the loop from task set to trained model.
JSONL sits at both ends โ the format Agno, OpenAI’s fine-tuning API, and every dataset tool already speak โ so a task set is portable in and the training file is portable out.
Quick Start
- Load a task set
Read a JSONL file into
EvalCaseobjects.python from praisonaiagents.eval import load_cases cases = load_cases("tasks.jsonl") - Run the suite on the loaded cases
Run the cases and get an
EvalReport.python report = my_runner.run(cases) - Export what passed
Write the passing runs as chat-format training data.
python from praisonaiagents.eval import export_sft export_sft(report, "train.jsonl", min_score=0.8)
Input Format
Each line is one JSON object describing a task.
The minimal shape carries a name, an input, and an expected answer.
{"name": "add", "input": "2+2?", "expected": "4"}
{"name": "capital", "input": "capital of France?", "expected": "Paris"}Aliases are accepted and unrecognised columns fold into metadata.
{"prompt": "2+2?", "answer": "4", "difficulty": "easy", "source": "sheet1"}Blank lines and // comments are skipped.
// Arithmetic set
{"input": "2+2?", "expected": "4"}
{"input": "3+3?", "expected": "6"}Output Format
Export writes one chat-format JSON object per line โ the shape OpenAI’s fine-tuning API expects.
{"messages":[{"role":"user","content":"2+2?"},{"role":"assistant","content":"4"}]}Pass system_prompt to prepend a system message to every row.
{"messages":[{"role":"system","content":"Be terse."},{"role":"user","content":"2+2?"},{"role":"assistant","content":"4"}]}The prompt is read from result.record["input"]; the completion is result.actual_output.
Train on this JSONL directly
praisonai-train llm understands the messages shape natively โ point a dataset entry at the exported file, no column rename needed.
# train.yaml
model_name: "unsloth/gemma-2-2b-it-bnb-4bit"
max_seq_length: 2048
dataset:
- name: "train.jsonl" # the file export_sft wrotepraisonai-train llm train.yamlThe trainer renames messages โ conversations internally and formats each row with the model’s chat template. See Supported dataset shapes.
How It Works
load_cases maps aliases, folds extra columns into metadata, and auto-names rows; export_sft keeps only passing runs at or above min_score.
Aliases map friendly column names onto the case fields.
| Field | Accepted aliases |
|---|---|
input | prompt, question, query, instruction |
expected | expected_output, answer, output, completion |
Behaviour Worth Knowing
The loader is forgiving where it costs nothing and strict where it matters.
- Unrecognised columns fold into
metadatainstead of being dropped, sosource/difficulty/idsurvive into the report. - Missing
nameauto-populates ascase_1,case_2, โฆ by position among data rows. - Blank lines and
//comment lines are skipped; the physical file line is preserved, sofile:900really is line 900 in the editor. - A malformed line is reported by
file:line, not a generic “invalid JSON”. - A non-object line (e.g.
[1, 2, 3]) is refused with “must be a JSON object”. - An empty file raises
DatasetErrorโ an empty task set would otherwise report a perfect pass rate over nothing. - Only
passed=Trueruns withscore >= min_scoreare exported. - A run missing either a prompt or a completion is skipped, never silently shrinking the set.
- A zero-record export raises
DatasetErrorrather than writing an empty file that looks like success. DatasetErrorsubclassesValueError, so existingexcept ValueErrorhandlers still catch it.
Python API
Import the five public helpers from the top-level eval package.
from praisonaiagents.eval import (
load_cases,
iter_jsonl,
export_sft,
sft_records,
DatasetError,
)load_cases
load_cases(path, *, case_cls=None) -> List[EvalCase] reads a JSONL task set into EvalCase objects.
| Param | Type | Default | Description |
|---|---|---|---|
path | str | required | Path to the .jsonl file |
case_cls | type | EvalCase | Override the case class (advanced) |
Raises: DatasetError on missing file, malformed line, non-object line, or empty file โ with file:line when the fault is inside the file.
iter_jsonl
iter_jsonl(path) -> Iterator[Dict[str, Any]] yields one dict per data line, with the same error handling but no aliasing or metadata folding.
| Param | Type | Default | Description |
|---|---|---|---|
path | str | required | Path to the .jsonl file |
export_sft
export_sft(report, path, *, min_score=1.0, system_prompt=None) -> int writes passing runs to path and returns the count written.
| Param | Type | Default | Description | |
|---|---|---|---|---|
report | EvalReport or List[EvalResult] | required | Anything exposing .results, or a bare list | |
path | str | required | Destination .jsonl (parent dirs are created) | |
min_score | float | 1.0 | Only runs with score >= min_score are exported | |
system_prompt | `str \ | None` | None | Prepended as a system message when set |
Raises: DatasetError if no run scored high enough, or if report is not an EvalReport / list.
sft_records
sft_records(report, *, min_score=1.0, system_prompt=None) -> List[Dict[str, Any]] applies the same selection logic but returns the records in memory instead of writing them.
| Param | Type | Default | Description | |
|---|---|---|---|---|
report | EvalReport or List[EvalResult] | required | Anything exposing .results, or a bare list | |
min_score | float | 1.0 | Only runs with score >= min_score are selected | |
system_prompt | `str \ | None` | None | Prepended as a system message when set |
DatasetError
DatasetError(ValueError) is raised for every dataset read or write failure. As a ValueError subclass, existing except ValueError handlers still catch it.
Common Patterns
Portable in, portable out
Load a Hugging Face-exported JSONL with prompt / answer columns and export a file OpenAI’s fine-tuning API accepts unchanged.
from praisonaiagents.eval import load_cases, export_sft
cases = load_cases("hf_export.jsonl")
report = my_runner.run(cases)
export_sft(report, "train.jsonl", min_score=0.8)Lower the bar for a small dataset
Keep more attempts when the suite is small by lowering min_score โ a deliberate trade against SFT quality.
from praisonaiagents.eval import export_sft
export_sft(report, "train.jsonl", min_score=0.5)Persist a system prompt with each training row
Pass system_prompt= so the fine-tuned model learns the same persona the eval was scored under.
from praisonaiagents.eval import export_sft
export_sft(report, "train.jsonl", system_prompt="Be terse.")Best Practices
Fold provenance columns into metadata, don't strip them
The loader keeps source / difficulty / id automatically โ leave them in your JSONL so the report carries where each case came from.
Set min_score above your CI gate, not equal to it
A run that barely passed is a fragile teacher. Export above your gate so training data reflects confident wins, not marginal ones.
Never edit a training file by hand
Regenerate train.jsonl from the eval report instead of editing it, so provenance stays honest and reproducible.
Keep // comment lines in the source dataset
Comment lines are skipped at load time and make the file self-documenting โ describe each section inline without breaking the loader.