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
4 changes: 3 additions & 1 deletion src/eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ const toInvocationEvidence = (params: {
const runCommand = async (params: {
commandSpec: z.infer<typeof RunEvalInputSchema.shape.adapter>
stdinJson: string
cwd?: string
}): Promise<{ invocation: CommandInvocation; stdout: string; stderr: string; timedOut: boolean }> => {
const timeoutMs = params.commandSpec.timeoutMs ?? DEFAULT_TIMEOUT_MS
const maxOutputBytes = params.commandSpec.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES
Expand All @@ -221,7 +222,7 @@ const runCommand = async (params: {
stdin: new TextEncoder().encode(params.stdinJson),
stdout: 'pipe',
stderr: 'pipe',
cwd: process.cwd(),
cwd: params.cwd ?? process.cwd(),
})

let timedOut = false
Expand Down Expand Up @@ -616,6 +617,7 @@ const runCommandGrader = async (params: {
commandRun = await runCommand({
commandSpec: params.grader.options,
stdinJson,
cwd: params.cwd,
})
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
Expand Down
50 changes: 49 additions & 1 deletion src/tests/eval.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from 'bun:test'
import { mkdir, rm } from 'node:fs/promises'
import { mkdir, realpath, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { EvalInputSchema, TaskSchema } from '../eval.schemas.ts'
Expand Down Expand Up @@ -533,6 +533,54 @@ describe('grade mode', () => {
}
})

test('runs command graders from the trial cwd', async () => {
const dir = join(tmpdir(), `eval-grader-cwd-${Date.now()}`)
await mkdir(dir, { recursive: true })
try {
const cwd = await realpath(dir)
const row = createCompletedRow()
row.trial.cwd = cwd
const payload = JSON.stringify({
mode: 'grade',
graders: [
{
id: 'cwd-grader',
type: 'command',
options: {
command: [
'bun',
'-e',
'if (process.cwd() !== Bun.argv[1]) { process.stderr.write(process.cwd()); process.exit(1) }',
cwd,
],
output: 'exit_code',
},
},
],
})

const cliPath = `${import.meta.dir}/../cli.ts`
const proc = Bun.spawn(['bun', cliPath, 'eval', payload], {
stdin: new TextEncoder().encode(`${JSON.stringify(row)}\n`),
stdout: 'pipe',
stderr: 'pipe',
})
const [stdout, , exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
])

expect(exitCode).toBe(0)
const graded = JSON.parse(stdout.trim())
const result = graded.graderResults[0]
expect(result.pass).toBe(true)
expect(result.outcome.invocation.exitCode).toBe(0)
} finally {
await rm(dir, { force: true, recursive: true })
}
})

test('grader_json success includes normalized invocation without retaining stdout text', async () => {
const dir = join(tmpdir(), `eval-grader-json-success-${Date.now()}`)
await mkdir(dir, { recursive: true })
Expand Down
Loading