diff --git a/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx b/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx
index 19ee08e4cd..d4890f698c 100644
--- a/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx
+++ b/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx
@@ -35,11 +35,10 @@ import {
import {
useDebouncedValue,
useHfDatasetSearch,
- useHfDatasetSplits,
useInfiniteScroll,
} from "@/hooks";
import { cn, formatCompact } from "@/lib/utils";
-import { useTrainingConfigStore } from "@/features/training";
+import { HfDatasetConfigSplitSelectors, useTrainingConfigStore } from "@/features/training";
import type { DatasetFormat } from "@/types/training";
import {
InformationCircleIcon,
@@ -48,7 +47,7 @@ import {
Upload04Icon,
} from "@hugeicons/core-free-icons";
import { HugeiconsIcon } from "@hugeicons/react";
-import { useEffect, useMemo, useRef, useState } from "react";
+import { useMemo, useRef, useState } from "react";
import { useShallow } from "zustand/react/shallow";
const FORMAT_OPTIONS: { value: DatasetFormat; label: string }[] = [
@@ -113,39 +112,6 @@ export function DatasetStep() {
hfResults.length,
);
- // Fetch configs & splits from HF datasets-server API
- const {
- configs: hfConfigs,
- splits: hfSplits,
- hasMultipleConfigs,
- hasMultipleSplits,
- isLoading: splitsLoading,
- error: splitsError,
- } = useHfDatasetSplits(
- datasetSource === "huggingface" ? dataset : null,
- datasetConfig,
- { accessToken: hfToken || undefined },
- );
-
- // Auto-select config when there is only one
- useEffect(() => {
- if (hfConfigs.length === 1 && datasetConfig !== hfConfigs[0]) {
- setDatasetConfig(hfConfigs[0]);
- }
- }, [hfConfigs, datasetConfig, setDatasetConfig]);
-
- // Auto-select split when there is only one, or default to "train"
- useEffect(() => {
- if (hfSplits.length === 0) return;
- if (hfSplits.length === 1 && datasetSplit !== hfSplits[0]) {
- setDatasetSplit(hfSplits[0]);
- } else if (!datasetSplit && hfSplits.includes("train")) {
- setDatasetSplit("train");
- } else if (!datasetSplit) {
- setDatasetSplit(hfSplits[0]);
- }
- }, [hfSplits, datasetSplit, setDatasetSplit]);
-
const handleFileUpload = () => {
setUploadedFile("my_dataset.jsonl");
};
@@ -304,98 +270,16 @@ export function DatasetStep() {
- {/* Config & Split selectors */}
- {dataset && splitsLoading && (
-
-
- Loading dataset configs and splits...
-
- )}
-
- {dataset && splitsError && (
-
- Could not fetch dataset splits: {splitsError}
-
- )}
-
- {dataset && !splitsLoading && !splitsError && hasMultipleConfigs && (
-
-
- Subset (Config)
-
-
-
-
-
- This dataset has multiple subsets (configurations).
- Select which one to use for training.
-
-
-
-
-
- )}
-
- {dataset && !splitsLoading && !splitsError && hasMultipleSplits && (
-
-
- Split
-
-
-
-
-
- Select which split of the dataset to use for training.
-
-
-
-
-
- )}
+
>
) : (
<>
diff --git a/studio/frontend/src/features/studio/sections/dataset-section.tsx b/studio/frontend/src/features/studio/sections/dataset-section.tsx
index ec876188e5..8fb3a15735 100644
--- a/studio/frontend/src/features/studio/sections/dataset-section.tsx
+++ b/studio/frontend/src/features/studio/sections/dataset-section.tsx
@@ -25,11 +25,10 @@ import {
import {
useDebouncedValue,
useHfDatasetSearch,
- useHfDatasetSplits,
useInfiniteScroll,
} from "@/hooks";
import { formatCompact } from "@/lib/utils";
-import { useTrainingConfigStore } from "@/features/training";
+import { HfDatasetConfigSplitSelectors, useTrainingConfigStore } from "@/features/training";
import {
CloudUploadIcon,
Database02Icon,
@@ -39,10 +38,20 @@ import {
ViewIcon,
} from "@hugeicons/core-free-icons";
import { HugeiconsIcon } from "@hugeicons/react";
-import { useEffect, useMemo, useRef, useState } from "react";
+import { useMemo, useRef, useState } from "react";
import { useShallow } from "zustand/react/shallow";
import { DatasetPreviewDialog } from "./dataset-preview-dialog";
+function isLikelyLocalDatasetRef(value: string) {
+ return (
+ value.startsWith("/") ||
+ value.startsWith("./") ||
+ value.startsWith("../") ||
+ value.includes("\\") ||
+ /\.(jsonl|json|csv|parquet)$/i.test(value)
+ );
+}
+
export function DatasetSection() {
const {
dataset,
@@ -102,37 +111,6 @@ export function DatasetSection() {
return ids;
}, [hfResults, dataset]);
- // Fetch configs & splits from HF datasets-server API
- const {
- configs: hfConfigs,
- splits: hfSplits,
- hasMultipleConfigs,
- hasMultipleSplits,
- isLoading: splitsLoading,
- error: splitsError,
- } = useHfDatasetSplits(dataset, datasetConfig, {
- accessToken: hfToken || undefined,
- });
-
- // Auto-select config when there is only one
- useEffect(() => {
- if (hfConfigs.length === 1 && datasetConfig !== hfConfigs[0]) {
- setDatasetConfig(hfConfigs[0]);
- }
- }, [hfConfigs, datasetConfig, setDatasetConfig]);
-
- // Auto-select split when there is only one, or default to "train" if available
- useEffect(() => {
- if (hfSplits.length === 0) return;
- if (hfSplits.length === 1 && datasetSplit !== hfSplits[0]) {
- setDatasetSplit(hfSplits[0]);
- } else if (!datasetSplit && hfSplits.includes("train")) {
- setDatasetSplit("train");
- } else if (!datasetSplit) {
- setDatasetSplit(hfSplits[0]);
- }
- }, [hfSplits, datasetSplit, setDatasetSplit]);
-
const comboboxAnchorRef = useRef(null);
const { scrollRef, sentinelRef } = useInfiniteScroll(
fetchMore,
@@ -258,104 +236,16 @@ export function DatasetSection() {
- {/* Config & Split selectors - shown after dataset is selected */}
- {dataset && !splitsLoading && !splitsError && (hasMultipleConfigs || hasMultipleSplits) && (
-
- {hasMultipleConfigs && (
-
-
- Subset (Config)
-
-
-
-
-
- This dataset has multiple subsets (configurations).
- Select which one to use for training.
-
-
-
-
-
- )}
-
- {hasMultipleSplits && (
-
-
- Split
-
-
-
-
-
- Select which split of the dataset to use for training.
-
-
-
-
-
- )}
-
- )}
-
- {/* Loading indicator for splits */}
- {dataset && splitsLoading && (
-
-
- Loading dataset configs and splits...
-
- )}
-
- {/* Error fetching splits */}
- {dataset && splitsError && (
-
- Could not fetch dataset splits: {splitsError}
-
- )}
+
diff --git a/studio/frontend/src/features/training/components/hf-dataset-config-split-selectors.tsx b/studio/frontend/src/features/training/components/hf-dataset-config-split-selectors.tsx
new file mode 100644
index 0000000000..4342ac44f0
--- /dev/null
+++ b/studio/frontend/src/features/training/components/hf-dataset-config-split-selectors.tsx
@@ -0,0 +1,271 @@
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { Spinner } from "@/components/ui/spinner";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+import {
+ Field,
+ FieldLabel,
+} from "@/components/ui/field";
+import { useHfDatasetSplits } from "@/hooks";
+import { InformationCircleIcon } from "@hugeicons/core-free-icons";
+import { HugeiconsIcon } from "@hugeicons/react";
+import { useEffect } from "react";
+
+type Props = {
+ variant: "wizard" | "studio";
+ enabled: boolean;
+ datasetName: string | null;
+ accessToken?: string;
+ datasetConfig: string | null;
+ setDatasetConfig: (v: string | null) => void;
+ datasetSplit: string | null;
+ setDatasetSplit: (v: string | null) => void;
+};
+
+export function HfDatasetConfigSplitSelectors({
+ variant,
+ enabled,
+ datasetName,
+ accessToken,
+ datasetConfig,
+ setDatasetConfig,
+ datasetSplit,
+ setDatasetSplit,
+}: Props) {
+ const {
+ configs: hfConfigs,
+ splits: hfSplits,
+ hasMultipleConfigs,
+ hasMultipleSplits,
+ isLoading,
+ error,
+ } = useHfDatasetSplits(enabled ? datasetName : null, datasetConfig, {
+ accessToken,
+ });
+
+ useEffect(() => {
+ if (hfConfigs.length === 1 && datasetConfig !== hfConfigs[0]) {
+ setDatasetConfig(hfConfigs[0]);
+ }
+ }, [hfConfigs, datasetConfig, setDatasetConfig]);
+
+ useEffect(() => {
+ if (hfSplits.length === 0) return;
+ if (hasMultipleConfigs && !datasetConfig) return;
+ if (hfSplits.length === 1 && datasetSplit !== hfSplits[0]) {
+ setDatasetSplit(hfSplits[0]);
+ } else if (!datasetSplit && hfSplits.includes("train")) {
+ setDatasetSplit("train");
+ } else if (!datasetSplit) {
+ setDatasetSplit(hfSplits[0]);
+ }
+ }, [
+ hfSplits,
+ hasMultipleConfigs,
+ datasetConfig,
+ datasetSplit,
+ setDatasetSplit,
+ ]);
+
+ if (!enabled || !datasetName) return null;
+
+ return (
+ <>
+ {isLoading && (
+
+
+ Loading dataset configs and splits...
+
+ )}
+
+ {error && (
+
+ Could not fetch dataset splits: {error}
+
+ )}
+
+ {!isLoading && !error && hasMultipleConfigs && (
+ <>
+ {variant === "wizard" ? (
+
+
+ Subset (Config)
+
+
+
+
+
+ This dataset has multiple subsets (configurations). Select
+ which one to use for training.
+
+
+
+
+
+ ) : (
+
+
+ Subset (Config)
+
+
+
+
+
+ This dataset has multiple subsets (configurations). Select
+ which one to use for training.
+
+
+
+
+
+ )}
+ >
+ )}
+
+ {!isLoading && !error && hasMultipleSplits && (
+ <>
+ {variant === "wizard" ? (
+
+
+ Split
+
+
+
+
+
+ Select which split of the dataset to use for training.
+
+
+
+
+
+ ) : (
+
+
+ Split
+
+
+
+
+
+ Select which split of the dataset to use for training.
+
+
+
+
+
+ )}
+ >
+ )}
+ >
+ );
+}
diff --git a/studio/frontend/src/features/training/index.ts b/studio/frontend/src/features/training/index.ts
index 48b9079fd3..d32332a19b 100644
--- a/studio/frontend/src/features/training/index.ts
+++ b/studio/frontend/src/features/training/index.ts
@@ -5,4 +5,5 @@ export {
} from "./stores/training-runtime-store";
export { useTrainingActions } from "./hooks/use-training-actions";
export { useTrainingRuntimeLifecycle } from "./hooks/use-training-runtime-lifecycle";
+export { HfDatasetConfigSplitSelectors } from "./components/hf-dataset-config-split-selectors";
export type { TrainingPhase } from "./types/runtime";
diff --git a/studio/frontend/src/hooks/use-hf-dataset-splits.ts b/studio/frontend/src/hooks/use-hf-dataset-splits.ts
index b09e1e3d60..ededfacbd6 100644
--- a/studio/frontend/src/hooks/use-hf-dataset-splits.ts
+++ b/studio/frontend/src/hooks/use-hf-dataset-splits.ts
@@ -117,10 +117,14 @@ export function useHfDatasetSplits(
// Derive unique configs
const configs = Array.from(new Set(entries.map((e) => e.config)));
- // Derive splits for the selected config (or all splits if no config selected)
- const filteredEntries = selectedConfig
- ? entries.filter((e) => e.config === selectedConfig)
- : entries;
+ // Derive splits for the active config.
+ // If dataset has >1 config and none is selected yet, return no splits so UI
+ // doesn't auto-pick/show a split before config is chosen.
+ const activeConfig =
+ selectedConfig ?? (configs.length === 1 ? configs[0] : null);
+ const filteredEntries = activeConfig
+ ? entries.filter((e) => e.config === activeConfig)
+ : [];
const splits = Array.from(new Set(filteredEntries.map((e) => e.split)));
return {
@@ -128,7 +132,7 @@ export function useHfDatasetSplits(
splits,
entries,
hasMultipleConfigs: configs.length > 1,
- hasMultipleSplits: splits.length > 1,
+ hasMultipleSplits: activeConfig ? splits.length > 1 : false,
isLoading,
error,
};