orizu 0.3.3 → 0.3.5
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 +19 -0
- package/dist/index.js +359 -20
- package/dist/preview-runtime.js +857 -0
- package/package.json +10 -1
- package/src/index.ts +448 -21
- package/src/preview-runtime.ts +976 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +2 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +22 -56
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +31 -6
package/README.md
CHANGED
|
@@ -169,10 +169,29 @@ orizu logout
|
|
|
169
169
|
--output-schema ./schemas/support-output.json</code></pre>
|
|
170
170
|
</td>
|
|
171
171
|
</tr>
|
|
172
|
+
<tr>
|
|
173
|
+
<td>Preview an app locally</td>
|
|
174
|
+
<td>
|
|
175
|
+
<pre><code>orizu apps preview \
|
|
176
|
+
--file ./apps/SupportLabeler.tsx \
|
|
177
|
+
--input-schema ./schemas/support-input.json \
|
|
178
|
+
--output-schema ./schemas/support-output.json \
|
|
179
|
+
--sample-row ./fixtures/sample-row.json \
|
|
180
|
+
--screenshot ./preview.png</code></pre>
|
|
181
|
+
</td>
|
|
182
|
+
</tr>
|
|
183
|
+
<tr>
|
|
184
|
+
<td>Preview runtime</td>
|
|
185
|
+
<td>The CLI uses the live Orizu web checkout when it is nearby, otherwise it uses the bundled preview runtime snapshot shipped in this package.</td>
|
|
186
|
+
</tr>
|
|
172
187
|
<tr>
|
|
173
188
|
<td>Link a dataset to an app</td>
|
|
174
189
|
<td><code>orizu apps link-dataset --app <appId> --dataset <datasetId></code></td>
|
|
175
190
|
</tr>
|
|
191
|
+
<tr>
|
|
192
|
+
<td>Export app source</td>
|
|
193
|
+
<td><code>orizu apps export --app <appId> --project ops-eval/support-qa --out ./apps/SupportLabeler.tsx</code></td>
|
|
194
|
+
</tr>
|
|
176
195
|
<tr>
|
|
177
196
|
<td colspan="2"><strong>Tasks</strong></td>
|
|
178
197
|
</tr>
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,10 @@ import { parseDatasetFile } from './file-parser.js';
|
|
|
14
14
|
import { streamJsonlRowChunks } from './jsonl-stream.js';
|
|
15
15
|
import { parseDatasetReference } from './dataset-download.js';
|
|
16
16
|
import { parseGlobalFlags } from './global-flags.js';
|
|
17
|
+
import { runLocalAppPreview } from './preview-runtime.js';
|
|
17
18
|
import { assertSecureTokenTransport, authedFetch, getBaseUrl, resolveLoginBaseUrl, setGlobalFlags, } from './http.js';
|
|
18
19
|
import { formatTaskCreateError } from './task-create-error.js';
|
|
20
|
+
const MAX_README_LENGTH = 200_000;
|
|
19
21
|
function getCliVersion() {
|
|
20
22
|
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
21
23
|
if (typeof packageJson.version !== 'string' || packageJson.version.length === 0) {
|
|
@@ -30,10 +32,14 @@ function printVersion() {
|
|
|
30
32
|
printLine(`orizu ${getCliVersion()}`);
|
|
31
33
|
}
|
|
32
34
|
function printUsage() {
|
|
33
|
-
printLine(`orizu global options:\n\n --local Use http://localhost:3000\n --server <url> Use a specific server origin (for example: https://preview.example.com)\n --version, -v Print the orizu CLI version\n\norizu commands:\n\n orizu login [--no-prompt-if-logged-in]\n orizu logout\n orizu whoami\n orizu env [--project <team/project>] [--project-id <projectId>]\n orizu log <event_type> --run-id <id> --sequence <n> --payload @event.json\n orizu teams list\n orizu teams create [--name <name>]\n orizu teams members list [--team <teamSlug>]\n orizu teams members add --email <email> [--team <teamSlug>]\n orizu teams members remove --email <email> [--team <teamSlug>]\n orizu teams members role --team <teamSlug> --email <email> --role <admin|member>\n orizu projects list [--team <teamSlug>]\n orizu projects create --name <name> [--team <teamSlug>]\n orizu prompts list --project <team/project>\n orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>] [--json]\n orizu prompts pull <prompt-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu prompts labels set <prompt-name> <label> --version <version-id> [--project <team/project>] [--json]\n orizu prompts scorers set-headline <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu prompts scorers add <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu scorers list --project <team/project>\n orizu scorers register --project <team/project> --name <name> --manifest <manifest.json> [--prompt-version <id>] [--runner-version <id>] [--label <label>] [--json]\n orizu scorers detail <scorer-id-or-name> --project <team/project> [--json]\n orizu scorers labels set <scorer-name> <label> --version <scorer-version-id> [--project <team/project>] [--json]\n orizu scores submit <results.jsonl|results.json> --scorer-version <id> --subject-version <prompt-version-id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu judges list --project <team/project>\n orizu judges pull <judge-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu runners push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runners exec (--prompt <prompt-version-id> | --prompt-version <id> --runner-version <id> | --scorer-version <id>) --dataset-version <id> --split-set <id-or-name> --split <name> [--runner-dir <dir>] --out <results.jsonl|results.jsonl.gz>\n orizu optimizers push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runs submit <results.jsonl|results.jsonl.gz> --prompt-version <id> --runner-version <id> --dataset-version <id> --split-set <id> --split <name> [--project <team/project>]\n orizu apps list [--project <team/project>]\n orizu apps create --project <team/project> --name <name> --dataset <datasetId> --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps update [--app <appId>] [--project <team/project>] --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps link-dataset --dataset <datasetId> [--app <appId>] [--project <team/project>] [--version <n>]\n orizu apps detail --app <appId> [--project <team/project>] [--json]\n orizu tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userIdOrEmail1,userIdOrEmail2> [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu tasks pause --task <taskId>\n orizu tasks unpause --task <taskId>\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>]\n orizu datasets push <path> [--project <team/project>] [--name <name>] [--json]\n orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--json]\n orizu datasets splits create <datasetVersionId> [--from-file <split.json>] [--json]\n orizu datasets download [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--format <csv|json|jsonl>] [--out <path>]\n orizu datasets append [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>\n orizu datasets delete [--dataset <datasetId|datasetUrl>] [--project <team/project>]\n orizu datasets lock [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--reason <text>]\n orizu datasets clone [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--name <name>]\n orizu tasks export [--task <taskId>] [--format <csv|json|jsonl>] [--out <path>]`);
|
|
35
|
+
printLine(`orizu global options:\n\n --local Use http://localhost:3000\n --server <url> Use a specific server origin (for example: https://preview.example.com)\n --version, -v Print the orizu CLI version\n\norizu commands:\n\n orizu login [--no-prompt-if-logged-in]\n orizu logout\n orizu whoami\n orizu env [--project <team/project>] [--project-id <projectId>]\n orizu log <event_type> --run-id <id> --sequence <n> --payload @event.json\n orizu teams list\n orizu teams create [--name <name>]\n orizu teams members list [--team <teamSlug>]\n orizu teams members add --email <email> [--team <teamSlug>]\n orizu teams members remove --email <email> [--team <teamSlug>]\n orizu teams members role --team <teamSlug> --email <email> --role <admin|member>\n orizu projects list [--team <teamSlug>]\n orizu projects create --name <name> [--team <teamSlug>]\n orizu prompts list --project <team/project>\n orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>] [--json]\n orizu prompts pull <prompt-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu prompts labels set <prompt-name> <label> --version <version-id> [--project <team/project>] [--json]\n orizu prompts scorers set-headline <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu prompts scorers add <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu scorers list --project <team/project>\n orizu scorers register --project <team/project> --name <name> --manifest <manifest.json> [--prompt-version <id>] [--runner-version <id>] [--label <label>] [--json]\n orizu scorers detail <scorer-id-or-name> --project <team/project> [--json]\n orizu scorers labels set <scorer-name> <label> --version <scorer-version-id> [--project <team/project>] [--json]\n orizu scorers exec --scorer-version <id> (--subject-version <prompt-version-id> | --optimization-run <id> --candidate <id>) --dataset-version <id> --split-set <id> --split <name> [--subject-results <jsonl>] [--dependency-score-run <alias=id>] [--dependency-results <alias=path>] [--no-submit] [--out <score.json>] [--project <team/project>] [--json]\n orizu scores submit <results.jsonl|results.json> --scorer-version <id> (--subject-version <prompt-version-id> | --optimization-run <id> --candidate <id>) [--aggregate] [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu judges list --project <team/project>\n orizu judges pull <judge-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu runners push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runners exec (--prompt <prompt-version-id> | --prompt-version <id> --runner-version <id> | --scorer-version <id>) --dataset-version <id> --split-set <id-or-name> --split <name> [--runner-dir <dir>] --out <results.jsonl|results.jsonl.gz>\n orizu optimizers push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runs submit <results.jsonl|results.jsonl.gz> --prompt-version <id> --runner-version <id> --dataset-version <id> --split-set <id> --split <name> [--project <team/project>]\n orizu apps list [--project <team/project>]\n orizu apps create --project <team/project> --name <name> --dataset <datasetId> --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps update [--app <appId>] [--project <team/project>] --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps link-dataset --dataset <datasetId> [--app <appId>] [--project <team/project>] [--version <n>]\n orizu apps detail --app <appId> [--project <team/project>] [--json]\n orizu apps export [--app <appId>] [--project <team/project>] [--version <n>] [--out <path>]\n orizu tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userIdOrEmail1,userIdOrEmail2> [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu tasks pause --task <taskId>\n orizu tasks unpause --task <taskId>\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>]\n orizu datasets push <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>] [--json]\n orizu datasets readme set <datasetId|dataset-name> [--project <team/project>] (--readme-file <README.md> | --readme-text <markdown>) [--json]\n orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--readme-file <README.md> | --readme-text <markdown>] [--json]\n orizu datasets splits create <datasetVersionId> [--from-file <split.json>] [--json]\n orizu datasets download [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--format <csv|json|jsonl>] [--out <path>]\n orizu datasets append [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>\n orizu datasets delete [--dataset <datasetId|datasetUrl>] [--project <team/project>]\n orizu datasets lock [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--reason <text>]\n orizu datasets clone [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--name <name>]\n orizu tasks export [--task <taskId>] [--format <csv|json|jsonl>] [--out <path>]`);
|
|
36
|
+
printPreviewUsage();
|
|
37
|
+
}
|
|
38
|
+
function printPreviewUsage() {
|
|
39
|
+
printLine(`\nLocal app preview:\n\n orizu apps preview --file <path> --input-schema <json-path> --output-schema <json-path> --sample-row <json-path> [--screenshot <png-path>] [--headed] [--keep-open] [--component <name>]`);
|
|
34
40
|
}
|
|
35
41
|
function printOptimizationUsage() {
|
|
36
|
-
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--log-dir logs]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--json]`);
|
|
42
|
+
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--log-dir logs]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--json]`);
|
|
37
43
|
}
|
|
38
44
|
let cliArgs = process.argv.slice(2);
|
|
39
45
|
function getArg(name) {
|
|
@@ -59,6 +65,20 @@ export function expandHomePath(path) {
|
|
|
59
65
|
}
|
|
60
66
|
return path;
|
|
61
67
|
}
|
|
68
|
+
function readReadmeMarkdownFromArgs(usage) {
|
|
69
|
+
const readmeFile = getArg('--readme-file');
|
|
70
|
+
const readmeText = getArg('--readme-text');
|
|
71
|
+
if (readmeFile !== null && readmeText !== null) {
|
|
72
|
+
throw new Error(`Use either --readme-file or --readme-text, not both.\n${usage}`);
|
|
73
|
+
}
|
|
74
|
+
const markdown = readmeFile !== null
|
|
75
|
+
? readFileSync(expandHomePath(readmeFile), 'utf8')
|
|
76
|
+
: readmeText;
|
|
77
|
+
if (markdown !== null && markdown.length > MAX_README_LENGTH) {
|
|
78
|
+
throw new Error(`README markdown is too large. Maximum size is ${MAX_README_LENGTH} characters.`);
|
|
79
|
+
}
|
|
80
|
+
return markdown;
|
|
81
|
+
}
|
|
62
82
|
function createCodeVerifier() {
|
|
63
83
|
return randomBytes(32).toString('base64url');
|
|
64
84
|
}
|
|
@@ -1244,14 +1264,19 @@ async function createDatasetVersion() {
|
|
|
1244
1264
|
const datasetId = getPositionalArg(3) || getArg('--dataset');
|
|
1245
1265
|
const versionLabel = getArg('--label') || getArg('--version-label') || null;
|
|
1246
1266
|
const project = getArg('--project');
|
|
1267
|
+
const usage = 'Usage: orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--readme-file <README.md> | --readme-text <markdown>] [--json]';
|
|
1247
1268
|
if (!datasetId) {
|
|
1248
|
-
throw new Error(
|
|
1269
|
+
throw new Error(usage);
|
|
1249
1270
|
}
|
|
1271
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
1250
1272
|
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project);
|
|
1251
1273
|
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/versions`, {
|
|
1252
1274
|
method: 'POST',
|
|
1253
1275
|
headers: { 'Content-Type': 'application/json' },
|
|
1254
|
-
body: JSON.stringify({
|
|
1276
|
+
body: JSON.stringify({
|
|
1277
|
+
versionLabel,
|
|
1278
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
1279
|
+
}),
|
|
1255
1280
|
});
|
|
1256
1281
|
if (!response.ok) {
|
|
1257
1282
|
throw new Error(`Failed to create dataset version: ${await response.text()}`);
|
|
@@ -1364,17 +1389,22 @@ async function submitScoreResults() {
|
|
|
1364
1389
|
const optimizationRunId = getArg('--optimization-run');
|
|
1365
1390
|
const candidateId = getArg('--candidate');
|
|
1366
1391
|
if (!resultsPath || !scorerVersionId || (!subjectPromptVersionId && (!optimizationRunId || !candidateId))) {
|
|
1367
|
-
throw new Error('Usage: orizu scores submit <results.jsonl|results.json> --project <team/project> --scorer-version <id> (--subject-version <prompt-version-id> | --optimization-run <id> --candidate <id>) [--dataset-version <id> --split-set <id> --split <name>] [--json]');
|
|
1392
|
+
throw new Error('Usage: orizu scores submit <results.jsonl|results.json> --project <team/project> --scorer-version <id> (--subject-version <prompt-version-id> | --optimization-run <id> --candidate <id>) [--aggregate] [--dataset-version <id> --split-set <id> --split <name>] [--json]');
|
|
1368
1393
|
}
|
|
1369
1394
|
const resultBytes = readSourceBytes(resultsPath);
|
|
1370
1395
|
const raw = resultsPath.endsWith('.gz')
|
|
1371
1396
|
? gunzipSync(resultBytes).toString('utf8')
|
|
1372
1397
|
: resultBytes.toString('utf8');
|
|
1373
|
-
const
|
|
1398
|
+
const scoreInput = normalizeScoreResultsInput(resultsPath, raw, hasArg('--aggregate'));
|
|
1399
|
+
if (scoreInput.aggregateWarning) {
|
|
1400
|
+
console.warn(scoreInput.aggregateWarning);
|
|
1401
|
+
}
|
|
1402
|
+
const aggregatePayload = scoreInput.aggregate || {};
|
|
1374
1403
|
const response = await authedFetch(`/api/cli/scores/submit?project=${encodeURIComponent(project)}`, {
|
|
1375
1404
|
method: 'POST',
|
|
1376
1405
|
headers: { 'Content-Type': 'application/json' },
|
|
1377
1406
|
body: JSON.stringify({
|
|
1407
|
+
...aggregatePayload,
|
|
1378
1408
|
scorerVersionId,
|
|
1379
1409
|
subjectPromptVersionId: subjectPromptVersionId || undefined,
|
|
1380
1410
|
datasetVersionId: getArg('--dataset-version') || undefined,
|
|
@@ -1382,7 +1412,7 @@ async function submitScoreResults() {
|
|
|
1382
1412
|
splitName: getArg('--split') || undefined,
|
|
1383
1413
|
optimizationRunId: optimizationRunId || undefined,
|
|
1384
1414
|
candidateId: candidateId || undefined,
|
|
1385
|
-
resultsJsonl,
|
|
1415
|
+
resultsJsonl: scoreInput.resultsJsonl,
|
|
1386
1416
|
}),
|
|
1387
1417
|
});
|
|
1388
1418
|
if (!response.ok) {
|
|
@@ -1396,23 +1426,150 @@ async function submitScoreResults() {
|
|
|
1396
1426
|
printLine(`Submitted score ${sanitizeTerminalText(data.scoreRun.id)} ` +
|
|
1397
1427
|
`(${formatPercent(data.scoreRun.scoreValue)})`);
|
|
1398
1428
|
}
|
|
1399
|
-
function
|
|
1429
|
+
function looksLikeAggregateScoreObject(value) {
|
|
1430
|
+
return (value.scoreValue !== undefined ||
|
|
1431
|
+
value.score_value !== undefined ||
|
|
1432
|
+
value.diagnostics !== undefined ||
|
|
1433
|
+
value.feedbackSummary !== undefined ||
|
|
1434
|
+
value.feedback_summary !== undefined ||
|
|
1435
|
+
value.rowEvidence !== undefined ||
|
|
1436
|
+
value.row_evidence !== undefined ||
|
|
1437
|
+
value.dependencyScoreRunIds !== undefined ||
|
|
1438
|
+
value.dependency_score_run_ids !== undefined);
|
|
1439
|
+
}
|
|
1440
|
+
function numberFromUnknown(value) {
|
|
1441
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
1442
|
+
return value;
|
|
1443
|
+
if (typeof value === 'string' && value.trim()) {
|
|
1444
|
+
const parsed = Number(value);
|
|
1445
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1446
|
+
}
|
|
1447
|
+
if (typeof value === 'boolean')
|
|
1448
|
+
return value ? 1 : 0;
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
function normalizeScoreResultsInput(sourcePath, raw, aggregateMode = false) {
|
|
1400
1452
|
const logicalPath = sourcePath.endsWith('.gz') ? sourcePath.slice(0, -3) : sourcePath;
|
|
1401
1453
|
if (!logicalPath.endsWith('.json'))
|
|
1402
|
-
return raw;
|
|
1454
|
+
return { resultsJsonl: raw };
|
|
1403
1455
|
try {
|
|
1404
1456
|
const parsed = JSON.parse(raw);
|
|
1457
|
+
if (aggregateMode) {
|
|
1458
|
+
if (!isRecord(parsed)) {
|
|
1459
|
+
throw new Error('Aggregate score results JSON must be an object');
|
|
1460
|
+
}
|
|
1461
|
+
return { aggregate: parsed };
|
|
1462
|
+
}
|
|
1463
|
+
if (isRecord(parsed) && looksLikeAggregateScoreObject(parsed)) {
|
|
1464
|
+
return {
|
|
1465
|
+
aggregate: parsed,
|
|
1466
|
+
aggregateWarning: 'Warning: aggregate score JSON detected. Pass --aggregate to make this explicit.',
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1405
1469
|
const rows = Array.isArray(parsed) ? parsed : [parsed];
|
|
1406
1470
|
if (rows.some(row => !isRecord(row))) {
|
|
1407
1471
|
throw new Error('JSON score results must be an object or an array of objects');
|
|
1408
1472
|
}
|
|
1409
|
-
return rows.map(row => JSON.stringify(row)).join('\n');
|
|
1473
|
+
return { resultsJsonl: rows.map(row => JSON.stringify(row)).join('\n') };
|
|
1410
1474
|
}
|
|
1411
1475
|
catch (error) {
|
|
1412
1476
|
const message = error instanceof Error ? error.message : String(error);
|
|
1413
1477
|
throw new Error(`Invalid score results JSON '${sourcePath}': ${message}`);
|
|
1414
1478
|
}
|
|
1415
1479
|
}
|
|
1480
|
+
function readOptionalJsonlFile(pathArg) {
|
|
1481
|
+
if (!pathArg)
|
|
1482
|
+
return undefined;
|
|
1483
|
+
const bytes = readSourceBytes(pathArg);
|
|
1484
|
+
return pathArg.endsWith('.gz')
|
|
1485
|
+
? gunzipSync(bytes).toString('utf8')
|
|
1486
|
+
: bytes.toString('utf8');
|
|
1487
|
+
}
|
|
1488
|
+
function parseAliasAssignments(value) {
|
|
1489
|
+
const result = {};
|
|
1490
|
+
if (!value)
|
|
1491
|
+
return result;
|
|
1492
|
+
for (const item of parseCommaSeparated(value)) {
|
|
1493
|
+
const equalsIndex = item.indexOf('=');
|
|
1494
|
+
if (equalsIndex <= 0 || equalsIndex === item.length - 1) {
|
|
1495
|
+
throw new Error(`Expected alias assignment in form alias=value, got '${item}'`);
|
|
1496
|
+
}
|
|
1497
|
+
result[item.slice(0, equalsIndex)] = item.slice(equalsIndex + 1);
|
|
1498
|
+
}
|
|
1499
|
+
return result;
|
|
1500
|
+
}
|
|
1501
|
+
function readDependencyResultAssignments(value) {
|
|
1502
|
+
const assignments = parseAliasAssignments(value);
|
|
1503
|
+
const result = {};
|
|
1504
|
+
for (const [alias, path] of Object.entries(assignments)) {
|
|
1505
|
+
result[alias] = readOptionalJsonlFile(path) || '';
|
|
1506
|
+
}
|
|
1507
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
1508
|
+
}
|
|
1509
|
+
async function execScorer() {
|
|
1510
|
+
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
1511
|
+
const scorerVersionId = getArg('--scorer-version');
|
|
1512
|
+
const subjectPromptVersionId = getArg('--subject-version') || getArg('--prompt-version');
|
|
1513
|
+
const optimizationRunId = getArg('--optimization-run');
|
|
1514
|
+
const candidateId = getArg('--candidate');
|
|
1515
|
+
const datasetVersionId = getArg('--dataset-version');
|
|
1516
|
+
const splitSetId = getArg('--split-set');
|
|
1517
|
+
const splitName = getArg('--split');
|
|
1518
|
+
const outPath = getArg('--out');
|
|
1519
|
+
if (!scorerVersionId ||
|
|
1520
|
+
(!subjectPromptVersionId && (!optimizationRunId || !candidateId)) ||
|
|
1521
|
+
!datasetVersionId ||
|
|
1522
|
+
!splitSetId ||
|
|
1523
|
+
!splitName) {
|
|
1524
|
+
throw new Error('Usage: orizu scorers exec --scorer-version <id> (--subject-version <prompt-version-id> | --optimization-run <id> --candidate <id>) --dataset-version <id> --split-set <id> --split <name> [--subject-results <jsonl>] [--dependency-score-run <alias=id>] [--dependency-results <alias=path>] [--no-submit] [--out <score.json>] [--project <team/project>] [--json]');
|
|
1525
|
+
}
|
|
1526
|
+
const response = await authedFetch(`/api/cli/scorers/exec?project=${encodeURIComponent(project)}`, {
|
|
1527
|
+
method: 'POST',
|
|
1528
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1529
|
+
body: JSON.stringify({
|
|
1530
|
+
scorerVersionId,
|
|
1531
|
+
subjectPromptVersionId: subjectPromptVersionId || undefined,
|
|
1532
|
+
optimizationRunId: optimizationRunId || undefined,
|
|
1533
|
+
candidateId: candidateId || undefined,
|
|
1534
|
+
datasetVersionId,
|
|
1535
|
+
splitSetId,
|
|
1536
|
+
splitName,
|
|
1537
|
+
submit: !hasArg('--no-submit'),
|
|
1538
|
+
reuseExisting: !hasArg('--no-reuse-existing'),
|
|
1539
|
+
subjectResultsJsonl: readOptionalJsonlFile(getArg('--subject-results') || getArg('--outputs')),
|
|
1540
|
+
dependencyScoreRunIds: parseAliasAssignments(getArg('--dependency-score-run') || getArg('--dependency-score-runs')),
|
|
1541
|
+
dependencyResultsJsonl: readDependencyResultAssignments(getArg('--dependency-results')),
|
|
1542
|
+
}),
|
|
1543
|
+
});
|
|
1544
|
+
if (!response.ok) {
|
|
1545
|
+
throw new Error(`Failed to execute scorer: ${await response.text()}`);
|
|
1546
|
+
}
|
|
1547
|
+
const data = await parseJsonResponse(response, 'Scorer exec');
|
|
1548
|
+
if (outPath) {
|
|
1549
|
+
writeFileSync(expandHomePath(outPath), `${JSON.stringify(data.scoreResult || data, null, 2)}\n`);
|
|
1550
|
+
}
|
|
1551
|
+
if (hasJsonFlag()) {
|
|
1552
|
+
printJson(data);
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
const scoreRun = isRecord(data.scoreRun) ? data.scoreRun : null;
|
|
1556
|
+
const scoreResult = isRecord(data.scoreResult) ? data.scoreResult : null;
|
|
1557
|
+
const scoreValue = scoreRun
|
|
1558
|
+
? scoreRun.scoreValue
|
|
1559
|
+
: scoreResult
|
|
1560
|
+
? scoreResult.scoreValue
|
|
1561
|
+
: null;
|
|
1562
|
+
if (scoreRun && typeof scoreRun.id === 'string') {
|
|
1563
|
+
printLine(`Executed scorer ${sanitizeTerminalText(scorerVersionId)} -> score ${sanitizeTerminalText(scoreRun.id)} (${formatPercent(numberFromUnknown(scoreValue))})`);
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
printLine(`Executed scorer ${sanitizeTerminalText(scorerVersionId)} (${formatPercent(numberFromUnknown(scoreValue))})`);
|
|
1567
|
+
}
|
|
1568
|
+
if (outPath) {
|
|
1569
|
+
printLine(`Wrote scorer result to ${sanitizeTerminalText(outPath)}`);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
const OPTIMIZATION_REPORT_MAX_BYTES = 2 * 1024 * 1024;
|
|
1416
1573
|
function parseOptionalNumberFlag(name) {
|
|
1417
1574
|
const value = getArg(name);
|
|
1418
1575
|
if (!value) {
|
|
@@ -1427,6 +1584,40 @@ function parseOptionalNumberFlag(name) {
|
|
|
1427
1584
|
function hasObjectKeys(value) {
|
|
1428
1585
|
return Object.keys(value).length > 0;
|
|
1429
1586
|
}
|
|
1587
|
+
function readOptimizationReportInput() {
|
|
1588
|
+
const report = getArg('--report');
|
|
1589
|
+
const reportFile = getArg('--report-file');
|
|
1590
|
+
if (report && reportFile) {
|
|
1591
|
+
throw new Error('Use either --report or --report-file, not both');
|
|
1592
|
+
}
|
|
1593
|
+
if (!report && !reportFile) {
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1596
|
+
let markdown;
|
|
1597
|
+
let sourceName;
|
|
1598
|
+
if (reportFile) {
|
|
1599
|
+
const expandedPath = expandHomePath(reportFile);
|
|
1600
|
+
markdown = readSourceFile(reportFile);
|
|
1601
|
+
sourceName = basename(expandedPath);
|
|
1602
|
+
}
|
|
1603
|
+
else if (report?.startsWith('@')) {
|
|
1604
|
+
const path = report.slice(1);
|
|
1605
|
+
const expandedPath = expandHomePath(path);
|
|
1606
|
+
markdown = readSourceFile(path);
|
|
1607
|
+
sourceName = basename(expandedPath);
|
|
1608
|
+
}
|
|
1609
|
+
else {
|
|
1610
|
+
markdown = report || '';
|
|
1611
|
+
sourceName = 'inline';
|
|
1612
|
+
}
|
|
1613
|
+
if (!markdown.trim()) {
|
|
1614
|
+
throw new Error('Optimization report markdown must not be blank');
|
|
1615
|
+
}
|
|
1616
|
+
if (Buffer.byteLength(markdown, 'utf8') > OPTIMIZATION_REPORT_MAX_BYTES) {
|
|
1617
|
+
throw new Error(`Optimization report exceeds ${OPTIMIZATION_REPORT_MAX_BYTES} bytes`);
|
|
1618
|
+
}
|
|
1619
|
+
return { markdown, sourceName };
|
|
1620
|
+
}
|
|
1430
1621
|
async function startOptimizationRun() {
|
|
1431
1622
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
1432
1623
|
const optimizerVersionId = getArg('--optimizer-version');
|
|
@@ -1503,6 +1694,10 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1503
1694
|
};
|
|
1504
1695
|
const metadata = readJsonObjectArg(getArg('--metadata'), 'optimization metadata');
|
|
1505
1696
|
const reason = getArg('--reason');
|
|
1697
|
+
const report = readOptimizationReportInput();
|
|
1698
|
+
if (report && (action === 'pause' || action === 'resume')) {
|
|
1699
|
+
throw new Error(`orizu optimizations ${action} does not accept optimization reports`);
|
|
1700
|
+
}
|
|
1506
1701
|
if ((action === 'pause' || action === 'cancel') && reason) {
|
|
1507
1702
|
metadata.reason = reason;
|
|
1508
1703
|
}
|
|
@@ -1513,6 +1708,10 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1513
1708
|
if (hasObjectKeys(metadata)) {
|
|
1514
1709
|
body.metadata = metadata;
|
|
1515
1710
|
}
|
|
1711
|
+
if (report) {
|
|
1712
|
+
body.reportMarkdown = report.markdown;
|
|
1713
|
+
body.reportSourceName = report.sourceName;
|
|
1714
|
+
}
|
|
1516
1715
|
if (action === 'finish') {
|
|
1517
1716
|
const bestScore = parseOptionalNumberFlag('--best-score');
|
|
1518
1717
|
if (bestScore !== undefined) {
|
|
@@ -1957,10 +2156,51 @@ async function createAppFromFile() {
|
|
|
1957
2156
|
}
|
|
1958
2157
|
const data = await parseJsonResponse(response, 'App create');
|
|
1959
2158
|
printLine(`Created app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) v${data.app.versionNum}`);
|
|
2159
|
+
if (data.app.url) {
|
|
2160
|
+
printLine(`View app: ${formatTerminalLink(data.app.url)}`);
|
|
2161
|
+
}
|
|
1960
2162
|
if (data.warnings?.length) {
|
|
1961
2163
|
printLine(`Warnings: ${sanitizeTerminalText(data.warnings.join('; '))}`);
|
|
1962
2164
|
}
|
|
1963
2165
|
}
|
|
2166
|
+
async function previewAppFromFile() {
|
|
2167
|
+
const filePath = getArg('--file');
|
|
2168
|
+
const inputSchemaPath = getArg('--input-schema');
|
|
2169
|
+
const outputSchemaPath = getArg('--output-schema');
|
|
2170
|
+
const sampleRowPath = getArg('--sample-row');
|
|
2171
|
+
const screenshotPath = getArg('--screenshot');
|
|
2172
|
+
const component = getArg('--component') || undefined;
|
|
2173
|
+
const headed = hasArg('--headed');
|
|
2174
|
+
const keepOpen = hasArg('--keep-open');
|
|
2175
|
+
if (!filePath || !inputSchemaPath || !outputSchemaPath || !sampleRowPath) {
|
|
2176
|
+
throw new Error('Usage: orizu apps preview --file <path> --input-schema <json-path> --output-schema <json-path> --sample-row <json-path> [--screenshot <png-path>] [--headed] [--keep-open] [--component <name>]');
|
|
2177
|
+
}
|
|
2178
|
+
const expandedFilePath = realpathSync(expandHomePath(filePath));
|
|
2179
|
+
const inputJsonSchema = readJsonFile(inputSchemaPath);
|
|
2180
|
+
const outputJsonSchema = readJsonFile(outputSchemaPath);
|
|
2181
|
+
const sampleRow = readJsonFile(sampleRowPath);
|
|
2182
|
+
const expandedScreenshotPath = screenshotPath ? expandHomePath(screenshotPath) : undefined;
|
|
2183
|
+
const result = await runLocalAppPreview({
|
|
2184
|
+
filePath: expandedFilePath,
|
|
2185
|
+
inputSchema: inputJsonSchema,
|
|
2186
|
+
outputSchema: outputJsonSchema,
|
|
2187
|
+
sampleRow,
|
|
2188
|
+
screenshotPath: expandedScreenshotPath,
|
|
2189
|
+
componentName: component,
|
|
2190
|
+
headed,
|
|
2191
|
+
keepOpen,
|
|
2192
|
+
});
|
|
2193
|
+
printLine(`Preview rendered: ${formatTerminalLink(result.url)}`);
|
|
2194
|
+
if (result.screenshotPath) {
|
|
2195
|
+
printLine(`Screenshot: ${sanitizeTerminalText(result.screenshotPath)}`);
|
|
2196
|
+
}
|
|
2197
|
+
if (headed || keepOpen) {
|
|
2198
|
+
printLine('Headed preview is running. Close Chromium or stop the command when you are done.');
|
|
2199
|
+
}
|
|
2200
|
+
if (result.warnings.length > 0) {
|
|
2201
|
+
printLine(`Warnings: ${sanitizeTerminalText(result.warnings.join('; '))}`);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
1964
2204
|
async function updateAppFromFile() {
|
|
1965
2205
|
const filePath = getArg('--file');
|
|
1966
2206
|
const inputSchemaPath = getArg('--input-schema');
|
|
@@ -2185,6 +2425,14 @@ async function updateTaskStatus(targetStatus) {
|
|
|
2185
2425
|
const action = targetStatus === 'paused' ? 'Paused' : 'Unpaused';
|
|
2186
2426
|
printLine(`${action} task ${sanitizeTerminalText(data.task.id)} [${sanitizeTerminalText(data.task.status)}]`);
|
|
2187
2427
|
}
|
|
2428
|
+
function defaultAppExportFilename(appName, versionNum) {
|
|
2429
|
+
const safeName = appName
|
|
2430
|
+
.trim()
|
|
2431
|
+
.replace(/[^A-Za-z0-9._-]+/g, '_')
|
|
2432
|
+
.replace(/^_+|_+$/g, '')
|
|
2433
|
+
.slice(0, 100);
|
|
2434
|
+
return `${safeName || 'app'}.v${versionNum}.tsx`;
|
|
2435
|
+
}
|
|
2188
2436
|
async function appDetail() {
|
|
2189
2437
|
const appId = getArg('--app');
|
|
2190
2438
|
const project = getArg('--project');
|
|
@@ -2223,6 +2471,40 @@ async function appDetail() {
|
|
|
2223
2471
|
printLine(` Created: ${sanitizeTerminalText(detail.createdAt)}`);
|
|
2224
2472
|
printLine(` Updated: ${sanitizeTerminalText(detail.updatedAt)}`);
|
|
2225
2473
|
}
|
|
2474
|
+
async function exportAppSource() {
|
|
2475
|
+
const project = getArg('--project');
|
|
2476
|
+
const versionArg = getArg('--version');
|
|
2477
|
+
const outPathArg = getArg('--out');
|
|
2478
|
+
let appId = getArg('--app');
|
|
2479
|
+
const versionNum = versionArg && /^[1-9]\d*$/.test(versionArg)
|
|
2480
|
+
? Number.parseInt(versionArg, 10)
|
|
2481
|
+
: undefined;
|
|
2482
|
+
if (versionArg && versionNum === undefined) {
|
|
2483
|
+
throw new Error('--version must be a positive integer');
|
|
2484
|
+
}
|
|
2485
|
+
if (!appId) {
|
|
2486
|
+
const selected = await selectAppIdInteractively(project);
|
|
2487
|
+
appId = selected.appId;
|
|
2488
|
+
}
|
|
2489
|
+
const query = new URLSearchParams();
|
|
2490
|
+
if (project) {
|
|
2491
|
+
query.set('project', project);
|
|
2492
|
+
}
|
|
2493
|
+
if (versionNum) {
|
|
2494
|
+
query.set('version', String(versionNum));
|
|
2495
|
+
}
|
|
2496
|
+
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
2497
|
+
const response = await authedFetch(`/api/cli/apps/${encodeURIComponent(appId)}/export${suffix}`);
|
|
2498
|
+
if (!response.ok) {
|
|
2499
|
+
throw new Error(`Failed to export app (${response.status}): ${await response.text()}`);
|
|
2500
|
+
}
|
|
2501
|
+
const data = await parseJsonResponse(response, 'App export');
|
|
2502
|
+
const filename = outPathArg
|
|
2503
|
+
? expandHomePath(outPathArg)
|
|
2504
|
+
: defaultAppExportFilename(data.app.name, data.version.versionNum);
|
|
2505
|
+
writeTextFileEnsuringDir(filename, data.version.code);
|
|
2506
|
+
printLine(`Saved app ${sanitizeTerminalText(data.app.name)} v${data.version.versionNum} source to ${sanitizeTerminalText(filename)}`);
|
|
2507
|
+
}
|
|
2226
2508
|
async function listTeamMembers() {
|
|
2227
2509
|
const teamSlug = await resolveTeamSlug(getArg('--team'));
|
|
2228
2510
|
printTeamMembers(await fetchTeamMembers(teamSlug));
|
|
@@ -2287,7 +2569,7 @@ async function changeTeamMemberRole() {
|
|
|
2287
2569
|
}
|
|
2288
2570
|
printLine(`Updated ${sanitizeTerminalText(member.email)} role to ${sanitizeTerminalText(role)}`);
|
|
2289
2571
|
}
|
|
2290
|
-
async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
2572
|
+
async function createDatasetFromRows(project, name, sourceType, rows, readmeMarkdown = null) {
|
|
2291
2573
|
const response = await authedFetch('/api/cli/datasets/upload', {
|
|
2292
2574
|
method: 'POST',
|
|
2293
2575
|
headers: {
|
|
@@ -2298,6 +2580,7 @@ async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
|
2298
2580
|
name,
|
|
2299
2581
|
rows,
|
|
2300
2582
|
sourceType,
|
|
2583
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
2301
2584
|
}),
|
|
2302
2585
|
});
|
|
2303
2586
|
if (!response.ok) {
|
|
@@ -2306,7 +2589,7 @@ async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
|
2306
2589
|
}
|
|
2307
2590
|
return parseJsonResponse(response, 'Dataset upload');
|
|
2308
2591
|
}
|
|
2309
|
-
async function uploadJsonlDatasetInChunks(file, project, datasetName) {
|
|
2592
|
+
async function uploadJsonlDatasetInChunks(file, project, datasetName, readmeMarkdown = null) {
|
|
2310
2593
|
let dataset = null;
|
|
2311
2594
|
let totalUploaded = 0;
|
|
2312
2595
|
let chunkIndex = 0;
|
|
@@ -2334,7 +2617,7 @@ async function uploadJsonlDatasetInChunks(file, project, datasetName) {
|
|
|
2334
2617
|
printLine(`Uploading chunk ${chunkIndex} (${chunk.length} rows)...`);
|
|
2335
2618
|
try {
|
|
2336
2619
|
if (!dataset) {
|
|
2337
|
-
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk);
|
|
2620
|
+
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk, readmeMarkdown);
|
|
2338
2621
|
dataset = data.dataset;
|
|
2339
2622
|
totalUploaded = data.dataset.rowCount;
|
|
2340
2623
|
continue;
|
|
@@ -2369,18 +2652,20 @@ async function uploadDataset() {
|
|
|
2369
2652
|
const projectArg = getArg('--project');
|
|
2370
2653
|
const fileArg = getArg('--file');
|
|
2371
2654
|
const name = getArg('--name');
|
|
2655
|
+
const usage = 'Usage: orizu datasets upload --file <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>]';
|
|
2372
2656
|
if (!fileArg) {
|
|
2373
|
-
throw new Error(
|
|
2657
|
+
throw new Error(usage);
|
|
2374
2658
|
}
|
|
2659
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2375
2660
|
const file = expandHomePath(fileArg);
|
|
2376
2661
|
const project = await resolveProjectSlug(projectArg);
|
|
2377
2662
|
const datasetName = name || basename(file);
|
|
2378
2663
|
if (extname(file).toLowerCase() === '.jsonl') {
|
|
2379
|
-
await uploadJsonlDatasetInChunks(file, project, datasetName);
|
|
2664
|
+
await uploadJsonlDatasetInChunks(file, project, datasetName, readmeMarkdown);
|
|
2380
2665
|
return;
|
|
2381
2666
|
}
|
|
2382
2667
|
const { rows, sourceType } = parseDatasetFile(file);
|
|
2383
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows);
|
|
2668
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown);
|
|
2384
2669
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`);
|
|
2385
2670
|
if (data.dataset.url) {
|
|
2386
2671
|
printLine(`View dataset: ${formatTerminalLink(data.dataset.url)}`);
|
|
@@ -2390,20 +2675,26 @@ async function pushDataset() {
|
|
|
2390
2675
|
const projectArg = getArg('--project');
|
|
2391
2676
|
const fileArg = getPositionalArg(2) || getArg('--file');
|
|
2392
2677
|
const name = getArg('--name');
|
|
2678
|
+
const usage = 'Usage: orizu datasets push <rows.csv|rows.json|rows.jsonl> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>] [--json]';
|
|
2393
2679
|
if (!fileArg) {
|
|
2394
|
-
throw new Error(
|
|
2680
|
+
throw new Error(usage);
|
|
2395
2681
|
}
|
|
2682
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2396
2683
|
const file = expandHomePath(fileArg);
|
|
2397
2684
|
const project = await resolveProjectSlug(projectArg);
|
|
2398
2685
|
const datasetName = name || basename(file);
|
|
2399
2686
|
const { rows, sourceType } = parseDatasetFile(file);
|
|
2400
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows);
|
|
2687
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown);
|
|
2401
2688
|
if (hasJsonFlag()) {
|
|
2402
|
-
|
|
2689
|
+
const payload = {
|
|
2403
2690
|
dataset_id: data.dataset.id,
|
|
2404
2691
|
name: data.dataset.name,
|
|
2405
2692
|
row_count: data.dataset.rowCount,
|
|
2406
|
-
}
|
|
2693
|
+
};
|
|
2694
|
+
if (data.readmeVersion?.id) {
|
|
2695
|
+
payload.readme_version_id = data.readmeVersion.id;
|
|
2696
|
+
}
|
|
2697
|
+
printJson(payload);
|
|
2407
2698
|
return;
|
|
2408
2699
|
}
|
|
2409
2700
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`);
|
|
@@ -2411,6 +2702,38 @@ async function pushDataset() {
|
|
|
2411
2702
|
printLine(`View dataset: ${formatTerminalLink(data.dataset.url)}`);
|
|
2412
2703
|
}
|
|
2413
2704
|
}
|
|
2705
|
+
async function setDatasetReadme() {
|
|
2706
|
+
const datasetId = getPositionalArg(3) || getArg('--dataset');
|
|
2707
|
+
const project = getArg('--project');
|
|
2708
|
+
const usage = 'Usage: orizu datasets readme set <datasetId|dataset-name> [--project <team/project>] (--readme-file <README.md> | --readme-text <markdown>) [--json]';
|
|
2709
|
+
if (!datasetId) {
|
|
2710
|
+
throw new Error(usage);
|
|
2711
|
+
}
|
|
2712
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2713
|
+
if (readmeMarkdown === null) {
|
|
2714
|
+
throw new Error(usage);
|
|
2715
|
+
}
|
|
2716
|
+
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project);
|
|
2717
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/readme`, {
|
|
2718
|
+
method: 'POST',
|
|
2719
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2720
|
+
body: JSON.stringify({ markdown: readmeMarkdown }),
|
|
2721
|
+
});
|
|
2722
|
+
if (!response.ok) {
|
|
2723
|
+
throw new Error(`Failed to save dataset README: ${await response.text()}`);
|
|
2724
|
+
}
|
|
2725
|
+
const data = await parseJsonResponse(response, 'Dataset README save');
|
|
2726
|
+
if (hasJsonFlag()) {
|
|
2727
|
+
printJson({
|
|
2728
|
+
dataset_id: data.version.dataset_id,
|
|
2729
|
+
readme_version_id: data.version.id,
|
|
2730
|
+
readme_version_num: data.version.version_num,
|
|
2731
|
+
});
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
printLine(`Saved README v${data.version.version_num} for dataset ` +
|
|
2735
|
+
`${sanitizeTerminalText(data.version.dataset_id)} (${sanitizeTerminalText(data.version.id)}).`);
|
|
2736
|
+
}
|
|
2414
2737
|
function getDatasetReferenceInput() {
|
|
2415
2738
|
const fromFlag = getArg('--dataset');
|
|
2416
2739
|
if (fromFlag) {
|
|
@@ -3094,6 +3417,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3094
3417
|
await setScorerLabel();
|
|
3095
3418
|
return;
|
|
3096
3419
|
}
|
|
3420
|
+
if (command === 'scorers' && subcommand === 'exec') {
|
|
3421
|
+
await execScorer();
|
|
3422
|
+
return;
|
|
3423
|
+
}
|
|
3097
3424
|
if (command === 'runners' && subcommand === 'push') {
|
|
3098
3425
|
await pushRunnerArtifact('runner');
|
|
3099
3426
|
return;
|
|
@@ -3143,6 +3470,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3143
3470
|
await createAppFromFile();
|
|
3144
3471
|
return;
|
|
3145
3472
|
}
|
|
3473
|
+
if (command === 'apps' && subcommand === 'preview') {
|
|
3474
|
+
await previewAppFromFile();
|
|
3475
|
+
return;
|
|
3476
|
+
}
|
|
3146
3477
|
if (command === 'apps' && subcommand === 'update') {
|
|
3147
3478
|
await updateAppFromFile();
|
|
3148
3479
|
return;
|
|
@@ -3155,6 +3486,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3155
3486
|
await appDetail();
|
|
3156
3487
|
return;
|
|
3157
3488
|
}
|
|
3489
|
+
if (command === 'apps' && subcommand === 'export') {
|
|
3490
|
+
await exportAppSource();
|
|
3491
|
+
return;
|
|
3492
|
+
}
|
|
3158
3493
|
if (command === 'tasks' && subcommand === 'list') {
|
|
3159
3494
|
await listTasks();
|
|
3160
3495
|
return;
|
|
@@ -3196,6 +3531,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3196
3531
|
return;
|
|
3197
3532
|
}
|
|
3198
3533
|
const datasetsAction = cliArgs[2];
|
|
3534
|
+
if (command === 'datasets' && subcommand === 'readme' && datasetsAction === 'set') {
|
|
3535
|
+
await setDatasetReadme();
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3199
3538
|
if (command === 'datasets' && subcommand === 'versions' && datasetsAction === 'create') {
|
|
3200
3539
|
await createDatasetVersion();
|
|
3201
3540
|
return;
|