@orchagent/cli 0.3.24 → 0.3.25
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/commands/test.js +52 -16
- package/package.json +1 -1
package/dist/commands/test.js
CHANGED
|
@@ -14,6 +14,27 @@ const chokidar_1 = __importDefault(require("chokidar"));
|
|
|
14
14
|
const errors_1 = require("../lib/errors");
|
|
15
15
|
const config_1 = require("../lib/config");
|
|
16
16
|
const llm_1 = require("../lib/llm");
|
|
17
|
+
/**
|
|
18
|
+
* Validate a fixture and return helpful errors
|
|
19
|
+
*/
|
|
20
|
+
function validateFixture(data, fixturePath) {
|
|
21
|
+
const fileName = path_1.default.basename(fixturePath);
|
|
22
|
+
if (typeof data !== 'object' || data === null) {
|
|
23
|
+
throw new errors_1.CliError(`Invalid fixture ${fileName}: must be a JSON object`);
|
|
24
|
+
}
|
|
25
|
+
const obj = data;
|
|
26
|
+
if (!obj.input || typeof obj.input !== 'object') {
|
|
27
|
+
throw new errors_1.CliError(`Invalid fixture ${fileName}: missing required "input" field.\n` +
|
|
28
|
+
` Expected format: { "input": {...}, "expected_output": {...} }`);
|
|
29
|
+
}
|
|
30
|
+
if (!obj.expected_output && !obj.expected_contains) {
|
|
31
|
+
throw new errors_1.CliError(`Invalid fixture ${fileName}: must have "expected_output" or "expected_contains".\n` +
|
|
32
|
+
` Add one of:\n` +
|
|
33
|
+
` "expected_output": {"key": "exact value to match"}\n` +
|
|
34
|
+
` "expected_contains": ["substring to find"]`);
|
|
35
|
+
}
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
17
38
|
/**
|
|
18
39
|
* Parse SKILL.md frontmatter
|
|
19
40
|
*/
|
|
@@ -70,8 +91,10 @@ function runCommand(command, args, cwd, verbose) {
|
|
|
70
91
|
* Check if a command exists
|
|
71
92
|
*/
|
|
72
93
|
async function commandExists(command) {
|
|
94
|
+
const isWindows = process.platform === 'win32';
|
|
95
|
+
const checker = isWindows ? 'where' : 'which';
|
|
73
96
|
try {
|
|
74
|
-
const proc = (0, child_process_1.spawn)(
|
|
97
|
+
const proc = (0, child_process_1.spawn)(checker, [command], { shell: true, stdio: 'ignore' });
|
|
75
98
|
return new Promise((resolve) => {
|
|
76
99
|
proc.on('close', (code) => resolve(code === 0));
|
|
77
100
|
proc.on('error', () => resolve(false));
|
|
@@ -204,22 +227,28 @@ async function discoverTests(agentDir) {
|
|
|
204
227
|
*/
|
|
205
228
|
async function runPythonTests(agentDir, verbose) {
|
|
206
229
|
process.stderr.write(chalk_1.default.blue('\nRunning Python tests...\n\n'));
|
|
207
|
-
// Check if pytest is available
|
|
230
|
+
// Check if pytest is available directly
|
|
208
231
|
const hasPytest = await commandExists('pytest');
|
|
209
|
-
let command;
|
|
210
|
-
let args;
|
|
211
232
|
if (hasPytest) {
|
|
212
|
-
|
|
213
|
-
|
|
233
|
+
const args = verbose ? ['-v'] : [];
|
|
234
|
+
const { code } = await runCommand('pytest', args, agentDir, verbose);
|
|
235
|
+
return code;
|
|
214
236
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
237
|
+
// Try Python commands in order of preference
|
|
238
|
+
const pythonCommands = process.platform === 'win32'
|
|
239
|
+
? ['python', 'py', 'python3']
|
|
240
|
+
: ['python3', 'python'];
|
|
241
|
+
for (const pythonCmd of pythonCommands) {
|
|
242
|
+
if (await commandExists(pythonCmd)) {
|
|
243
|
+
const args = ['-m', 'pytest'];
|
|
244
|
+
if (verbose)
|
|
245
|
+
args.push('-v');
|
|
246
|
+
const { code } = await runCommand(pythonCmd, args, agentDir, verbose);
|
|
247
|
+
return code;
|
|
248
|
+
}
|
|
220
249
|
}
|
|
221
|
-
|
|
222
|
-
return
|
|
250
|
+
process.stderr.write(chalk_1.default.red('No Python interpreter found. Install Python and pytest.\n'));
|
|
251
|
+
return 1;
|
|
223
252
|
}
|
|
224
253
|
/**
|
|
225
254
|
* Run JavaScript/TypeScript tests
|
|
@@ -292,8 +321,8 @@ async function runFixtureTests(agentDir, fixtures, verbose, config) {
|
|
|
292
321
|
throw new errors_1.CliError('No LLM key found for fixture tests.\n' +
|
|
293
322
|
'Set an environment variable (e.g., OPENAI_API_KEY) or run `orchagent keys add <provider>`');
|
|
294
323
|
}
|
|
295
|
-
const { provider, key } = detected;
|
|
296
|
-
const model = (0, llm_1.getDefaultModel)(provider);
|
|
324
|
+
const { provider, key, model: serverModel } = detected;
|
|
325
|
+
const model = serverModel ?? (0, llm_1.getDefaultModel)(provider);
|
|
297
326
|
let passed = 0;
|
|
298
327
|
let failed = 0;
|
|
299
328
|
for (const fixturePath of fixtures) {
|
|
@@ -301,7 +330,14 @@ async function runFixtureTests(agentDir, fixtures, verbose, config) {
|
|
|
301
330
|
process.stderr.write(` ${fixtureName}: `);
|
|
302
331
|
try {
|
|
303
332
|
const raw = await promises_1.default.readFile(fixturePath, 'utf-8');
|
|
304
|
-
|
|
333
|
+
let parsed;
|
|
334
|
+
try {
|
|
335
|
+
parsed = JSON.parse(raw);
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
throw new errors_1.CliError(`Invalid JSON in ${path_1.default.basename(fixturePath)}: ${e.message}`);
|
|
339
|
+
}
|
|
340
|
+
const fixture = validateFixture(parsed, fixturePath);
|
|
305
341
|
// Build and call LLM
|
|
306
342
|
const fullPrompt = (0, llm_1.buildPrompt)(prompt, fixture.input);
|
|
307
343
|
const result = await (0, llm_1.callLlm)(provider, key, model, fullPrompt, outputSchema);
|