import { Navigate, createFileRoute } from "@tanstack/react-router" import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { useEffect, useRef, useState } from "react" import type { ReviewerEvalStatus } from "@/lib/api" import { AppShell, SettingsSection } from "@/components/AppShell" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Skeleton } from "@/components/ui/skeleton" import { api } from "@/lib/api" import { useSession } from "@/lib/session" export const Route = createFileRoute("/admin_/evals")({ component: ReviewerEvalPage }) function ReviewerEvalPage() { const session = useSession() if (session.isLoading) { return (
) } if (!session.data) return if (!session.data.is_admin) return return ( ) } function ReviewerEvalRunner() { const qc = useQueryClient() const [limit, setLimit] = useState("") const [error, setError] = useState(null) const status = useQuery({ queryKey: ["reviewerEval"], queryFn: api.getReviewerEval, refetchInterval: (query) => query.state.data?.status === "running" ? 5000 : false, }) const data = status.data const running = data?.status === "running" const onSuccess = (next: ReviewerEvalStatus) => { qc.setQueryData(["reviewerEval"], next) setError(null) } const onError = (e: Error) => setError(e.message) const start = useMutation({ mutationFn: () => { const n = limit.trim() ? Number(limit.trim()) : null if (n !== null && (!Number.isInteger(n) || n <= 0)) { throw new Error("Limit must be a positive whole number") } return api.startReviewerEval(n) }, onSuccess, onError, }) const cancel = useMutation({ mutationFn: () => api.cancelReviewerEval(), onSuccess, onError, }) return (
setLimit(e.target.value)} /> {running && ( )}
{data && (
Status: {data.status} {data.langsmith_project ? ` · ${data.langsmith_project}` : ""} {data.limit ? ` · limit ${data.limit}` : ""} {data.started_at && ( Started: {new Date(data.started_at).toLocaleString()} )} {data.finished_at && ( Finished: {new Date(data.finished_at).toLocaleString()} )} {data.experiment_url && ( View experiment in LangSmith )} {data.error && {data.error}}
)} {error &&

{error}

}
) } function ReviewerEvalLogs() { const status = useQuery({ queryKey: ["reviewerEval"], queryFn: api.getReviewerEval }) const logTail = status.data?.log_tail ?? null const running = status.data?.status === "running" const scrollRef = useRef(null) const [follow, setFollow] = useState(true) useEffect(() => { if (follow && scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight } }, [logTail, follow]) return ( setFollow(e.target.checked)} /> Follow } >
{logTail ? (
 {
              const el = e.currentTarget
              const atBottom =
                el.scrollHeight - el.scrollTop - el.clientHeight < 24
              setFollow(atBottom)
            }}
            className="max-h-[28rem] overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/50 p-3 font-mono text-xs text-foreground"
          >
            {logTail}
          
) : (

{running ? "Waiting for output…" : "No output yet. Run an eval to see logs here."}

)}
) }