Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions apps/cli/src/commands/results/eval-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ export function registerEvalRoutes(
});

// ── Project-scoped variants ────────────────────────────────────────────
app.get('/api/projects/:projectId/eval/discover', async (c) => {
app.get('/api/benchmarks/:projectId/eval/discover', async (c) => {
const cwd = getCwd(c);
try {
const files = await discoverEvalFiles(cwd);
Expand All @@ -375,7 +375,7 @@ export function registerEvalRoutes(
}
});

app.get('/api/projects/:projectId/eval/targets', async (c) => {
app.get('/api/benchmarks/:projectId/eval/targets', async (c) => {
const cwd = getCwd(c);
try {
const names = await discoverTargetsInProject(cwd);
Expand All @@ -385,7 +385,7 @@ export function registerEvalRoutes(
}
});

app.post('/api/projects/:projectId/eval/run', async (c) => {
app.post('/api/benchmarks/:projectId/eval/run', async (c) => {
const cwd = getCwd(c);

let body: RunEvalRequest;
Expand Down Expand Up @@ -459,7 +459,7 @@ export function registerEvalRoutes(
}
});

app.get('/api/projects/:projectId/eval/status/:id', (c) => {
app.get('/api/benchmarks/:projectId/eval/status/:id', (c) => {
const id = c.req.param('id');
const run = activeRuns.get(id ?? '');
if (!run) return c.json({ error: 'Run not found' }, 404);
Expand All @@ -475,7 +475,7 @@ export function registerEvalRoutes(
});
});

app.get('/api/projects/:projectId/eval/runs', (c) => {
app.get('/api/benchmarks/:projectId/eval/runs', (c) => {
const runs = [...activeRuns.values()].map((r) => ({
id: r.id,
status: r.status,
Expand All @@ -488,7 +488,7 @@ export function registerEvalRoutes(
return c.json({ runs });
});

app.post('/api/projects/:projectId/eval/preview', async (c) => {
app.post('/api/benchmarks/:projectId/eval/preview', async (c) => {
let body: RunEvalRequest;
try {
body = await c.req.json<RunEvalRequest>();
Expand Down
52 changes: 27 additions & 25 deletions apps/cli/src/commands/results/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
* - GET /api/runs/:filename — load results from a specific run workspace
* - GET /api/feedback — read feedback reviews
* - POST /api/feedback — write feedback reviews
* - GET /api/projects — list registered projects
* - GET /api/projects/:projectId/runs — project-scoped run list
* - GET /api/benchmarks — list registered benchmarks
* - GET /api/benchmarks/:projectId/runs — benchmark-scoped run list
*
* All data routes (runs, suites, categories, evals, experiments, targets)
* exist in both unscoped (/api/...) and project-scoped (/api/projects/:projectId/...)
* exist in both unscoped (/api/...) and benchmark-scoped (/api/benchmarks/:projectId/...)
* variants. They share handler functions via DataContext, differing only in
* how searchDir is resolved.
*
Expand Down Expand Up @@ -773,7 +773,7 @@ export function createApp(
};
}

app.get('/api/projects', async (c) => {
app.get('/api/benchmarks', async (c) => {
const registry = loadProjectRegistry();
const projects = await Promise.all(
registry.projects.map(async (p) => {
Expand Down Expand Up @@ -802,7 +802,7 @@ export function createApp(
return c.json({ projects });
});

app.post('/api/projects', async (c) => {
app.post('/api/benchmarks', async (c) => {
if (readOnly) {
return c.json({ error: 'Studio is running in read-only mode' }, 403);
}
Expand All @@ -816,7 +816,7 @@ export function createApp(
}
});

app.delete('/api/projects/:projectId', (c) => {
app.delete('/api/benchmarks/:projectId', (c) => {
if (readOnly) {
return c.json({ error: 'Studio is running in read-only mode' }, 403);
}
Expand All @@ -825,7 +825,7 @@ export function createApp(
return c.json({ ok: true });
});

app.get('/api/projects/:projectId/summary', async (c) => {
app.get('/api/benchmarks/:projectId/summary', async (c) => {
const project = getProject(c.req.param('projectId') ?? '');
if (!project) return c.json({ error: 'Project not found' }, 404);
try {
Expand All @@ -846,7 +846,7 @@ export function createApp(
}
});

app.post('/api/projects/discover', async (c) => {
app.post('/api/benchmarks/discover', async (c) => {
if (readOnly) {
return c.json({ error: 'Studio is running in read-only mode' }, 403);
}
Expand All @@ -862,7 +862,7 @@ export function createApp(
});

/** Aggregate runs from all registered projects, sorted by timestamp descending. */
app.get('/api/projects/all-runs', async (c) => {
app.get('/api/benchmarks/all-runs', async (c) => {
const registry = loadProjectRegistry();
const allRuns: Array<{
filename: string;
Expand Down Expand Up @@ -1026,44 +1026,46 @@ export function createApp(
// ── Data routes (project-scoped) ──────────────────────────────────────
// Same handlers as above, with project-resolved DataContext via withProject.

app.get('/api/projects/:projectId/config', (c) =>
app.get('/api/benchmarks/:projectId/config', (c) =>
withProject(c, (ctx, dataCtx) =>
handleConfig(ctx, dataCtx, {
readOnly,
multiProjectDashboard: options?.multiProjectDashboard,
}),
),
);
app.get('/api/projects/:projectId/remote/status', (c) =>
app.get('/api/benchmarks/:projectId/remote/status', (c) =>
withProject(c, async (ctx, dataCtx) =>
ctx.json(await getRemoteResultsStatus(dataCtx.searchDir)),
),
);
app.post('/api/projects/:projectId/remote/sync', (c) =>
app.post('/api/benchmarks/:projectId/remote/sync', (c) =>
withProject(c, async (ctx, dataCtx) => ctx.json(await syncRemoteResults(dataCtx.searchDir))),
);
app.get('/api/projects/:projectId/runs', (c) => withProject(c, handleRuns));
app.get('/api/projects/:projectId/runs/:filename', (c) => withProject(c, handleRunDetail));
app.get('/api/projects/:projectId/runs/:filename/suites', (c) => withProject(c, handleRunSuites));
app.get('/api/projects/:projectId/runs/:filename/categories', (c) =>
app.get('/api/benchmarks/:projectId/runs', (c) => withProject(c, handleRuns));
app.get('/api/benchmarks/:projectId/runs/:filename', (c) => withProject(c, handleRunDetail));
app.get('/api/benchmarks/:projectId/runs/:filename/suites', (c) =>
withProject(c, handleRunSuites),
);
app.get('/api/benchmarks/:projectId/runs/:filename/categories', (c) =>
withProject(c, handleRunCategories),
);
app.get('/api/projects/:projectId/runs/:filename/categories/:category/suites', (c) =>
app.get('/api/benchmarks/:projectId/runs/:filename/categories/:category/suites', (c) =>
withProject(c, handleCategorySuites),
);
app.get('/api/projects/:projectId/runs/:filename/evals/:evalId', (c) =>
app.get('/api/benchmarks/:projectId/runs/:filename/evals/:evalId', (c) =>
withProject(c, handleEvalDetail),
);
app.get('/api/projects/:projectId/runs/:filename/evals/:evalId/files', (c) =>
app.get('/api/benchmarks/:projectId/runs/:filename/evals/:evalId/files', (c) =>
withProject(c, handleEvalFiles),
);
app.get('/api/projects/:projectId/runs/:filename/evals/:evalId/files/*', (c) =>
app.get('/api/benchmarks/:projectId/runs/:filename/evals/:evalId/files/*', (c) =>
withProject(c, handleEvalFileContent),
);
app.get('/api/projects/:projectId/experiments', (c) => withProject(c, handleExperiments));
app.get('/api/projects/:projectId/compare', (c) => withProject(c, handleCompare));
app.get('/api/projects/:projectId/targets', (c) => withProject(c, handleTargets));
app.get('/api/projects/:projectId/feedback', (c) => withProject(c, handleFeedbackRead));
app.get('/api/benchmarks/:projectId/experiments', (c) => withProject(c, handleExperiments));
app.get('/api/benchmarks/:projectId/compare', (c) => withProject(c, handleCompare));
app.get('/api/benchmarks/:projectId/targets', (c) => withProject(c, handleTargets));
app.get('/api/benchmarks/:projectId/feedback', (c) => withProject(c, handleFeedbackRead));

// ── Eval runner routes (discovery, launch, status) ────────────────────

Expand Down Expand Up @@ -1308,7 +1310,7 @@ export const resultsServeCommand = command({
console.log('Run an evaluation to see results: agentv eval <eval-file>');
}
console.log(`Dashboard: http://localhost:${listenPort}`);
console.log(`Projects API: http://localhost:${listenPort}/api/projects`);
console.log(`Benchmarks API: http://localhost:${listenPort}/api/benchmarks`);
console.log('Press Ctrl+C to stop');

const { serve: startServer } = await import('@hono/node-server');
Expand Down
12 changes: 6 additions & 6 deletions apps/studio/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export function isPassing(score: number, passThreshold: number = DEFAULT_PASS_TH

export const projectListOptions = queryOptions({
queryKey: ['projects'],
queryFn: () => fetchJson<ProjectListResponse>('/api/projects'),
queryFn: () => fetchJson<ProjectListResponse>('/api/benchmarks'),
refetchInterval: 10_000,
});

Expand All @@ -240,7 +240,7 @@ export function useProjectList() {

export const allProjectRunsOptions = queryOptions({
queryKey: ['projects', 'all-runs'],
queryFn: () => fetchJson<RunListResponse>('/api/projects/all-runs'),
queryFn: () => fetchJson<RunListResponse>('/api/benchmarks/all-runs'),
refetchInterval: 5_000,
});

Expand All @@ -249,7 +249,7 @@ export function useAllProjectRuns() {
}

export async function addProjectApi(projectPath: string): Promise<ProjectEntry> {
const res = await fetch('/api/projects', {
const res = await fetch('/api/benchmarks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: projectPath }),
Expand All @@ -262,7 +262,7 @@ export async function addProjectApi(projectPath: string): Promise<ProjectEntry>
}

export async function removeProjectApi(projectId: string): Promise<void> {
const res = await fetch(`/api/projects/${encodeURIComponent(projectId)}`, {
const res = await fetch(`/api/benchmarks/${encodeURIComponent(projectId)}`, {
method: 'DELETE',
});
if (!res.ok) {
Expand All @@ -271,7 +271,7 @@ export async function removeProjectApi(projectId: string): Promise<void> {
}

export async function discoverProjectsApi(dirPath: string): Promise<ProjectEntry[]> {
const res = await fetch('/api/projects/discover', {
const res = await fetch('/api/benchmarks/discover', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: dirPath }),
Expand All @@ -286,7 +286,7 @@ export async function discoverProjectsApi(dirPath: string): Promise<ProjectEntry

/** Build the API base URL for a project-scoped request. */
function projectApiBase(projectId: string): string {
return `/api/projects/${encodeURIComponent(projectId)}`;
return `/api/benchmarks/${encodeURIComponent(projectId)}`;
}

export function projectRunListOptions(projectId: string) {
Expand Down
Loading