-
- Router matched or beat {job.direction === "reverse" ? "the baseline" : "your current model"}
-
-
{pct(routerMatchedOrBeatPct(job.direction, results))}
-
of {(job.judged_count ?? 0).toLocaleString()} judged responses
-
- 0 ? "border-t" : ""}>
-
+ {job.keys.length > 1 && (
+
+
)}
+ {/* results == null re-stated for TS narrowing; hasVerdicts alone cannot narrow it */}
+ {!hasVerdicts || results == null ? (
+
{emptyResultsText(job, resultsError)}
+ ) : (
+ <>
+
+
+ Router matched or beat {job.direction === "reverse" ? "the baseline" : "your current model"}
+
+
+ {pct(routerMatchedOrBeatPct(job.direction, results))}
+
+
+ of {(job.judged_count ?? 0).toLocaleString()} judged responses
+
+
+
+ {results.by_current_model.length > 0 && (
+
+ )}
+ {results.by_tier.length > 0 && (
+
0 ? "border-t" : ""}>
+
+
+ )}
+ >
+ )}
>
);
};
@@ -306,9 +375,9 @@ const DIRECTION_OPTIONS: readonly { value: ShadowEvalDirection; label: string }[
const START_FORM_DESCRIPTION: Record
= {
forward:
- "Duplicates a sampled slice of the key's traffic through the auto-router and has an LLM judge compare both answers blind. The router's answers are never served to users; judge calls bill to the shadowed key.",
+ "Duplicates a sampled slice of the selected keys' traffic through the auto-router and has an LLM judge compare both answers blind. Each key gets its own turn budget. The router's answers are never served to users; judge calls bill to the shadowed key.",
reverse:
- "Duplicates a sampled slice of the traffic the auto-router already serves against a fixed baseline model and has an LLM judge compare both answers blind. The baseline's answers are never served to users; judge calls bill to the shadowed key.",
+ "Duplicates a sampled slice of the traffic the auto-router already serves against a fixed baseline model and has an LLM judge compare both answers blind. Each key gets its own turn budget. The baseline's answers are never served to users; judge calls bill to the shadowed key.",
};
const DURATION_OPTIONS = [
@@ -333,7 +402,7 @@ const Field: React.FC<{ label: string; htmlFor?: string; className?: string; chi
);
-const KeySelect: React.FC<{ value: string; onChange: (token: string) => void }> = ({ value, onChange }) => {
+const KeySelect: React.FC<{ value: string[]; onChange: (tokens: string[]) => void }> = ({ value, onChange }) => {
const [search, setSearch] = useState("");
const { data, isPending, isError, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteKeys(50, {
selectedKeyAlias: search || null,
@@ -350,7 +419,7 @@ const KeySelect: React.FC<{ value: string; onChange: (token: string) => void }>
[data],
);
return (
-