| "use client"; |
|
|
| import { useState, useEffect } from "react"; |
| import { ArrowUp, Cpu, Database, Plus, Loader2, ChevronRight } from "lucide-react"; |
| import Navbar from "@/components/Navbar"; |
| import Footer from "@/components/Footer"; |
| import { useRouter } from "next/navigation"; |
| import { Button } from "@/components/ui/button"; |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; |
| import { Label } from "@/components/ui/label"; |
| import { Textarea } from "@/components/ui/textarea"; |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; |
| import { Badge } from "@/components/ui/badge"; |
| import { |
| Select, |
| SelectContent, |
| SelectItem, |
| SelectTrigger, |
| SelectValue, |
| } from "@/components/ui/select"; |
| import { Combobox } from "@/components/ui/combobox"; |
| import { toast } from "@/components/ui/toaster"; |
|
|
| interface DistillationRequest { |
| id: string; |
| sourceDataset: string; |
| studentModel: string; |
| submitterName?: string; |
| additionalNotes: string; |
| upvotes: number; |
| createdAt: string; |
| status: "pending" | "in_progress" | "completed"; |
| } |
|
|
| interface DatasetRequest { |
| id: string; |
| sourceModel: string; |
| submitterName?: string; |
| datasetSize: string; |
| reasoningDepth: string; |
| topics: string[]; |
| additionalNotes: string; |
| upvotes: number; |
| createdAt: string; |
| status: "pending" | "in_progress" | "completed"; |
| } |
|
|
| interface TeichAIDataset { |
| id: string; |
| name: string; |
| } |
|
|
| interface OpenRouterModel { |
| id: string; |
| name: string; |
| } |
|
|
| interface HuggingFaceModel { |
| id: string; |
| name: string; |
| } |
|
|
| const STUDENT_MODELS = [ |
| "Qwen3-4B", |
| "Qwen3-4B-Thinking-2507", |
| "Qwen3-4B-Instruct-2507", |
| "Qwen3-8B", |
| "Qwen3-14B", |
| "Qwen3-30B-A3B-Thinking-2507", |
| "Qwen3-32B", |
| "Nemotron-Cascade-14B-Thinking", |
| "Nemotron-Cascade-8B-Thinking", |
| "Other", |
| ]; |
|
|
| const SOURCE_MODEL_OTHER_HF = "Other (HuggingFace)"; |
|
|
| const REASONING_DEPTHS = ["low", "medium", "high"]; |
| const DATASET_SIZES = ["100x", "250x", "500x", "1000x", "3000x", "11000x"]; |
| const TOPICS = [ |
| "Coding", |
| "Math", |
| "Science", |
| "Web Development", |
| "Data Science", |
| "Machine Learning", |
| "Creative Writing", |
| "Reasoning", |
| "Logic", |
| "General Knowledge", |
| ]; |
|
|
| export default function Home() { |
| const router = useRouter(); |
| const [distillationRequests, setDistillationRequests] = useState<DistillationRequest[]>([]); |
| const [datasetRequests, setDatasetRequests] = useState<DatasetRequest[]>([]); |
| const [loading, setLoading] = useState(true); |
| const [submitting, setSubmitting] = useState(false); |
| const [showDistillForm, setShowDistillForm] = useState(false); |
| const [showDatasetForm, setShowDatasetForm] = useState(false); |
|
|
| |
| const [teichaiDatasets, setTeichaiDatasets] = useState<TeichAIDataset[]>([]); |
| const [openrouterModels, setOpenrouterModels] = useState<OpenRouterModel[]>([]); |
| const [loadingDatasets, setLoadingDatasets] = useState(false); |
| const [loadingModels, setLoadingModels] = useState(false); |
|
|
| const [huggingfaceModels, setHuggingfaceModels] = useState<HuggingFaceModel[]>([]); |
| const [hfModelQuery, setHfModelQuery] = useState(""); |
| const [loadingHfModels, setLoadingHfModels] = useState(false); |
|
|
| const [distillHuggingfaceModels, setDistillHuggingfaceModels] = useState<HuggingFaceModel[]>([]); |
| const [distillHfModelQuery, setDistillHfModelQuery] = useState(""); |
| const [loadingDistillHfModels, setLoadingDistillHfModels] = useState(false); |
|
|
| |
| const [sourceDataset, setSourceDataset] = useState(""); |
| const [sourceDatasetOther, setSourceDatasetOther] = useState(""); |
| const [studentModel, setStudentModel] = useState(""); |
| const [studentModelOther, setStudentModelOther] = useState(""); |
| const [distillSubmitterName, setDistillSubmitterName] = useState(""); |
| const [distillNotes, setDistillNotes] = useState(""); |
|
|
| |
| const [sourceModel, setSourceModel] = useState(""); |
| const [sourceModelOther, setSourceModelOther] = useState(""); |
| const [datasetSubmitterName, setDatasetSubmitterName] = useState(""); |
| const [datasetSize, setDatasetSize] = useState("250x"); |
| const [reasoningDepth, setReasoningDepth] = useState("high"); |
| const [selectedTopics, setSelectedTopics] = useState<string[]>([]); |
| const [datasetNotes, setDatasetNotes] = useState(""); |
|
|
| useEffect(() => { |
| fetchRequests(); |
| fetchTeichaiDatasets(); |
| fetchOpenrouterModels(); |
| }, []); |
|
|
| useEffect(() => { |
| let cancelled = false; |
| const q = hfModelQuery.trim(); |
|
|
| const timer = setTimeout(async () => { |
| setLoadingHfModels(true); |
| try { |
| const res = await fetch(`/api/huggingface-models?q=${encodeURIComponent(q)}&limit=20`); |
| const data = await res.json(); |
| if (!cancelled) { |
| setHuggingfaceModels(Array.isArray(data) ? data : []); |
| } |
| } catch (error) { |
| console.error("Error fetching Hugging Face models:", error); |
| if (!cancelled) { |
| setHuggingfaceModels([]); |
| } |
| } finally { |
| if (!cancelled) { |
| setLoadingHfModels(false); |
| } |
| } |
| }, 250); |
|
|
| return () => { |
| cancelled = true; |
| clearTimeout(timer); |
| }; |
| }, [hfModelQuery]); |
|
|
| useEffect(() => { |
| let cancelled = false; |
| const q = distillHfModelQuery.trim(); |
|
|
| const timer = setTimeout(async () => { |
| setLoadingDistillHfModels(true); |
| try { |
| const res = await fetch(`/api/huggingface-models?q=${encodeURIComponent(q)}&limit=20`); |
| const data = await res.json(); |
| if (!cancelled) { |
| setDistillHuggingfaceModels(Array.isArray(data) ? data : []); |
| } |
| } catch (error) { |
| console.error("Error fetching Hugging Face models:", error); |
| if (!cancelled) { |
| setDistillHuggingfaceModels([]); |
| } |
| } finally { |
| if (!cancelled) { |
| setLoadingDistillHfModels(false); |
| } |
| } |
| }, 250); |
|
|
| return () => { |
| cancelled = true; |
| clearTimeout(timer); |
| }; |
| }, [distillHfModelQuery]); |
|
|
| async function fetchRequests() { |
| try { |
| const [distillRes, datasetRes] = await Promise.all([ |
| fetch("/api/distillation"), |
| fetch("/api/dataset"), |
| ]); |
| const distillData = await distillRes.json(); |
| const datasetData = await datasetRes.json(); |
| setDistillationRequests(Array.isArray(distillData) ? distillData : []); |
| setDatasetRequests(Array.isArray(datasetData) ? datasetData : []); |
| } catch (error) { |
| console.error("Error fetching requests:", error); |
| } finally { |
| setLoading(false); |
| } |
| } |
|
|
| async function fetchTeichaiDatasets() { |
| setLoadingDatasets(true); |
| try { |
| const res = await fetch("/api/teichai-datasets"); |
| const data = await res.json(); |
| setTeichaiDatasets(Array.isArray(data) ? data : []); |
| } catch (error) { |
| console.error("Error fetching TeichAI datasets:", error); |
| } finally { |
| setLoadingDatasets(false); |
| } |
| } |
|
|
| async function fetchOpenrouterModels() { |
| setLoadingModels(true); |
| try { |
| const res = await fetch("/api/openrouter-models"); |
| const data = await res.json(); |
| setOpenrouterModels(Array.isArray(data) ? data : []); |
| } catch (error) { |
| console.error("Error fetching OpenRouter models:", error); |
| } finally { |
| setLoadingModels(false); |
| } |
| } |
|
|
| async function handleDistillSubmit(e: React.FormEvent) { |
| e.preventDefault(); |
| const resolvedSourceDataset = sourceDataset === "Other" ? sourceDatasetOther.trim() : sourceDataset; |
| const resolvedStudentModel = studentModel === "Other" ? studentModelOther.trim() : studentModel; |
| const resolvedSubmitterName = distillSubmitterName.trim(); |
| if (!resolvedSourceDataset || !resolvedStudentModel) { |
| toast({ title: "Error", description: "Please fill in required fields", variant: "destructive" }); |
| return; |
| } |
| setSubmitting(true); |
| try { |
| const res = await fetch("/api/distillation", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ |
| sourceDataset: resolvedSourceDataset, |
| studentModel: resolvedStudentModel, |
| submitterName: resolvedSubmitterName || undefined, |
| additionalNotes: distillNotes, |
| }), |
| }); |
| if (res.ok) { |
| toast({ title: "Success", description: "Distillation request submitted!" }); |
| setSourceDataset(""); |
| setSourceDatasetOther(""); |
| setStudentModel(""); |
| setStudentModelOther(""); |
| setDistillHfModelQuery(""); |
| setDistillHuggingfaceModels([]); |
| setDistillSubmitterName(""); |
| setDistillNotes(""); |
| setShowDistillForm(false); |
| fetchRequests(); |
| } else { |
| throw new Error("Failed to submit"); |
| } |
| } catch (error) { |
| toast({ title: "Error", description: "Failed to submit request", variant: "destructive" }); |
| } finally { |
| setSubmitting(false); |
| } |
| } |
|
|
| async function handleDatasetSubmit(e: React.FormEvent) { |
| e.preventDefault(); |
| const resolvedSourceModel = |
| sourceModel === SOURCE_MODEL_OTHER_HF ? sourceModelOther.trim() : sourceModel; |
| const resolvedSubmitterName = datasetSubmitterName.trim(); |
| if (!resolvedSourceModel) { |
| toast({ title: "Error", description: "Please select a source model", variant: "destructive" }); |
| return; |
| } |
| setSubmitting(true); |
| try { |
| const res = await fetch("/api/dataset", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ |
| sourceModel: resolvedSourceModel, |
| submitterName: resolvedSubmitterName || undefined, |
| datasetSize, |
| reasoningDepth, |
| topics: selectedTopics, |
| additionalNotes: datasetNotes, |
| }), |
| }); |
| if (res.ok) { |
| toast({ title: "Success", description: "Dataset request submitted!" }); |
| setSourceModel(openrouterModels[0]?.id || ""); |
| setSourceModelOther(""); |
| setHfModelQuery(""); |
| setHuggingfaceModels([]); |
| setDatasetSubmitterName(""); |
| setDatasetSize("250x"); |
| setReasoningDepth("high"); |
| setSelectedTopics([]); |
| setDatasetNotes(""); |
| setShowDatasetForm(false); |
| fetchRequests(); |
| } else { |
| throw new Error("Failed to submit"); |
| } |
| } catch (error) { |
| toast({ title: "Error", description: "Failed to submit request", variant: "destructive" }); |
| } finally { |
| setSubmitting(false); |
| } |
| } |
|
|
| async function handleUpvote(type: "distillation" | "dataset", id: string) { |
| try { |
| const endpoint = type === "distillation" ? "/api/distillation" : "/api/dataset"; |
| const res = await fetch(endpoint, { |
| method: "PATCH", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ id }), |
| }); |
| const data = await res.json(); |
|
|
| if (!res.ok) { |
| if (res.status === 404) { |
| toast({ title: "Not found", description: "This request no longer exists", variant: "destructive" }); |
| return; |
| } |
| toast({ title: "Error", description: data?.error || "Failed to vote", variant: "destructive" }); |
| return; |
| } |
|
|
| if (type === "distillation") { |
| setDistillationRequests((prev) => |
| prev.map((r) => (r.id === id ? { ...r, upvotes: data.upvotes } : r)) |
| ); |
| } else { |
| setDatasetRequests((prev) => |
| prev.map((r) => (r.id === id ? { ...r, upvotes: data.upvotes } : r)) |
| ); |
| } |
|
|
| if (data.action === "unvoted") { |
| toast({ title: "Vote removed", description: "Your vote has been removed" }); |
| } else { |
| toast({ title: "Voted!", description: "Your vote has been recorded" }); |
| } |
| } catch (error) { |
| toast({ title: "Error", description: "Failed to vote", variant: "destructive" }); |
| } |
| } |
|
|
| function toggleTopic(topic: string) { |
| setSelectedTopics((prev) => |
| prev.includes(topic) ? prev.filter((t) => t !== topic) : [...prev, topic] |
| ); |
| } |
|
|
| function getStatusBadge(status: string) { |
| switch (status) { |
| case "completed": |
| return <Badge variant="success">Completed</Badge>; |
| case "in_progress": |
| return <Badge variant="warning">In Progress</Badge>; |
| default: |
| return <Badge variant="secondary">Pending</Badge>; |
| } |
| } |
|
|
| function formatDate(dateStr: string) { |
| return new Date(dateStr).toLocaleDateString("en-US", { |
| month: "short", |
| day: "numeric", |
| year: "numeric", |
| }); |
| } |
|
|
| function goToDiscussion(type: "distillation" | "dataset", id: string) { |
| router.push(`/requests/${type}/${id}`); |
| } |
|
|
| return ( |
| <main className="min-h-screen bg-background"> |
| <Navbar /> |
| |
| {/* Hero */} |
| <section className="relative overflow-hidden pt-24 pb-16"> |
| <div className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top,rgba(255,76,0,0.10),transparent_55%)] dark:bg-[radial-gradient(circle_at_top,rgba(255,76,0,0.16),transparent_55%)]" /> |
| <div className="mx-auto max-w-6xl px-4 sm:px-6"> |
| <div className="max-w-3xl"> |
| <p className="mb-3 text-sm font-medium text-primary">Community Requests</p> |
| <h1 className="mb-6 text-4xl font-bold leading-tight tracking-tight text-foreground md:text-5xl"> |
| Request Model Distillations & Datasets |
| </h1> |
| <p className="mb-8 text-lg leading-relaxed text-muted-foreground"> |
| Submit your requests for new distilled models or reasoning datasets. Vote on requests |
| from other community members to help us prioritize what to build next. |
| </p> |
| </div> |
| </div> |
| </section> |
| |
| {/* Main Content */} |
| <section className="py-8"> |
| <div className="mx-auto max-w-6xl px-4 sm:px-6"> |
| <Tabs defaultValue="distillation" className="w-full"> |
| <TabsList className="mb-6 grid w-full grid-cols-2"> |
| <TabsTrigger value="distillation" className="flex items-center gap-2"> |
| <Cpu className="h-4 w-4" /> |
| Model Distillation |
| </TabsTrigger> |
| <TabsTrigger value="dataset" className="flex items-center gap-2"> |
| <Database className="h-4 w-4" /> |
| Dataset |
| </TabsTrigger> |
| </TabsList> |
| |
| {/* Distillation Tab */} |
| <TabsContent value="distillation"> |
| <div className="mb-6 flex items-center justify-between"> |
| <h2 className="text-xl font-semibold text-foreground">Distillation Requests</h2> |
| <Button onClick={() => setShowDistillForm(!showDistillForm)}> |
| <Plus className="h-4 w-4" /> |
| New Request |
| </Button> |
| </div> |
| |
| {showDistillForm && ( |
| <Card className="mb-6"> |
| <CardHeader> |
| <CardTitle>Request a Distilled Model</CardTitle> |
| <CardDescription> |
| Select one of our existing datasets to distill into a student model |
| </CardDescription> |
| </CardHeader> |
| <CardContent> |
| <form onSubmit={handleDistillSubmit} className="space-y-4"> |
| <div className="grid gap-4 md:grid-cols-2"> |
| <div className="space-y-2"> |
| <Label htmlFor="dataset">Source Dataset *</Label> |
| <Combobox |
| options={[...teichaiDatasets, { id: "Other", name: "Other" }]} |
| value={sourceDataset} |
| onValueChange={(v) => { |
| setSourceDataset(v); |
| if (v !== "Other") setSourceDatasetOther(""); |
| }} |
| placeholder="Select a TeichAI dataset" |
| searchPlaceholder="Search datasets..." |
| emptyMessage="No datasets found" |
| loading={loadingDatasets} |
| /> |
| </div> |
| <div className="space-y-2"> |
| <Label htmlFor="student">Student Model *</Label> |
| <Select |
| value={studentModel} |
| onValueChange={(v) => { |
| setStudentModel(v); |
| if (v !== "Other") { |
| setStudentModelOther(""); |
| setDistillHfModelQuery(""); |
| setDistillHuggingfaceModels([]); |
| } else { |
| setStudentModelOther(""); |
| setDistillHfModelQuery(""); |
| setDistillHuggingfaceModels([]); |
| } |
| }} |
| > |
| <SelectTrigger> |
| <SelectValue placeholder="Select student model" /> |
| </SelectTrigger> |
| <SelectContent> |
| {STUDENT_MODELS.map((model) => ( |
| <SelectItem key={model} value={model}> |
| {model} |
| </SelectItem> |
| ))} |
| </SelectContent> |
| </Select> |
| </div> |
| </div> |
| {sourceDataset === "Other" && ( |
| <div className="space-y-2"> |
| <Label htmlFor="datasetOther">Source Dataset (Other) *</Label> |
| <input |
| id="datasetOther" |
| className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" |
| value={sourceDatasetOther} |
| onChange={(e) => setSourceDatasetOther(e.target.value)} |
| placeholder="Type the dataset name" |
| /> |
| </div> |
| )} |
| {studentModel === "Other" && ( |
| <div className="space-y-2"> |
| <Label htmlFor="studentOther">Hugging Face Student Model *</Label> |
| <Combobox |
| options={distillHuggingfaceModels} |
| value={studentModelOther} |
| onValueChange={setStudentModelOther} |
| placeholder="Search safetensors models" |
| searchPlaceholder="Type to search models..." |
| emptyMessage={distillHfModelQuery.trim() ? "No models found" : "Start typing to search"} |
| loading={loadingDistillHfModels} |
| searchValue={distillHfModelQuery} |
| onSearchValueChange={setDistillHfModelQuery} |
| disableLocalFilter |
| /> |
| </div> |
| )} |
| <div className="space-y-2"> |
| <Label htmlFor="distillSubmitterName">Name (optional)</Label> |
| <input |
| id="distillSubmitterName" |
| title="Name" |
| className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" |
| value={distillSubmitterName} |
| onChange={(e) => setDistillSubmitterName(e.target.value)} |
| placeholder="Your name (optional)" |
| /> |
| </div> |
| <div className="space-y-2"> |
| <Label htmlFor="notes">Additional Notes</Label> |
| <Textarea |
| id="notes" |
| placeholder="Any specific requirements or context..." |
| value={distillNotes} |
| onChange={(e) => setDistillNotes(e.target.value)} |
| /> |
| </div> |
| <div className="flex gap-2"> |
| <Button type="submit" disabled={submitting}> |
| {submitting && <Loader2 className="h-4 w-4 animate-spin" />} |
| Submit Request |
| </Button> |
| <Button type="button" variant="outline" onClick={() => setShowDistillForm(false)}> |
| Cancel |
| </Button> |
| </div> |
| </form> |
| </CardContent> |
| </Card> |
| )} |
| |
| {loading ? ( |
| <div className="flex justify-center py-12"> |
| <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /> |
| </div> |
| ) : distillationRequests.length === 0 ? ( |
| <Card className="p-12 text-center"> |
| <p className="text-muted-foreground">No distillation requests yet. Be the first!</p> |
| </Card> |
| ) : ( |
| <div className="space-y-4"> |
| {distillationRequests.map((request) => ( |
| <Card |
| key={request.id} |
| className="group cursor-pointer transition-all hover:shadow-md" |
| onClick={() => goToDiscussion("distillation", request.id)} |
| role="link" |
| tabIndex={0} |
| onKeyDown={(e) => { |
| if (e.key === "Enter" || e.key === " ") { |
| e.preventDefault(); |
| goToDiscussion("distillation", request.id); |
| } |
| }} |
| > |
| <CardContent className="p-5"> |
| <div className="flex items-start gap-4"> |
| <button |
| onClick={(e) => { |
| e.stopPropagation(); |
| handleUpvote("distillation", request.id); |
| }} |
| className="flex flex-col items-center gap-1 rounded-lg border border-border bg-muted px-3 py-2 transition-colors hover:border-primary hover:bg-accent" |
| > |
| <ArrowUp className="h-4 w-4 text-primary" /> |
| <span className="text-sm font-semibold">{request.upvotes}</span> |
| </button> |
| <div className="flex-1"> |
| <div className="mb-2 flex flex-wrap items-center gap-2"> |
| <h3 className="font-medium text-foreground"> |
| {request.sourceDataset} → {request.studentModel} |
| </h3> |
| {getStatusBadge(request.status)} |
| </div> |
| {request.submitterName ? ( |
| <p className="mb-2 text-sm text-muted-foreground">By {request.submitterName}</p> |
| ) : null} |
| {request.additionalNotes && ( |
| <p className="mb-2 text-sm text-muted-foreground">{request.additionalNotes}</p> |
| )} |
| <p className="text-xs text-muted-foreground"> |
| Requested on {formatDate(request.createdAt)} |
| </p> |
| </div> |
| <div className="flex items-center text-muted-foreground"> |
| <ChevronRight className="h-5 w-5 opacity-50 transition-opacity group-hover:opacity-100" /> |
| </div> |
| </div> |
| </CardContent> |
| </Card> |
| ))} |
| </div> |
| )} |
| </TabsContent> |
| |
| {/* Dataset Tab */} |
| <TabsContent value="dataset"> |
| <div className="mb-6 flex items-center justify-between"> |
| <h2 className="text-xl font-semibold text-foreground">Dataset</h2> |
| <Button onClick={() => setShowDatasetForm(!showDatasetForm)}> |
| <Plus className="h-4 w-4" /> |
| New Request |
| </Button> |
| </div> |
| |
| {showDatasetForm && ( |
| <Card className="mb-6"> |
| <CardHeader> |
| <CardTitle>Request a Dataset</CardTitle> |
| <CardDescription> |
| Specify which model to generate reasoning data from |
| </CardDescription> |
| </CardHeader> |
| <CardContent> |
| <form onSubmit={handleDatasetSubmit} className="space-y-4"> |
| <div className="grid gap-4 md:grid-cols-2"> |
| <div className="space-y-2"> |
| <Label htmlFor="sourceModel">Source Model *</Label> |
| <Combobox |
| options={[ |
| ...openrouterModels, |
| { id: SOURCE_MODEL_OTHER_HF, name: SOURCE_MODEL_OTHER_HF }, |
| ]} |
| value={sourceModel} |
| onValueChange={(v) => { |
| setSourceModel(v); |
| if (v !== SOURCE_MODEL_OTHER_HF) { |
| setSourceModelOther(""); |
| setHfModelQuery(""); |
| setHuggingfaceModels([]); |
| } |
| }} |
| placeholder="Select a model" |
| searchPlaceholder="Search OpenRouter models..." |
| emptyMessage="No models found" |
| loading={loadingModels} |
| /> |
| </div> |
| <div className="space-y-2"> |
| <Label htmlFor="size">Dataset Size</Label> |
| <Select value={datasetSize} onValueChange={setDatasetSize}> |
| <SelectTrigger> |
| <SelectValue /> |
| </SelectTrigger> |
| <SelectContent> |
| {DATASET_SIZES.map((size) => ( |
| <SelectItem key={size} value={size}> |
| {size} samples |
| </SelectItem> |
| ))} |
| </SelectContent> |
| </Select> |
| </div> |
| </div> |
| {sourceModel === SOURCE_MODEL_OTHER_HF && ( |
| <div className="space-y-2"> |
| <Label htmlFor="sourceModelOther">Hugging Face Model *</Label> |
| <Combobox |
| options={huggingfaceModels} |
| value={sourceModelOther} |
| onValueChange={setSourceModelOther} |
| placeholder="Search safetensors models" |
| searchPlaceholder="Type to search models..." |
| emptyMessage={hfModelQuery.trim() ? "No models found" : "Start typing to search"} |
| loading={loadingHfModels} |
| searchValue={hfModelQuery} |
| onSearchValueChange={setHfModelQuery} |
| disableLocalFilter |
| /> |
| </div> |
| )} |
| <div className="space-y-2"> |
| <Label htmlFor="datasetSubmitterName">Name (optional)</Label> |
| <input |
| id="datasetSubmitterName" |
| title="Name" |
| className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" |
| value={datasetSubmitterName} |
| onChange={(e) => setDatasetSubmitterName(e.target.value)} |
| placeholder="Your name (optional)" |
| /> |
| </div> |
| <div className="space-y-2"> |
| <Label htmlFor="depth">Reasoning Depth</Label> |
| <Select value={reasoningDepth} onValueChange={setReasoningDepth}> |
| <SelectTrigger className="w-[200px]"> |
| <SelectValue /> |
| </SelectTrigger> |
| <SelectContent> |
| {REASONING_DEPTHS.map((depth) => ( |
| <SelectItem key={depth} value={depth}> |
| {depth.charAt(0).toUpperCase() + depth.slice(1)} |
| </SelectItem> |
| ))} |
| </SelectContent> |
| </Select> |
| </div> |
| <div className="space-y-2"> |
| <Label>Topics (select multiple)</Label> |
| <div className="flex flex-wrap gap-2"> |
| {TOPICS.map((topic) => ( |
| <button |
| key={topic} |
| type="button" |
| onClick={() => toggleTopic(topic)} |
| className={`rounded-full px-3 py-1 text-sm transition-colors ${selectedTopics.includes(topic) |
| ? "bg-primary text-primary-foreground" |
| : "bg-muted text-muted-foreground hover:bg-accent" |
| }`} |
| > |
| {topic} |
| </button> |
| ))} |
| </div> |
| </div> |
| <div className="space-y-2"> |
| <Label htmlFor="datasetNotes">Additional Notes</Label> |
| <Textarea |
| id="datasetNotes" |
| placeholder="Any specific requirements or context..." |
| value={datasetNotes} |
| onChange={(e) => setDatasetNotes(e.target.value)} |
| /> |
| </div> |
| <div className="flex gap-2"> |
| <Button type="submit" disabled={submitting}> |
| {submitting && <Loader2 className="h-4 w-4 animate-spin" />} |
| Submit Request |
| </Button> |
| <Button type="button" variant="outline" onClick={() => setShowDatasetForm(false)}> |
| Cancel |
| </Button> |
| </div> |
| </form> |
| </CardContent> |
| </Card> |
| )} |
| |
| {loading ? ( |
| <div className="flex justify-center py-12"> |
| <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /> |
| </div> |
| ) : datasetRequests.length === 0 ? ( |
| <Card className="p-12 text-center"> |
| <p className="text-muted-foreground">No dataset requests yet. Be the first!</p> |
| </Card> |
| ) : ( |
| <div className="space-y-4"> |
| {datasetRequests.map((request) => ( |
| <Card |
| key={request.id} |
| className="group cursor-pointer transition-all hover:shadow-md" |
| onClick={() => goToDiscussion("dataset", request.id)} |
| role="link" |
| tabIndex={0} |
| onKeyDown={(e) => { |
| if (e.key === "Enter" || e.key === " ") { |
| e.preventDefault(); |
| goToDiscussion("dataset", request.id); |
| } |
| }} |
| > |
| <CardContent className="p-5"> |
| <div className="flex items-start gap-4"> |
| <button |
| onClick={(e) => { |
| e.stopPropagation(); |
| handleUpvote("dataset", request.id); |
| }} |
| className="flex flex-col items-center gap-1 rounded-lg border border-border bg-muted px-3 py-2 transition-colors hover:border-primary hover:bg-accent" |
| > |
| <ArrowUp className="h-4 w-4 text-primary" /> |
| <span className="text-sm font-semibold">{request.upvotes}</span> |
| </button> |
| <div className="flex-1"> |
| <div className="mb-2 flex flex-wrap items-center gap-2"> |
| <h3 className="font-medium text-foreground"> |
| {request.sourceModel} Dataset ({request.datasetSize}) |
| </h3> |
| {getStatusBadge(request.status)} |
| <Badge variant="outline">{request.reasoningDepth} reasoning</Badge> |
| </div> |
| {request.submitterName ? ( |
| <p className="mb-2 text-sm text-muted-foreground">By {request.submitterName}</p> |
| ) : null} |
| {request.topics.length > 0 && ( |
| <div className="mb-2 flex flex-wrap gap-1"> |
| {request.topics.map((topic) => ( |
| <Badge key={topic} variant="secondary" className="text-xs"> |
| {topic} |
| </Badge> |
| ))} |
| </div> |
| )} |
| {request.additionalNotes && ( |
| <p className="mb-2 text-sm text-muted-foreground">{request.additionalNotes}</p> |
| )} |
| <p className="text-xs text-muted-foreground"> |
| Requested on {formatDate(request.createdAt)} |
| </p> |
| </div> |
| <div className="flex items-center text-muted-foreground"> |
| <ChevronRight className="h-5 w-5 opacity-50 transition-opacity group-hover:opacity-100" /> |
| </div> |
| </div> |
| </CardContent> |
| </Card> |
| ))} |
| </div> |
| )} |
| </TabsContent> |
| </Tabs> |
| </div> |
| </section> |
| |
| {/* Info Section */} |
| <section className="py-16"> |
| <div className="mx-auto max-w-6xl px-4 sm:px-6"> |
| <Card className="overflow-hidden"> |
| <CardContent className="p-8 md:p-12"> |
| <div className="max-w-2xl"> |
| <h2 className="mb-4 text-2xl font-bold text-foreground">How It Works</h2> |
| <ul className="space-y-3 text-muted-foreground"> |
| <li className="flex items-start gap-2"> |
| <span className="font-bold text-primary">1.</span> |
| Submit a request for a model distillation or reasoning dataset |
| </li> |
| <li className="flex items-start gap-2"> |
| <span className="font-bold text-primary">2.</span> |
| Upvote requests from other community members |
| </li> |
| <li className="flex items-start gap-2"> |
| <span className="font-bold text-primary">3.</span> |
| We prioritize requests based on community interest |
| </li> |
| <li className="flex items-start gap-2"> |
| <span className="font-bold text-primary">4.</span> |
| Models and datasets are published on our Hugging Face page |
| </li> |
| <li className="flex items-start gap-2"> |
| <span className="font-bold text-primary">Note:</span> |
| We will do our best to fulfill as many requests as possible, but we can’t guarantee anything due to time and money constraints. |
| </li> |
| </ul> |
| </div> |
| </CardContent> |
| </Card> |
| </div> |
| </section> |
| |
| <Footer /> |
| </main> |
| ); |
| } |
|
|