Skip to content

Commit 67788a2

Browse files
Add browser upload flow and wire homepage actions
1 parent 1c3d21c commit 67788a2

10 files changed

Lines changed: 730 additions & 97 deletions

File tree

web/app/api/proxy/[...path]/route.ts

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,72 @@ type RouteContext = {
88
};
99
};
1010

11-
export async function GET(request: NextRequest, { params }: RouteContext) {
11+
function getUpstreamUrl(request: NextRequest, params: RouteContext["params"]) {
1212
const normalizedPath = params.path[0] === "api" ? params.path.slice(1) : params.path;
1313
const upstreamPath = normalizedPath.join("/");
1414
const upstreamUrl = new URL(`${API_BASE}/api/${upstreamPath}`);
1515
upstreamUrl.search = request.nextUrl.search;
16+
return upstreamUrl;
17+
}
18+
19+
function copyRequestHeaders(request: NextRequest) {
20+
const headers = new Headers();
21+
for (const header of ["accept", "authorization", "x-api-key", "x-upload-token"]) {
22+
const value = request.headers.get(header);
23+
if (value) {
24+
headers.set(header, value);
25+
}
26+
}
27+
return headers;
28+
}
29+
30+
async function forwardRequest(
31+
request: NextRequest,
32+
{ params }: RouteContext,
33+
method: "GET" | "POST"
34+
) {
35+
const upstreamUrl = getUpstreamUrl(request, params);
36+
const headers = copyRequestHeaders(request);
1637

1738
try {
39+
let body: FormData | string | ArrayBuffer | undefined;
40+
41+
if (method === "POST") {
42+
const contentType = request.headers.get("content-type") ?? "";
43+
if (contentType.includes("multipart/form-data")) {
44+
body = await request.formData();
45+
} else if (contentType.includes("application/json") || contentType.includes("text/")) {
46+
body = await request.text();
47+
if (contentType) {
48+
headers.set("content-type", contentType);
49+
}
50+
} else {
51+
const buffer = await request.arrayBuffer();
52+
body = buffer.byteLength > 0 ? buffer : undefined;
53+
if (contentType) {
54+
headers.set("content-type", contentType);
55+
}
56+
}
57+
}
58+
1859
const upstream = await fetch(upstreamUrl, {
60+
method,
1961
cache: "no-store",
20-
headers: {
21-
accept: request.headers.get("accept") ?? "*/*"
22-
}
62+
headers,
63+
body
2364
});
2465

25-
const body = await upstream.arrayBuffer();
26-
const response = new NextResponse(body, { status: upstream.status });
66+
const responseBody = await upstream.arrayBuffer();
67+
const response = new NextResponse(responseBody, { status: upstream.status });
2768
const contentType = upstream.headers.get("content-type");
69+
const requestId = upstream.headers.get("x-request-id");
2870

2971
if (contentType) {
3072
response.headers.set("content-type", contentType);
3173
}
74+
if (requestId) {
75+
response.headers.set("x-request-id", requestId);
76+
}
3277

3378
return response;
3479
} catch {
@@ -38,3 +83,11 @@ export async function GET(request: NextRequest, { params }: RouteContext) {
3883
);
3984
}
4085
}
86+
87+
export async function GET(request: NextRequest, context: RouteContext) {
88+
return forwardRequest(request, context, "GET");
89+
}
90+
91+
export async function POST(request: NextRequest, context: RouteContext) {
92+
return forwardRequest(request, context, "POST");
93+
}

web/app/page.tsx

Lines changed: 17 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import Link from "next/link";
22

3+
import { WorkflowShowcase } from "@/components/workflow-showcase";
4+
35
const signalCards = [
46
{
57
title: "Prompt-driven benchmark creation",
@@ -21,21 +23,6 @@ const agenticFlow = [
2123
"Publish traces, returns, and planning cost in a polished public benchmark."
2224
];
2325

24-
const benchmarkTabs = [
25-
"Create",
26-
"Evaluate",
27-
"Upload",
28-
"Compare"
29-
];
30-
31-
const benchmarkSteps = [
32-
"Prompt-based world specification",
33-
"Reference traces and task defaults",
34-
"Multi-view metric summaries",
35-
"Leaderboards per track",
36-
"Run-level trace inspection"
37-
];
38-
3926
const manufacturerGrid = [
4027
"Benchmark tracks",
4128
"Environment defaults",
@@ -70,10 +57,10 @@ export default function HomePage() {
7057
</p>
7158
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
7259
<Link
73-
href="/leaderboard"
60+
href="/upload"
7461
className="button-primary px-8 py-4 text-sm font-semibold"
7562
>
76-
Start Exploring
63+
Open Upload Studio
7764
</Link>
7865
<Link
7966
href="/tasks"
@@ -190,61 +177,7 @@ export default function HomePage() {
190177
<h2 className="max-w-4xl text-5xl font-semibold leading-[1.08] tracking-[-0.05em] text-[var(--ink)]">
191178
Benchmark agents in the speed of modern product design.
192179
</h2>
193-
194-
<div className="mt-8 grid grid-cols-2 md:grid-cols-4">
195-
{benchmarkTabs.map((tab, index) => (
196-
<div key={tab} className={`eyebrow-tab ${index === 0 ? "is-active" : ""}`}>
197-
{tab}
198-
</div>
199-
))}
200-
</div>
201-
202-
<div className="site-panel paper-matrix rounded-b-[34px] rounded-t-none p-6 md:p-10">
203-
<div className="rounded-[28px] border border-[var(--line)] bg-[var(--paper-strong)] p-6 md:p-10">
204-
<h3 className="text-center text-4xl font-semibold tracking-[-0.04em] text-[var(--ink)]">
205-
From idea to multi-view benchmark concept
206-
</h3>
207-
<div className="mx-auto mt-8 max-w-3xl rounded-[28px] border border-[var(--line)] bg-white p-5 shadow-[0_24px_60px_rgba(38,28,16,0.08)]">
208-
<div className="h-[280px] rounded-[20px] border border-[var(--line)] bg-[linear-gradient(180deg,rgba(250,248,244,0.96),rgba(244,236,226,0.98))]">
209-
<div className="grid h-full grid-cols-[0.85fr_1.15fr_0.75fr] gap-4 p-5">
210-
<div className="rounded-[18px] border border-[var(--line)] bg-[var(--paper)] p-4">
211-
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-[var(--muted)]">Prompt</p>
212-
<p className="mt-3 text-sm leading-7 text-[var(--muted)]">
213-
Define a partially observable task with delayed reward and explicit planning budget.
214-
</p>
215-
</div>
216-
<div className="rounded-[18px] border border-[var(--line)] bg-[radial-gradient(circle_at_top,rgba(190,170,145,0.18),transparent_32%),#fffdfa] p-4">
217-
<div className="grid h-full grid-cols-2 gap-3">
218-
<div className="rounded-[16px] border border-[var(--line)] bg-[var(--sand)]" />
219-
<div className="rounded-[16px] border border-[var(--line)] bg-[var(--paper)]" />
220-
<div className="rounded-[16px] border border-[var(--line)] bg-[var(--paper)]" />
221-
<div className="rounded-[16px] border border-[var(--line)] bg-[var(--sand)]" />
222-
</div>
223-
</div>
224-
<div className="rounded-[18px] border border-[var(--line)] bg-[var(--paper)] p-4">
225-
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-[var(--muted)]">Review</p>
226-
<div className="mt-3 space-y-3">
227-
<div className="h-14 rounded-[14px] border border-[var(--line)] bg-[var(--sand)]" />
228-
<div className="h-20 rounded-[14px] border border-[var(--line)] bg-[var(--paper-strong)]" />
229-
</div>
230-
</div>
231-
</div>
232-
</div>
233-
</div>
234-
235-
<div className="mt-6 grid gap-3 md:grid-cols-5">
236-
{benchmarkSteps.map((step) => (
237-
<div key={step} className="site-soft-panel rounded-[18px] px-4 py-5 text-sm font-medium leading-6 text-[var(--ink)]">
238-
{step}
239-
</div>
240-
))}
241-
</div>
242-
243-
<p className="mt-6 text-center text-3xl font-semibold tracking-[-0.04em] text-[var(--ink)]">
244-
Your benchmark becomes a public product surface instantly.
245-
</p>
246-
</div>
247-
</div>
180+
<WorkflowShowcase />
248181
</div>
249182
</section>
250183

@@ -279,7 +212,9 @@ export default function HomePage() {
279212
<div className="flex-1 text-sm text-[#9a9287]">
280213
Compare planning cost and success rate across benchmark tracks with reproducible seeds...
281214
</div>
282-
<div className="button-secondary whitespace-nowrap px-4 py-3 text-sm font-semibold">Ask now</div>
215+
<Link href="/upload" className="button-secondary whitespace-nowrap px-4 py-3 text-sm font-semibold">
216+
Open upload
217+
</Link>
283218
</div>
284219
<p className="mt-4 text-center text-sm text-[var(--muted)]">
285220
Replace brittle demo prep with one polished benchmark narrative.
@@ -356,9 +291,14 @@ export default function HomePage() {
356291
<p className="mt-3 text-center text-base leading-7 text-[var(--muted)]">
357292
Join the future of benchmark creation with AI-assisted task design and public evaluation tools.
358293
</p>
359-
<div className="mt-6 rounded-[16px] border border-[var(--line)] px-4 py-4 text-center text-sm font-semibold text-[var(--ink)]">
294+
<a
295+
href="https://github.com/biru-codeastromer/WorldModel-Gym"
296+
target="_blank"
297+
rel="noreferrer"
298+
className="mt-6 block rounded-[16px] border border-[var(--line)] px-4 py-4 text-center text-sm font-semibold text-[var(--ink)] transition hover:bg-[var(--paper)]"
299+
>
360300
Continue with GitHub
361-
</div>
301+
</a>
362302
<div className="mt-6 text-center text-xs font-semibold uppercase tracking-[0.18em] text-[var(--muted)]">
363303
or
364304
</div>
@@ -372,9 +312,9 @@ export default function HomePage() {
372312
</label>
373313
))}
374314
</div>
375-
<div className="button-primary mt-6 w-full px-6 py-4 text-center text-sm font-semibold">
315+
<Link href="/upload" className="button-primary mt-6 block w-full px-6 py-4 text-center text-sm font-semibold">
376316
Request Research Access
377-
</div>
317+
</Link>
378318
</div>
379319
</div>
380320
</section>

web/app/sitemap.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
77
return [
88
{ url: `${base}/`, lastModified: now },
99
{ url: `${base}/tasks`, lastModified: now },
10-
{ url: `${base}/leaderboard`, lastModified: now }
10+
{ url: `${base}/leaderboard`, lastModified: now },
11+
{ url: `${base}/upload`, lastModified: now }
1112
];
1213
}

web/app/tasks/page.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import Link from "next/link";
2+
13
import { fetchTasks, TaskRecord } from "@/lib/api";
24

35
const curatedTasks: TaskRecord[] = [
@@ -34,6 +36,20 @@ export default async function TasksPage() {
3436
{JSON.stringify(task.defaults ?? {}, null, 2)}
3537
</pre>
3638
</div>
39+
<div className="mt-6 flex flex-wrap gap-3">
40+
<Link
41+
href={`/upload?env=${encodeURIComponent(task.id)}&track=test`}
42+
className="button-primary px-4 py-3 text-sm font-semibold"
43+
>
44+
Upload Run
45+
</Link>
46+
<Link
47+
href="/leaderboard"
48+
className="button-secondary px-4 py-3 text-sm font-semibold"
49+
>
50+
Compare on Leaderboard
51+
</Link>
52+
</div>
3753
</article>
3854
);
3955

0 commit comments

Comments
 (0)