feat(web): info icon on every param with hover/click tooltip; add help text to all params
Browse files
server/models/chatterbox_en.py
CHANGED
|
@@ -23,38 +23,43 @@ class Adapter:
|
|
| 23 |
ParamSpec(
|
| 24 |
name="exaggeration", label="Exaggeration", type="float",
|
| 25 |
default=0.5, min=0.0, max=2.0, step=0.05,
|
| 26 |
-
help="
|
| 27 |
group="basic",
|
| 28 |
),
|
| 29 |
ParamSpec(
|
| 30 |
name="cfg_weight", label="CFG weight", type="float",
|
| 31 |
default=0.5, min=0.0, max=1.0, step=0.05,
|
|
|
|
| 32 |
group="basic",
|
| 33 |
),
|
| 34 |
ParamSpec(
|
| 35 |
name="temperature", label="Temperature", type="float",
|
| 36 |
default=0.8, min=0.1, max=1.5, step=0.05,
|
|
|
|
| 37 |
group="basic",
|
| 38 |
),
|
| 39 |
ParamSpec(
|
| 40 |
name="seed", label="Seed", type="int",
|
| 41 |
default=-1, min=-1, step=1,
|
| 42 |
-
help="-1 draws a random seed
|
| 43 |
group="advanced",
|
| 44 |
),
|
| 45 |
ParamSpec(
|
| 46 |
name="repetition_penalty", label="Repetition penalty", type="float",
|
| 47 |
default=1.2, min=1.0, max=3.0, step=0.05,
|
|
|
|
| 48 |
group="advanced",
|
| 49 |
),
|
| 50 |
ParamSpec(
|
| 51 |
name="min_p", label="Min p", type="float",
|
| 52 |
default=0.05, min=0.0, max=1.0, step=0.01,
|
|
|
|
| 53 |
group="advanced",
|
| 54 |
),
|
| 55 |
ParamSpec(
|
| 56 |
name="top_p", label="Top p", type="float",
|
| 57 |
default=1.0, min=0.0, max=1.0, step=0.01,
|
|
|
|
| 58 |
group="advanced",
|
| 59 |
),
|
| 60 |
]
|
|
|
|
| 23 |
ParamSpec(
|
| 24 |
name="exaggeration", label="Exaggeration", type="float",
|
| 25 |
default=0.5, min=0.0, max=2.0, step=0.05,
|
| 26 |
+
help="How emotive the speech is. Higher pushes prosody, pacing, and emphasis; lower stays flat and neutral.",
|
| 27 |
group="basic",
|
| 28 |
),
|
| 29 |
ParamSpec(
|
| 30 |
name="cfg_weight", label="CFG weight", type="float",
|
| 31 |
default=0.5, min=0.0, max=1.0, step=0.05,
|
| 32 |
+
help="Classifier-free guidance. Higher sticks closer to the reference voice; lower allows more variation but may drift in identity.",
|
| 33 |
group="basic",
|
| 34 |
),
|
| 35 |
ParamSpec(
|
| 36 |
name="temperature", label="Temperature", type="float",
|
| 37 |
default=0.8, min=0.1, max=1.5, step=0.05,
|
| 38 |
+
help="Sampling randomness. Lower = deterministic and safer; higher = more creative but riskier and prone to artifacts.",
|
| 39 |
group="basic",
|
| 40 |
),
|
| 41 |
ParamSpec(
|
| 42 |
name="seed", label="Seed", type="int",
|
| 43 |
default=-1, min=-1, step=1,
|
| 44 |
+
help="Reproducibility. -1 draws a fresh random seed every run; any non-negative value pins the result so you can reproduce it.",
|
| 45 |
group="advanced",
|
| 46 |
),
|
| 47 |
ParamSpec(
|
| 48 |
name="repetition_penalty", label="Repetition penalty", type="float",
|
| 49 |
default=1.2, min=1.0, max=3.0, step=0.05,
|
| 50 |
+
help="Discourages repeating the same tokens. >1 reduces stuttering and loops; too high hurts natural fluency.",
|
| 51 |
group="advanced",
|
| 52 |
),
|
| 53 |
ParamSpec(
|
| 54 |
name="min_p", label="Min p", type="float",
|
| 55 |
default=0.05, min=0.0, max=1.0, step=0.01,
|
| 56 |
+
help="Cuts off tokens whose probability is below this fraction of the top token's. Higher trims more aggressively.",
|
| 57 |
group="advanced",
|
| 58 |
),
|
| 59 |
ParamSpec(
|
| 60 |
name="top_p", label="Top p", type="float",
|
| 61 |
default=1.0, min=0.0, max=1.0, step=0.01,
|
| 62 |
+
help="Nucleus sampling. Keep tokens until cumulative probability reaches this. Lower = safer/conservative.",
|
| 63 |
group="advanced",
|
| 64 |
),
|
| 65 |
]
|
server/models/chatterbox_mtl.py
CHANGED
|
@@ -50,37 +50,43 @@ class Adapter:
|
|
| 50 |
ParamSpec(
|
| 51 |
name="exaggeration", label="Exaggeration", type="float",
|
| 52 |
default=0.5, min=0.0, max=2.0, step=0.05,
|
|
|
|
| 53 |
group="basic",
|
| 54 |
),
|
| 55 |
ParamSpec(
|
| 56 |
name="cfg_weight", label="CFG weight", type="float",
|
| 57 |
default=0.5, min=0.0, max=1.0, step=0.05,
|
|
|
|
| 58 |
group="basic",
|
| 59 |
),
|
| 60 |
ParamSpec(
|
| 61 |
name="temperature", label="Temperature", type="float",
|
| 62 |
default=0.8, min=0.1, max=1.5, step=0.05,
|
|
|
|
| 63 |
group="basic",
|
| 64 |
),
|
| 65 |
ParamSpec(
|
| 66 |
name="repetition_penalty", label="Repetition penalty", type="float",
|
| 67 |
default=2.0, min=1.0, max=3.0, step=0.05,
|
|
|
|
| 68 |
group="basic",
|
| 69 |
),
|
| 70 |
ParamSpec(
|
| 71 |
name="seed", label="Seed", type="int",
|
| 72 |
default=-1, min=-1, step=1,
|
| 73 |
-
help="-1 draws a random seed
|
| 74 |
group="advanced",
|
| 75 |
),
|
| 76 |
ParamSpec(
|
| 77 |
name="min_p", label="Min p", type="float",
|
| 78 |
default=0.05, min=0.0, max=1.0, step=0.01,
|
|
|
|
| 79 |
group="advanced",
|
| 80 |
),
|
| 81 |
ParamSpec(
|
| 82 |
name="top_p", label="Top p", type="float",
|
| 83 |
default=1.0, min=0.0, max=1.0, step=0.01,
|
|
|
|
| 84 |
group="advanced",
|
| 85 |
),
|
| 86 |
]
|
|
|
|
| 50 |
ParamSpec(
|
| 51 |
name="exaggeration", label="Exaggeration", type="float",
|
| 52 |
default=0.5, min=0.0, max=2.0, step=0.05,
|
| 53 |
+
help="How emotive the speech is. Higher pushes prosody and emphasis; lower stays flat and neutral.",
|
| 54 |
group="basic",
|
| 55 |
),
|
| 56 |
ParamSpec(
|
| 57 |
name="cfg_weight", label="CFG weight", type="float",
|
| 58 |
default=0.5, min=0.0, max=1.0, step=0.05,
|
| 59 |
+
help="Classifier-free guidance. Higher sticks closer to the reference voice; lower allows more variation but may drift in identity.",
|
| 60 |
group="basic",
|
| 61 |
),
|
| 62 |
ParamSpec(
|
| 63 |
name="temperature", label="Temperature", type="float",
|
| 64 |
default=0.8, min=0.1, max=1.5, step=0.05,
|
| 65 |
+
help="Sampling randomness. Lower = deterministic and safer; higher = more creative but riskier and prone to artifacts.",
|
| 66 |
group="basic",
|
| 67 |
),
|
| 68 |
ParamSpec(
|
| 69 |
name="repetition_penalty", label="Repetition penalty", type="float",
|
| 70 |
default=2.0, min=1.0, max=3.0, step=0.05,
|
| 71 |
+
help="Discourages repeating the same tokens. Higher than for English because non-Latin scripts loop more easily.",
|
| 72 |
group="basic",
|
| 73 |
),
|
| 74 |
ParamSpec(
|
| 75 |
name="seed", label="Seed", type="int",
|
| 76 |
default=-1, min=-1, step=1,
|
| 77 |
+
help="Reproducibility. -1 draws a fresh random seed every run; any non-negative value pins the result so you can reproduce it.",
|
| 78 |
group="advanced",
|
| 79 |
),
|
| 80 |
ParamSpec(
|
| 81 |
name="min_p", label="Min p", type="float",
|
| 82 |
default=0.05, min=0.0, max=1.0, step=0.01,
|
| 83 |
+
help="Cuts off tokens whose probability is below this fraction of the top token's. Higher trims more aggressively.",
|
| 84 |
group="advanced",
|
| 85 |
),
|
| 86 |
ParamSpec(
|
| 87 |
name="top_p", label="Top p", type="float",
|
| 88 |
default=1.0, min=0.0, max=1.0, step=0.01,
|
| 89 |
+
help="Nucleus sampling. Keep tokens until cumulative probability reaches this. Lower = safer/conservative.",
|
| 90 |
group="advanced",
|
| 91 |
),
|
| 92 |
]
|
server/models/chatterbox_turbo.py
CHANGED
|
@@ -34,37 +34,43 @@ class Adapter:
|
|
| 34 |
ParamSpec(
|
| 35 |
name="temperature", label="Temperature", type="float",
|
| 36 |
default=0.8, min=0.1, max=1.5, step=0.05,
|
|
|
|
| 37 |
group="basic",
|
| 38 |
),
|
| 39 |
ParamSpec(
|
| 40 |
name="top_p", label="Top p", type="float",
|
| 41 |
default=0.95, min=0.0, max=1.0, step=0.01,
|
|
|
|
| 42 |
group="basic",
|
| 43 |
),
|
| 44 |
ParamSpec(
|
| 45 |
name="repetition_penalty", label="Repetition penalty", type="float",
|
| 46 |
default=1.2, min=1.0, max=3.0, step=0.05,
|
|
|
|
| 47 |
group="basic",
|
| 48 |
),
|
| 49 |
ParamSpec(
|
| 50 |
name="seed", label="Seed", type="int",
|
| 51 |
default=-1, min=-1, step=1,
|
| 52 |
-
help="-1 draws a random seed
|
| 53 |
group="advanced",
|
| 54 |
),
|
| 55 |
ParamSpec(
|
| 56 |
name="top_k", label="Top k", type="int",
|
| 57 |
default=1000, min=1, max=4000, step=1,
|
|
|
|
| 58 |
group="advanced",
|
| 59 |
),
|
| 60 |
ParamSpec(
|
| 61 |
name="exaggeration", label="Exaggeration", type="float",
|
| 62 |
default=0.0, min=0.0, max=2.0, step=0.05,
|
|
|
|
| 63 |
group="advanced",
|
| 64 |
),
|
| 65 |
ParamSpec(
|
| 66 |
name="cfg_weight", label="CFG weight", type="float",
|
| 67 |
default=0.0, min=0.0, max=1.0, step=0.05,
|
|
|
|
| 68 |
group="advanced",
|
| 69 |
),
|
| 70 |
]
|
|
|
|
| 34 |
ParamSpec(
|
| 35 |
name="temperature", label="Temperature", type="float",
|
| 36 |
default=0.8, min=0.1, max=1.5, step=0.05,
|
| 37 |
+
help="Sampling randomness. Lower = deterministic and safer; higher = more creative but riskier and prone to artifacts.",
|
| 38 |
group="basic",
|
| 39 |
),
|
| 40 |
ParamSpec(
|
| 41 |
name="top_p", label="Top p", type="float",
|
| 42 |
default=0.95, min=0.0, max=1.0, step=0.01,
|
| 43 |
+
help="Nucleus sampling. Keep tokens until cumulative probability reaches this. Lower = safer/conservative.",
|
| 44 |
group="basic",
|
| 45 |
),
|
| 46 |
ParamSpec(
|
| 47 |
name="repetition_penalty", label="Repetition penalty", type="float",
|
| 48 |
default=1.2, min=1.0, max=3.0, step=0.05,
|
| 49 |
+
help="Discourages repeating the same tokens. >1 reduces stuttering and loops; too high hurts natural fluency.",
|
| 50 |
group="basic",
|
| 51 |
),
|
| 52 |
ParamSpec(
|
| 53 |
name="seed", label="Seed", type="int",
|
| 54 |
default=-1, min=-1, step=1,
|
| 55 |
+
help="Reproducibility. -1 draws a fresh random seed every run; any non-negative value pins the result so you can reproduce it.",
|
| 56 |
group="advanced",
|
| 57 |
),
|
| 58 |
ParamSpec(
|
| 59 |
name="top_k", label="Top k", type="int",
|
| 60 |
default=1000, min=1, max=4000, step=1,
|
| 61 |
+
help="Sample only from the top-k most likely tokens. Higher = more diversity. Turbo defaults to a wide pool.",
|
| 62 |
group="advanced",
|
| 63 |
),
|
| 64 |
ParamSpec(
|
| 65 |
name="exaggeration", label="Exaggeration", type="float",
|
| 66 |
default=0.0, min=0.0, max=2.0, step=0.05,
|
| 67 |
+
help="How emotive the speech is. Turbo defaults to 0 (flat); raise it for more expressive prosody.",
|
| 68 |
group="advanced",
|
| 69 |
),
|
| 70 |
ParamSpec(
|
| 71 |
name="cfg_weight", label="CFG weight", type="float",
|
| 72 |
default=0.0, min=0.0, max=1.0, step=0.05,
|
| 73 |
+
help="Classifier-free guidance. Higher sticks closer to the reference voice; lower allows more variation.",
|
| 74 |
group="advanced",
|
| 75 |
),
|
| 76 |
]
|
web/src/components/InfoTip.tsx
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useRef, useState } from "react";
|
| 2 |
+
|
| 3 |
+
type Props = { text: string };
|
| 4 |
+
|
| 5 |
+
export default function InfoTip({ text }: Props) {
|
| 6 |
+
const [hover, setHover] = useState(false);
|
| 7 |
+
const [sticky, setSticky] = useState(false);
|
| 8 |
+
const ref = useRef<HTMLSpanElement>(null);
|
| 9 |
+
const open = hover || sticky;
|
| 10 |
+
|
| 11 |
+
useEffect(() => {
|
| 12 |
+
if (!sticky) return;
|
| 13 |
+
function onDocClick(e: MouseEvent) {
|
| 14 |
+
if (ref.current && !ref.current.contains(e.target as Node)) {
|
| 15 |
+
setSticky(false);
|
| 16 |
+
}
|
| 17 |
+
}
|
| 18 |
+
document.addEventListener("mousedown", onDocClick);
|
| 19 |
+
return () => document.removeEventListener("mousedown", onDocClick);
|
| 20 |
+
}, [sticky]);
|
| 21 |
+
|
| 22 |
+
return (
|
| 23 |
+
<span ref={ref} className="relative inline-flex align-middle">
|
| 24 |
+
<button
|
| 25 |
+
type="button"
|
| 26 |
+
aria-label="What does this parameter do?"
|
| 27 |
+
onClick={(e) => {
|
| 28 |
+
e.stopPropagation();
|
| 29 |
+
setSticky((s) => !s);
|
| 30 |
+
}}
|
| 31 |
+
onMouseEnter={() => setHover(true)}
|
| 32 |
+
onMouseLeave={() => setHover(false)}
|
| 33 |
+
className="inline-flex size-4 items-center justify-center rounded-full border border-border text-[10px] font-mono italic text-muted-foreground hover:text-[hsl(var(--ember))] hover:border-[hsl(var(--ember))]/60 transition-colors leading-none"
|
| 34 |
+
>
|
| 35 |
+
i
|
| 36 |
+
</button>
|
| 37 |
+
{open && (
|
| 38 |
+
<span
|
| 39 |
+
role="tooltip"
|
| 40 |
+
className="absolute left-1/2 -translate-x-1/2 bottom-full mb-2 w-56 px-3 py-2 rounded-sm border border-border bg-paper text-[11px] leading-snug text-foreground shadow-lg z-50 normal-case tracking-normal pointer-events-none"
|
| 41 |
+
>
|
| 42 |
+
{text}
|
| 43 |
+
</span>
|
| 44 |
+
)}
|
| 45 |
+
</span>
|
| 46 |
+
);
|
| 47 |
+
}
|
web/src/components/ParamsPanel.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
import type { ParamSpec } from "@/lib/api";
|
|
|
|
| 2 |
|
| 3 |
type Props = {
|
| 4 |
specs: ParamSpec[];
|
|
@@ -6,6 +7,15 @@ type Props = {
|
|
| 6 |
onChange: (next: Record<string, unknown>) => void;
|
| 7 |
};
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
function renderControl(
|
| 10 |
s: ParamSpec,
|
| 11 |
values: Record<string, unknown>,
|
|
@@ -17,7 +27,7 @@ function renderControl(
|
|
| 17 |
const v = (values[s.name] ?? s.default) as number;
|
| 18 |
return (
|
| 19 |
<div key={s.name} className="space-y-1.5">
|
| 20 |
-
<
|
| 21 |
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
|
| 22 |
<input
|
| 23 |
id={id}
|
|
@@ -40,9 +50,6 @@ function renderControl(
|
|
| 40 |
<span className="label-mono text-muted-foreground">(random per generate)</span>
|
| 41 |
)}
|
| 42 |
</div>
|
| 43 |
-
{s.help && (
|
| 44 |
-
<p className="text-[11px] text-muted-foreground/80 italic">{s.help}</p>
|
| 45 |
-
)}
|
| 46 |
</div>
|
| 47 |
);
|
| 48 |
}
|
|
@@ -51,7 +58,7 @@ function renderControl(
|
|
| 51 |
return (
|
| 52 |
<div key={s.name} className="space-y-1.5">
|
| 53 |
<div className="flex items-baseline justify-between">
|
| 54 |
-
<
|
| 55 |
<span className="font-mono text-[12px] text-foreground tracking-wider">
|
| 56 |
{Number.isFinite(n) ? n.toFixed(2) : String(current)}
|
| 57 |
</span>
|
|
@@ -67,20 +74,13 @@ function renderControl(
|
|
| 67 |
onChange={(e) => set(s.name, Number(e.target.value))}
|
| 68 |
className="w-full accent-[hsl(var(--ember))]"
|
| 69 |
/>
|
| 70 |
-
{s.help && (
|
| 71 |
-
<p className="text-[11px] text-muted-foreground/80 italic">{s.help}</p>
|
| 72 |
-
)}
|
| 73 |
</div>
|
| 74 |
);
|
| 75 |
}
|
| 76 |
if (s.type === "bool") {
|
| 77 |
return (
|
| 78 |
-
<
|
| 79 |
-
|
| 80 |
-
htmlFor={id}
|
| 81 |
-
className="flex items-center justify-between cursor-pointer"
|
| 82 |
-
>
|
| 83 |
-
<span className="label-mono">{s.label}</span>
|
| 84 |
<input
|
| 85 |
id={id}
|
| 86 |
aria-label={s.label}
|
|
@@ -89,12 +89,12 @@ function renderControl(
|
|
| 89 |
onChange={(e) => set(s.name, e.target.checked)}
|
| 90 |
className="accent-[hsl(var(--ember))]"
|
| 91 |
/>
|
| 92 |
-
</
|
| 93 |
);
|
| 94 |
}
|
| 95 |
return (
|
| 96 |
<div key={s.name} className="space-y-1.5">
|
| 97 |
-
<
|
| 98 |
<select
|
| 99 |
id={id}
|
| 100 |
aria-label={s.label}
|
|
|
|
| 1 |
import type { ParamSpec } from "@/lib/api";
|
| 2 |
+
import InfoTip from "@/components/InfoTip";
|
| 3 |
|
| 4 |
type Props = {
|
| 5 |
specs: ParamSpec[];
|
|
|
|
| 7 |
onChange: (next: Record<string, unknown>) => void;
|
| 8 |
};
|
| 9 |
|
| 10 |
+
function ParamLabel({ id, label, help }: { id: string; label: string; help?: string }) {
|
| 11 |
+
return (
|
| 12 |
+
<span className="inline-flex items-center gap-1.5">
|
| 13 |
+
<label htmlFor={id} className="label-mono">{label}</label>
|
| 14 |
+
{help && <InfoTip text={help} />}
|
| 15 |
+
</span>
|
| 16 |
+
);
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
function renderControl(
|
| 20 |
s: ParamSpec,
|
| 21 |
values: Record<string, unknown>,
|
|
|
|
| 27 |
const v = (values[s.name] ?? s.default) as number;
|
| 28 |
return (
|
| 29 |
<div key={s.name} className="space-y-1.5">
|
| 30 |
+
<ParamLabel id={id} label={s.label} help={s.help} />
|
| 31 |
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
|
| 32 |
<input
|
| 33 |
id={id}
|
|
|
|
| 50 |
<span className="label-mono text-muted-foreground">(random per generate)</span>
|
| 51 |
)}
|
| 52 |
</div>
|
|
|
|
|
|
|
|
|
|
| 53 |
</div>
|
| 54 |
);
|
| 55 |
}
|
|
|
|
| 58 |
return (
|
| 59 |
<div key={s.name} className="space-y-1.5">
|
| 60 |
<div className="flex items-baseline justify-between">
|
| 61 |
+
<ParamLabel id={id} label={s.label} help={s.help} />
|
| 62 |
<span className="font-mono text-[12px] text-foreground tracking-wider">
|
| 63 |
{Number.isFinite(n) ? n.toFixed(2) : String(current)}
|
| 64 |
</span>
|
|
|
|
| 74 |
onChange={(e) => set(s.name, Number(e.target.value))}
|
| 75 |
className="w-full accent-[hsl(var(--ember))]"
|
| 76 |
/>
|
|
|
|
|
|
|
|
|
|
| 77 |
</div>
|
| 78 |
);
|
| 79 |
}
|
| 80 |
if (s.type === "bool") {
|
| 81 |
return (
|
| 82 |
+
<div key={s.name} className="flex items-center justify-between">
|
| 83 |
+
<ParamLabel id={id} label={s.label} help={s.help} />
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
<input
|
| 85 |
id={id}
|
| 86 |
aria-label={s.label}
|
|
|
|
| 89 |
onChange={(e) => set(s.name, e.target.checked)}
|
| 90 |
className="accent-[hsl(var(--ember))]"
|
| 91 |
/>
|
| 92 |
+
</div>
|
| 93 |
);
|
| 94 |
}
|
| 95 |
return (
|
| 96 |
<div key={s.name} className="space-y-1.5">
|
| 97 |
+
<ParamLabel id={id} label={s.label} help={s.help} />
|
| 98 |
<select
|
| 99 |
id={id}
|
| 100 |
aria-label={s.label}
|