@sebastiantuyu/agest 0.3.1 → 0.3.3-next.1
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/adapters/remote.js +2 -1
- package/dist/cli.js +30 -3
- package/dist/config.d.ts +1 -0
- package/dist/context.js +1 -1
- package/dist/discover.d.ts +16 -0
- package/dist/discover.js +62 -0
- package/dist/index.d.ts +1 -1
- package/dist/runner.d.ts +1 -1
- package/dist/runner.js +17 -12
- package/dist/types.d.ts +4 -1
- package/package.json +15 -14
package/dist/adapters/remote.js
CHANGED
|
@@ -24,12 +24,13 @@
|
|
|
24
24
|
*/
|
|
25
25
|
export function remote(endpoint, options = {}) {
|
|
26
26
|
const { headers = {}, method = "POST", body: extraBody, buildRequest = defaultBuildRequest, parseResponse, metadata: staticMetadata, } = options;
|
|
27
|
-
return async (input) => {
|
|
27
|
+
return async (input, execOptions) => {
|
|
28
28
|
let res;
|
|
29
29
|
try {
|
|
30
30
|
const fetchOptions = {
|
|
31
31
|
method,
|
|
32
32
|
headers: { "Content-Type": "application/json", ...headers },
|
|
33
|
+
signal: execOptions?.signal,
|
|
33
34
|
};
|
|
34
35
|
if (method !== "GET") {
|
|
35
36
|
const built = buildRequest(input);
|
package/dist/cli.js
CHANGED
|
@@ -2,11 +2,35 @@
|
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import { main as stats } from "./stats.js";
|
|
4
4
|
import { main as preview } from "./preview.js";
|
|
5
|
+
import { DEFAULT_PATTERN, discoverTestFiles } from "./discover.js";
|
|
5
6
|
const command = process.argv[2];
|
|
7
|
+
function parseRunArgs(args) {
|
|
8
|
+
const targets = [];
|
|
9
|
+
let pattern;
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
const a = args[i];
|
|
12
|
+
if (a === "--pattern" || a === "-p") {
|
|
13
|
+
pattern = args[++i];
|
|
14
|
+
if (pattern === undefined) {
|
|
15
|
+
console.error(" Error: --pattern requires a value");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else if (a.startsWith("--pattern=")) {
|
|
20
|
+
pattern = a.slice("--pattern=".length);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
targets.push(a);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { pattern, targets };
|
|
27
|
+
}
|
|
6
28
|
async function run() {
|
|
7
|
-
const
|
|
29
|
+
const { pattern, targets } = parseRunArgs(process.argv.slice(3));
|
|
30
|
+
const files = await discoverTestFiles(targets, { pattern });
|
|
8
31
|
if (files.length === 0) {
|
|
9
|
-
|
|
32
|
+
const effective = pattern ?? DEFAULT_PATTERN;
|
|
33
|
+
console.error(` No test files found (pattern: ${effective})`);
|
|
10
34
|
process.exit(1);
|
|
11
35
|
}
|
|
12
36
|
for (const file of files) {
|
|
@@ -29,7 +53,10 @@ if (!command || !commands[command]) {
|
|
|
29
53
|
Usage: agest <command>
|
|
30
54
|
|
|
31
55
|
Commands:
|
|
32
|
-
run Run test file(s)
|
|
56
|
+
run Run test file(s), directories, or glob patterns
|
|
57
|
+
agest run tests/ # walks for ${DEFAULT_PATTERN}
|
|
58
|
+
agest run src/agest --pattern "**/*.test.ts"
|
|
59
|
+
agest run "tests/**/*.agest.ts" path/to/file.agest.ts
|
|
33
60
|
stats Show aggregated test statistics
|
|
34
61
|
preview Generate an HTML report preview
|
|
35
62
|
`);
|
package/dist/config.d.ts
CHANGED
package/dist/context.js
CHANGED
|
@@ -98,7 +98,7 @@ export class AgentContext {
|
|
|
98
98
|
for (const hook of this._beforeEachHooks) {
|
|
99
99
|
await hook();
|
|
100
100
|
}
|
|
101
|
-
const result = await executeScene(this._executor, scene, config.timeout, config.judge, config.turns);
|
|
101
|
+
const result = await executeScene(this._executor, scene, config.timeout, config.judge, config.turns, config.runs);
|
|
102
102
|
orderedResults[i] = result;
|
|
103
103
|
// Run afterEach hooks
|
|
104
104
|
for (const hook of this._afterEachHooks) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const DEFAULT_PATTERN = "**/*.agest.ts";
|
|
2
|
+
export interface DiscoverOptions {
|
|
3
|
+
pattern?: string;
|
|
4
|
+
cwd?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a mix of file paths, directories, and glob patterns into a
|
|
8
|
+
* deduplicated, sorted list of absolute file paths.
|
|
9
|
+
*
|
|
10
|
+
* Rules per target:
|
|
11
|
+
* - directory: search recursively for `pattern` (default `**\/*.agest.ts`)
|
|
12
|
+
* - glob (contains *, ?, [], {}): expand it
|
|
13
|
+
* - file: use as-is
|
|
14
|
+
* - anything else: try as glob (zero matches is fine)
|
|
15
|
+
*/
|
|
16
|
+
export declare function discoverTestFiles(targets: string[], options?: DiscoverOptions): Promise<string[]>;
|
package/dist/discover.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
export const DEFAULT_PATTERN = "**/*.agest.ts";
|
|
4
|
+
const GLOB_CHARS = /[*?[\]{}]/;
|
|
5
|
+
function hasGlobChars(value) {
|
|
6
|
+
return GLOB_CHARS.test(value);
|
|
7
|
+
}
|
|
8
|
+
async function statSafe(path) {
|
|
9
|
+
try {
|
|
10
|
+
const stat = await fs.stat(path);
|
|
11
|
+
return { isFile: stat.isFile(), isDir: stat.isDirectory() };
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return { isFile: false, isDir: false };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function expandGlob(pattern, cwd) {
|
|
18
|
+
const out = [];
|
|
19
|
+
// fs.promises.glob is available in Node >= 22 (the package's required engine).
|
|
20
|
+
for await (const match of fs.glob(pattern, { cwd })) {
|
|
21
|
+
out.push(isAbsolute(match) ? match : resolve(cwd, match));
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a mix of file paths, directories, and glob patterns into a
|
|
27
|
+
* deduplicated, sorted list of absolute file paths.
|
|
28
|
+
*
|
|
29
|
+
* Rules per target:
|
|
30
|
+
* - directory: search recursively for `pattern` (default `**\/*.agest.ts`)
|
|
31
|
+
* - glob (contains *, ?, [], {}): expand it
|
|
32
|
+
* - file: use as-is
|
|
33
|
+
* - anything else: try as glob (zero matches is fine)
|
|
34
|
+
*/
|
|
35
|
+
export async function discoverTestFiles(targets, options = {}) {
|
|
36
|
+
const cwd = options.cwd ?? process.cwd();
|
|
37
|
+
const pattern = options.pattern ?? DEFAULT_PATTERN;
|
|
38
|
+
const work = targets.length === 0 ? ["."] : targets;
|
|
39
|
+
const found = new Set();
|
|
40
|
+
for (const target of work) {
|
|
41
|
+
if (hasGlobChars(target)) {
|
|
42
|
+
for (const f of await expandGlob(target, cwd))
|
|
43
|
+
found.add(f);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const stat = await statSafe(isAbsolute(target) ? target : resolve(cwd, target));
|
|
47
|
+
if (stat.isDir) {
|
|
48
|
+
const trimmed = target.replace(/\/+$/, "");
|
|
49
|
+
const dirPattern = `${trimmed}/${pattern}`;
|
|
50
|
+
for (const f of await expandGlob(dirPattern, cwd))
|
|
51
|
+
found.add(f);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (stat.isFile) {
|
|
55
|
+
found.add(isAbsolute(target) ? target : resolve(cwd, target));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
for (const f of await expandGlob(target, cwd))
|
|
59
|
+
found.add(f);
|
|
60
|
+
}
|
|
61
|
+
return [...found].sort();
|
|
62
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type { AgestConfig, JudgeConfig, JudgeExecutor } from "./config";
|
|
|
7
7
|
export type { LogLevel } from "./logger";
|
|
8
8
|
export type { AgentExpectation, AgentMatchers } from "./assertions";
|
|
9
9
|
export type { JudgeCriteria } from "./judge";
|
|
10
|
-
export type { AgentExecutor, AgentResponse, AgentReport, SceneResult, RunResult, JudgeVerdict, JudgeResult, HookFn, } from "./types";
|
|
10
|
+
export type { AgentExecutor, ExecutorOptions, AgentResponse, AgentReport, SceneResult, RunResult, JudgeVerdict, JudgeResult, HookFn, } from "./types";
|
|
11
11
|
export interface AgentOptions {
|
|
12
12
|
name?: string;
|
|
13
13
|
}
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { AgentExecutor, AgentResponse, SceneDefinition, SceneResult } from "./types";
|
|
2
2
|
import type { JudgeConfig } from "./config";
|
|
3
3
|
export declare function extractField(response: AgentResponse, field: string): unknown;
|
|
4
|
-
export declare function executeScene(executor: AgentExecutor, scene: SceneDefinition, globalTimeout?: number, judgeConfig?: JudgeConfig, globalTurns?: number): Promise<SceneResult>;
|
|
4
|
+
export declare function executeScene(executor: AgentExecutor, scene: SceneDefinition, globalTimeout?: number, judgeConfig?: JudgeConfig, globalTurns?: number, globalRuns?: number): Promise<SceneResult>;
|
package/dist/runner.js
CHANGED
|
@@ -35,19 +35,24 @@ async function executeSingleRun(executor, scene, timeoutMs, turns, judgeConfig)
|
|
|
35
35
|
let duration;
|
|
36
36
|
try {
|
|
37
37
|
const start = performance.now();
|
|
38
|
-
|
|
38
|
+
const input = scene.prompt;
|
|
39
39
|
for (let t = 0; t < turns; t++) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
42
|
+
try {
|
|
43
|
+
response = await executor(input, { signal: controller.signal });
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (err.name === "AbortError" || controller.signal.aborted) {
|
|
47
|
+
throw new Error(`Scene timed out after ${timeoutMs}ms`);
|
|
48
|
+
}
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
}
|
|
47
54
|
if (response.executionError)
|
|
48
55
|
break;
|
|
49
|
-
if (t < turns - 1)
|
|
50
|
-
input = response.text;
|
|
51
56
|
}
|
|
52
57
|
duration = performance.now() - start;
|
|
53
58
|
}
|
|
@@ -104,10 +109,10 @@ async function executeSingleRun(executor, scene, timeoutMs, turns, judgeConfig)
|
|
|
104
109
|
}
|
|
105
110
|
return { passed, error, response, duration, judgement };
|
|
106
111
|
}
|
|
107
|
-
export async function executeScene(executor, scene, globalTimeout, judgeConfig, globalTurns) {
|
|
112
|
+
export async function executeScene(executor, scene, globalTimeout, judgeConfig, globalTurns, globalRuns) {
|
|
108
113
|
const timeoutMs = scene.timeout ?? globalTimeout ?? DEFAULT_SCENE_TIMEOUT;
|
|
109
114
|
const turns = scene.turns ?? globalTurns ?? 1;
|
|
110
|
-
const numRuns = scene.runs ?? 1;
|
|
115
|
+
const numRuns = scene.runs ?? globalRuns ?? 1;
|
|
111
116
|
// Single run — original fast path
|
|
112
117
|
if (numRuns <= 1) {
|
|
113
118
|
const run = await executeSingleRun(executor, scene, timeoutMs, turns, judgeConfig);
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface ExecutorOptions {
|
|
2
|
+
signal?: AbortSignal;
|
|
3
|
+
}
|
|
4
|
+
export type AgentExecutor = (input: string, options?: ExecutorOptions) => Promise<AgentResponse>;
|
|
2
5
|
export interface AgentResponse {
|
|
3
6
|
text: string;
|
|
4
7
|
refusal?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebastiantuyu/agest",
|
|
3
|
-
"version": "0.3.1",
|
|
3
|
+
"version": "0.3.3-next.1",
|
|
4
4
|
"description": "A testing library for agents",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,25 +37,26 @@
|
|
|
37
37
|
"site:preview": "npx serve site -p 3000",
|
|
38
38
|
"release:patch": "npm version patch && git push && git push --tags",
|
|
39
39
|
"release:minor": "npm version minor && git push && git push --tags",
|
|
40
|
-
"release:major": "npm version major && git push && git push --tags"
|
|
40
|
+
"release:major": "npm version major && git push && git push --tags",
|
|
41
|
+
"release:next": "npm version prerelease --preid=next && git push && git push --tags"
|
|
41
42
|
},
|
|
42
43
|
"engines": {
|
|
43
44
|
"node": ">=22.0.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@langchain/core": "
|
|
47
|
-
"@langchain/langgraph": "
|
|
48
|
-
"@langchain/openai": "
|
|
49
|
-
"@types/node": "
|
|
50
|
-
"@vitest/coverage-v8": "
|
|
51
|
-
"dotenv": "
|
|
52
|
-
"langchain": "
|
|
53
|
-
"tsx": "
|
|
54
|
-
"typescript": "
|
|
55
|
-
"vitest": "
|
|
56
|
-
"zod": "
|
|
47
|
+
"@langchain/core": "1.1.39",
|
|
48
|
+
"@langchain/langgraph": "1.2.8",
|
|
49
|
+
"@langchain/openai": "1.4.4",
|
|
50
|
+
"@types/node": "22.19.17",
|
|
51
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
52
|
+
"dotenv": "17.4.1",
|
|
53
|
+
"langchain": "1.3.1",
|
|
54
|
+
"tsx": "4.21.0",
|
|
55
|
+
"typescript": "5.9.3",
|
|
56
|
+
"vitest": "3.2.4",
|
|
57
|
+
"zod": "4.3.6"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@supercharge/promise-pool": "
|
|
60
|
+
"@supercharge/promise-pool": "3.3.0"
|
|
60
61
|
}
|
|
61
62
|
}
|