@metrio-ai/cli 1.5.0 → 1.5.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/commands/verify.js +3 -2
- package/dist/verify/execute.js +42 -27
- package/package.json +2 -2
- package/dist/verify/agent-runs.js +0 -23
package/dist/commands/verify.js
CHANGED
|
@@ -307,13 +307,14 @@ async function runVerifyCommand(argv) {
|
|
|
307
307
|
const targets = resolveTargets(o);
|
|
308
308
|
(0, shared_1.loadEnv)(o.envFile);
|
|
309
309
|
const apiKey = (0, shared_1.resolveApiKey)(o.apiKey);
|
|
310
|
-
|
|
310
|
+
// The SDK appends paths as-is, so a trailing slash would give `//v1/...`.
|
|
311
|
+
const baseUrl = ((0, shared_1.resolveBaseUrl)(o.baseUrl) || 'https://api.metrio.ai').replace(/\/+$/, ''); // the SDK's default
|
|
311
312
|
const client = new client_1.MetrioAI({ apiKey, baseUrl });
|
|
312
313
|
const results = await (0, execute_1.runAll)(client, targets, {
|
|
313
314
|
concurrency: o.concurrency,
|
|
314
315
|
failFast: o.failFast,
|
|
315
316
|
timezone: o.timezone,
|
|
316
|
-
|
|
317
|
+
apiKey,
|
|
317
318
|
onResult: (r, done, total) => process.stderr.write((0, sync_1.safe)(`[${done}/${total}] ${r.resource} › ${r.case} ${r.status}\n`)),
|
|
318
319
|
});
|
|
319
320
|
process.stdout.write(o.json ? (0, report_1.formatJson)(results) : (0, report_1.formatHuman)(results));
|
package/dist/verify/execute.js
CHANGED
|
@@ -9,47 +9,62 @@ exports.runAll = runAll;
|
|
|
9
9
|
const client_1 = require("@metrio-ai/client");
|
|
10
10
|
const shared_1 = require("../commands/shared");
|
|
11
11
|
const prompt_1 = require("../commands/prompt");
|
|
12
|
-
const agent_runs_1 = require("./agent-runs");
|
|
13
12
|
const assert_1 = require("./assert");
|
|
14
13
|
const scrubKey = (s, key) => (key ? s.split(key).join('***') : s);
|
|
15
|
-
async function runAgentCase(
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
async function runAgentCase(client, t, c, base, apiKey, timezone) {
|
|
15
|
+
const started = Date.now();
|
|
16
|
+
let body;
|
|
17
|
+
let status = 200;
|
|
18
|
+
try {
|
|
19
|
+
body = await client.agents.runs.create(t.ref, {
|
|
20
|
+
messages: c.messages,
|
|
21
|
+
variables: (0, prompt_1.mergeVariables)([], c.variables),
|
|
22
|
+
timezone,
|
|
23
|
+
mode: 'sync',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
// Without a status it is a network or client-side validation failure: the generic error path.
|
|
28
|
+
if (!(err instanceof client_1.MetrioApiError) || err.statusCode === undefined)
|
|
29
|
+
throw err;
|
|
30
|
+
status = err.statusCode;
|
|
31
|
+
const text = typeof err.responseData === 'string' ? err.responseData : JSON.stringify(err.responseData ?? '');
|
|
32
|
+
try {
|
|
33
|
+
body = JSON.parse(text);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
body = { error: text.slice(0, 500) };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const latencyMs = Date.now() - started;
|
|
40
|
+
const taskId = body?.runId !== undefined ? String(body.runId) : undefined;
|
|
24
41
|
const ids = taskId ? { taskId } : {};
|
|
25
|
-
if (
|
|
26
|
-
const detail = typeof
|
|
42
|
+
if (status < 200 || status >= 300) {
|
|
43
|
+
const detail = typeof body?.error === 'string' ? body.error : JSON.stringify(body?.error ?? '');
|
|
27
44
|
let extra = '';
|
|
28
|
-
if (
|
|
29
|
-
const d = typeof
|
|
45
|
+
if (status >= 400 && status < 500 && body?.details !== undefined) {
|
|
46
|
+
const d = typeof body.details === 'string' ? body.details : JSON.stringify(body.details);
|
|
30
47
|
extra = ` (${d.length > 200 ? `${d.slice(0, 200)}...` : d})`;
|
|
31
48
|
}
|
|
32
|
-
return { ...base, status: 'error', failures: [], error: { message: scrubKey(`http=${
|
|
49
|
+
return { ...base, status: 'error', failures: [], error: { message: scrubKey(`http=${status} ${detail}${extra}`.trim(), apiKey), status }, latencyMs, ...ids };
|
|
33
50
|
}
|
|
34
|
-
if (typeof
|
|
35
|
-
return { ...base, status: 'error', failures: [], error: { message: scrubKey(`unexpected response: http=${
|
|
51
|
+
if (typeof body?.status !== 'string') {
|
|
52
|
+
return { ...base, status: 'error', failures: [], error: { message: scrubKey(`unexpected response: http=${status} without a run status`, apiKey) }, latencyMs, ...ids };
|
|
36
53
|
}
|
|
37
|
-
if (
|
|
38
|
-
const err =
|
|
54
|
+
if (body.status === 'failed') {
|
|
55
|
+
const err = body.error;
|
|
39
56
|
const detail = typeof err === 'string' ? err : (err?.message ?? 'no reason given');
|
|
40
|
-
return { ...base, status: 'error', failures: [], error: { message: scrubKey(`agent run failed: ${detail}`,
|
|
57
|
+
return { ...base, status: 'error', failures: [], error: { message: scrubKey(`agent run failed: ${detail}`, apiKey) }, latencyMs, ...ids };
|
|
41
58
|
}
|
|
42
|
-
const obs = (0, assert_1.observeAgent)(
|
|
59
|
+
const obs = (0, assert_1.observeAgent)(body, latencyMs);
|
|
43
60
|
const failures = (0, assert_1.evaluate)(c.expect, obs);
|
|
44
61
|
return { ...base, status: failures.length ? 'fail' : 'pass', failures, latencyMs: obs.latencyMs, tokens: obs.tokens, ...ids };
|
|
45
62
|
}
|
|
46
|
-
async function runCase(client, t, c, timezone,
|
|
63
|
+
async function runCase(client, t, c, timezone, apiKey = '') {
|
|
47
64
|
const base = { resource: t.label, case: c.name };
|
|
48
65
|
try {
|
|
49
66
|
if (t.kind === 'agent') {
|
|
50
|
-
|
|
51
|
-
throw new Error('agent targets need agentApi (base URL and API key)');
|
|
52
|
-
return await runAgentCase(agentApi, t, c, base, timezone);
|
|
67
|
+
return await runAgentCase(client, t, c, base, apiKey, timezone);
|
|
53
68
|
}
|
|
54
69
|
let obs;
|
|
55
70
|
let version;
|
|
@@ -124,7 +139,7 @@ async function runCase(client, t, c, timezone, agentApi) {
|
|
|
124
139
|
status: 'error',
|
|
125
140
|
failures: [],
|
|
126
141
|
error: {
|
|
127
|
-
message: scrubKey(err instanceof Error ? err.message : String(err),
|
|
142
|
+
message: scrubKey(err instanceof Error ? err.message : String(err), apiKey),
|
|
128
143
|
status: err instanceof client_1.MetrioApiError ? err.statusCode : undefined,
|
|
129
144
|
},
|
|
130
145
|
};
|
|
@@ -137,7 +152,7 @@ async function runAll(client, targets, opts) {
|
|
|
137
152
|
return (0, shared_1.mapWithConcurrency)(jobs, opts.concurrency, async ({ t, c }) => {
|
|
138
153
|
const result = stop
|
|
139
154
|
? { resource: t.label, case: c.name, status: 'skipped', failures: [] }
|
|
140
|
-
: await runCase(client, t, c, opts.timezone, opts.
|
|
155
|
+
: await runCase(client, t, c, opts.timezone, opts.apiKey);
|
|
141
156
|
if (opts.failFast && (result.status === 'fail' || result.status === 'error'))
|
|
142
157
|
stop = true;
|
|
143
158
|
done += 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metrio-ai/cli",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Metrio AI command-line interface: run prompts and workflows, manage Metrio resources",
|
|
5
5
|
"bin": {
|
|
6
6
|
"metrio": "dist/index.js"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"access": "public"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@metrio-ai/client": "^1.
|
|
31
|
+
"@metrio-ai/client": "^1.6.3",
|
|
32
32
|
"ajv": "^8.17.1",
|
|
33
33
|
"diff": "^5.2.0",
|
|
34
34
|
"yaml": "^2.8.3"
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runAgentSync = runAgentSync;
|
|
4
|
-
async function runAgentSync(o) {
|
|
5
|
-
const doFetch = o.fetchImpl ?? fetch;
|
|
6
|
-
const url = `${o.baseUrl.replace(/\/+$/, '')}/v1/agents/${o.agentId}/runs`;
|
|
7
|
-
const started = Date.now();
|
|
8
|
-
const res = await doFetch(url, {
|
|
9
|
-
method: 'POST',
|
|
10
|
-
headers: { 'Content-Type': 'application/json', metrioai_api_key: o.apiKey },
|
|
11
|
-
body: JSON.stringify({ messages: o.messages, ...(o.variables ? { variables: o.variables } : {}), ...(o.timezone ? { timezone: o.timezone } : {}), mode: 'sync' }),
|
|
12
|
-
});
|
|
13
|
-
const text = await res.text();
|
|
14
|
-
const latencyMs = Date.now() - started;
|
|
15
|
-
let body;
|
|
16
|
-
try {
|
|
17
|
-
body = JSON.parse(text);
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
body = { error: text.slice(0, 500) };
|
|
21
|
-
}
|
|
22
|
-
return { status: res.status, body, latencyMs };
|
|
23
|
-
}
|