fix(dspy): skip FailedPrediction steps in BootstrapFinetune finetune data prep - #87
Conversation
Greptile SummaryThis PR prevents
Confidence Score: 4/5This PR is not yet safe to merge because its all-failed path still launches fine-tuning with no training records and can abort compilation. The adapter crash is prevented, but when every captured step fails parsing, the replacement path forwards an empty dataset to a real fine-tuning backend; the new compile test passes only because that backend boundary is mocked. Files Needing Attention: dspy/teleprompt/bootstrap_finetune.py, tests/teleprompt/test_bootstrap_finetune.py Important Files Changed
Reviews (1): Last reviewed commit: "fix(dspy): skip FailedPrediction steps i..." | Re-trigger Greptile |
| if isinstance(trace_instance[2], FailedPrediction): | ||
| logger.debug( | ||
| "Skipping a trace step with a FailedPrediction output " | ||
| "(unparseable LM response) while preparing finetune data." | ||
| ) | ||
| continue |
There was a problem hiding this comment.
When every trace step is a FailedPrediction, this guard removes every training record, but compile still passes the empty group to finetune_lms. The real path then calls lm.finetune(train_data=[]); local fine-tuning cannot construct a dataset without samples, and remote providers reject empty training files, so compilation still fails. The new all-failed test does not catch this because it mocks finetune_lms while confirming that the submitted train_data is empty. Empty groups need to be handled before fine-tuning is launched.
Knowledge Base Used:
Warning
GitHub issue creation failed
Detail attempted to publish this bug to GitHub, but the issue could not be created. This fix PR was created without that issue, and missing tracker references are shown as
Unknown issue.You can review and merge this PR normally. Please review your tracker integration settings before the next publish run.
Detail bug report: View on Detail
📝 Changes Description
Closes Unknown issue
Bug:
BootstrapFinetune.compilecrashes withAttributeError: 'FailedPrediction' object has no attribute 'get'whenever any bootstrapped trace step contains an unparseable LM response.bootstrap_trace_datacatchesAdapterParseErrorand appends aFailedPredictionas the trace tuple's third element, butBootstrapFinetune._prepare_finetune_dataforwarded thatFailedPredictionstraight intoadapter.format_finetune_data, which callsoutputs.get(...). SinceFailedPredictionhas no.get, compilation aborts before fine-tuning even starts — on any model prone to malformed outputs (e.g. small/open models). The existing metric-score filter does not protect against this, because a failed parse still gets a truthy score (format_reward or format_failure_score, e.g.-1), so the failed entry is retained.Fix: Skip trace steps whose output is a
FailedPredictionbefore callingbuild_call_data_from_trace/adapter.format_finetune_datain_prepare_finetune_data, mirroring the defensive guards already present in the sibling finetune teleprompters (grpo.py,gepa_utils.py). Failed steps are skipped while successfully-parsed steps are preserved for fine-tuning. This guards against bothFailedPrediction-producing paths inbootstrap_trace.py(theAdapterParseErrorpath and the generic-crashcapture_crashespath), soBootstrapFinetuneis robust regardless of which trace source emits them.Testing: Verified with
DummyLM-based unit and end-to-end tests (the existing test suite usesDummyLM, which always emits adapter-parseable output, so it never reached the failing path — which is why CI missed the bug):FailedPredictiontrace step is skipped while valid steps are preserved, including when a truthy metric score would otherwise retain the failed entry, and with the realChatAdapter(the actual crash site).compile()test that forces anAdapterParseErrorduring bootstrapping (the reported reproduction) and confirms the run completes instead of crashing.ChatAdapterandXMLAdapter(both calloutputs.get(...)); both now skip the failed step and keep the valid one. (JSONAdapter.format_finetune_dataraisesNotImplementedErrorunconditionally and is not a viableBootstrapFinetuneadapter regardless of this fix.)ruff checkreports only two pre-existing warnings (both in untouched lines of the test file).Authored by Detail: Automatic Fixes (AI-assisted). I verified the bug by reproducing the crash before the fix and confirming it is resolved after, and I wrote and understand every line of the change and its tests.
✅ Contributor Checklist
fix(dspy): ..., matching the PR-title regex)fix(dspy): skip FailedPrediction steps in BootstrapFinetune finetune data prepNone. The change is additive and scoped to
dspy/teleprompt/bootstrap_finetune.py; the sharedbootstrap_trace.pyemission logic is untouched, so GRPO/GEPA behavior is unaffected. A separate latent defect — thecapture_failed_parsesflag inbootstrap_trace.pyis declared but never wired into theAdapterParseErrorhandler — is intentionally left out of scope (this fix makesBootstrapFinetunerobust regardless of that flag's behavior).Automatic Fixes PRs can be configured here.