@root-signals/scorable-cli 0.12.0 → 0.13.0

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.
Files changed (35) hide show
  1. package/dist/commands/evaluator/create.d.ts.map +1 -1
  2. package/dist/commands/evaluator/create.js +6 -5
  3. package/dist/commands/evaluator/execute-by-name.d.ts +1 -0
  4. package/dist/commands/evaluator/execute-by-name.d.ts.map +1 -1
  5. package/dist/commands/evaluator/execute-by-name.js +22 -2
  6. package/dist/commands/evaluator/execute.d.ts +1 -0
  7. package/dist/commands/evaluator/execute.d.ts.map +1 -1
  8. package/dist/commands/evaluator/execute.js +22 -2
  9. package/dist/commands/evaluator/update.d.ts.map +1 -1
  10. package/dist/commands/evaluator/update.js +6 -5
  11. package/dist/commands/judge/create.d.ts.map +1 -1
  12. package/dist/commands/judge/create.js +7 -5
  13. package/dist/commands/judge/exec-openai-generic.d.ts.map +1 -1
  14. package/dist/commands/judge/exec-openai-generic.js +16 -11
  15. package/dist/commands/judge/exec-openai.d.ts.map +1 -1
  16. package/dist/commands/judge/exec-openai.js +16 -11
  17. package/dist/commands/judge/execute-by-name.d.ts +1 -0
  18. package/dist/commands/judge/execute-by-name.d.ts.map +1 -1
  19. package/dist/commands/judge/execute-by-name.js +22 -2
  20. package/dist/commands/judge/execute.d.ts +1 -0
  21. package/dist/commands/judge/execute.d.ts.map +1 -1
  22. package/dist/commands/judge/execute.js +22 -2
  23. package/dist/commands/judge/generate.d.ts.map +1 -1
  24. package/dist/commands/judge/generate.js +5 -4
  25. package/dist/commands/judge/update.d.ts.map +1 -1
  26. package/dist/commands/judge/update.js +7 -5
  27. package/dist/commands/otel-filter/create.d.ts.map +1 -1
  28. package/dist/commands/otel-filter/create.js +6 -6
  29. package/dist/lib/filter-yaml.d.ts +8 -0
  30. package/dist/lib/filter-yaml.d.ts.map +1 -1
  31. package/dist/lib/filter-yaml.js +1 -1
  32. package/dist/utils.d.ts +9 -0
  33. package/dist/utils.d.ts.map +1 -1
  34. package/dist/utils.js +27 -7
  35. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA+E9D"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAiF9D"}
@@ -1,6 +1,8 @@
1
1
  import ora from "ora";
2
+ import { z } from "zod";
2
3
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
4
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
5
+ import { parseJsonArg } from "../../utils.js";
4
6
  export function registerCreateCommand(evaluator) {
5
7
  evaluator
6
8
  .command("create")
@@ -36,13 +38,12 @@ export function registerCreateCommand(evaluator) {
36
38
  if (opts.objectiveVersionId)
37
39
  payload.objective_version_id = opts.objectiveVersionId;
38
40
  if (opts.models) {
39
- try {
40
- payload.models = JSON.parse(opts.models);
41
- }
42
- catch {
43
- printError("Invalid JSON format for --models.");
41
+ const r = parseJsonArg(opts.models, z.array(z.string()));
42
+ if (!r.ok) {
43
+ printError("Invalid JSON format for --models. Expected an array of model name strings.");
44
44
  return;
45
45
  }
46
+ payload.models = r.value;
46
47
  }
47
48
  const spinner = ora("Creating...").start();
48
49
  try {
@@ -4,6 +4,7 @@ export declare function executeEvaluatorByName(evaluatorName: string, opts: {
4
4
  request?: string;
5
5
  response?: string;
6
6
  turns?: string;
7
+ tools?: string;
7
8
  contexts?: string;
8
9
  expectedOutput?: string;
9
10
  tag?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"execute-by-name.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/execute-by-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA2Ef;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA4ErE"}
1
+ {"version":3,"file":"execute-by-name.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/execute-by-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAyFf;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAmFrE"}
@@ -1,7 +1,7 @@
1
1
  import ora from "ora";
2
2
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
3
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
4
- import { isTurnArray, isStringArray, isStringRecord } from "../../utils.js";
4
+ import { isTurnArray, isToolCatalog, isStringArray, isStringRecord } from "../../utils.js";
5
5
  async function readStdinDefault() {
6
6
  const chunks = [];
7
7
  for await (const chunk of process.stdin)
@@ -47,6 +47,20 @@ export async function executeEvaluatorByName(evaluatorName, opts, readStdin = re
47
47
  return;
48
48
  }
49
49
  }
50
+ if (opts.tools) {
51
+ try {
52
+ const parsed = JSON.parse(opts.tools);
53
+ if (!isToolCatalog(parsed)) {
54
+ printError("Invalid JSON for --tools. Expected array of tool objects.");
55
+ return;
56
+ }
57
+ payload.tools = parsed;
58
+ }
59
+ catch {
60
+ printError("Invalid JSON for --tools.");
61
+ return;
62
+ }
63
+ }
50
64
  if (opts.contexts) {
51
65
  try {
52
66
  const parsed = JSON.parse(opts.contexts);
@@ -100,7 +114,8 @@ export function registerExecuteByNameCommand(evaluator) {
100
114
  .option("--user-id <id>", "User identifier for tracking purposes")
101
115
  .option("--session-id <id>", "Session identifier for tracking purposes")
102
116
  .option("--system-prompt <text>", "System prompt that was used for the LLM call")
103
- .option("--turns <json>", 'JSON array of conversation turns. E.g., \'[{"role":"user","content":"Hello"}]\'')
117
+ .option("--turns <json>", 'JSON array of conversation turns. Roles: user|assistant|tool. Assistant turns may carry `tool_calls`; tool results use a `tool` role with `tool_call_id`. E.g., \'[{"role":"user","content":"Hello"}]\'')
118
+ .option("--tools <json>", "JSON array of OpenAI-style tool definitions available to the agent.")
104
119
  .option("--variables <json>", 'JSON object of extra template variables. E.g., \'{"lang":"EN"}\'')
105
120
  .addHelpText("after", `
106
121
  Examples:
@@ -124,6 +139,11 @@ Examples:
124
139
  $ scorable evaluator execute-by-name "Helpfulness" \\
125
140
  --turns '[{"role":"user","content":"Hello, I need help with my order"},{"role":"assistant","content":"I\\'d be happy to help! What\\'s your order number?"},{"role":"user","content":"It\\'s ORDER-12345"}]'
126
141
 
142
+ # Tool-aware evaluation: assistant tool_calls + tool-role result + tool catalog
143
+ $ scorable evaluator execute-by-name "Tool Selection" \\
144
+ --turns '[{"role":"user","content":"What is the weather in Paris?"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"get_weather","arguments":"{\\"city\\":\\"Paris\\"}"}}]},{"role":"tool","tool_call_id":"call_1","content":"{\\"temp\\":12}"},{"role":"assistant","content":"It is 12C in Paris."}]' \\
145
+ --tools '[{"type":"function","function":{"name":"get_weather","description":"Get weather for a city","parameters":{"type":"object","properties":{"city":{"type":"string"}}}}}]'
146
+
127
147
  # Pipe a response from your app via stdin
128
148
  $ ./my-app respond "Do you offer refunds?" | \\
129
149
  scorable evaluator execute-by-name "Helpfulness" --request "Do you offer refunds?"`)
@@ -4,6 +4,7 @@ export declare function executeEvaluator(evaluatorId: string, opts: {
4
4
  request?: string;
5
5
  response?: string;
6
6
  turns?: string;
7
+ tools?: string;
7
8
  contexts?: string;
8
9
  expectedOutput?: string;
9
10
  tag?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAwFf;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA2F/D"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAsGf;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAkG/D"}
@@ -1,7 +1,7 @@
1
1
  import ora from "ora";
2
2
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
3
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
4
- import { isTurnArray, isStringArray, isStringRecord } from "../../utils.js";
4
+ import { isTurnArray, isToolCatalog, isStringArray, isStringRecord } from "../../utils.js";
5
5
  async function readStdinDefault() {
6
6
  const chunks = [];
7
7
  for await (const chunk of process.stdin)
@@ -47,6 +47,20 @@ export async function executeEvaluator(evaluatorId, opts, readStdin = readStdinD
47
47
  return;
48
48
  }
49
49
  }
50
+ if (opts.tools) {
51
+ try {
52
+ const parsed = JSON.parse(opts.tools);
53
+ if (!isToolCatalog(parsed)) {
54
+ printError("Invalid JSON for --tools. Expected array of tool objects.");
55
+ return;
56
+ }
57
+ payload.tools = parsed;
58
+ }
59
+ catch {
60
+ printError("Invalid JSON for --tools.");
61
+ return;
62
+ }
63
+ }
50
64
  if (opts.contexts) {
51
65
  try {
52
66
  const parsed = JSON.parse(opts.contexts);
@@ -113,7 +127,8 @@ export function registerExecuteCommand(evaluator) {
113
127
  .option("--user-id <id>", "User identifier for tracking purposes")
114
128
  .option("--session-id <id>", "Session identifier for tracking purposes")
115
129
  .option("--system-prompt <text>", "System prompt that was used for the LLM call")
116
- .option("--turns <json>", 'JSON array of conversation turns. E.g., \'[{"role":"user","content":"Hello"}]\'')
130
+ .option("--turns <json>", 'JSON array of conversation turns. Roles: user|assistant|tool. Assistant turns may carry `tool_calls`; tool results use a `tool` role with `tool_call_id`. E.g., \'[{"role":"user","content":"Hello"}]\'')
131
+ .option("--tools <json>", "JSON array of OpenAI-style tool definitions available to the agent.")
117
132
  .option("--variables <json>", 'JSON object of extra template variables. E.g., \'{"lang":"EN"}\'')
118
133
  .option("--file-ids <json>", "JSON array of file UUIDs from 'scorable file upload'. E.g., '[\"uuid1\"]'")
119
134
  .addHelpText("after", `
@@ -139,6 +154,11 @@ Examples:
139
154
  $ scorable evaluator execute <evaluatorId> \\
140
155
  --turns '[{"role":"user","content":"Hello, I need help with my order"},{"role":"assistant","content":"I\\'d be happy to help! What\\'s your order number?"},{"role":"user","content":"It\\'s ORDER-12345"},{"role":"assistant","content":"I found your order. It\\'s currently in transit."}]'
141
156
 
157
+ # Tool-aware evaluation: assistant tool_calls, tool-role result, and tool catalog
158
+ $ scorable evaluator execute <evaluatorId> \\
159
+ --turns '[{"role":"user","content":"What is the weather in Paris?"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"get_weather","arguments":"{\\"city\\":\\"Paris\\"}"}}]},{"role":"tool","tool_call_id":"call_1","content":"{\\"temp\\":12}"},{"role":"assistant","content":"It is 12C in Paris."}]' \\
160
+ --tools '[{"type":"function","function":{"name":"get_weather","description":"Get weather for a city","parameters":{"type":"object","properties":{"city":{"type":"string"}}}}}]'
161
+
142
162
  # With custom template variables and tracking
143
163
  $ scorable evaluator execute <evaluatorId> \\
144
164
  --request "How do I cancel my subscription?" \\
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAwD9D"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CA0D9D"}
@@ -1,6 +1,8 @@
1
1
  import ora from "ora";
2
+ import { z } from "zod";
2
3
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
4
  import { printInfo, printSuccess, printError, printJson, handleSdkError } from "../../output.js";
5
+ import { parseJsonArg } from "../../utils.js";
4
6
  export function registerUpdateCommand(evaluator) {
5
7
  evaluator
6
8
  .command("update <evaluatorId>")
@@ -22,13 +24,12 @@ export function registerUpdateCommand(evaluator) {
22
24
  if (opts.objectiveVersionId !== undefined)
23
25
  payload.objective_version_id = opts.objectiveVersionId;
24
26
  if (opts.models !== undefined) {
25
- try {
26
- payload.models = JSON.parse(opts.models);
27
- }
28
- catch {
29
- printError("Invalid JSON format for --models.");
27
+ const r = parseJsonArg(opts.models, z.array(z.string()));
28
+ if (!r.ok) {
29
+ printError("Invalid JSON format for --models. Expected an array of model name strings.");
30
30
  return;
31
31
  }
32
+ payload.models = r.value;
32
33
  }
33
34
  if (Object.keys(payload).length === 0) {
34
35
  printInfo("No update parameters provided. Aborting.");
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA+C1D"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA+C1D"}
@@ -1,6 +1,9 @@
1
1
  import ora from "ora";
2
+ import { z } from "zod";
2
3
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
4
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
5
+ import { parseJsonArg } from "../../utils.js";
6
+ const EvaluatorRefsSchema = z.array(z.object({ id: z.string() }).passthrough());
4
7
  export function registerCreateCommand(judge) {
5
8
  judge
6
9
  .command("create")
@@ -15,13 +18,12 @@ export function registerCreateCommand(judge) {
15
18
  if (opts.stage)
16
19
  payload.stage = opts.stage;
17
20
  if (opts.evaluatorReferences) {
18
- try {
19
- payload.evaluator_references = JSON.parse(opts.evaluatorReferences);
20
- }
21
- catch {
22
- printError("Invalid JSON format for --evaluator-references.");
21
+ const r = parseJsonArg(opts.evaluatorReferences, EvaluatorRefsSchema);
22
+ if (!r.ok) {
23
+ printError('Invalid JSON format for --evaluator-references. Expected an array of objects with an "id" string.');
23
24
  return;
24
25
  }
26
+ payload.evaluator_references = r.value;
25
27
  }
26
28
  const spinner = ora("Creating...").start();
27
29
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"exec-openai-generic.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/exec-openai-generic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA6CrE"}
1
+ {"version":3,"file":"exec-openai-generic.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/exec-openai-generic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAiDrE"}
@@ -1,6 +1,10 @@
1
+ import { z } from "zod";
1
2
  import { requireApiKey } from "../../auth.js";
2
3
  import { printInfo, printSuccess, printError, printWarning, printJson, handleSdkError, } from "../../output.js";
4
+ import { parseJsonArg } from "../../utils.js";
3
5
  import { apiRequest } from "../../client.js";
6
+ const MessagesSchema = z.array(z.object({ role: z.string() }).passthrough());
7
+ const ExtraBodySchema = z.record(z.string(), z.unknown());
4
8
  export function registerExecOpenaiGenericCommand(judge) {
5
9
  judge
6
10
  .command("exec-openai-generic")
@@ -10,21 +14,22 @@ export function registerExecOpenaiGenericCommand(judge) {
10
14
  .option("--extra-body <json>", "Optional JSON string for extra_body parameters")
11
15
  .action(async (opts) => {
12
16
  const apiKey = await requireApiKey();
13
- let messages;
14
- try {
15
- messages = JSON.parse(opts.messages);
16
- }
17
- catch {
18
- printError("Invalid JSON for --messages. Aborting.");
17
+ const messagesResult = parseJsonArg(opts.messages, MessagesSchema);
18
+ if (!messagesResult.ok) {
19
+ printError("Invalid JSON for --messages. Expected an array of objects each with a string `role`. Aborting.");
19
20
  return;
20
21
  }
21
- const payload = { model: opts.model, messages };
22
+ const payload = {
23
+ model: opts.model,
24
+ messages: messagesResult.value,
25
+ };
22
26
  if (opts.extraBody) {
23
- try {
24
- payload["extra_body"] = JSON.parse(opts.extraBody);
27
+ const extraResult = parseJsonArg(opts.extraBody, ExtraBodySchema);
28
+ if (extraResult.ok) {
29
+ payload["extra_body"] = extraResult.value;
25
30
  }
26
- catch {
27
- printWarning("Invalid JSON for --extra-body. Skipping.");
31
+ else {
32
+ printWarning("Invalid JSON for --extra-body (expected an object). Skipping.");
28
33
  }
29
34
  }
30
35
  printInfo(`Executing a Judge using generic OpenAI endpoint. Judge ID/Name: ${opts.model}`);
@@ -1 +1 @@
1
- {"version":3,"file":"exec-openai.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/exec-openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAwD9D"}
1
+ {"version":3,"file":"exec-openai.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/exec-openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA4D9D"}
@@ -1,6 +1,10 @@
1
+ import { z } from "zod";
1
2
  import { requireApiKey } from "../../auth.js";
2
3
  import { printInfo, printSuccess, printError, printWarning, printJson, handleSdkError, } from "../../output.js";
4
+ import { parseJsonArg } from "../../utils.js";
3
5
  import { apiRequest } from "../../client.js";
6
+ const MessagesSchema = z.array(z.object({ role: z.string() }).passthrough());
7
+ const ExtraBodySchema = z.record(z.string(), z.unknown());
4
8
  export function registerExecOpenaiCommand(judge) {
5
9
  judge
6
10
  .command("exec-openai <judgeIdInPath>")
@@ -10,21 +14,22 @@ export function registerExecOpenaiCommand(judge) {
10
14
  .option("--extra-body <json>", "Optional JSON string for extra_body parameters")
11
15
  .action(async (judgeIdInPath, opts) => {
12
16
  const apiKey = await requireApiKey();
13
- let messages;
14
- try {
15
- messages = JSON.parse(opts.messages);
16
- }
17
- catch {
18
- printError("Invalid JSON for --messages. Aborting.");
17
+ const messagesResult = parseJsonArg(opts.messages, MessagesSchema);
18
+ if (!messagesResult.ok) {
19
+ printError("Invalid JSON for --messages. Expected an array of objects each with a string `role`. Aborting.");
19
20
  return;
20
21
  }
21
- const payload = { model: opts.model, messages };
22
+ const payload = {
23
+ model: opts.model,
24
+ messages: messagesResult.value,
25
+ };
22
26
  if (opts.extraBody) {
23
- try {
24
- payload["extra_body"] = JSON.parse(opts.extraBody);
27
+ const extraResult = parseJsonArg(opts.extraBody, ExtraBodySchema);
28
+ if (extraResult.ok) {
29
+ payload["extra_body"] = extraResult.value;
25
30
  }
26
- catch {
27
- printWarning("Invalid JSON for --extra-body. Skipping.");
31
+ else {
32
+ printWarning("Invalid JSON for --extra-body (expected an object). Skipping.");
28
33
  }
29
34
  }
30
35
  printInfo(`Executing Judge ID (via path): ${judgeIdInPath} using OpenAI chat completions format.`);
@@ -4,6 +4,7 @@ export declare function executeJudgeByName(judgeName: string, opts: {
4
4
  request?: string;
5
5
  response?: string;
6
6
  turns?: string;
7
+ tools?: string;
7
8
  contexts?: string;
8
9
  expectedOutput?: string;
9
10
  tag?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"execute-by-name.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/execute-by-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAkEjE"}
1
+ {"version":3,"file":"execute-by-name.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/execute-by-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA2Ef;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAyEjE"}
@@ -1,7 +1,7 @@
1
1
  import ora from "ora";
2
2
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
3
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
4
- import { isTurnArray, isStringArray } from "../../utils.js";
4
+ import { isTurnArray, isToolCatalog, isStringArray } from "../../utils.js";
5
5
  async function readStdinDefault() {
6
6
  const chunks = [];
7
7
  for await (const chunk of process.stdin)
@@ -47,6 +47,20 @@ export async function executeJudgeByName(judgeName, opts, readStdin = readStdinD
47
47
  return;
48
48
  }
49
49
  }
50
+ if (opts.tools) {
51
+ try {
52
+ const parsed = JSON.parse(opts.tools);
53
+ if (!isToolCatalog(parsed)) {
54
+ printError("Invalid JSON for --tools. Expected array of tool objects.");
55
+ return;
56
+ }
57
+ payload.tools = parsed;
58
+ }
59
+ catch {
60
+ printError("Invalid JSON for --tools.");
61
+ return;
62
+ }
63
+ }
50
64
  if (opts.contexts) {
51
65
  try {
52
66
  const parsed = JSON.parse(opts.contexts);
@@ -86,7 +100,8 @@ export function registerExecuteByNameCommand(judge) {
86
100
  .option("--user-id <id>", "User identifier for tracking purposes")
87
101
  .option("--session-id <id>", "Session identifier for tracking purposes")
88
102
  .option("--system-prompt <text>", "System prompt that was used for the LLM call")
89
- .option("--turns <json>", 'JSON array of conversation turns. E.g., \'[{"role":"user","content":"Hello"}]\'')
103
+ .option("--turns <json>", 'JSON array of conversation turns. Roles: user|assistant|tool. Assistant turns may carry `tool_calls`; tool results use a `tool` role with `tool_call_id`. E.g., \'[{"role":"user","content":"Hello"}]\'')
104
+ .option("--tools <json>", "JSON array of OpenAI-style tool definitions available to the agent.")
90
105
  .addHelpText("after", `
91
106
  Examples:
92
107
  # Evaluate a customer service response
@@ -104,6 +119,11 @@ Examples:
104
119
  $ scorable judge execute-by-name "Customer Service Judge" \\
105
120
  --turns '[{"role":"user","content":"I haven\\'t received my refund and it\\'s been 10 days."},{"role":"assistant","content":"Thank you for contacting us. I will investigate this immediately and provide an update within 24 hours."}]'
106
121
 
122
+ # Tool-aware evaluation: assistant tool_calls + tool-role result + tool catalog
123
+ $ scorable judge execute-by-name "Agent Quality Judge" \\
124
+ --turns '[{"role":"user","content":"What is the weather in Paris?"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"get_weather","arguments":"{\\"city\\":\\"Paris\\"}"}}]},{"role":"tool","tool_call_id":"call_1","content":"{\\"temp\\":12}"},{"role":"assistant","content":"It is 12C in Paris."}]' \\
125
+ --tools '[{"type":"function","function":{"name":"get_weather","description":"Get weather for a city","parameters":{"type":"object","properties":{"city":{"type":"string"}}}}}]'
126
+
107
127
  # Pipe the LLM response from stdin
108
128
  $ echo "We offer a 30-day money-back guarantee on all purchases." | \\
109
129
  scorable judge execute-by-name "Custom Returns Policy Judge" --request "Do you offer refunds?"`)
@@ -4,6 +4,7 @@ export declare function executeJudge(judgeId: string, opts: {
4
4
  request?: string;
5
5
  response?: string;
6
6
  turns?: string;
7
+ tools?: string;
7
8
  contexts?: string;
8
9
  expectedOutput?: string;
9
10
  tag?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAyE3D"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,iBAAe,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIjD;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,SAAS,0BAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA2Ef;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAgF3D"}
@@ -1,7 +1,7 @@
1
1
  import ora from "ora";
2
2
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
3
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
4
- import { isTurnArray, isStringArray } from "../../utils.js";
4
+ import { isTurnArray, isToolCatalog, isStringArray } from "../../utils.js";
5
5
  async function readStdinDefault() {
6
6
  const chunks = [];
7
7
  for await (const chunk of process.stdin)
@@ -47,6 +47,20 @@ export async function executeJudge(judgeId, opts, readStdin = readStdinDefault)
47
47
  return;
48
48
  }
49
49
  }
50
+ if (opts.tools) {
51
+ try {
52
+ const parsed = JSON.parse(opts.tools);
53
+ if (!isToolCatalog(parsed)) {
54
+ printError("Invalid JSON for --tools. Expected array of tool objects.");
55
+ return;
56
+ }
57
+ payload.tools = parsed;
58
+ }
59
+ catch {
60
+ printError("Invalid JSON for --tools.");
61
+ return;
62
+ }
63
+ }
50
64
  if (opts.contexts) {
51
65
  try {
52
66
  const parsed = JSON.parse(opts.contexts);
@@ -86,7 +100,8 @@ export function registerExecuteCommand(judge) {
86
100
  .option("--user-id <id>", "User identifier for tracking purposes")
87
101
  .option("--session-id <id>", "Session identifier for tracking purposes")
88
102
  .option("--system-prompt <text>", "System prompt that was used for the LLM call")
89
- .option("--turns <json>", 'JSON array of conversation turns. E.g., \'[{"role":"user","content":"Hello"}]\'')
103
+ .option("--turns <json>", 'JSON array of conversation turns. Roles: user|assistant|tool. Assistant turns may carry `tool_calls`; tool results use a `tool` role with `tool_call_id`. E.g., \'[{"role":"user","content":"Hello"}]\'')
104
+ .option("--tools <json>", "JSON array of OpenAI-style tool definitions available to the agent.")
90
105
  .addHelpText("after", `
91
106
  Examples:
92
107
  # Evaluate a customer service response about return policy
@@ -104,6 +119,11 @@ Examples:
104
119
  $ scorable judge execute <judgeId> \\
105
120
  --turns '[{"role":"user","content":"Hello, I need help with my order"},{"role":"assistant","content":"I\\'d be happy to help! What\\'s your order number?"},{"role":"user","content":"It\\'s ORDER-12345"},{"role":"assistant","content":"I found your order. It\\'s currently in transit."}]'
106
121
 
122
+ # Tool-aware evaluation: assistant tool_calls, tool-role result, and tool catalog
123
+ $ scorable judge execute <judgeId> \\
124
+ --turns '[{"role":"user","content":"What is the weather in Paris?"},{"role":"assistant","content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"get_weather","arguments":"{\\"city\\":\\"Paris\\"}"}}]},{"role":"tool","tool_call_id":"call_1","content":"{\\"temp\\":12}"},{"role":"assistant","content":"It is 12C in Paris."}]' \\
125
+ --tools '[{"type":"function","function":{"name":"get_weather","description":"Get weather for a city","parameters":{"type":"object","properties":{"city":{"type":"string"}}}}}]'
126
+
107
127
  # With tracking metadata and tags
108
128
  $ scorable judge execute <judgeId> \\
109
129
  --request "How do I reset my password?" \\
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA8I5D"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA8I5D"}
@@ -1,7 +1,9 @@
1
1
  import ora from "ora";
2
2
  import chalk from "chalk";
3
+ import { z } from "zod";
3
4
  import { requireApiKey, getSdkClient } from "../../auth.js";
4
5
  import { printSuccess, printError, printWarning, printInfo, printJson, handleSdkError, } from "../../output.js";
6
+ import { parseJsonArg } from "../../utils.js";
5
7
  import { CliError } from "../../types.js";
6
8
  import { uploadFile } from "../file/upload.js";
7
9
  export function registerGenerateCommand(judge) {
@@ -40,13 +42,12 @@ offer to connect the guest with a human agent when uncertain."`)
40
42
  const apiKey = await requireApiKey();
41
43
  let extra_contexts;
42
44
  if (opts.extraContexts) {
43
- try {
44
- extra_contexts = JSON.parse(opts.extraContexts);
45
- }
46
- catch {
45
+ const r = parseJsonArg(opts.extraContexts, z.record(z.string(), z.string()));
46
+ if (!r.ok) {
47
47
  printError("Invalid --extra-contexts JSON: must be a key-value object.");
48
48
  throw new CliError(1, "Invalid extra-contexts JSON");
49
49
  }
50
+ extra_contexts = r.value;
50
51
  }
51
52
  const spinner = ora("Generating judge (this may take a moment)...").start();
52
53
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAkD1D"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAkD1D"}
@@ -1,6 +1,9 @@
1
1
  import ora from "ora";
2
+ import { z } from "zod";
2
3
  import { requireApiKey, getSdkClient } from "../../auth.js";
3
4
  import { printInfo, printSuccess, printError, printJson, handleSdkError } from "../../output.js";
5
+ import { parseJsonArg } from "../../utils.js";
6
+ const EvaluatorRefsSchema = z.array(z.object({ id: z.string() }).passthrough());
4
7
  export function registerUpdateCommand(judge) {
5
8
  judge
6
9
  .command("update <judgeId>")
@@ -16,13 +19,12 @@ export function registerUpdateCommand(judge) {
16
19
  if (opts.stage !== undefined)
17
20
  payload.stage = opts.stage;
18
21
  if (opts.evaluatorReferences !== undefined) {
19
- try {
20
- payload.evaluator_references = JSON.parse(opts.evaluatorReferences);
21
- }
22
- catch {
23
- printError("Invalid JSON format for --evaluator-references.");
22
+ const r = parseJsonArg(opts.evaluatorReferences, EvaluatorRefsSchema);
23
+ if (!r.ok) {
24
+ printError('Invalid JSON format for --evaluator-references. Expected an array of objects with an "id" string.');
24
25
  return;
25
26
  }
27
+ payload.evaluator_references = r.value;
26
28
  }
27
29
  if (Object.keys(payload).length === 0) {
28
30
  printInfo("No update parameters provided. Aborting.");
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/otel-filter/create.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CA6J/D"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/otel-filter/create.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBpC,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CA+J/D"}
@@ -3,7 +3,8 @@ import ora from "ora";
3
3
  import { requireApiKey } from "../../auth.js";
4
4
  import { apiRequest } from "../../client.js";
5
5
  import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
6
- import { loadFilterYaml } from "../../lib/filter-yaml.js";
6
+ import { loadFilterYaml, Match } from "../../lib/filter-yaml.js";
7
+ import { parseJsonArg } from "../../utils.js";
7
8
  export function registerCreateCommand(otelFilter) {
8
9
  otelFilter
9
10
  .command("create")
@@ -71,13 +72,12 @@ YAML manifest (only needed for custom extractor_rules):
71
72
  }
72
73
  let filterCriteria;
73
74
  if (opts.filterCriteria) {
74
- try {
75
- filterCriteria = JSON.parse(opts.filterCriteria);
76
- }
77
- catch {
78
- printError("Invalid JSON for --filter-criteria.");
75
+ const r = parseJsonArg(opts.filterCriteria, Match);
76
+ if (!r.ok) {
77
+ printError('Invalid JSON for --filter-criteria. Expected {"conditions":[{column,operator,value?,key?},...]}.');
79
78
  process.exit(1);
80
79
  }
80
+ filterCriteria = r.value;
81
81
  }
82
82
  let samplingRate;
83
83
  if (opts.samplingRate !== undefined) {
@@ -1,4 +1,12 @@
1
1
  import { z } from "zod";
2
+ export declare const Match: z.ZodObject<{
3
+ conditions: z.ZodDefault<z.ZodArray<z.ZodObject<{
4
+ column: z.ZodString;
5
+ operator: z.ZodString;
6
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
7
+ key: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strict>>>;
9
+ }, z.core.$strict>;
2
10
  export declare const FilterYamlSchema: z.ZodObject<{
3
11
  name: z.ZodString;
4
12
  evaluator_id: z.ZodOptional<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"filter-yaml.d.ts","sourceRoot":"","sources":["../../src/lib/filter-yaml.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyFxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAczB,CAAC;AAEL,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAczD"}
1
+ {"version":3,"file":"filter-yaml.d.ts","sourceRoot":"","sources":["../../src/lib/filter-yaml.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,KAAK;;;;;;;kBAIP,CAAC;AAyEZ,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAczB,CAAC;AAEL,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAczD"}
@@ -8,7 +8,7 @@ const MatchCondition = z
8
8
  key: z.string().optional(),
9
9
  })
10
10
  .strict();
11
- const Match = z
11
+ export const Match = z
12
12
  .object({
13
13
  conditions: z.array(MatchCondition).default([]),
14
14
  })
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,14 @@
1
+ import { type ZodType } from "zod";
1
2
  import type { Turn } from "@root-signals/scorable";
2
3
  export declare function isTurnArray(v: unknown): v is Turn[];
4
+ export declare function isToolCatalog(v: unknown): v is Record<string, unknown>[];
3
5
  export declare function isStringArray(v: unknown): v is string[];
4
6
  export declare function isStringRecord(v: unknown): v is Record<string, string>;
7
+ export type JsonArgResult<T> = {
8
+ ok: true;
9
+ value: T;
10
+ } | {
11
+ ok: false;
12
+ };
13
+ export declare function parseJsonArg<T>(raw: string, schema: ZodType<T>): JsonArgResult<T>;
5
14
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAanD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,EAAE,CAEvD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOtE"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,KAAK,OAAO,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAanD,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAEnD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAExE;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,EAAE,CAEvD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOtE;AAID,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC;AAEtE,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAUjF"}
package/dist/utils.js CHANGED
@@ -1,11 +1,18 @@
1
+ import { z } from "zod";
2
+ const TurnSchema = z.object({
3
+ role: z.enum(["user", "assistant", "tool"]),
4
+ content: z.string().nullable().optional(),
5
+ contexts: z.array(z.string()).nullable().optional(),
6
+ tool_calls: z.array(z.record(z.string(), z.unknown())).nullable().optional(),
7
+ tool_call_id: z.string().nullable().optional(),
8
+ });
9
+ const TurnArraySchema = z.array(TurnSchema);
10
+ const ToolCatalogSchema = z.array(z.record(z.string(), z.unknown()));
1
11
  export function isTurnArray(v) {
2
- return (Array.isArray(v) &&
3
- v.every((t) => typeof t === "object" &&
4
- t !== null &&
5
- "role" in t &&
6
- typeof t.role === "string" &&
7
- "content" in t &&
8
- typeof t.content === "string"));
12
+ return TurnArraySchema.safeParse(v).success;
13
+ }
14
+ export function isToolCatalog(v) {
15
+ return ToolCatalogSchema.safeParse(v).success;
9
16
  }
10
17
  export function isStringArray(v) {
11
18
  return Array.isArray(v) && v.every((s) => typeof s === "string");
@@ -16,3 +23,16 @@ export function isStringRecord(v) {
16
23
  !Array.isArray(v) &&
17
24
  Object.values(v).every((val) => typeof val === "string"));
18
25
  }
26
+ export function parseJsonArg(raw, schema) {
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(raw);
30
+ }
31
+ catch {
32
+ return { ok: false };
33
+ }
34
+ const result = schema.safeParse(parsed);
35
+ if (!result.success)
36
+ return { ok: false };
37
+ return { ok: true, value: result.data };
38
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@root-signals/scorable-cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "CLI for Scorable",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Scorable",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@inquirer/prompts": "^8.3.2",
35
- "@root-signals/scorable": "^0.8.0",
35
+ "@root-signals/scorable": "^0.10.0",
36
36
  "chalk": "^5.6.2",
37
37
  "cli-table3": "^0.6.5",
38
38
  "commander": "^14.0.3",