mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-08 03:07:53 +00:00
Refine evals objective selection and recommendation URL defaults
This commit is contained in:
parent
132f7af6ca
commit
1d03382f26
10 changed files with 138 additions and 25 deletions
|
|
@ -147,7 +147,9 @@ export function MethodologyContent() {
|
|||
Evals
|
||||
</Link>
|
||||
<span className="text-border">/</span>
|
||||
<Link href="/evals/workers" className="transition-colors hover:text-foreground">
|
||||
<Link
|
||||
href="/evals/recommendations"
|
||||
className="transition-colors hover:text-foreground">
|
||||
Recommendations
|
||||
</Link>
|
||||
<span className="text-border">/</span>
|
||||
|
|
@ -183,7 +185,7 @@ export function MethodologyContent() {
|
|||
variants={fadeUpVariants}
|
||||
className="mt-10 flex flex-wrap items-center gap-5">
|
||||
<Link
|
||||
href="/evals/workers"
|
||||
href="/evals/recommendations"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-full border border-foreground/20 bg-foreground/5 px-5 py-2.5 text-sm font-semibold text-foreground transition-colors hover:bg-foreground/10">
|
||||
View recommendations <InlineArrow />
|
||||
</Link>
|
||||
|
|
@ -500,7 +502,7 @@ export function MethodologyContent() {
|
|||
Links
|
||||
</p>
|
||||
<div className="mt-4 flex flex-col gap-3">
|
||||
<SmallLink href="/evals/workers" label="Recommendations" />
|
||||
<SmallLink href="/evals/recommendations" label="Recommendations" />
|
||||
<SmallLink href="/evals" label="Raw eval data" />
|
||||
</div>
|
||||
<div className="mt-5 border-t border-border/40 pt-5">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
export { generateMetadata } from "../../../workers/[roleId]/compare/page"
|
||||
export { default } from "../../../workers/[roleId]/compare/page"
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export { generateMetadata } from "../../workers/[roleId]/page"
|
||||
export { default } from "../../workers/[roleId]/page"
|
||||
94
apps/web-roo-code/src/app/evals/recommendations/page.tsx
Normal file
94
apps/web-roo-code/src/app/evals/recommendations/page.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import type { Metadata } from "next"
|
||||
import { Fraunces, IBM_Plex_Sans } from "next/font/google"
|
||||
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
import { getEngineerRoles, getAllRecommendations } from "@/lib/mock-recommendations"
|
||||
|
||||
import { WorkersContent } from "../workers/workers-content"
|
||||
|
||||
// ── SEO Metadata ────────────────────────────────────────────────────────────
|
||||
|
||||
const TITLE = "Build with Roo Code Cloud | Roo Code Evals"
|
||||
const DESCRIPTION =
|
||||
"Outcome-first, eval-backed recommendations for shipping production code. Start from your objective and pick a tradeoff."
|
||||
const OG_DESCRIPTION = "Outcome-first recommendations for shipping production code"
|
||||
const PATH = "/evals/recommendations"
|
||||
|
||||
const display = Fraunces({ subsets: ["latin"], variable: "--font-display" })
|
||||
const body = IBM_Plex_Sans({ subsets: ["latin"], weight: ["400", "500", "600"], variable: "--font-body" })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
alternates: {
|
||||
canonical: `${SEO.url}${PATH}`,
|
||||
},
|
||||
openGraph: {
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
url: `${SEO.url}${PATH}`,
|
||||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
type: "website",
|
||||
},
|
||||
twitter: {
|
||||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
keywords: [
|
||||
...SEO.keywords,
|
||||
"AI coding",
|
||||
"coding agents",
|
||||
"roo code cloud",
|
||||
"model recommendations",
|
||||
"coding evals",
|
||||
"model comparison",
|
||||
"shipping code",
|
||||
"prototype",
|
||||
],
|
||||
}
|
||||
|
||||
// ── Page Component ──────────────────────────────────────────────────────────
|
||||
|
||||
export default function RecommendationsPage() {
|
||||
const roles = getEngineerRoles()
|
||||
const recommendations = getAllRecommendations()
|
||||
|
||||
// Aggregate totals
|
||||
const totalEvalRuns = recommendations.reduce((sum, recommendation) => sum + recommendation.totalEvalRuns, 0)
|
||||
const totalExercises = recommendations.reduce((sum, recommendation) => sum + recommendation.totalExercises, 0)
|
||||
const uniqueModels = new Set(
|
||||
recommendations.flatMap((recommendation) => recommendation.allCandidates.map((candidate) => candidate.modelId)),
|
||||
)
|
||||
const totalModels = uniqueModels.size
|
||||
|
||||
const lastUpdated = recommendations
|
||||
.map((r) => r.lastUpdated)
|
||||
.sort()
|
||||
.pop()
|
||||
|
||||
return (
|
||||
<div className={`${body.variable} ${display.variable} [font-family:var(--font-body)]`}>
|
||||
<WorkersContent
|
||||
roles={roles}
|
||||
recommendations={recommendations}
|
||||
totalEvalRuns={totalEvalRuns}
|
||||
totalExercises={totalExercises}
|
||||
totalModels={totalModels}
|
||||
lastUpdated={lastUpdated}
|
||||
workersRootPath="/evals/recommendations"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -8,5 +8,5 @@ type PageProps = {
|
|||
|
||||
export default async function WorkersV2Page({ searchParams }: PageProps) {
|
||||
const sp = (await searchParams) ?? {}
|
||||
permanentRedirect(`/evals/workers${buildQueryString(sp)}`)
|
||||
permanentRedirect(`/evals/recommendations${buildQueryString(sp)}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -660,7 +660,7 @@ export function CandidatesContent({
|
|||
totalExercises,
|
||||
lastUpdated,
|
||||
cloudUrls,
|
||||
workersRootPath = "/evals/workers",
|
||||
workersRootPath = "/evals/recommendations",
|
||||
}: CandidatesContentProps) {
|
||||
const searchParams = useSearchParams()
|
||||
const theme = ROLE_THEMES[roleId] ?? DEFAULT_THEME
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ export function ComparisonChart({
|
|||
recommendation,
|
||||
role,
|
||||
roleId,
|
||||
workersRootPath = "/evals/workers",
|
||||
workersRootPath = "/evals/recommendations",
|
||||
}: ComparisonChartProps) {
|
||||
const searchParams = useSearchParams()
|
||||
const { allCandidates } = recommendation
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
|
|||
const title = `Compare Models — ${role.name} | Roo Code Evals`
|
||||
const description = `Interactive comparison of AI models for the ${role.name} setup. Compare composite score, success rate, cost efficiency, and speed.`
|
||||
const ogDescription = `Compare Models — ${role.name}`
|
||||
const path = `/evals/workers/${roleId}/compare`
|
||||
const path = `/evals/recommendations/${roleId}/compare`
|
||||
|
||||
return {
|
||||
title,
|
||||
|
|
@ -82,7 +82,7 @@ export default async function CompareModelsPage({ params }: PageProps) {
|
|||
recommendation={recommendation}
|
||||
role={recommendation.role}
|
||||
roleId={roleId}
|
||||
workersRootPath="/evals/workers"
|
||||
workersRootPath="/evals/recommendations"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
|
|||
const title = `${role.name} — Recommended Models | Roo Code Evals`
|
||||
const description = `Eval-backed recommendations for ${role.name}. Compare models by success rate, cost, and speed across 5 languages.`
|
||||
const ogDescription = `${role.name} — Recommended Models`
|
||||
const path = `/evals/workers/${roleId}`
|
||||
const path = `/evals/recommendations/${roleId}`
|
||||
|
||||
return {
|
||||
title,
|
||||
|
|
@ -100,7 +100,7 @@ export default async function RoleCandidatesPage({ params }: PageProps) {
|
|||
totalExercises={totalExercises}
|
||||
lastUpdated={lastUpdated}
|
||||
cloudUrls={cloudUrls}
|
||||
workersRootPath="/evals/workers"
|
||||
workersRootPath="/evals/recommendations"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ export function WorkersContent({
|
|||
totalExercises: _totalExercises,
|
||||
totalModels: _totalModels,
|
||||
lastUpdated,
|
||||
workersRootPath = "/evals/workers",
|
||||
workersRootPath = "/evals/recommendations",
|
||||
}: WorkersContentProps) {
|
||||
const enableOutcomeLayer = ENABLE_OUTCOME_LAYER
|
||||
const router = useRouter()
|
||||
|
|
@ -214,6 +214,10 @@ export function WorkersContent({
|
|||
if (!outcome) return null
|
||||
return isEvalOutcomeId(outcome) ? outcome : null
|
||||
}, [searchParams])
|
||||
const effectiveOutcomeId = useMemo(() => {
|
||||
if (selectedOutcomeId) return selectedOutcomeId
|
||||
return EVAL_OUTCOMES[0]?.id ?? null
|
||||
}, [selectedOutcomeId])
|
||||
|
||||
const selectedMode = useMemo((): EvalOptimizationMode => {
|
||||
const mode = searchParams.get("mode")
|
||||
|
|
@ -255,18 +259,27 @@ export function WorkersContent({
|
|||
const roleById = useMemo(() => new Map(roles.map((r) => [r.id, r])), [roles])
|
||||
|
||||
const selectedOutcome = useMemo(() => {
|
||||
if (!selectedOutcomeId) return null
|
||||
return EVAL_OUTCOMES.find((o) => o.id === selectedOutcomeId) ?? null
|
||||
}, [selectedOutcomeId])
|
||||
if (!effectiveOutcomeId) return null
|
||||
return EVAL_OUTCOMES.find((o) => o.id === effectiveOutcomeId) ?? null
|
||||
}, [effectiveOutcomeId])
|
||||
|
||||
const setupQuery = useMemo(() => {
|
||||
if (!selectedOutcomeId) return ""
|
||||
if (!effectiveOutcomeId) return ""
|
||||
const params = new URLSearchParams()
|
||||
params.set("outcome", selectedOutcomeId)
|
||||
params.set("outcome", effectiveOutcomeId)
|
||||
params.set("mode", selectedMode)
|
||||
const query = params.toString()
|
||||
return query ? `?${query}` : ""
|
||||
}, [selectedOutcomeId, selectedMode])
|
||||
}, [effectiveOutcomeId, selectedMode])
|
||||
|
||||
const profileViewQuery = useMemo(() => {
|
||||
if (!effectiveOutcomeId) return ""
|
||||
const params = new URLSearchParams()
|
||||
params.set("outcome", effectiveOutcomeId)
|
||||
params.set("mode", selectedMode)
|
||||
params.set("view", "profile")
|
||||
return `?${params.toString()}`
|
||||
}, [effectiveOutcomeId, selectedMode])
|
||||
|
||||
const isProfileView = useMemo(() => {
|
||||
return searchParams.get("view") === "profile"
|
||||
|
|
@ -277,22 +290,22 @@ export function WorkersContent({
|
|||
"A default setup built from our eval signals. It’s a baseline, not a guarantee."
|
||||
const profileHowItWorks = selectedOutcome?.builderProfile?.howItWorks ?? selectedOutcome?.whyItWorks ?? []
|
||||
const objectiveDefaultModel = useMemo(() => {
|
||||
if (!selectedOutcomeId) return null
|
||||
return pickObjectiveDefaultModelV1(selectedOutcomeId, selectedMode)
|
||||
}, [selectedOutcomeId, selectedMode])
|
||||
if (!effectiveOutcomeId) return null
|
||||
return pickObjectiveDefaultModelV1(effectiveOutcomeId, selectedMode)
|
||||
}, [effectiveOutcomeId, selectedMode])
|
||||
const objectiveDefaultModelLabel = useMemo(() => {
|
||||
if (!objectiveDefaultModel?.modelId) return "—"
|
||||
return formatModelIdForUi(objectiveDefaultModel.modelId)
|
||||
}, [objectiveDefaultModel])
|
||||
const examplePrompt = selectedOutcome?.builderProfile?.examplePrompt ?? ""
|
||||
const cloudSetupHref = useMemo(() => {
|
||||
if (!selectedOutcomeId) return "/cloud-agents/setup"
|
||||
if (!effectiveOutcomeId) return "/cloud-agents/setup"
|
||||
const params = new URLSearchParams()
|
||||
params.set("outcome", selectedOutcomeId)
|
||||
params.set("outcome", effectiveOutcomeId)
|
||||
params.set("mode", selectedMode)
|
||||
if (examplePrompt) params.set("prompt", examplePrompt)
|
||||
return `/cloud-agents/setup?${params.toString()}`
|
||||
}, [examplePrompt, selectedMode, selectedOutcomeId])
|
||||
}, [examplePrompt, selectedMode, effectiveOutcomeId])
|
||||
|
||||
const profileCapabilities = useMemo(() => {
|
||||
if (!selectedOutcome) return []
|
||||
|
|
@ -741,7 +754,7 @@ export function WorkersContent({
|
|||
<motion.div className="mt-8 space-y-2" variants={containerVariants}>
|
||||
{EVAL_OUTCOMES.map((outcome) => {
|
||||
const Icon = outcome.icon
|
||||
const isSelected = outcome.id === selectedOutcomeId
|
||||
const isSelected = outcome.id === effectiveOutcomeId
|
||||
|
||||
return (
|
||||
<motion.button
|
||||
|
|
@ -946,7 +959,7 @@ export function WorkersContent({
|
|||
<ArrowRight className="size-4" />
|
||||
</Link>
|
||||
<Link
|
||||
href={`${workersRootPath}${setupQuery}&view=profile#outcomes`}
|
||||
href={`${workersRootPath}${profileViewQuery}#outcomes`}
|
||||
className="inline-flex flex-1 items-center justify-center gap-2 rounded-2xl border border-border/60 bg-background/10 px-4 py-3 text-sm font-semibold text-muted-foreground transition-colors hover:border-border hover:bg-background/15 hover:text-foreground">
|
||||
Learn more / customize
|
||||
<ArrowRight className="size-4" />
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue