From 970a029108d2606c9005becf958909675f97f9cc Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Tue, 10 Mar 2026 19:50:53 +0000 Subject: [PATCH 1/4] fix: stream HF dataset when manual slice is specified Instead of downloading the full dataset and then slicing, use streaming mode to only fetch the rows needed (up to slice_end + 1) when a manual dataset slice is configured. --- studio/backend/core/training/trainer.py | 27 ++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/studio/backend/core/training/trainer.py b/studio/backend/core/training/trainer.py index 31e05d0bda..0c3aaa6aee 100644 --- a/studio/backend/core/training/trainer.py +++ b/studio/backend/core/training/trainer.py @@ -1866,10 +1866,31 @@ class UnslothTrainer: elif dataset_source: # Load from Hugging Face - load_kwargs = {"path": dataset_source, "split": train_split or "train"} + split_name = train_split or "train" + load_kwargs = {"path": dataset_source, "split": split_name} if subset: load_kwargs["name"] = subset - dataset = load_dataset(**load_kwargs) + + if dataset_slice_end is not None: + # Manual slice — stream only the rows we need instead of + # downloading the entire dataset. + rows_to_stream = dataset_slice_end + 1 + print( + f"[dataset-slice] Manual slice specified " + f"(start={dataset_slice_start}, end={dataset_slice_end}), " + f"streaming {rows_to_stream} rows\n" + ) + stream = load_dataset(**load_kwargs, streaming=True) + dataset = Dataset.from_list(list(stream.take(rows_to_stream))) + print( + f"[dataset-slice] Downloaded {len(dataset)} rows " + f"(requested {rows_to_stream})\n" + ) + self._update_progress( + status_message=f"Streamed {len(dataset)} rows from HuggingFace" + ) + else: + dataset = load_dataset(**load_kwargs) # Check if stopped during dataset loading if self.should_stop: @@ -1877,7 +1898,7 @@ class UnslothTrainer: return None self._update_progress(status_message=f"Loaded dataset from HuggingFace: {dataset_source}") - print(f"Loaded dataset from Hugging Face: {dataset_source}\n") + print(f"Loaded dataset from Hugging Face: {dataset_source} ({len(dataset)} rows)\n") # Resolve eval split from a separate HF split (explicit or auto-detected) if eval_enabled: From 226f251589f9efa41afbcb7685b1f9b4400ce0a9 Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Tue, 10 Mar 2026 20:12:42 +0000 Subject: [PATCH 2/4] fix: guard against negative dataset_slice_end before streaming Fall back to full download when dataset_slice_end is negative, avoiding an empty stream.take(0) that would produce a broken dataset. --- studio/backend/core/training/trainer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/backend/core/training/trainer.py b/studio/backend/core/training/trainer.py index 0c3aaa6aee..c72a54af00 100644 --- a/studio/backend/core/training/trainer.py +++ b/studio/backend/core/training/trainer.py @@ -1871,7 +1871,7 @@ class UnslothTrainer: if subset: load_kwargs["name"] = subset - if dataset_slice_end is not None: + if dataset_slice_end is not None and dataset_slice_end >= 0: # Manual slice — stream only the rows we need instead of # downloading the entire dataset. rows_to_stream = dataset_slice_end + 1 From 5dcbf86d0988a0cdbfefa13fb41d65d37789e37a Mon Sep 17 00:00:00 2001 From: imagineer99 Date: Tue, 10 Mar 2026 20:13:34 +0000 Subject: [PATCH 3/4] fix: reject negative manual dataset slices Prevent negative Train Split Start/End values in the dataset advanced UI and sanitize payload mapping so negative slice values are never sent to the backend. Made-with: Cursor --- .../studio/sections/dataset-section.tsx | 17 +++++++++++++++-- .../src/features/training/api/mappers.ts | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/studio/frontend/src/features/studio/sections/dataset-section.tsx b/studio/frontend/src/features/studio/sections/dataset-section.tsx index 7ff9e675ab..4f20133b5c 100644 --- a/studio/frontend/src/features/studio/sections/dataset-section.tsx +++ b/studio/frontend/src/features/studio/sections/dataset-section.tsx @@ -89,6 +89,13 @@ function formatUpdatedDate(timestamp: number | null): string { return new Date(timestamp * 1000).toLocaleDateString(); } +function normalizeSliceInput(value: string): string | null { + const trimmed = value.trim(); + if (!trimmed) return null; + if (!/^\d+$/.test(trimmed)) return null; + return trimmed; +} + export function DatasetSection() { const { dataset, @@ -783,11 +790,14 @@ export function DatasetSection() { - setDatasetSliceStart(e.target.value || null) + setDatasetSliceStart(normalizeSliceInput(e.target.value)) } /> @@ -815,11 +825,14 @@ export function DatasetSection() { - setDatasetSliceEnd(e.target.value || null) + setDatasetSliceEnd(normalizeSliceInput(e.target.value)) } /> diff --git a/studio/frontend/src/features/training/api/mappers.ts b/studio/frontend/src/features/training/api/mappers.ts index fed17a538c..53869fdd59 100644 --- a/studio/frontend/src/features/training/api/mappers.ts +++ b/studio/frontend/src/features/training/api/mappers.ts @@ -12,7 +12,7 @@ function parseSliceValue(value: string | null): number | null { const trimmed = value.trim(); if (!trimmed) return null; const num = Number(trimmed); - if (!Number.isFinite(num) || !Number.isInteger(num)) return null; + if (!Number.isFinite(num) || !Number.isInteger(num) || num < 0) return null; return num; } From 21ef22a9ff6162e1c5b33f49ada76262d7b9dc81 Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Tue, 10 Mar 2026 20:21:23 +0000 Subject: [PATCH 4/4] fix: skip streaming when dataset_slice_start > dataset_slice_end Prevents training on the wrong row range when start exceeds end by falling back to full download where existing clamping handles it. --- studio/backend/core/training/trainer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/studio/backend/core/training/trainer.py b/studio/backend/core/training/trainer.py index c72a54af00..2e37cad189 100644 --- a/studio/backend/core/training/trainer.py +++ b/studio/backend/core/training/trainer.py @@ -1871,7 +1871,10 @@ class UnslothTrainer: if subset: load_kwargs["name"] = subset - if dataset_slice_end is not None and dataset_slice_end >= 0: + _slice_start = dataset_slice_start or 0 + if (dataset_slice_end is not None + and dataset_slice_end >= 0 + and dataset_slice_end >= _slice_start): # Manual slice — stream only the rows we need instead of # downloading the entire dataset. rows_to_stream = dataset_slice_end + 1