156 lines
5.7 KiB
TypeScript
156 lines
5.7 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
|
|
type State =
|
|
| { kind: "idle" }
|
|
| { kind: "sending" }
|
|
| { kind: "ok"; message: string; model: string }
|
|
| { kind: "error"; message: string };
|
|
|
|
export default function SubmitPage() {
|
|
const [model, setModel] = useState("");
|
|
const [notes, setNotes] = useState("");
|
|
const [state, setState] = useState<State>({ kind: "idle" });
|
|
|
|
async function onSubmit(e: React.FormEvent) {
|
|
e.preventDefault();
|
|
setState({ kind: "sending" });
|
|
try {
|
|
const res = await fetch("/api/submit", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ model, notes }),
|
|
});
|
|
const data = await res.json();
|
|
if (!res.ok) {
|
|
setState({
|
|
kind: "error",
|
|
message: data.error ?? "Something went wrong.",
|
|
});
|
|
return;
|
|
}
|
|
setState({ kind: "ok", message: data.message, model: data.model });
|
|
setModel("");
|
|
setNotes("");
|
|
} catch {
|
|
setState({ kind: "error", message: "Network error." });
|
|
}
|
|
}
|
|
|
|
const busy = state.kind === "sending";
|
|
|
|
return (
|
|
<main className="mx-auto max-w-3xl px-6 py-14">
|
|
<div className="rise">
|
|
<h1 className="font-display text-4xl leading-tight sm:text-5xl">
|
|
Suggest a model
|
|
</h1>
|
|
<p className="mt-5 max-w-xl text-sm leading-relaxed text-dim">
|
|
Share a Hugging Face model you think belongs in the lab. Karti reviews
|
|
every suggestion, inspects the files and license, and decides what to
|
|
run. Nothing is downloaded or executed automatically. No account
|
|
needed.
|
|
</p>
|
|
</div>
|
|
|
|
<form
|
|
onSubmit={onSubmit}
|
|
className="rise panel mt-10 p-6 sm:p-8"
|
|
style={{ animationDelay: "80ms" }}
|
|
>
|
|
<label className="label block" htmlFor="model">
|
|
huggingface model
|
|
</label>
|
|
<input
|
|
id="model"
|
|
value={model}
|
|
onChange={(e) => setModel(e.target.value)}
|
|
placeholder="Qwen/Qwen3-8B"
|
|
required
|
|
spellCheck={false}
|
|
className="mt-3 w-full border border-rule bg-void px-4 py-3 font-mono text-sm text-ink outline-none transition-colors placeholder:text-dimmer focus:border-signal"
|
|
/>
|
|
<p className="mt-2 text-[11px] text-dimmer">
|
|
`owner/model`, or paste the full huggingface.co URL.
|
|
</p>
|
|
|
|
<label className="label mt-8 block" htmlFor="notes">
|
|
anything we should know{" "}
|
|
<span className="text-dimmer">(optional)</span>
|
|
</label>
|
|
<textarea
|
|
id="notes"
|
|
value={notes}
|
|
onChange={(e) => setNotes(e.target.value)}
|
|
rows={3}
|
|
placeholder="Recommended quant, chat template quirks, what it is meant to be good at…"
|
|
className="mt-3 w-full resize-y border border-rule bg-void px-4 py-3 font-mono text-xs text-ink outline-none transition-colors placeholder:text-dimmer focus:border-signal"
|
|
/>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={busy || !model.trim()}
|
|
className="mt-8 w-full border border-rule-bright px-4 py-3 text-xs uppercase tracking-widest transition-colors hover:border-signal hover:text-signal disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:border-rule-bright disabled:hover:text-ink"
|
|
>
|
|
{busy ? "sending suggestion…" : "send suggestion"}
|
|
</button>
|
|
|
|
{state.kind === "error" && (
|
|
<p className="mt-5 border-l-2 border-fail px-4 py-2 text-xs leading-relaxed text-fail">
|
|
{state.message}
|
|
</p>
|
|
)}
|
|
{state.kind === "ok" && (
|
|
<div className="mt-5 border-l-2 border-pass px-4 py-2">
|
|
<p className="text-xs text-pass">{state.model} suggested.</p>
|
|
<p className="mt-1 text-[11px] leading-relaxed text-dim">
|
|
{state.message}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</form>
|
|
|
|
<section className="mt-12 grid gap-8 sm:grid-cols-2">
|
|
<div>
|
|
<h2 className="label mb-4">what helps review</h2>
|
|
<ul className="space-y-3 text-[11px] leading-relaxed text-dim">
|
|
<li className="border-l border-rule pl-4">
|
|
<span className="text-ink">safetensors weights.</span> We do not
|
|
load `.bin` checkpoints — they are pickles, and loading one is
|
|
remote code execution on our hardware.
|
|
</li>
|
|
<li className="border-l border-rule pl-4">
|
|
<span className="text-ink">A clear source.</span> Public or gated
|
|
is fine to suggest; access is reviewed manually.
|
|
</li>
|
|
<li className="border-l border-rule pl-4">
|
|
<span className="text-ink">Useful context.</span> Tell us the
|
|
recommended quant, license, and what the model is meant to do.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h2 className="label mb-4">what happens next</h2>
|
|
<ul className="space-y-3 text-[11px] leading-relaxed text-dim">
|
|
<li className="border-l border-rule pl-4">
|
|
If Karti approves and schedules it, Lumbridge Bench records
|
|
quality and serving performance on named hardware with the exact
|
|
serving flags.
|
|
</li>
|
|
<li className="border-l border-rule pl-4">
|
|
Private eval samples and internal review notes never become
|
|
public. Selected aggregate score cards can be published after
|
|
review.
|
|
</li>
|
|
<li className="border-l border-rule pl-4">
|
|
A suggestion is not a promise to run a model. There is no
|
|
automatic queue or public-code worker.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|