Spaces:
Sleeping
Sleeping
| import React, { useState } from "react"; | |
| import { apiUrl } from "../services/api"; | |
| const modes = [ | |
| { value: "single", label: "Single" }, | |
| { value: "comprehensive", label: "Comprehensive" }, | |
| { value: "ultra-realistic", label: "Ultra Realistic" }, | |
| { value: "huggingface", label: "HuggingFace/CSV Dataset" }, | |
| ]; | |
| export default function Synthetic() { | |
| const [mode, setMode] = useState("single"); | |
| const [text, setText] = useState("କବି ସମ୍ରାଟ ଉପେନ୍ଦ୍ର ଭଞ୍ଜ ..."); | |
| const [datasetUrl, setDatasetUrl] = useState(""); | |
| const [textColumn, setTextColumn] = useState("text"); | |
| const [maxSamples, setMaxSamples] = useState(""); | |
| const [jobName, setJobName] = useState(""); | |
| const [result, setResult] = useState(null); | |
| const [error, setError] = useState(null); | |
| const [loading, setLoading] = useState(false); | |
| const [files, setFiles] = useState([]); | |
| const handleGenerate = async () => { | |
| setError(null); | |
| setResult(null); | |
| setLoading(true); | |
| try { | |
| const body = { mode, output_subdir: jobName || undefined }; | |
| if (mode === "huggingface") { | |
| body.dataset_url = datasetUrl || undefined; | |
| body.text_column = textColumn || "text"; | |
| body.max_samples = maxSamples ? Number(maxSamples) : undefined; | |
| } else { | |
| body.text = text; | |
| } | |
| const res = await fetch(apiUrl("/api/synthetic/generate"), { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify(body), | |
| }); | |
| if (!res.ok) throw new Error(await res.text()); | |
| const data = await res.json(); | |
| setResult(data); | |
| if (data.files && Array.isArray(data.files)) { | |
| setFiles(data.files); | |
| } else { | |
| setFiles([]); | |
| } | |
| } catch (e) { | |
| setError(e.message || String(e)); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="max-w-6xl mx-auto p-4"> | |
| <h1 className="text-xl font-semibold mb-4">Synthetic Text Generator</h1> | |
| {error && ( | |
| <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4"> | |
| {error} | |
| </div> | |
| )} | |
| <div className="border rounded p-4 mb-4 bg-white"> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label className="block text-sm font-medium mb-1">Mode</label> | |
| <select | |
| className="w-full border p-2 rounded" | |
| value={mode} | |
| onChange={(e) => setMode(e.target.value)} | |
| > | |
| {modes.map((m) => ( | |
| <option key={m.value} value={m.value}> | |
| {m.label} | |
| </option> | |
| ))} | |
| </select> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium mb-1">Job Name (optional)</label> | |
| <input | |
| className="w-full border p-2 rounded" | |
| placeholder="e.g., demo_run_01" | |
| value={jobName} | |
| onChange={(e) => setJobName(e.target.value)} | |
| /> | |
| </div> | |
| {mode !== "huggingface" && ( | |
| <div className="md:col-span-2"> | |
| <label className="block text-sm font-medium mb-1">Text</label> | |
| <textarea | |
| className="w-full border p-2 rounded h-28" | |
| value={text} | |
| onChange={(e) => setText(e.target.value)} | |
| /> | |
| </div> | |
| )} | |
| {mode === "huggingface" && ( | |
| <> | |
| <div className="md:col-span-2"> | |
| <label className="block text-sm font-medium mb-1">HuggingFace CSV URL</label> | |
| <input | |
| className="w-full border p-2 rounded" | |
| placeholder="https://huggingface.co/datasets/.../raw/main/data.csv" | |
| value={datasetUrl} | |
| onChange={(e) => setDatasetUrl(e.target.value)} | |
| /> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium mb-1">Text Column</label> | |
| <input | |
| className="w-full border p-2 rounded" | |
| value={textColumn} | |
| onChange={(e) => setTextColumn(e.target.value)} | |
| /> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium mb-1">Max Samples (optional)</label> | |
| <input | |
| type="number" | |
| className="w-full border p-2 rounded" | |
| value={maxSamples} | |
| onChange={(e) => setMaxSamples(e.target.value)} | |
| /> | |
| </div> | |
| </> | |
| )} | |
| </div> | |
| <div className="mt-4"> | |
| <button | |
| onClick={handleGenerate} | |
| disabled={loading} | |
| className={`px-4 py-2 rounded text-white ${loading ? "bg-gray-500" : "bg-blue-700 hover:bg-blue-800"}`} | |
| > | |
| {loading ? "Generating..." : "Generate"} | |
| </button> | |
| </div> | |
| </div> | |
| {result && ( | |
| <div className="border rounded p-4 bg-white"> | |
| <h2 className="font-semibold mb-2">Result</h2> | |
| {result.output_dir && ( | |
| <p className="mb-2"> | |
| Output Directory: {" "} | |
| <a href={apiUrl(result.output_dir)} target="_blank" className="text-blue-700 underline" rel="noreferrer"> | |
| {apiUrl(result.output_dir)} | |
| </a> | |
| </p> | |
| )} | |
| {result.csv && ( | |
| <p className="mb-2"> | |
| Dataset CSV: {" "} | |
| <a href={apiUrl(result.csv)} target="_blank" className="text-blue-700 underline" rel="noreferrer"> | |
| {apiUrl(result.csv)} | |
| </a> | |
| </p> | |
| )} | |
| {result.images_dir && ( | |
| <p> | |
| Images Directory: {" "} | |
| <a href={apiUrl(result.images_dir)} target="_blank" className="text-blue-700 underline" rel="noreferrer"> | |
| {apiUrl(result.images_dir)} | |
| </a> | |
| </p> | |
| )} | |
| {files && files.length > 0 && ( | |
| <> | |
| <h3 className="font-semibold mt-4 mb-2">Preview</h3> | |
| <div className="grid grid-cols-2 md:grid-cols-4 gap-2"> | |
| {files.slice(0, 24).map((url, idx) => ( | |
| <a key={idx} href={apiUrl(url)} target="_blank" rel="noreferrer" className="block"> | |
| <img src={apiUrl(url)} alt={`img-${idx}`} className="w-full h-32 object-cover rounded border" /> | |
| </a> | |
| ))} | |
| </div> | |
| {files.length > 24 && ( | |
| <p className="text-sm text-gray-600 mt-2">Showing first 24 of {files.length} images.</p> | |
| )} | |
| </> | |
| )} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |