@root-signals/scorable-cli 0.9.0 → 0.10.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.
- package/README.md +87 -0
- package/dist/commands/evaluator/delete.js +1 -1
- package/dist/commands/evaluator/update.js +1 -1
- package/dist/commands/file/upload.d.ts +6 -1
- package/dist/commands/file/upload.d.ts.map +1 -1
- package/dist/commands/file/upload.js +8 -4
- package/dist/commands/judge/delete.js +1 -1
- package/dist/commands/judge/exec-openai.js +1 -1
- package/dist/commands/judge/generate.d.ts.map +1 -1
- package/dist/commands/judge/generate.js +13 -0
- package/dist/commands/model/create.d.ts +3 -0
- package/dist/commands/model/create.d.ts.map +1 -0
- package/dist/commands/model/create.js +48 -0
- package/dist/commands/model/delete.d.ts +3 -0
- package/dist/commands/model/delete.d.ts.map +1 -0
- package/dist/commands/model/delete.js +37 -0
- package/dist/commands/model/get.d.ts +3 -0
- package/dist/commands/model/get.d.ts.map +1 -0
- package/dist/commands/model/get.js +23 -0
- package/dist/commands/model/index.d.ts +3 -0
- package/dist/commands/model/index.d.ts.map +1 -0
- package/dist/commands/model/index.js +15 -0
- package/dist/commands/model/list.d.ts +3 -0
- package/dist/commands/model/list.d.ts.map +1 -0
- package/dist/commands/model/list.js +36 -0
- package/dist/commands/model/update.d.ts +3 -0
- package/dist/commands/model/update.d.ts.map +1 -0
- package/dist/commands/model/update.js +54 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/output.d.ts +2 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +17 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -136,6 +136,40 @@ Prompts for confirmation. Use `--yes` to skip.
|
|
|
136
136
|
scorable judge duplicate <judge_id>
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
### Generate a judge
|
|
140
|
+
|
|
141
|
+
AI-powered judge generation from a plain-language description of what you want to evaluate.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
scorable judge generate --intent "I am building a customer support chatbot. Evaluate that responses are helpful and follow our refund policy."
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Attach a policy document so the generated evaluators can check compliance against it:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Upload and generate in one step
|
|
151
|
+
scorable judge generate --intent "Evaluate responses against the attached policy." --file ./policy.pdf
|
|
152
|
+
|
|
153
|
+
# Or reuse an already-uploaded file
|
|
154
|
+
scorable judge generate --intent "Evaluate responses against the attached policy." --file-id <file_uuid>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Options: `--intent` (required), `--file` (path to PDF/PNG/JPG — uploads and attaches), `--file-id` (UUID of an already-uploaded file), `--visibility` (`private`/`public`, default `private`), `--name`, `--stage`, `--extra-contexts` (JSON object, e.g. `'{"Domain":"hotel","Tone":"formal"}'`), `--reasoning-effort` (`off`/`low`/`medium`/`high`), `--judge-id` (regenerate an existing judge), `--overwrite`, `--context-aware`
|
|
158
|
+
|
|
159
|
+
## File Management
|
|
160
|
+
|
|
161
|
+
### Upload a file
|
|
162
|
+
|
|
163
|
+
Upload a PDF or image for use as context in judge generation or evaluator execution.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
scorable file upload ./policy.pdf
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Returns a file UUID that can be passed to `judge generate --file-id` or `evaluator execute --file-ids`.
|
|
170
|
+
|
|
171
|
+
Supported formats: PDF, PNG, JPG, JPEG, WEBP, SVG.
|
|
172
|
+
|
|
139
173
|
## Judge Execution
|
|
140
174
|
|
|
141
175
|
### Execute by ID
|
|
@@ -242,6 +276,59 @@ scorable evaluator execute-by-name "My Evaluator" --request "What is 2+2?" --res
|
|
|
242
276
|
|
|
243
277
|
Accepts the same options as `execute`, including `--variables`.
|
|
244
278
|
|
|
279
|
+
## Custom Model Management
|
|
280
|
+
|
|
281
|
+
Bring your own LLM (BYO-LLM) — register a custom or self-hosted model, then reference it from evaluators and judges.
|
|
282
|
+
|
|
283
|
+
### List models
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
scorable model list
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Shows ID, name, provider, and visibility. Options: `--page-size`, `--cursor`, `--ordering`.
|
|
290
|
+
|
|
291
|
+
### Get a model
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
scorable model get <model_id>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Create a model
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# SaaS provider (key inline)
|
|
301
|
+
scorable model create --name my-gpt --model gpt-5.5 --key sk-...
|
|
302
|
+
|
|
303
|
+
# Self-hosted / custom endpoint
|
|
304
|
+
scorable model create \
|
|
305
|
+
--name azure/gpt-5.5 \
|
|
306
|
+
--model azure/gpt-5.5 \
|
|
307
|
+
--url https://my-azure-openai.openai.azure.com \
|
|
308
|
+
--key sk-...
|
|
309
|
+
|
|
310
|
+
# Read the key from stdin (keeps it out of shell history)
|
|
311
|
+
echo "$MY_PROVIDER_KEY" | scorable model create --name my-gpt --model gpt-5.5 --key -
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Options: `--name` (required), `--model`, `--url` (for self-hosted endpoints), `--key` (provider API key; `-` reads from stdin), `--max-token-count`, `--max-output-token-count`.
|
|
315
|
+
|
|
316
|
+
### Update a model
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
scorable model update <model_id> --max-output-token-count 4096
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`update` is a PATCH — only fields you pass are sent. All `create` flags are accepted as optional updates, including `--key -` for stdin.
|
|
323
|
+
|
|
324
|
+
### Delete a model
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
scorable model delete <model_id>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Prompts for confirmation. Use `--yes` to skip.
|
|
331
|
+
|
|
245
332
|
## Execution Logs
|
|
246
333
|
|
|
247
334
|
### List execution logs
|
|
@@ -10,7 +10,7 @@ export function registerDeleteCommand(evaluator) {
|
|
|
10
10
|
.action(async (evaluatorId, opts) => {
|
|
11
11
|
const apiKey = await requireApiKey();
|
|
12
12
|
if (!opts.yes) {
|
|
13
|
-
if (!process.stdin.isTTY
|
|
13
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
14
14
|
throw new CliError(1, "Refusing to prompt for confirmation in a non-interactive environment. Pass --yes to skip the prompt.");
|
|
15
15
|
}
|
|
16
16
|
const { confirm } = await import("@inquirer/prompts");
|
|
@@ -7,7 +7,7 @@ export function registerUpdateCommand(evaluator) {
|
|
|
7
7
|
.description("Update an existing evaluator (PATCH)")
|
|
8
8
|
.option("--name <name>", "The new name for the evaluator")
|
|
9
9
|
.option("--scoring-criteria <text>", "The new scoring criteria (prompt text)")
|
|
10
|
-
.option("--models <json>", "JSON array of model names. E.g., '[\"gpt-
|
|
10
|
+
.option("--models <json>", "JSON array of model names. E.g., '[\"gpt-5.5\"]'")
|
|
11
11
|
.option("--objective-id <id>", "The new objective ID")
|
|
12
12
|
.option("--objective-version-id <id>", "The new objective version ID")
|
|
13
13
|
.action(async (evaluatorId, opts) => {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
export
|
|
2
|
+
export interface UploadedFile {
|
|
3
|
+
id: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function uploadFile(filePath: string, { silent }?: {
|
|
6
|
+
silent?: boolean;
|
|
7
|
+
}): Promise<UploadedFile>;
|
|
3
8
|
export declare function registerUploadCommand(file: Command): void;
|
|
4
9
|
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/commands/file/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/commands/file/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,EAAE,MAAc,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC5C,OAAO,CAAC,YAAY,CAAC,CA0BvB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CA2BzD"}
|
|
@@ -4,11 +4,12 @@ import * as path from "path";
|
|
|
4
4
|
import { requireApiKey } from "../../auth.js";
|
|
5
5
|
import { printSuccess, printError, printJson, handleSdkError } from "../../output.js";
|
|
6
6
|
import { getSdkClient } from "../../auth.js";
|
|
7
|
-
|
|
7
|
+
import { CliError } from "../../types.js";
|
|
8
|
+
export async function uploadFile(filePath, { silent = false } = {}) {
|
|
8
9
|
const apiKey = await requireApiKey();
|
|
9
10
|
if (!fs.existsSync(filePath)) {
|
|
10
11
|
printError(`File not found: ${filePath}`);
|
|
11
|
-
|
|
12
|
+
throw new CliError(1, "file_not_found");
|
|
12
13
|
}
|
|
13
14
|
const client = getSdkClient(apiKey);
|
|
14
15
|
const fileName = path.basename(filePath);
|
|
@@ -18,8 +19,11 @@ export async function uploadFile(filePath) {
|
|
|
18
19
|
try {
|
|
19
20
|
const result = await client.files.upload(blob, fileName);
|
|
20
21
|
spinner.stop();
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
if (!silent) {
|
|
23
|
+
printSuccess("File uploaded successfully!");
|
|
24
|
+
printJson(result);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
23
27
|
}
|
|
24
28
|
catch (e) {
|
|
25
29
|
spinner.stop();
|
|
@@ -10,7 +10,7 @@ export function registerDeleteCommand(judge) {
|
|
|
10
10
|
.action(async (judgeId, opts) => {
|
|
11
11
|
const apiKey = await requireApiKey();
|
|
12
12
|
if (!opts.yes) {
|
|
13
|
-
if (!process.stdin.isTTY
|
|
13
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
14
14
|
throw new CliError(1, "Refusing to prompt for confirmation in a non-interactive environment. Pass --yes to skip the prompt.");
|
|
15
15
|
}
|
|
16
16
|
const { confirm } = await import("@inquirer/prompts");
|
|
@@ -5,7 +5,7 @@ export function registerExecOpenaiCommand(judge) {
|
|
|
5
5
|
judge
|
|
6
6
|
.command("exec-openai <judgeIdInPath>")
|
|
7
7
|
.description("Execute a specific judge via the OpenAI compatible API")
|
|
8
|
-
.requiredOption("--model <model>", "LLM model for judge execution (e.g., gpt-
|
|
8
|
+
.requiredOption("--model <model>", "LLM model for judge execution (e.g., gpt-5.5)")
|
|
9
9
|
.requiredOption("--messages <json>", "JSON string of the messages payload")
|
|
10
10
|
.option("--extra-body <json>", "Optional JSON string for extra_body parameters")
|
|
11
11
|
.action(async (judgeIdInPath, opts) => {
|
|
@@ -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;
|
|
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"}
|
|
@@ -3,6 +3,7 @@ import chalk from "chalk";
|
|
|
3
3
|
import { requireApiKey, getSdkClient } from "../../auth.js";
|
|
4
4
|
import { printSuccess, printError, printWarning, printInfo, printJson, handleSdkError, } from "../../output.js";
|
|
5
5
|
import { CliError } from "../../types.js";
|
|
6
|
+
import { uploadFile } from "../file/upload.js";
|
|
6
7
|
export function registerGenerateCommand(judge) {
|
|
7
8
|
judge
|
|
8
9
|
.command("generate")
|
|
@@ -23,6 +24,8 @@ offer to connect the guest with a human agent when uncertain."`)
|
|
|
23
24
|
.option("--overwrite", "Overwrite existing judge with the same name", false)
|
|
24
25
|
.option("--extra-contexts <json>", 'JSON object of additional context key-value pairs, e.g. \'{"Tone Of Voice":"formal","Domain":"hotel chatbot"}\'')
|
|
25
26
|
.option("--context-aware", "Enable context-aware evaluators for RAG applications (hallucination detection, context drift, etc.)", false)
|
|
27
|
+
.option("--file <path>", "Path to a file (PDF, PNG, JPG) to upload and attach as context for the judge")
|
|
28
|
+
.option("--file-id <id>", "ID of an already-uploaded file to use as context for the judge")
|
|
26
29
|
.action(async (opts) => {
|
|
27
30
|
const acceptedVisibilities = ["private", "public"];
|
|
28
31
|
const visibility = opts.visibility;
|
|
@@ -30,6 +33,10 @@ offer to connect the guest with a human agent when uncertain."`)
|
|
|
30
33
|
printError(`Invalid --visibility value: "${visibility}". Accepted values: private, public.`);
|
|
31
34
|
throw new CliError(1, "invalid_visibility");
|
|
32
35
|
}
|
|
36
|
+
if (opts.file && opts.fileId) {
|
|
37
|
+
printError("Cannot use both --file and --file-id. Provide one or the other.");
|
|
38
|
+
throw new CliError(1, "conflicting_file_options");
|
|
39
|
+
}
|
|
33
40
|
const apiKey = await requireApiKey();
|
|
34
41
|
let extra_contexts;
|
|
35
42
|
if (opts.extraContexts) {
|
|
@@ -43,6 +50,11 @@ offer to connect the guest with a human agent when uncertain."`)
|
|
|
43
50
|
}
|
|
44
51
|
const spinner = ora("Generating judge (this may take a moment)...").start();
|
|
45
52
|
try {
|
|
53
|
+
let fileId = opts.fileId;
|
|
54
|
+
if (opts.file) {
|
|
55
|
+
const uploaded = await uploadFile(opts.file, { silent: true });
|
|
56
|
+
fileId = uploaded.id;
|
|
57
|
+
}
|
|
46
58
|
const client = getSdkClient(apiKey);
|
|
47
59
|
const result = (await client.judges.generate({
|
|
48
60
|
intent: opts.intent,
|
|
@@ -51,6 +63,7 @@ offer to connect the guest with a human agent when uncertain."`)
|
|
|
51
63
|
name: opts.name,
|
|
52
64
|
stage: opts.stage,
|
|
53
65
|
judge_id: opts.judgeId,
|
|
66
|
+
file_id: fileId,
|
|
54
67
|
extra_contexts: extra_contexts ?? null,
|
|
55
68
|
enable_context_aware_evaluators: opts.contextAware || undefined,
|
|
56
69
|
...(opts.reasoningEffort && {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/model/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAgD1D"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { requireApiKey, getSdkClient } from "../../auth.js";
|
|
3
|
+
import { printSuccess, printJson, handleSdkError } from "../../output.js";
|
|
4
|
+
async function readKeyFromStdin() {
|
|
5
|
+
const chunks = [];
|
|
6
|
+
for await (const chunk of process.stdin)
|
|
7
|
+
chunks.push(chunk);
|
|
8
|
+
return Buffer.concat(chunks).toString().trim();
|
|
9
|
+
}
|
|
10
|
+
export function registerCreateCommand(model) {
|
|
11
|
+
model
|
|
12
|
+
.command("create")
|
|
13
|
+
.description("Create a new custom model")
|
|
14
|
+
.requiredOption("--name <name>", "Model name (unique within your organization)")
|
|
15
|
+
.option("--model <model>", "Underlying model identifier (e.g. 'gpt-5.5')")
|
|
16
|
+
.option("--url <url>", "API base URL (for self-hosted or non-SaaS endpoints)")
|
|
17
|
+
.option("--key <value>", "Provider API key. Pass '-' to read the key from stdin (avoids shell history leak)")
|
|
18
|
+
.option("--max-token-count <number>", "Maximum total tokens", parseInt)
|
|
19
|
+
.option("--max-output-token-count <number>", "Maximum output tokens", parseInt)
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const apiKey = await requireApiKey();
|
|
22
|
+
const payload = { name: opts.name };
|
|
23
|
+
if (opts.model !== undefined)
|
|
24
|
+
payload.model = opts.model;
|
|
25
|
+
if (opts.url !== undefined)
|
|
26
|
+
payload.url = opts.url;
|
|
27
|
+
if (opts.maxTokenCount !== undefined)
|
|
28
|
+
payload.max_token_count = opts.maxTokenCount;
|
|
29
|
+
if (opts.maxOutputTokenCount !== undefined) {
|
|
30
|
+
payload.max_output_token_count = opts.maxOutputTokenCount;
|
|
31
|
+
}
|
|
32
|
+
if (opts.key !== undefined) {
|
|
33
|
+
payload.default_key = opts.key === "-" ? await readKeyFromStdin() : opts.key;
|
|
34
|
+
}
|
|
35
|
+
const spinner = ora("Creating...").start();
|
|
36
|
+
try {
|
|
37
|
+
const client = getSdkClient(apiKey);
|
|
38
|
+
const result = await client.models.create(payload);
|
|
39
|
+
spinner.stop();
|
|
40
|
+
printSuccess("Model created successfully!");
|
|
41
|
+
printJson(result);
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
spinner.stop();
|
|
45
|
+
handleSdkError(e);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/model/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAoC1D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { requireApiKey, getSdkClient } from "../../auth.js";
|
|
3
|
+
import { printSuccess, handleSdkError } from "../../output.js";
|
|
4
|
+
import { CliError } from "../../types.js";
|
|
5
|
+
export function registerDeleteCommand(model) {
|
|
6
|
+
model
|
|
7
|
+
.command("delete <modelId>")
|
|
8
|
+
.description("Delete a custom model by its ID")
|
|
9
|
+
.option("--yes", "Skip confirmation prompt")
|
|
10
|
+
.action(async (modelId, opts) => {
|
|
11
|
+
const apiKey = await requireApiKey();
|
|
12
|
+
if (!opts.yes) {
|
|
13
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
14
|
+
throw new CliError(1, "Refusing to prompt for confirmation in a non-interactive environment. Pass --yes to skip the prompt.");
|
|
15
|
+
}
|
|
16
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
17
|
+
const ok = await confirm({
|
|
18
|
+
message: "Are you sure you want to delete this model?",
|
|
19
|
+
default: false,
|
|
20
|
+
});
|
|
21
|
+
if (!ok) {
|
|
22
|
+
throw new CliError(1, "Aborted");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const spinner = ora("Deleting...").start();
|
|
26
|
+
try {
|
|
27
|
+
const client = getSdkClient(apiKey);
|
|
28
|
+
await client.models.delete(modelId);
|
|
29
|
+
spinner.stop();
|
|
30
|
+
printSuccess(`Model ${modelId} deleted successfully.`);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
spinner.stop();
|
|
34
|
+
handleSdkError(e);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../src/commands/model/get.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAkBvD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { requireApiKey, getSdkClient } from "../../auth.js";
|
|
3
|
+
import { printSuccess, printJson, handleSdkError } from "../../output.js";
|
|
4
|
+
export function registerGetCommand(model) {
|
|
5
|
+
model
|
|
6
|
+
.command("get <modelId>")
|
|
7
|
+
.description("Get a specific model by its ID")
|
|
8
|
+
.action(async (modelId) => {
|
|
9
|
+
const apiKey = await requireApiKey();
|
|
10
|
+
const spinner = ora("Fetching...").start();
|
|
11
|
+
try {
|
|
12
|
+
const client = getSdkClient(apiKey);
|
|
13
|
+
const result = await client.models.get(modelId);
|
|
14
|
+
spinner.stop();
|
|
15
|
+
printSuccess(`Model '${result.name}' details:`);
|
|
16
|
+
printJson(result);
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
spinner.stop();
|
|
20
|
+
handleSdkError(e);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/model/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAU5D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { registerListCommand } from "./list.js";
|
|
3
|
+
import { registerGetCommand } from "./get.js";
|
|
4
|
+
import { registerCreateCommand } from "./create.js";
|
|
5
|
+
import { registerUpdateCommand } from "./update.js";
|
|
6
|
+
import { registerDeleteCommand } from "./delete.js";
|
|
7
|
+
export function registerModelCommands(program) {
|
|
8
|
+
const model = new Command("model").description("Custom model management commands");
|
|
9
|
+
registerListCommand(model);
|
|
10
|
+
registerGetCommand(model);
|
|
11
|
+
registerCreateCommand(model);
|
|
12
|
+
registerUpdateCommand(model);
|
|
13
|
+
registerDeleteCommand(model);
|
|
14
|
+
program.addCommand(model);
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/model/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAgCxD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { requireApiKey, getSdkClient } from "../../auth.js";
|
|
3
|
+
import { printMessage, printModelTable, handleSdkError } from "../../output.js";
|
|
4
|
+
export function registerListCommand(model) {
|
|
5
|
+
model
|
|
6
|
+
.command("list")
|
|
7
|
+
.description("List models")
|
|
8
|
+
.option("--page-size <number>", "Number of results to return per page", parseInt)
|
|
9
|
+
.option("--cursor <cursor>", "The pagination cursor value")
|
|
10
|
+
.option("--ordering <field>", "Which field to use for ordering the results")
|
|
11
|
+
.action(async (opts) => {
|
|
12
|
+
const apiKey = await requireApiKey();
|
|
13
|
+
const params = {};
|
|
14
|
+
if (opts.pageSize !== undefined)
|
|
15
|
+
params.page_size = opts.pageSize;
|
|
16
|
+
if (opts.cursor !== undefined)
|
|
17
|
+
params.cursor = opts.cursor;
|
|
18
|
+
if (opts.ordering !== undefined)
|
|
19
|
+
params.ordering = opts.ordering;
|
|
20
|
+
const spinner = ora("Fetching...").start();
|
|
21
|
+
try {
|
|
22
|
+
const client = getSdkClient(apiKey);
|
|
23
|
+
const response = await client.models.list(params);
|
|
24
|
+
spinner.stop();
|
|
25
|
+
if (!response.results.length) {
|
|
26
|
+
printMessage("No models found.");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
printModelTable(response.results, response.next);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
spinner.stop();
|
|
33
|
+
handleSdkError(e);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/model/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAyD1D"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { requireApiKey, getSdkClient } from "../../auth.js";
|
|
3
|
+
import { printInfo, printSuccess, printJson, handleSdkError } from "../../output.js";
|
|
4
|
+
async function readKeyFromStdin() {
|
|
5
|
+
const chunks = [];
|
|
6
|
+
for await (const chunk of process.stdin)
|
|
7
|
+
chunks.push(chunk);
|
|
8
|
+
return Buffer.concat(chunks).toString().trim();
|
|
9
|
+
}
|
|
10
|
+
export function registerUpdateCommand(model) {
|
|
11
|
+
model
|
|
12
|
+
.command("update <modelId>")
|
|
13
|
+
.description("Update an existing model (PATCH — only provided fields are sent)")
|
|
14
|
+
.option("--name <name>", "New model name")
|
|
15
|
+
.option("--model <model>", "Underlying model identifier")
|
|
16
|
+
.option("--url <url>", "API base URL")
|
|
17
|
+
.option("--key <value>", "Provider API key. Pass '-' to read the key from stdin (avoids shell history leak)")
|
|
18
|
+
.option("--max-token-count <number>", "Maximum total tokens", parseInt)
|
|
19
|
+
.option("--max-output-token-count <number>", "Maximum output tokens", parseInt)
|
|
20
|
+
.action(async (modelId, opts) => {
|
|
21
|
+
const apiKey = await requireApiKey();
|
|
22
|
+
const payload = {};
|
|
23
|
+
if (opts.name !== undefined)
|
|
24
|
+
payload.name = opts.name;
|
|
25
|
+
if (opts.model !== undefined)
|
|
26
|
+
payload.model = opts.model;
|
|
27
|
+
if (opts.url !== undefined)
|
|
28
|
+
payload.url = opts.url;
|
|
29
|
+
if (opts.maxTokenCount !== undefined)
|
|
30
|
+
payload.max_token_count = opts.maxTokenCount;
|
|
31
|
+
if (opts.maxOutputTokenCount !== undefined) {
|
|
32
|
+
payload.max_output_token_count = opts.maxOutputTokenCount;
|
|
33
|
+
}
|
|
34
|
+
if (opts.key !== undefined) {
|
|
35
|
+
payload.default_key = opts.key === "-" ? await readKeyFromStdin() : opts.key;
|
|
36
|
+
}
|
|
37
|
+
if (Object.keys(payload).length === 0) {
|
|
38
|
+
printInfo("No update parameters provided. Aborting.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const spinner = ora("Updating...").start();
|
|
42
|
+
try {
|
|
43
|
+
const client = getSdkClient(apiKey);
|
|
44
|
+
const result = await client.models.patch(modelId, payload);
|
|
45
|
+
spinner.stop();
|
|
46
|
+
printSuccess(`Model ${modelId} updated successfully!`);
|
|
47
|
+
printJson(result);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
spinner.stop();
|
|
51
|
+
handleSdkError(e);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoDpC,wBAAgB,SAAS,IAAI,OAAO,CAwBnC"}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { registerSkillsAddCommand } from "./commands/skills-add.js";
|
|
|
13
13
|
import { registerFileCommands } from "./commands/file/index.js";
|
|
14
14
|
import { registerOtelFilterCommands } from "./commands/otel-filter/index.js";
|
|
15
15
|
import { registerOtelTraceCommands } from "./commands/otel-trace/index.js";
|
|
16
|
+
import { registerModelCommands } from "./commands/model/index.js";
|
|
16
17
|
const { version } = createRequire(import.meta.url)("../package.json");
|
|
17
18
|
function buildBanner(ver) {
|
|
18
19
|
const logo = chalk.hex("#4D9FFF");
|
|
@@ -67,6 +68,7 @@ export function createCli() {
|
|
|
67
68
|
registerFileCommands(program);
|
|
68
69
|
registerOtelFilterCommands(program);
|
|
69
70
|
registerOtelTraceCommands(program);
|
|
71
|
+
registerModelCommands(program);
|
|
70
72
|
return program;
|
|
71
73
|
}
|
|
72
74
|
if (realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
package/dist/output.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Judge, EvaluatorListItem, ExecutionLogList } from "@root-signals/scorable";
|
|
1
|
+
import type { Judge, EvaluatorListItem, ExecutionLogList, ModelList } from "@root-signals/scorable";
|
|
2
2
|
export declare function printJson(data: unknown): void;
|
|
3
3
|
export declare function printError(msg: string): void;
|
|
4
4
|
export declare function handleSdkError(e: unknown): never;
|
|
@@ -8,5 +8,6 @@ export declare function printWarning(msg: string): void;
|
|
|
8
8
|
export declare function printMessage(msg: string): void;
|
|
9
9
|
export declare function printJudgeTable(judges: Judge[], nextCursor?: string): void;
|
|
10
10
|
export declare function printEvaluatorTable(evaluators: EvaluatorListItem[], nextCursor?: string): void;
|
|
11
|
+
export declare function printModelTable(models: ModelList[], nextCursor?: string): void;
|
|
11
12
|
export declare function printExecutionLogTable(logs: ExecutionLogList[], nextCursor?: string): void;
|
|
12
13
|
//# sourceMappingURL=output.d.ts.map
|
package/dist/output.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAoBpG,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAE7C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAKhD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB1E;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB9F;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB9E;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAoB1F"}
|
package/dist/output.js
CHANGED
|
@@ -80,6 +80,23 @@ export function printEvaluatorTable(evaluators, nextCursor) {
|
|
|
80
80
|
printInfo(`Next page available. Use --cursor "${cursor}"`);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
export function printModelTable(models, nextCursor) {
|
|
84
|
+
const table = new Table({
|
|
85
|
+
head: ["ID", "Name", "Provider", "Visibility"].map((h) => chalk.bold.cyan(h)),
|
|
86
|
+
chars: UNICODE_CHARS,
|
|
87
|
+
colWidths: [38, 32, 22, 18],
|
|
88
|
+
wordWrap: true,
|
|
89
|
+
});
|
|
90
|
+
for (const m of models) {
|
|
91
|
+
const provider = m.provider?.name ?? "";
|
|
92
|
+
table.push([m.id, m.name, provider, m.visibility ?? ""]);
|
|
93
|
+
}
|
|
94
|
+
console.log(table.toString());
|
|
95
|
+
if (nextCursor) {
|
|
96
|
+
const cursor = nextCursor.split("cursor=")[1] ?? nextCursor;
|
|
97
|
+
printInfo(`Next page available. Use --cursor "${cursor}"`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
83
100
|
export function printExecutionLogTable(logs, nextCursor) {
|
|
84
101
|
const table = new Table({
|
|
85
102
|
head: ["ID", "Item Name", "Type", "Score", "Created At"].map((h) => chalk.bold.cyan(h)),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@root-signals/scorable-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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.
|
|
35
|
+
"@root-signals/scorable": "^0.7.0",
|
|
36
36
|
"chalk": "^5.6.2",
|
|
37
37
|
"cli-table3": "^0.6.5",
|
|
38
38
|
"commander": "^14.0.3",
|