klyro 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/eval.d.ts +9 -1
- package/dist/cli/eval.js +46 -24
- package/dist/index.js +2 -1
- package/package.json +1 -1
package/dist/cli/eval.d.ts
CHANGED
|
@@ -79,6 +79,8 @@ export interface EvalResult {
|
|
|
79
79
|
notes: string;
|
|
80
80
|
skipped: boolean;
|
|
81
81
|
};
|
|
82
|
+
/** Isolated workdir the scenario ran in (tmp unless --cwd). Debugging aid. */
|
|
83
|
+
workDir?: string;
|
|
82
84
|
}
|
|
83
85
|
export interface RunEvalOptions {
|
|
84
86
|
inputPath: string;
|
|
@@ -90,10 +92,16 @@ export interface RunEvalOptions {
|
|
|
90
92
|
model?: string;
|
|
91
93
|
/** Live model id for grading `judge.rubric` (env endpoint + key required). */
|
|
92
94
|
judgeModel?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Shared workdir for JSONL scenarios. When omitted each scenario runs in
|
|
97
|
+
* a fresh tmp dir (deleted afterwards) so scripted tool calls can never
|
|
98
|
+
* touch the caller's directory. Pass explicitly to inspect artifacts.
|
|
99
|
+
*/
|
|
100
|
+
cwd?: string;
|
|
93
101
|
}
|
|
94
102
|
export declare function runEval(opts: RunEvalOptions): Promise<number>;
|
|
95
103
|
export declare function scriptedAdapterFromSpec(spec: Array<Array<unknown[]>> | undefined): ProviderAdapter;
|
|
96
104
|
export declare function runScenario(sc: EvalScenario, judgeOpts?: {
|
|
97
105
|
adapter: ProviderAdapter;
|
|
98
106
|
model: string;
|
|
99
|
-
}): Promise<EvalResult>;
|
|
107
|
+
}, workDir?: string): Promise<EvalResult>;
|
package/dist/cli/eval.js
CHANGED
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
* otherwise.
|
|
41
41
|
*/
|
|
42
42
|
import * as fs from 'node:fs';
|
|
43
|
+
import * as fsp from 'node:fs/promises';
|
|
44
|
+
import * as os from 'node:os';
|
|
45
|
+
import * as path from 'node:path';
|
|
43
46
|
import * as readline from 'node:readline/promises';
|
|
44
47
|
import { stdin as input, stdout, stderr } from 'node:process';
|
|
45
48
|
import { run } from '../agent/runtime.js';
|
|
@@ -153,7 +156,7 @@ export async function runEval(opts) {
|
|
|
153
156
|
const results = [];
|
|
154
157
|
for (const sc of scenarios) {
|
|
155
158
|
const start = Date.now();
|
|
156
|
-
const r = await runScenario(sc, judgeAdapter && opts.judgeModel ? { adapter: judgeAdapter, model: opts.judgeModel } : undefined);
|
|
159
|
+
const r = await runScenario(sc, judgeAdapter && opts.judgeModel ? { adapter: judgeAdapter, model: opts.judgeModel } : undefined, opts.cwd);
|
|
157
160
|
r.durationMs = Date.now() - start;
|
|
158
161
|
results.push(r);
|
|
159
162
|
if (opts.output === 'json') {
|
|
@@ -242,35 +245,53 @@ function tupleToEvent(tuple) {
|
|
|
242
245
|
throw new Error(`scriptedAdapterFromSpec: unknown event kind: ${kind}`);
|
|
243
246
|
}
|
|
244
247
|
}
|
|
245
|
-
export async function runScenario(sc, judgeOpts) {
|
|
248
|
+
export async function runScenario(sc, judgeOpts, workDir) {
|
|
246
249
|
const failures = [];
|
|
247
250
|
const model = sc.model ?? 'mock';
|
|
248
251
|
const adapter = scriptedAdapterFromSpec(sc.scripted_events);
|
|
249
252
|
const registry = builtinRegistry();
|
|
250
253
|
const policy = new PolicyEngine(builtinRules(), DEFAULT_POLICY_CONFIG);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
254
|
+
// Isolation (fix: scripted tool calls must never run in the caller's
|
|
255
|
+
// directory — a JSONL scenario writing a.txt/b.txt used to pollute it).
|
|
256
|
+
// Explicit workDir is shared as-is (inspect artifacts); otherwise each
|
|
257
|
+
// scenario gets a fresh tmp dir that is removed afterwards.
|
|
258
|
+
const owned = !workDir;
|
|
259
|
+
const cwd = workDir ?? await fsp.mkdtemp(path.join(os.tmpdir(), 'klyro-eval-jsonl-'));
|
|
260
|
+
await fsp.mkdir(cwd, { recursive: true });
|
|
261
|
+
let result;
|
|
262
|
+
try {
|
|
263
|
+
result = await run({
|
|
264
|
+
task: sc.task,
|
|
265
|
+
cwd,
|
|
266
|
+
model,
|
|
267
|
+
maxSteps: sc.maxSteps,
|
|
268
|
+
maxTokens: sc.maxTokens,
|
|
269
|
+
nonInteractive: true,
|
|
270
|
+
...(sc.verify
|
|
271
|
+
? {
|
|
272
|
+
verify: {
|
|
273
|
+
enabled: true,
|
|
274
|
+
...(sc.verify.command !== undefined ? { command: sc.verify.command } : {}),
|
|
275
|
+
...(sc.verify.mode !== undefined ? { mode: sc.verify.mode } : {}),
|
|
276
|
+
},
|
|
277
|
+
}
|
|
278
|
+
: {}),
|
|
279
|
+
}, {
|
|
280
|
+
adapter,
|
|
281
|
+
registry,
|
|
282
|
+
policy,
|
|
283
|
+
approval: new DenyAllApprovalPrompt(),
|
|
284
|
+
systemPrompt: ({ cwd }) => `You are Klyro. cwd=${cwd}.`,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
finally {
|
|
288
|
+
if (owned) {
|
|
289
|
+
try {
|
|
290
|
+
await fsp.rm(cwd, { recursive: true, force: true });
|
|
265
291
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
registry,
|
|
270
|
-
policy,
|
|
271
|
-
approval: new DenyAllApprovalPrompt(),
|
|
272
|
-
systemPrompt: ({ cwd }) => `You are Klyro. cwd=${cwd}.`,
|
|
273
|
-
});
|
|
292
|
+
catch { /* ignore */ }
|
|
293
|
+
}
|
|
294
|
+
}
|
|
274
295
|
const exp = sc.expect ?? {};
|
|
275
296
|
if (exp.status !== undefined && result.status !== exp.status) {
|
|
276
297
|
failures.push(`status: expected ${exp.status}, got ${result.status}`);
|
|
@@ -311,6 +332,7 @@ export async function runScenario(sc, judgeOpts) {
|
|
|
311
332
|
toolCalls: result.toolCalls,
|
|
312
333
|
text: result.finalText,
|
|
313
334
|
durationMs: 0,
|
|
335
|
+
workDir: cwd,
|
|
314
336
|
...(judge ? { judge } : {}),
|
|
315
337
|
};
|
|
316
338
|
}
|
package/dist/index.js
CHANGED
|
@@ -390,6 +390,7 @@ async function main() {
|
|
|
390
390
|
.option('--parallel <n>', 'Parallelism (default 1)', (v) => parsePositiveInt('--parallel', v))
|
|
391
391
|
.option('--model <id>', 'Model for eval')
|
|
392
392
|
.option('--judge-model <id>', 'Live model id for grading judge.rubric (needs endpoint + key)')
|
|
393
|
+
.option('--cwd <path>', 'Shared scenario workdir (default: isolated tmp per scenario)')
|
|
393
394
|
.action(async (input, opts) => {
|
|
394
395
|
const output = (opts.output ?? 'human');
|
|
395
396
|
if (opts.suite) {
|
|
@@ -400,7 +401,7 @@ async function main() {
|
|
|
400
401
|
process.stderr.write('klyro eval: missing input (provide <input> or --suite)\n');
|
|
401
402
|
process.exit(2);
|
|
402
403
|
}
|
|
403
|
-
const code = await runEval({ inputPath: input, output, suite: opts.suite, filter: opts.filter, runs: opts.runs, parallel: opts.parallel, model: opts.model, judgeModel: opts.judgeModel });
|
|
404
|
+
const code = await runEval({ inputPath: input, output, suite: opts.suite, filter: opts.filter, runs: opts.runs, parallel: opts.parallel, model: opts.model, judgeModel: opts.judgeModel, cwd: opts.cwd });
|
|
404
405
|
process.exit(code);
|
|
405
406
|
});
|
|
406
407
|
program
|