@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.
@@ -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
- const baseUrl = (0, shared_1.resolveBaseUrl)(o.baseUrl) || 'https://api.metrio.ai'; // the SDK's default
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
- agentApi: { apiKey, baseUrl },
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));
@@ -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(api, t, c, base, timezone) {
16
- const res = await (0, agent_runs_1.runAgentSync)({
17
- ...api,
18
- agentId: t.ref,
19
- messages: c.messages,
20
- variables: (0, prompt_1.mergeVariables)([], c.variables),
21
- timezone,
22
- });
23
- const taskId = res.body?.runId !== undefined ? String(res.body.runId) : undefined;
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 (res.status < 200 || res.status >= 300) {
26
- const detail = typeof res.body?.error === 'string' ? res.body.error : JSON.stringify(res.body?.error ?? '');
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 (res.status >= 400 && res.status < 500 && res.body?.details !== undefined) {
29
- const d = typeof res.body.details === 'string' ? res.body.details : JSON.stringify(res.body.details);
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=${res.status} ${detail}${extra}`.trim(), api.apiKey), status: res.status }, latencyMs: res.latencyMs, ...ids };
49
+ return { ...base, status: 'error', failures: [], error: { message: scrubKey(`http=${status} ${detail}${extra}`.trim(), apiKey), status }, latencyMs, ...ids };
33
50
  }
34
- if (typeof res.body?.status !== 'string') {
35
- return { ...base, status: 'error', failures: [], error: { message: scrubKey(`unexpected response: http=${res.status} without a run status`, api.apiKey) }, latencyMs: res.latencyMs, ...ids };
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 (res.body.status === 'failed') {
38
- const err = res.body.error;
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}`, api.apiKey) }, latencyMs: res.latencyMs, ...ids };
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)(res.body, res.latencyMs);
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, agentApi) {
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
- if (!agentApi)
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), agentApi?.apiKey ?? ''),
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.agentApi);
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.0",
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.5.2",
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
- }