@2kw/ai 6.0.0 → 6.1.0-dev.10
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 +6 -1
- package/dist/commands/agent-labels.d.ts +3 -0
- package/dist/commands/agent-labels.js +72 -0
- package/dist/commands/agent-versions.d.ts +3 -0
- package/dist/commands/agent-versions.js +103 -0
- package/dist/commands/agents.d.ts +3 -0
- package/dist/commands/agents.js +154 -0
- package/dist/commands/ai.d.ts +54 -0
- package/dist/commands/ai.js +194 -2
- package/dist/commands/billing.js +34 -0
- package/dist/commands/conversations.d.ts +3 -0
- package/dist/commands/conversations.js +81 -0
- package/dist/commands/dataset-versions.js +44 -1
- package/dist/commands/datasets.js +23 -1
- package/dist/commands/experiments.js +209 -0
- package/dist/commands/extractions.js +14 -1
- package/dist/commands/files.d.ts +3 -0
- package/dist/commands/files.js +143 -0
- package/dist/commands/installations.d.ts +3 -0
- package/dist/commands/installations.js +177 -0
- package/dist/commands/knowledge-documents.d.ts +3 -0
- package/dist/commands/knowledge-documents.js +155 -0
- package/dist/commands/knowledge.d.ts +3 -0
- package/dist/commands/knowledge.js +197 -0
- package/dist/commands/prompt-versions.js +24 -1
- package/dist/commands/providers.js +38 -0
- package/dist/commands/queues.d.ts +11 -0
- package/dist/commands/queues.js +244 -0
- package/dist/commands/schema-versions.js +24 -1
- package/dist/commands/scores.js +43 -5
- package/dist/commands/tracing.js +40 -0
- package/dist/index.js +12 -0
- package/package.json +2 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getClient, runAction } from "../lib/client.js";
|
|
3
|
+
import { formatDetail, formatSuccess } from "../lib/output.js";
|
|
4
|
+
export function makeConversationsCommand() {
|
|
5
|
+
const cmd = new Command("conversations").description("Manage conversations");
|
|
6
|
+
cmd
|
|
7
|
+
.command("create")
|
|
8
|
+
.description("Create a conversation")
|
|
9
|
+
.option("--items <json>", "Initial items as a JSON array")
|
|
10
|
+
.option("--metadata <json>", "Metadata as JSON")
|
|
11
|
+
.action(async (opts, command) => {
|
|
12
|
+
await runAction(command, async () => {
|
|
13
|
+
const client = getClient(command);
|
|
14
|
+
const { data } = await client.POST("/v1/conversations", {
|
|
15
|
+
body: {
|
|
16
|
+
items: opts.items ? JSON.parse(opts.items) : undefined,
|
|
17
|
+
metadata: opts.metadata ? JSON.parse(opts.metadata) : undefined,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
formatDetail(data, command);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
cmd
|
|
24
|
+
.command("get")
|
|
25
|
+
.description("Retrieve a conversation by id")
|
|
26
|
+
.argument("<conversationId>", "Conversation ID")
|
|
27
|
+
.action(async (conversationId, _opts, command) => {
|
|
28
|
+
await runAction(command, async () => {
|
|
29
|
+
const client = getClient(command);
|
|
30
|
+
const { data } = await client.GET("/v1/conversations/{conversationId}", {
|
|
31
|
+
params: { path: { conversationId } },
|
|
32
|
+
});
|
|
33
|
+
formatDetail(data, command);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
cmd
|
|
37
|
+
.command("update")
|
|
38
|
+
.description("Replace a conversation's metadata")
|
|
39
|
+
.argument("<conversationId>", "Conversation ID")
|
|
40
|
+
.requiredOption("--metadata <json>", "New metadata as JSON")
|
|
41
|
+
.action(async (conversationId, opts, command) => {
|
|
42
|
+
await runAction(command, async () => {
|
|
43
|
+
const client = getClient(command);
|
|
44
|
+
const { data } = await client.POST("/v1/conversations/{conversationId}", {
|
|
45
|
+
params: { path: { conversationId } },
|
|
46
|
+
body: { metadata: JSON.parse(opts.metadata) },
|
|
47
|
+
});
|
|
48
|
+
formatDetail(data, command);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
cmd
|
|
52
|
+
.command("delete")
|
|
53
|
+
.description("Delete a conversation and its recorded responses")
|
|
54
|
+
.argument("<conversationId>", "Conversation ID")
|
|
55
|
+
.action(async (conversationId, _opts, command) => {
|
|
56
|
+
await runAction(command, async () => {
|
|
57
|
+
const client = getClient(command);
|
|
58
|
+
await client.DELETE("/v1/conversations/{conversationId}", {
|
|
59
|
+
params: { path: { conversationId } },
|
|
60
|
+
});
|
|
61
|
+
formatSuccess(`Conversation ${conversationId} deleted.`, command);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
const items = new Command("items").description("List a conversation's items");
|
|
65
|
+
items
|
|
66
|
+
.command("list")
|
|
67
|
+
.description("List a conversation's items, oldest first")
|
|
68
|
+
.argument("<conversationId>", "Conversation ID")
|
|
69
|
+
.action(async (conversationId, _opts, command) => {
|
|
70
|
+
await runAction(command, async () => {
|
|
71
|
+
const client = getClient(command);
|
|
72
|
+
const { data } = await client.GET("/v1/conversations/{conversationId}/items", {
|
|
73
|
+
params: { path: { conversationId } },
|
|
74
|
+
});
|
|
75
|
+
formatDetail(data, command);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
cmd.addCommand(items);
|
|
79
|
+
return cmd;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { getClient, runAction } from "../lib/client.js";
|
|
3
3
|
import { resolveLatestVersionId } from "../lib/datasets.js";
|
|
4
|
-
import { formatPage, formatDetail } from "../lib/output.js";
|
|
4
|
+
import { formatPage, formatDetail, formatSuccess } from "../lib/output.js";
|
|
5
5
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
6
6
|
export function makeDatasetVersionsCommand() {
|
|
7
7
|
const cmd = new Command("versions").description("Manage dataset versions");
|
|
@@ -85,6 +85,49 @@ export function makeDatasetVersionsCommand() {
|
|
|
85
85
|
});
|
|
86
86
|
});
|
|
87
87
|
cmd.addCommand(items);
|
|
88
|
+
cmd
|
|
89
|
+
.command("delete-item")
|
|
90
|
+
.description("Soft-delete an item in a dataset version (defaults to latest version)")
|
|
91
|
+
.argument("<itemId>", "Dataset item ID")
|
|
92
|
+
.requiredOption("--dataset <id>", "Dataset ID")
|
|
93
|
+
.option("--version-id <id>", "Version ID (defaults to latest)")
|
|
94
|
+
.action(async (itemId, opts, command) => {
|
|
95
|
+
await runAction(command, async () => {
|
|
96
|
+
const client = getClient(command);
|
|
97
|
+
const versionId = opts.versionId ?? (await resolveLatestVersionId(client, opts.dataset));
|
|
98
|
+
await client.DELETE("/v1/datasets/{id}/versions/{versionId}/items/{itemId}", {
|
|
99
|
+
params: {
|
|
100
|
+
// OpenAPI spec omits `id` in declared path params though `{id}` is in the URL template.
|
|
101
|
+
path: { id: opts.dataset, versionId, itemId },
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
formatSuccess(`Item ${itemId} deleted.`, command);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
cmd
|
|
108
|
+
.command("update-item-expected-output")
|
|
109
|
+
.description("Update the expected output of a dataset item (defaults to latest version)")
|
|
110
|
+
.argument("<itemId>", "Dataset item ID")
|
|
111
|
+
.requiredOption("--dataset <id>", "Dataset ID")
|
|
112
|
+
.option("--version-id <id>", "Version ID (defaults to latest)")
|
|
113
|
+
.option("--expected-output <json>", "New expected output (JSON); omit to clear it")
|
|
114
|
+
.action(async (itemId, opts, command) => {
|
|
115
|
+
await runAction(command, async () => {
|
|
116
|
+
const client = getClient(command);
|
|
117
|
+
const versionId = opts.versionId ?? (await resolveLatestVersionId(client, opts.dataset));
|
|
118
|
+
const body = {};
|
|
119
|
+
if (opts.expectedOutput !== undefined)
|
|
120
|
+
body.expectedOutput = JSON.parse(opts.expectedOutput);
|
|
121
|
+
const { data } = await client.PATCH("/v1/datasets/{id}/versions/{versionId}/items/{itemId}/expected-output", {
|
|
122
|
+
params: {
|
|
123
|
+
// OpenAPI spec omits `id` in declared path params though `{id}` is in the URL template.
|
|
124
|
+
path: { id: opts.dataset, versionId, itemId },
|
|
125
|
+
},
|
|
126
|
+
body: body,
|
|
127
|
+
});
|
|
128
|
+
formatDetail(data, command);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
88
131
|
return cmd;
|
|
89
132
|
}
|
|
90
133
|
//# sourceMappingURL=dataset-versions.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { getClient, runAction } from "../lib/client.js";
|
|
3
3
|
import { resolveLatestVersionId } from "../lib/datasets.js";
|
|
4
|
-
import { formatPage, formatDetail, formatSuccess } from "../lib/output.js";
|
|
4
|
+
import { formatPage, formatDetail, formatList, formatSuccess } from "../lib/output.js";
|
|
5
5
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
6
6
|
import { makeDatasetVersionsCommand } from "./dataset-versions.js";
|
|
7
7
|
export function makeDatasetsCommand() {
|
|
@@ -144,6 +144,28 @@ export function makeDatasetsCommand() {
|
|
|
144
144
|
formatDetail(data, command);
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
|
+
cmd
|
|
148
|
+
.command("add-items")
|
|
149
|
+
.description("Add multiple items to a dataset in a single request (uses the latest version)")
|
|
150
|
+
.requiredOption("--dataset <id>", "Dataset ID")
|
|
151
|
+
.requiredOption("--items <json>", "Array of items (JSON), each with input and optional expectedOutput/tags/metadata")
|
|
152
|
+
.action(async (opts, command) => {
|
|
153
|
+
await runAction(command, async () => {
|
|
154
|
+
const client = getClient(command);
|
|
155
|
+
const versionId = await resolveLatestVersionId(client, opts.dataset);
|
|
156
|
+
const items = JSON.parse(opts.items);
|
|
157
|
+
const { data } = await client.POST("/v1/datasets/{id}/versions/{versionId}/items/bulk", {
|
|
158
|
+
params: {
|
|
159
|
+
// OpenAPI spec omits `id` in declared path params though `{id}` is in the URL template.
|
|
160
|
+
path: { id: opts.dataset, versionId },
|
|
161
|
+
},
|
|
162
|
+
body: items,
|
|
163
|
+
});
|
|
164
|
+
formatList(data, command, [
|
|
165
|
+
"id", "input", "expectedOutput", "tags",
|
|
166
|
+
]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
147
169
|
cmd.addCommand(makeDatasetVersionsCommand());
|
|
148
170
|
return cmd;
|
|
149
171
|
}
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import ora from "ora";
|
|
4
|
+
import Table from "cli-table3";
|
|
4
5
|
import { getClient, runAction } from "../lib/client.js";
|
|
5
6
|
import { formatPage, formatDetail, formatList, formatSuccess, withSpinner } from "../lib/output.js";
|
|
6
7
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
7
8
|
import { isJsonOutput } from "../lib/config.js";
|
|
8
9
|
const TERMINAL_STATUSES = new Set(["COMPLETED", "FAILED"]);
|
|
10
|
+
/** Summarize a single cell of the comparison matrix (a variant's result for one item). */
|
|
11
|
+
function formatComparisonCell(result) {
|
|
12
|
+
if (!result)
|
|
13
|
+
return chalk.dim("—");
|
|
14
|
+
if (result.error)
|
|
15
|
+
return chalk.red("ERROR");
|
|
16
|
+
const scores = result.scores ?? [];
|
|
17
|
+
if (scores.length > 0) {
|
|
18
|
+
const avg = scores.reduce((sum, s) => sum + (s.score ?? 0), 0) / scores.length;
|
|
19
|
+
return avg.toFixed(2);
|
|
20
|
+
}
|
|
21
|
+
return result.durationMs != null ? `${result.durationMs}ms` : chalk.dim("done");
|
|
22
|
+
}
|
|
9
23
|
export function makeExperimentsCommand() {
|
|
10
24
|
const cmd = new Command("experiments").description("Manage experiments");
|
|
11
25
|
// ── list ──────────────────────────────────────────────────────
|
|
@@ -158,6 +172,53 @@ export function makeExperimentsCommand() {
|
|
|
158
172
|
formatList(data, command, ["id", "name", "taskType", "description", "sortOrder"]);
|
|
159
173
|
});
|
|
160
174
|
});
|
|
175
|
+
// ── update-variant ───────────────────────────────────────────────
|
|
176
|
+
cmd
|
|
177
|
+
.command("update-variant")
|
|
178
|
+
.description("Update a variant")
|
|
179
|
+
.argument("<variantId>", "Variant ID")
|
|
180
|
+
.requiredOption("--experiment <id>", "Experiment ID")
|
|
181
|
+
.option("-n, --name <name>", "Variant name")
|
|
182
|
+
.option("--task-type <type>", "Task type")
|
|
183
|
+
.option("--config <json>", "Variant configuration (JSON)")
|
|
184
|
+
.option("-d, --description <text>", "Variant description")
|
|
185
|
+
.option("--sort-order <n>", "Sort order", parseInt)
|
|
186
|
+
.action(async (variantId, opts, command) => {
|
|
187
|
+
await runAction(command, async () => {
|
|
188
|
+
const client = getClient(command);
|
|
189
|
+
const body = {};
|
|
190
|
+
if (opts.name !== undefined)
|
|
191
|
+
body.name = opts.name;
|
|
192
|
+
if (opts.taskType !== undefined)
|
|
193
|
+
body.taskType = opts.taskType;
|
|
194
|
+
if (opts.config)
|
|
195
|
+
body.configuration = JSON.parse(opts.config);
|
|
196
|
+
if (opts.description !== undefined)
|
|
197
|
+
body.description = opts.description;
|
|
198
|
+
if (opts.sortOrder !== undefined)
|
|
199
|
+
body.sortOrder = opts.sortOrder;
|
|
200
|
+
const { data } = await client.PUT("/v1/experiments/{id}/variants/{variantId}", {
|
|
201
|
+
params: { path: { id: opts.experiment, variantId } },
|
|
202
|
+
body: body,
|
|
203
|
+
});
|
|
204
|
+
formatDetail(data, command);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
// ── delete-variant ───────────────────────────────────────────────
|
|
208
|
+
cmd
|
|
209
|
+
.command("delete-variant")
|
|
210
|
+
.description("Delete a variant")
|
|
211
|
+
.argument("<variantId>", "Variant ID")
|
|
212
|
+
.requiredOption("--experiment <id>", "Experiment ID")
|
|
213
|
+
.action(async (variantId, opts, command) => {
|
|
214
|
+
await runAction(command, async () => {
|
|
215
|
+
const client = getClient(command);
|
|
216
|
+
await client.DELETE("/v1/experiments/{id}/variants/{variantId}", {
|
|
217
|
+
params: { path: { id: opts.experiment, variantId } },
|
|
218
|
+
});
|
|
219
|
+
formatSuccess(`Variant ${variantId} deleted.`, command);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
161
222
|
// ── run ───────────────────────────────────────────────────────
|
|
162
223
|
cmd
|
|
163
224
|
.command("run")
|
|
@@ -250,6 +311,154 @@ export function makeExperimentsCommand() {
|
|
|
250
311
|
});
|
|
251
312
|
});
|
|
252
313
|
cmd.addCommand(results);
|
|
314
|
+
// ── set-baseline ─────────────────────────────────────────────────
|
|
315
|
+
cmd
|
|
316
|
+
.command("set-baseline")
|
|
317
|
+
.description("Mark a run as the baseline for its variant")
|
|
318
|
+
.requiredOption("--experiment <id>", "Experiment ID")
|
|
319
|
+
.requiredOption("--run <id>", "Run ID")
|
|
320
|
+
.action(async (opts, command) => {
|
|
321
|
+
await runAction(command, async () => {
|
|
322
|
+
const client = getClient(command);
|
|
323
|
+
const { data } = await client.PUT("/v1/experiments/{id}/runs/{runId}/baseline", {
|
|
324
|
+
params: { path: { id: opts.experiment, runId: opts.run } },
|
|
325
|
+
});
|
|
326
|
+
formatDetail(data, command);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
// ── clear-baseline ───────────────────────────────────────────────
|
|
330
|
+
cmd
|
|
331
|
+
.command("clear-baseline")
|
|
332
|
+
.description("Clear the baseline flag on a run")
|
|
333
|
+
.requiredOption("--experiment <id>", "Experiment ID")
|
|
334
|
+
.requiredOption("--run <id>", "Run ID")
|
|
335
|
+
.action(async (opts, command) => {
|
|
336
|
+
await runAction(command, async () => {
|
|
337
|
+
const client = getClient(command);
|
|
338
|
+
const { data } = await client.DELETE("/v1/experiments/{id}/runs/{runId}/baseline", {
|
|
339
|
+
params: { path: { id: opts.experiment, runId: opts.run } },
|
|
340
|
+
});
|
|
341
|
+
formatDetail(data, command);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
// ── comparison ───────────────────────────────────────────────────
|
|
345
|
+
const comparison = new Command("comparison").description("Get the comparison matrix for an experiment (dataset items × variants)");
|
|
346
|
+
addPaginationOptions(comparison);
|
|
347
|
+
comparison.argument("<id>", "Experiment ID");
|
|
348
|
+
comparison.action(async (id, opts, command) => {
|
|
349
|
+
await runAction(command, async () => {
|
|
350
|
+
const client = getClient(command);
|
|
351
|
+
const { data } = await client.GET("/v1/experiments/{id}/comparison", {
|
|
352
|
+
params: {
|
|
353
|
+
path: { id },
|
|
354
|
+
query: paginationParams(opts),
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
if (isJsonOutput(command)) {
|
|
358
|
+
console.log(JSON.stringify(data, null, 2));
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const result = data;
|
|
362
|
+
const aggregates = result?.aggregates ?? [];
|
|
363
|
+
const page = result?.items;
|
|
364
|
+
const variantNames = new Map(aggregates.map((a) => [a.variantId, a.variantName ?? a.variantId]));
|
|
365
|
+
const variantIds = [...variantNames.keys()];
|
|
366
|
+
if (!page?.content || page.content.length === 0) {
|
|
367
|
+
console.log(chalk.dim("No comparison data found."));
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
const table = new Table({
|
|
371
|
+
head: ["Item", ...variantIds.map((v) => variantNames.get(v) ?? v)].map((h) => chalk.cyan(h)),
|
|
372
|
+
wordWrap: true,
|
|
373
|
+
});
|
|
374
|
+
for (const item of page.content) {
|
|
375
|
+
const resultsByVariant = item.resultsByVariantId ?? {};
|
|
376
|
+
table.push([
|
|
377
|
+
item.datasetItemId,
|
|
378
|
+
...variantIds.map((v) => formatComparisonCell(resultsByVariant[v])),
|
|
379
|
+
]);
|
|
380
|
+
}
|
|
381
|
+
console.log(table.toString());
|
|
382
|
+
console.log(chalk.dim(`Page ${(page.number ?? 0) + 1} of ${page.totalPages ?? 0} (${page.totalElements ?? 0} total)`));
|
|
383
|
+
}
|
|
384
|
+
if (aggregates.length > 0) {
|
|
385
|
+
const aggTable = new Table({
|
|
386
|
+
head: ["Variant", "Items", "Avg Duration", "Avg Tokens", "Total Cost"].map((h) => chalk.cyan(h)),
|
|
387
|
+
wordWrap: true,
|
|
388
|
+
});
|
|
389
|
+
for (const agg of aggregates) {
|
|
390
|
+
aggTable.push([
|
|
391
|
+
agg.variantName ?? agg.variantId,
|
|
392
|
+
agg.itemCount ?? "—",
|
|
393
|
+
agg.avgDurationMs != null ? `${Math.round(agg.avgDurationMs)}ms` : "—",
|
|
394
|
+
agg.avgTokens != null ? Math.round(agg.avgTokens) : "—",
|
|
395
|
+
agg.totalCost != null ? `$${agg.totalCost}` : "—",
|
|
396
|
+
]);
|
|
397
|
+
}
|
|
398
|
+
console.log(chalk.bold("\nAggregates:"));
|
|
399
|
+
console.log(aggTable.toString());
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
cmd.addCommand(comparison);
|
|
404
|
+
// ── regression ───────────────────────────────────────────────────
|
|
405
|
+
const regression = new Command("regression").description("Compare a run against its baseline and report regressions/improvements");
|
|
406
|
+
regression.requiredOption("--experiment <id>", "Experiment ID");
|
|
407
|
+
regression.requiredOption("--run <id>", "Run ID");
|
|
408
|
+
regression.option("--baseline-run <id>", "Baseline run ID (defaults to the run marked baseline, then the previous COMPLETED run)");
|
|
409
|
+
regression.option("--threshold <number>", "Score delta threshold for classifying a regression", parseFloat);
|
|
410
|
+
regression.action(async (opts, command) => {
|
|
411
|
+
await runAction(command, async () => {
|
|
412
|
+
const client = getClient(command);
|
|
413
|
+
const { data } = await client.GET("/v1/experiments/{id}/runs/{runId}/regression", {
|
|
414
|
+
params: {
|
|
415
|
+
path: { id: opts.experiment, runId: opts.run },
|
|
416
|
+
query: {
|
|
417
|
+
baselineRunId: opts.baselineRun,
|
|
418
|
+
threshold: opts.threshold,
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
if (isJsonOutput(command)) {
|
|
423
|
+
console.log(JSON.stringify(data, null, 2));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const report = data;
|
|
427
|
+
const summary = report?.summary ?? {};
|
|
428
|
+
console.log(chalk.bold(`Regression report for run ${report?.runId}`));
|
|
429
|
+
console.log(chalk.dim(`Baseline: ${report?.baselineRunId ?? "—"} (${report?.baselineSource ?? "NONE"})` +
|
|
430
|
+
(report?.threshold != null ? `, threshold ${report.threshold}` : "")));
|
|
431
|
+
const summaryTable = new Table();
|
|
432
|
+
for (const [key, value] of Object.entries(summary)) {
|
|
433
|
+
summaryTable.push({ [chalk.cyan(key)]: value == null ? chalk.dim("—") : String(value) });
|
|
434
|
+
}
|
|
435
|
+
console.log(summaryTable.toString());
|
|
436
|
+
const groups = [
|
|
437
|
+
["Regressed", report?.regressed ?? []],
|
|
438
|
+
["Improved", report?.improved ?? []],
|
|
439
|
+
["Ground Truth Changed", report?.gtChanged ?? []],
|
|
440
|
+
];
|
|
441
|
+
for (const [label, items] of groups) {
|
|
442
|
+
if (items.length === 0)
|
|
443
|
+
continue;
|
|
444
|
+
console.log(chalk.bold(`\n${label}:`));
|
|
445
|
+
const table = new Table({
|
|
446
|
+
head: ["Item", "Baseline", "Current", "Delta"].map((h) => chalk.cyan(h)),
|
|
447
|
+
wordWrap: true,
|
|
448
|
+
});
|
|
449
|
+
for (const item of items) {
|
|
450
|
+
table.push([
|
|
451
|
+
item.datasetItemId,
|
|
452
|
+
item.baselineScore ?? "—",
|
|
453
|
+
item.currentScore ?? "—",
|
|
454
|
+
item.delta ?? "—",
|
|
455
|
+
]);
|
|
456
|
+
}
|
|
457
|
+
console.log(table.toString());
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
cmd.addCommand(regression);
|
|
253
462
|
return cmd;
|
|
254
463
|
}
|
|
255
464
|
//# sourceMappingURL=experiments.js.map
|
|
@@ -2,7 +2,7 @@ import { Command } from "commander";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { extname } from "node:path";
|
|
4
4
|
import { getClient, runAction } from "../lib/client.js";
|
|
5
|
-
import { formatPage, formatDetail, withSpinner } from "../lib/output.js";
|
|
5
|
+
import { formatPage, formatDetail, formatSuccess, withSpinner } from "../lib/output.js";
|
|
6
6
|
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
7
7
|
const IMAGE_MIME_TYPES = {
|
|
8
8
|
".png": "image/png",
|
|
@@ -74,6 +74,19 @@ export function makeExtractionsCommand() {
|
|
|
74
74
|
formatDetail(data, command);
|
|
75
75
|
});
|
|
76
76
|
});
|
|
77
|
+
cmd
|
|
78
|
+
.command("delete")
|
|
79
|
+
.description("Delete an extraction")
|
|
80
|
+
.argument("<id>", "Extraction ID")
|
|
81
|
+
.action(async (id, _opts, command) => {
|
|
82
|
+
await runAction(command, async () => {
|
|
83
|
+
const client = getClient(command);
|
|
84
|
+
await client.DELETE("/v1/extractions/{id}", {
|
|
85
|
+
params: { path: { id } },
|
|
86
|
+
});
|
|
87
|
+
formatSuccess(`Extraction ${id} deleted.`, command);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
77
90
|
const list = new Command("list").description("List extractions");
|
|
78
91
|
addPaginationOptions(list);
|
|
79
92
|
list.option("-s, --search <term>", "Search text");
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { getClient, resolveAuthHeader, runAction } from "../lib/client.js";
|
|
5
|
+
import { resolveConfig } from "../lib/config.js";
|
|
6
|
+
import { BackboneApiError } from "../lib/errors.js";
|
|
7
|
+
import { formatPage, formatDetail, formatSuccess, withSpinner } from "../lib/output.js";
|
|
8
|
+
import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
|
|
9
|
+
import { fileToBlob } from "../lib/multipart.js";
|
|
10
|
+
/**
|
|
11
|
+
* Upload a single file with purpose=agent_input|knowledge. Bypasses the typed
|
|
12
|
+
* client the same way convert.ts and transcribe.ts do: the generated types
|
|
13
|
+
* describe the multipart body as an opaque `file: string`, which is not
|
|
14
|
+
* something FormData can produce a match for.
|
|
15
|
+
*/
|
|
16
|
+
async function uploadFile(command, filePath, purpose) {
|
|
17
|
+
const config = resolveConfig(command);
|
|
18
|
+
const authHeader = await resolveAuthHeader(config);
|
|
19
|
+
const { blob, filename } = fileToBlob(filePath);
|
|
20
|
+
const formData = new FormData();
|
|
21
|
+
formData.append("file", blob, filename);
|
|
22
|
+
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
23
|
+
const url = `${baseUrl}/v1/files?purpose=${encodeURIComponent(purpose)}`;
|
|
24
|
+
const res = await fetch(url, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { Authorization: authHeader },
|
|
27
|
+
body: formData,
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
let body;
|
|
31
|
+
try {
|
|
32
|
+
body = await res.json();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
body = {
|
|
36
|
+
error: res.statusText,
|
|
37
|
+
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
38
|
+
status: res.status,
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
throw new BackboneApiError(body);
|
|
43
|
+
}
|
|
44
|
+
return (await res.json());
|
|
45
|
+
}
|
|
46
|
+
/** Stream a file's bytes from /v1/files/{fileId}/content via a raw fetch. */
|
|
47
|
+
async function downloadFile(command, fileId) {
|
|
48
|
+
const config = resolveConfig(command);
|
|
49
|
+
const authHeader = await resolveAuthHeader(config);
|
|
50
|
+
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
51
|
+
const url = `${baseUrl}/v1/files/${encodeURIComponent(fileId)}/content`;
|
|
52
|
+
const res = await fetch(url, {
|
|
53
|
+
headers: { Authorization: authHeader },
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
let body;
|
|
57
|
+
try {
|
|
58
|
+
body = await res.json();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
body = {
|
|
62
|
+
error: res.statusText,
|
|
63
|
+
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
64
|
+
status: res.status,
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
throw new BackboneApiError(body);
|
|
69
|
+
}
|
|
70
|
+
const disposition = res.headers.get("content-disposition") ?? "";
|
|
71
|
+
const match = /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i.exec(disposition);
|
|
72
|
+
const filename = match ? decodeURIComponent(match[1]) : undefined;
|
|
73
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
74
|
+
return { bytes, filename, contentType: res.headers.get("content-type") ?? undefined };
|
|
75
|
+
}
|
|
76
|
+
export function makeFilesCommand() {
|
|
77
|
+
const cmd = new Command("files").description("Manage uploaded files");
|
|
78
|
+
const list = new Command("list").description("List files");
|
|
79
|
+
addPaginationOptions(list);
|
|
80
|
+
list.option("--purpose <purpose>", "Filter by purpose (agent_input, knowledge)");
|
|
81
|
+
list.action(async (opts, command) => {
|
|
82
|
+
await runAction(command, async () => {
|
|
83
|
+
const client = getClient(command);
|
|
84
|
+
const { data } = await client.GET("/v1/files", {
|
|
85
|
+
params: { query: { purpose: opts.purpose, ...paginationParams(opts) } },
|
|
86
|
+
});
|
|
87
|
+
formatPage(data, command, ["id", "filename", "purpose", "bytes", "created_at"]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
cmd.addCommand(list);
|
|
91
|
+
cmd
|
|
92
|
+
.command("get")
|
|
93
|
+
.description("Retrieve a file's metadata")
|
|
94
|
+
.argument("<fileId>", "File ID")
|
|
95
|
+
.action(async (fileId, _opts, command) => {
|
|
96
|
+
await runAction(command, async () => {
|
|
97
|
+
const client = getClient(command);
|
|
98
|
+
const { data } = await client.GET("/v1/files/{fileId}", {
|
|
99
|
+
params: { path: { fileId } },
|
|
100
|
+
});
|
|
101
|
+
formatDetail(data, command);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
cmd
|
|
105
|
+
.command("upload")
|
|
106
|
+
.description("Upload a file")
|
|
107
|
+
.argument("<path>", "Local file path")
|
|
108
|
+
.requiredOption("--purpose <purpose>", "File purpose (agent_input, knowledge)")
|
|
109
|
+
.action(async (path, opts, command) => {
|
|
110
|
+
await runAction(command, async () => {
|
|
111
|
+
const data = await withSpinner("Uploading...", () => uploadFile(command, path, opts.purpose));
|
|
112
|
+
formatDetail(data, command);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
cmd
|
|
116
|
+
.command("download")
|
|
117
|
+
.description("Download a file's content")
|
|
118
|
+
.argument("<fileId>", "File ID")
|
|
119
|
+
.option("-o, --output <path>", "Write bytes to this path instead of the file's own name")
|
|
120
|
+
.action(async (fileId, opts, command) => {
|
|
121
|
+
await runAction(command, async () => {
|
|
122
|
+
const { bytes, filename } = await withSpinner("Downloading...", () => downloadFile(command, fileId));
|
|
123
|
+
const outputPath = opts.output ?? filename ?? fileId;
|
|
124
|
+
writeFileSync(outputPath, bytes);
|
|
125
|
+
console.error(chalk.green(`Wrote ${bytes.length} bytes to ${outputPath}`));
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
cmd
|
|
129
|
+
.command("delete")
|
|
130
|
+
.description("Delete an agent_input file (knowledge files are not deletable through this API)")
|
|
131
|
+
.argument("<fileId>", "File ID")
|
|
132
|
+
.action(async (fileId, _opts, command) => {
|
|
133
|
+
await runAction(command, async () => {
|
|
134
|
+
const client = getClient(command);
|
|
135
|
+
await client.DELETE("/v1/files/{fileId}", {
|
|
136
|
+
params: { path: { fileId } },
|
|
137
|
+
});
|
|
138
|
+
formatSuccess(`File ${fileId} deleted.`, command);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
return cmd;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=files.js.map
|