import React, { useState } from "react"; import { Database, Upload, Plus, Trash2, Sparkles, RefreshCw, ArrowRight, Download, } from "lucide-react"; import { TrainingDataSample } from "../types"; interface DatasetStudioProps { dataset: TrainingDataSample[]; setDataset: React.Dispatch>; onProceed: () => void; } export const DatasetStudio: React.FC = ({ dataset, setDataset, onProceed, }) => { const [activeView, setActiveView] = useState<"samples" | "json_editor" | "synthetic_generator">("samples"); const [jsonText, setJsonText] = useState(() => JSON.stringify(dataset, null, 2)); const [domainPrompt, setDomainPrompt] = useState("MCP function-calling and Python data engineering tasks"); const [samplesCount, setSamplesCount] = useState(5); const [taskFormat, setTaskFormat] = useState("alpaca"); const [generating, setGenerating] = useState(false); const [filterCategory, setFilterCategory] = useState("all"); // New manual item const [newItemInstruction, setNewItemInstruction] = useState(""); const [newItemInput, setNewItemInput] = useState(""); const [newItemOutput, setNewItemOutput] = useState(""); const handleAddNewItem = () => { if (!newItemInstruction.trim() || !newItemOutput.trim()) return; const newSample: TrainingDataSample = { id: `sample-${Date.now()}`, instruction: newItemInstruction.trim(), input: newItemInput.trim() || undefined, output: newItemOutput.trim(), category: "Custom", difficulty: "Medium", }; setDataset((prev) => [newSample, ...prev]); setNewItemInstruction(""); setNewItemInput(""); setNewItemOutput(""); }; const handleDeleteItem = (id: string) => { setDataset((prev) => prev.filter((item) => item.id !== id)); }; const handleApplyJsonEditor = () => { try { const parsed = JSON.parse(jsonText); if (Array.isArray(parsed)) { setDataset(parsed); } } catch (e: any) { console.error(e); } }; const handleGenerateSyntheticData = async () => { setGenerating(true); try { const res = await fetch("/api/dataset/generate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ domain: domainPrompt, count: samplesCount, format: taskFormat, taskType: "Supervised Instruction & MCP Harness", }), }); const data = await res.json(); if (data.success && Array.isArray(data.data)) { setDataset((prev) => [...data.data, ...prev]); setJsonText(JSON.stringify([...data.data, ...dataset], null, 2)); } } catch (e: any) { console.error(e); } finally { setGenerating(false); } }; const handleFileUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { try { const content = event.target?.result as string; let parsed: any[] = []; if (file.name.endsWith(".jsonl")) { parsed = content .split("\n") .filter((line) => line.trim()) .map((line) => JSON.parse(line)); } else if (file.name.endsWith(".json")) { const raw = JSON.parse(content); parsed = Array.isArray(raw) ? raw : [raw]; } else if (file.name.endsWith(".csv")) { const lines = content.split("\n").filter((l) => l.trim()); for (let i = 1; i < lines.length; i++) { const values = lines[i].split(","); parsed.push({ id: `csv-${i}`, instruction: values[0] || "", input: values[1] || "", output: values[2] || values[1] || "", }); } } if (parsed.length > 0) { setDataset((prev) => [...parsed, ...prev]); setJsonText(JSON.stringify([...parsed, ...dataset], null, 2)); } } catch (err: any) { console.error(err); } }; reader.readAsText(file); }; const filteredDataset = filterCategory === "all" ? dataset : dataset.filter((d) => d.category === filterCategory || (filterCategory === "mcp" && d.isMcpSample)); const totalTokensEst = dataset.reduce( (acc, item) => acc + (item.instruction.length + (item.input?.length || 0) + item.output.length) / 4, 0 ); return (
{/* Top Banner & Stats */}
STRUCTURED DATASET STUDIO

Import, Format & Synthesize Training Data

Feed structured JSON, JSONL, CSV, or generate custom synthetic pairs with Gemini AI.

{/* Dataset Stats Strip */}
Total Samples
{dataset.length} pairs
Est. Tokens
{Math.round(totalTokensEst).toLocaleString()}
Format
Alpaca / Tool
{/* View Switcher Tabs & Actions */}
{/* Upload Button */}
{/* VIEW 1: Samples List */} {activeView === "samples" && (
{/* Quick Manual Add Form */}
Add Custom Instruction Pair
setNewItemInstruction(e.target.value)} className="bg-zinc-950 border border-zinc-800 rounded px-2.5 py-1.5 text-xs text-zinc-200 placeholder-zinc-600 focus:ring-1 focus:ring-blue-600 outline-none" /> setNewItemInput(e.target.value)} className="bg-zinc-950 border border-zinc-800 rounded px-2.5 py-1.5 text-xs text-zinc-200 placeholder-zinc-600 focus:ring-1 focus:ring-blue-600 outline-none" />