Skip to content

feat(agent): --distill flag recording LLM calls as a ChatML NDJSON fine-tuning dataset - #136

Merged
SkyeAv merged 1 commit into
mainfrom
feat/agent-distill
Sep 3, 2026
Merged

feat(agent): --distill flag recording LLM calls as a ChatML NDJSON fine-tuning dataset#136
SkyeAv merged 1 commit into
mainfrom
feat/agent-distill

Conversation

@SkyeAv

@SkyeAv SkyeAv commented Sep 3, 2026

Copy link
Copy Markdown
Owner

What

Adds a boolean --distill (-d, -dt) flag to tablassert agent that records every LLM call of the run — inner CodeAgent conversations, plus judge and tier-2 reflexion when enabled — as one ChatML JSON object per line in <state-dir>/distill/records.ndjson.

The dataset is append-only across invocations, so a fine-tuning corpus accumulates run over run, and the messages column is plain ChatML, which Unsloth Studio auto-detects on JSONL upload (verified against unsloth.ai docs) — no column mapping or standardize_sharegpt step.

Record format

{"messages": [{"role": "system", ...}, ..., {"role": "assistant", ...}],
 "purpose": "agent|judge|reflexion", "model_id": "...", "pmc_id": "PMC...",
 "call_index": 0, "timestamp": "...", "token_usage": {...}}

pmc_id joins against state.json to keep only MAPPED runs as training targets; call_index lets you keep each run's final (most complete) conversation.

How

  • Capture seam: agent.make_distilling_model wraps model.generate (modeled on the existing FakeModel) — one central seam covering agent, judge, and reflexion; recording failures log and never break a batch.
  • Zero-dependency recorder: tablassert.distill.DistillRecorder (pure JSONL append).
  • New [distill] extra (datasets) gating the new tablassert distill-export --distill-dir ... --out ... command, which converts the NDJSON to an on-disk HF dataset (save_to_disk).
  • --distill is rejected with --optimize (the GEPA path bypasses the recording seam).

Tests & docs

  • tests/test_distill.py (12 tests: serializers, append-across-invocations, never-raises, wrapper over FakeModel, HF export round-trip), CLI flag/forwarding tests, and a FakeModel-driven supervisor end-to-end test.
  • Docs: docs/cli.md (flag table + distill-export section), docs/agent.md ("Distilling a fine-tuning dataset"), docs-coverage mapping updated.
  • Full suite: 1192 passed, ruff + pyright clean.

Note

tests/test_agent_eval.py::test_gepa_metric_returns_prediction fails in a venv with dspy installed — pre-existing and unrelated (gepa_metric/quality_score are byte-identical to HEAD; the test was always skipped before because the optimize extra was not installed locally). Worth a separate fix.

…ne-tuning dataset

Add a boolean --distill (-d, -dt) flag to tablassert agent that appends
every LLM call of the run (inner agent, judge, reflexion) as one ChatML
record per line to <state-dir>/distill/records.ndjson. The file is
append-only across invocations so a corpus accumulates run over run, and
loads directly in Unsloth Studio / datasets.load_dataset("json").

- tablassert.distill: zero-dependency DistillRecorder + ChatML serializers;
  recording never raises into a batch
- agent.make_distilling_model: Model wrapper around generate() (the single
  capture seam), tagged per-article with pmc_id for state.json filtering
- tablassert distill-export: convert the NDJSON to an on-disk HF dataset
  (new [distill] extra shipping datasets)
- docs (cli.md, agent.md) + docs-coverage mapping; CLI/recorder/wrapper/
  supervisor end-to-end tests
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 98cc2681-0d78-4caa-a498-6105b248cbcb


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@SkyeAv
SkyeAv merged commit 3603031 into main Sep 3, 2026
4 of 5 checks passed
@SkyeAv
SkyeAv deleted the feat/agent-distill branch September 3, 2026 22:16
SkyeAv added a commit that referenced this pull request Sep 4, 2026
Cut 16.6.0 and bump the package version in pyproject.toml, uv.lock,
and CITATION.cff.

Minor: two capabilities and one activated release filter. The agent
gains --distill (-d / -dt): every LLM call of a run (inner agent,
judge, reflexion) is appended as one ChatML record per line to
<state-dir>/distill/records.ndjson, tagged with the article's
pmc_id, for fine-tuning in Unsloth Studio or via
datasets.load_dataset("json"); distill-export converts the corpus
to an on-disk HF dataset behind the new [distill] extra (#136).
uuid_on_collision: merge now recomputes number_of_cases as the exact
union of a build-internal supporting_case_ids carrier when present,
replacing first-wins under-reporting and its scalar-conflict report,
and strips the carrier before write in both dedup paths (916f509).
The release-mode applied_to_treat filter is renamed
drop_low_number_of_cases and now reads the number_of_cases column
the pipeline actually emits; the old case_count column never
survived coercion to the significance phase, so the filter had been
a silent no-op since 16.1.0 — release builds now actually drop
treatment edges backed by fewer than 25 cases (#137). Also repairs
a stale gepa_metric test fixture that CI's missing dspy extra had
masked (2dcfcb3, tests only).

Changelog:
- Versioned the Unreleased section as 16.6.0 - 2026-09-04.
- Added the missing --distill entry under Added with its PR link
  (#136).
- Added the missing drop_low_number_of_cases entry under Fixed with
  its PR link (#137), stating the prior no-op honestly.
- Kept the supporting_case_ids union entry; it was committed
  directly to main and carries no PR link.
- Normalized that entry's double-hyphen to the house em dash.

Docs: docs/configuration/graph.md documents the number_of_cases
exception to merge first-wins (8b669d8); #136 already updated
docs/cli.md and docs/agent.md.

Testing:
- uv run pytest -q -> 1197 passed, 3 skipped (96% coverage)
- uv run ruff check . && uv run ruff format --check . && uv run pyright -> clean / 0 errors
- uv lock --check -> up to date
- uv run mkdocs build --strict -> clean
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant