orizu 0.3.2 → 0.3.4
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 +15 -0
- package/dist/index.js +281 -14
- package/package.json +1 -1
- package/src/index.ts +427 -14
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +46 -13
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +46 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +123 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +46 -1
package/README.md
CHANGED
|
@@ -101,6 +101,17 @@ orizu logout
|
|
|
101
101
|
<td>Create a project</td>
|
|
102
102
|
<td><code>orizu projects create --name "Support QA" --team ops-eval</code></td>
|
|
103
103
|
</tr>
|
|
104
|
+
<tr>
|
|
105
|
+
<td colspan="2"><strong>Prompts</strong></td>
|
|
106
|
+
</tr>
|
|
107
|
+
<tr>
|
|
108
|
+
<td>List prompts</td>
|
|
109
|
+
<td><code>orizu prompts list --project ops-eval/support-qa</code></td>
|
|
110
|
+
</tr>
|
|
111
|
+
<tr>
|
|
112
|
+
<td>Show prompt comments</td>
|
|
113
|
+
<td><code>orizu prompts comments <promptIdOrName> --project ops-eval/support-qa</code></td>
|
|
114
|
+
</tr>
|
|
104
115
|
<tr>
|
|
105
116
|
<td colspan="2"><strong>Datasets</strong></td>
|
|
106
117
|
</tr>
|
|
@@ -162,6 +173,10 @@ orizu logout
|
|
|
162
173
|
<td>Link a dataset to an app</td>
|
|
163
174
|
<td><code>orizu apps link-dataset --app <appId> --dataset <datasetId></code></td>
|
|
164
175
|
</tr>
|
|
176
|
+
<tr>
|
|
177
|
+
<td>Export app source</td>
|
|
178
|
+
<td><code>orizu apps export --app <appId> --project ops-eval/support-qa --out ./apps/SupportLabeler.tsx</code></td>
|
|
179
|
+
</tr>
|
|
165
180
|
<tr>
|
|
166
181
|
<td colspan="2"><strong>Tasks</strong></td>
|
|
167
182
|
</tr>
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { parseDatasetReference } from './dataset-download.js';
|
|
|
16
16
|
import { parseGlobalFlags } from './global-flags.js';
|
|
17
17
|
import { assertSecureTokenTransport, authedFetch, getBaseUrl, resolveLoginBaseUrl, setGlobalFlags, } from './http.js';
|
|
18
18
|
import { formatTaskCreateError } from './task-create-error.js';
|
|
19
|
+
const MAX_README_LENGTH = 200_000;
|
|
19
20
|
function getCliVersion() {
|
|
20
21
|
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
21
22
|
if (typeof packageJson.version !== 'string' || packageJson.version.length === 0) {
|
|
@@ -30,10 +31,10 @@ function printVersion() {
|
|
|
30
31
|
printLine(`orizu ${getCliVersion()}`);
|
|
31
32
|
}
|
|
32
33
|
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 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>]`);
|
|
34
|
+
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 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>]`);
|
|
34
35
|
}
|
|
35
36
|
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]\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]`);
|
|
37
|
+
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
38
|
}
|
|
38
39
|
let cliArgs = process.argv.slice(2);
|
|
39
40
|
function getArg(name) {
|
|
@@ -59,6 +60,20 @@ export function expandHomePath(path) {
|
|
|
59
60
|
}
|
|
60
61
|
return path;
|
|
61
62
|
}
|
|
63
|
+
function readReadmeMarkdownFromArgs(usage) {
|
|
64
|
+
const readmeFile = getArg('--readme-file');
|
|
65
|
+
const readmeText = getArg('--readme-text');
|
|
66
|
+
if (readmeFile !== null && readmeText !== null) {
|
|
67
|
+
throw new Error(`Use either --readme-file or --readme-text, not both.\n${usage}`);
|
|
68
|
+
}
|
|
69
|
+
const markdown = readmeFile !== null
|
|
70
|
+
? readFileSync(expandHomePath(readmeFile), 'utf8')
|
|
71
|
+
: readmeText;
|
|
72
|
+
if (markdown !== null && markdown.length > MAX_README_LENGTH) {
|
|
73
|
+
throw new Error(`README markdown is too large. Maximum size is ${MAX_README_LENGTH} characters.`);
|
|
74
|
+
}
|
|
75
|
+
return markdown;
|
|
76
|
+
}
|
|
62
77
|
function createCodeVerifier() {
|
|
63
78
|
return randomBytes(32).toString('base64url');
|
|
64
79
|
}
|
|
@@ -480,6 +495,63 @@ function printTaskStatusSummary(data) {
|
|
|
480
495
|
});
|
|
481
496
|
}
|
|
482
497
|
}
|
|
498
|
+
function compactTerminalText(value, maxLength = 160) {
|
|
499
|
+
const compact = sanitizeTerminalText(value).replace(/\s+/g, ' ').trim();
|
|
500
|
+
if (compact.length <= maxLength) {
|
|
501
|
+
return compact;
|
|
502
|
+
}
|
|
503
|
+
return `${compact.slice(0, Math.max(0, maxLength - 1)).trim()}…`;
|
|
504
|
+
}
|
|
505
|
+
function printIndentedBody(value, indent) {
|
|
506
|
+
const lines = sanitizeTerminalText(value || '').split(/\r?\n/);
|
|
507
|
+
for (const line of lines) {
|
|
508
|
+
printLine(`${indent}${line}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function formatPromptCommentAnchor(anchor) {
|
|
512
|
+
if (!anchor) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
const lineLabel = anchor.startLine && anchor.endLine
|
|
516
|
+
? anchor.startLine === anchor.endLine
|
|
517
|
+
? `line ${anchor.startLine}`
|
|
518
|
+
: `lines ${anchor.startLine}-${anchor.endLine}`
|
|
519
|
+
: null;
|
|
520
|
+
const selectedText = anchor.text ? `"${compactTerminalText(anchor.text, 180)}"` : null;
|
|
521
|
+
if (lineLabel && selectedText) {
|
|
522
|
+
return `${lineLabel}: ${selectedText}`;
|
|
523
|
+
}
|
|
524
|
+
return lineLabel || selectedText;
|
|
525
|
+
}
|
|
526
|
+
function printPromptComments(data) {
|
|
527
|
+
const versionLabel = data.version.versionLabel || (data.version.versionNumber ? `v${data.version.versionNumber}` : data.version.id);
|
|
528
|
+
printLine(`Prompt: ${sanitizeTerminalText(data.prompt.name)} (${sanitizeTerminalText(data.prompt.id)})`);
|
|
529
|
+
printLine(`Version: ${sanitizeTerminalText(String(versionLabel))} (${sanitizeTerminalText(data.version.id)})`);
|
|
530
|
+
printLine(`Comments: ${data.summary.threadCount} thread${data.summary.threadCount === 1 ? '' : 's'} ` +
|
|
531
|
+
`(${data.summary.openThreadCount} open, ${data.summary.resolvedThreadCount} resolved, ${data.summary.replyCount} replies)`);
|
|
532
|
+
if (data.comments.length === 0) {
|
|
533
|
+
printLine('\nNo comments found for this prompt version.');
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
for (const [index, thread] of data.comments.entries()) {
|
|
537
|
+
const authorName = thread.author?.name || 'Unknown';
|
|
538
|
+
const anchor = formatPromptCommentAnchor(thread.anchor);
|
|
539
|
+
printLine('');
|
|
540
|
+
printLine(`${index + 1}. [${thread.status}] ${sanitizeTerminalText(authorName)} · ${sanitizeTerminalText(thread.createdAt)}`);
|
|
541
|
+
if (anchor) {
|
|
542
|
+
printLine(` Selection: ${anchor}`);
|
|
543
|
+
}
|
|
544
|
+
printIndentedBody(thread.body, ' ');
|
|
545
|
+
if (thread.replies.length > 0) {
|
|
546
|
+
printLine(` Replies (${thread.replies.length})`);
|
|
547
|
+
for (const reply of thread.replies) {
|
|
548
|
+
const replyAuthor = reply.author?.name || 'Unknown';
|
|
549
|
+
printLine(` - ${sanitizeTerminalText(replyAuthor)} · ${sanitizeTerminalText(reply.createdAt)}`);
|
|
550
|
+
printIndentedBody(reply.body, ' ');
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
483
555
|
const DEFAULT_AUTH_CALLBACK_PORT = 43123;
|
|
484
556
|
function resolveAuthCallbackPort() {
|
|
485
557
|
const envPort = process.env.ORIZU_AUTH_PORT;
|
|
@@ -788,6 +860,33 @@ async function listJudges() {
|
|
|
788
860
|
const data = await parseJsonResponse(response, 'Judges list');
|
|
789
861
|
printPromptSummaries(data.judges, 'No judges found.');
|
|
790
862
|
}
|
|
863
|
+
async function listPromptComments() {
|
|
864
|
+
const promptRef = getPositionalArg(2);
|
|
865
|
+
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
866
|
+
const label = getArg('--label');
|
|
867
|
+
const version = getArg('--version');
|
|
868
|
+
if (!promptRef) {
|
|
869
|
+
throw new Error('Usage: orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <version-id>] [--json]');
|
|
870
|
+
}
|
|
871
|
+
if (label && version) {
|
|
872
|
+
throw new Error('Use either --label or --version, not both');
|
|
873
|
+
}
|
|
874
|
+
const params = new URLSearchParams({ project });
|
|
875
|
+
if (label)
|
|
876
|
+
params.set('label', label);
|
|
877
|
+
if (version)
|
|
878
|
+
params.set('version', version);
|
|
879
|
+
const response = await authedFetch(`/api/cli/prompts/${encodeURIComponent(promptRef)}/comments?${params.toString()}`);
|
|
880
|
+
if (!response.ok) {
|
|
881
|
+
throw new Error(`Failed to fetch prompt comments: ${await response.text()}`);
|
|
882
|
+
}
|
|
883
|
+
const data = await parseJsonResponse(response, 'Prompt comments');
|
|
884
|
+
if (hasJsonFlag()) {
|
|
885
|
+
printJson(data);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
printPromptComments(data);
|
|
889
|
+
}
|
|
791
890
|
async function listScorers() {
|
|
792
891
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
793
892
|
const response = await authedFetch(`/api/cli/scorers?project=${encodeURIComponent(project)}`);
|
|
@@ -1160,14 +1259,19 @@ async function createDatasetVersion() {
|
|
|
1160
1259
|
const datasetId = getPositionalArg(3) || getArg('--dataset');
|
|
1161
1260
|
const versionLabel = getArg('--label') || getArg('--version-label') || null;
|
|
1162
1261
|
const project = getArg('--project');
|
|
1262
|
+
const usage = 'Usage: orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--readme-file <README.md> | --readme-text <markdown>] [--json]';
|
|
1163
1263
|
if (!datasetId) {
|
|
1164
|
-
throw new Error(
|
|
1264
|
+
throw new Error(usage);
|
|
1165
1265
|
}
|
|
1266
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
1166
1267
|
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project);
|
|
1167
1268
|
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/versions`, {
|
|
1168
1269
|
method: 'POST',
|
|
1169
1270
|
headers: { 'Content-Type': 'application/json' },
|
|
1170
|
-
body: JSON.stringify({
|
|
1271
|
+
body: JSON.stringify({
|
|
1272
|
+
versionLabel,
|
|
1273
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
1274
|
+
}),
|
|
1171
1275
|
});
|
|
1172
1276
|
if (!response.ok) {
|
|
1173
1277
|
throw new Error(`Failed to create dataset version: ${await response.text()}`);
|
|
@@ -1329,6 +1433,7 @@ function normalizeScoreResultsInput(sourcePath, raw) {
|
|
|
1329
1433
|
throw new Error(`Invalid score results JSON '${sourcePath}': ${message}`);
|
|
1330
1434
|
}
|
|
1331
1435
|
}
|
|
1436
|
+
const OPTIMIZATION_REPORT_MAX_BYTES = 2 * 1024 * 1024;
|
|
1332
1437
|
function parseOptionalNumberFlag(name) {
|
|
1333
1438
|
const value = getArg(name);
|
|
1334
1439
|
if (!value) {
|
|
@@ -1343,6 +1448,40 @@ function parseOptionalNumberFlag(name) {
|
|
|
1343
1448
|
function hasObjectKeys(value) {
|
|
1344
1449
|
return Object.keys(value).length > 0;
|
|
1345
1450
|
}
|
|
1451
|
+
function readOptimizationReportInput() {
|
|
1452
|
+
const report = getArg('--report');
|
|
1453
|
+
const reportFile = getArg('--report-file');
|
|
1454
|
+
if (report && reportFile) {
|
|
1455
|
+
throw new Error('Use either --report or --report-file, not both');
|
|
1456
|
+
}
|
|
1457
|
+
if (!report && !reportFile) {
|
|
1458
|
+
return null;
|
|
1459
|
+
}
|
|
1460
|
+
let markdown;
|
|
1461
|
+
let sourceName;
|
|
1462
|
+
if (reportFile) {
|
|
1463
|
+
const expandedPath = expandHomePath(reportFile);
|
|
1464
|
+
markdown = readSourceFile(reportFile);
|
|
1465
|
+
sourceName = basename(expandedPath);
|
|
1466
|
+
}
|
|
1467
|
+
else if (report?.startsWith('@')) {
|
|
1468
|
+
const path = report.slice(1);
|
|
1469
|
+
const expandedPath = expandHomePath(path);
|
|
1470
|
+
markdown = readSourceFile(path);
|
|
1471
|
+
sourceName = basename(expandedPath);
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
markdown = report || '';
|
|
1475
|
+
sourceName = 'inline';
|
|
1476
|
+
}
|
|
1477
|
+
if (!markdown.trim()) {
|
|
1478
|
+
throw new Error('Optimization report markdown must not be blank');
|
|
1479
|
+
}
|
|
1480
|
+
if (Buffer.byteLength(markdown, 'utf8') > OPTIMIZATION_REPORT_MAX_BYTES) {
|
|
1481
|
+
throw new Error(`Optimization report exceeds ${OPTIMIZATION_REPORT_MAX_BYTES} bytes`);
|
|
1482
|
+
}
|
|
1483
|
+
return { markdown, sourceName };
|
|
1484
|
+
}
|
|
1346
1485
|
async function startOptimizationRun() {
|
|
1347
1486
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
1348
1487
|
const optimizerVersionId = getArg('--optimizer-version');
|
|
@@ -1419,6 +1558,10 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1419
1558
|
};
|
|
1420
1559
|
const metadata = readJsonObjectArg(getArg('--metadata'), 'optimization metadata');
|
|
1421
1560
|
const reason = getArg('--reason');
|
|
1561
|
+
const report = readOptimizationReportInput();
|
|
1562
|
+
if (report && (action === 'pause' || action === 'resume')) {
|
|
1563
|
+
throw new Error(`orizu optimizations ${action} does not accept optimization reports`);
|
|
1564
|
+
}
|
|
1422
1565
|
if ((action === 'pause' || action === 'cancel') && reason) {
|
|
1423
1566
|
metadata.reason = reason;
|
|
1424
1567
|
}
|
|
@@ -1429,6 +1572,10 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1429
1572
|
if (hasObjectKeys(metadata)) {
|
|
1430
1573
|
body.metadata = metadata;
|
|
1431
1574
|
}
|
|
1575
|
+
if (report) {
|
|
1576
|
+
body.reportMarkdown = report.markdown;
|
|
1577
|
+
body.reportSourceName = report.sourceName;
|
|
1578
|
+
}
|
|
1432
1579
|
if (action === 'finish') {
|
|
1433
1580
|
const bestScore = parseOptionalNumberFlag('--best-score');
|
|
1434
1581
|
if (bestScore !== undefined) {
|
|
@@ -1473,6 +1620,27 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1473
1620
|
printLine(`Cancelled optimization run ${id}`);
|
|
1474
1621
|
}
|
|
1475
1622
|
}
|
|
1623
|
+
async function exportOptimizationRun() {
|
|
1624
|
+
const runId = getPositionalArg(2) || getArg('--run-id');
|
|
1625
|
+
const outPathArg = getArg('--out');
|
|
1626
|
+
if (!runId) {
|
|
1627
|
+
throw new Error('Usage: orizu optimizations export <run-id> [--out <path>] [--json]');
|
|
1628
|
+
}
|
|
1629
|
+
const response = await authedFetch(`/api/cli/optimization-runs/${encodeURIComponent(runId)}/export`);
|
|
1630
|
+
if (!response.ok) {
|
|
1631
|
+
throw new Error(`Failed to export optimization run: ${await response.text()}`);
|
|
1632
|
+
}
|
|
1633
|
+
const data = await parseJsonResponse(response, 'Optimization export');
|
|
1634
|
+
if (hasJsonFlag() && !outPathArg) {
|
|
1635
|
+
printJson(data);
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
const filename = outPathArg
|
|
1639
|
+
? expandHomePath(outPathArg)
|
|
1640
|
+
: `${runId}.optimization.json`;
|
|
1641
|
+
writeTextFileEnsuringDir(filename, `${JSON.stringify(data, null, 2)}\n`);
|
|
1642
|
+
printLine(`Saved optimization export to ${sanitizeTerminalText(filename)}`);
|
|
1643
|
+
}
|
|
1476
1644
|
function removeFlagWithValue(args, flag) {
|
|
1477
1645
|
const filtered = [];
|
|
1478
1646
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -2080,6 +2248,14 @@ async function updateTaskStatus(targetStatus) {
|
|
|
2080
2248
|
const action = targetStatus === 'paused' ? 'Paused' : 'Unpaused';
|
|
2081
2249
|
printLine(`${action} task ${sanitizeTerminalText(data.task.id)} [${sanitizeTerminalText(data.task.status)}]`);
|
|
2082
2250
|
}
|
|
2251
|
+
function defaultAppExportFilename(appName, versionNum) {
|
|
2252
|
+
const safeName = appName
|
|
2253
|
+
.trim()
|
|
2254
|
+
.replace(/[^A-Za-z0-9._-]+/g, '_')
|
|
2255
|
+
.replace(/^_+|_+$/g, '')
|
|
2256
|
+
.slice(0, 100);
|
|
2257
|
+
return `${safeName || 'app'}.v${versionNum}.tsx`;
|
|
2258
|
+
}
|
|
2083
2259
|
async function appDetail() {
|
|
2084
2260
|
const appId = getArg('--app');
|
|
2085
2261
|
const project = getArg('--project');
|
|
@@ -2118,6 +2294,40 @@ async function appDetail() {
|
|
|
2118
2294
|
printLine(` Created: ${sanitizeTerminalText(detail.createdAt)}`);
|
|
2119
2295
|
printLine(` Updated: ${sanitizeTerminalText(detail.updatedAt)}`);
|
|
2120
2296
|
}
|
|
2297
|
+
async function exportAppSource() {
|
|
2298
|
+
const project = getArg('--project');
|
|
2299
|
+
const versionArg = getArg('--version');
|
|
2300
|
+
const outPathArg = getArg('--out');
|
|
2301
|
+
let appId = getArg('--app');
|
|
2302
|
+
const versionNum = versionArg && /^[1-9]\d*$/.test(versionArg)
|
|
2303
|
+
? Number.parseInt(versionArg, 10)
|
|
2304
|
+
: undefined;
|
|
2305
|
+
if (versionArg && versionNum === undefined) {
|
|
2306
|
+
throw new Error('--version must be a positive integer');
|
|
2307
|
+
}
|
|
2308
|
+
if (!appId) {
|
|
2309
|
+
const selected = await selectAppIdInteractively(project);
|
|
2310
|
+
appId = selected.appId;
|
|
2311
|
+
}
|
|
2312
|
+
const query = new URLSearchParams();
|
|
2313
|
+
if (project) {
|
|
2314
|
+
query.set('project', project);
|
|
2315
|
+
}
|
|
2316
|
+
if (versionNum) {
|
|
2317
|
+
query.set('version', String(versionNum));
|
|
2318
|
+
}
|
|
2319
|
+
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
2320
|
+
const response = await authedFetch(`/api/cli/apps/${encodeURIComponent(appId)}/export${suffix}`);
|
|
2321
|
+
if (!response.ok) {
|
|
2322
|
+
throw new Error(`Failed to export app (${response.status}): ${await response.text()}`);
|
|
2323
|
+
}
|
|
2324
|
+
const data = await parseJsonResponse(response, 'App export');
|
|
2325
|
+
const filename = outPathArg
|
|
2326
|
+
? expandHomePath(outPathArg)
|
|
2327
|
+
: defaultAppExportFilename(data.app.name, data.version.versionNum);
|
|
2328
|
+
writeTextFileEnsuringDir(filename, data.version.code);
|
|
2329
|
+
printLine(`Saved app ${sanitizeTerminalText(data.app.name)} v${data.version.versionNum} source to ${sanitizeTerminalText(filename)}`);
|
|
2330
|
+
}
|
|
2121
2331
|
async function listTeamMembers() {
|
|
2122
2332
|
const teamSlug = await resolveTeamSlug(getArg('--team'));
|
|
2123
2333
|
printTeamMembers(await fetchTeamMembers(teamSlug));
|
|
@@ -2182,7 +2392,7 @@ async function changeTeamMemberRole() {
|
|
|
2182
2392
|
}
|
|
2183
2393
|
printLine(`Updated ${sanitizeTerminalText(member.email)} role to ${sanitizeTerminalText(role)}`);
|
|
2184
2394
|
}
|
|
2185
|
-
async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
2395
|
+
async function createDatasetFromRows(project, name, sourceType, rows, readmeMarkdown = null) {
|
|
2186
2396
|
const response = await authedFetch('/api/cli/datasets/upload', {
|
|
2187
2397
|
method: 'POST',
|
|
2188
2398
|
headers: {
|
|
@@ -2193,6 +2403,7 @@ async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
|
2193
2403
|
name,
|
|
2194
2404
|
rows,
|
|
2195
2405
|
sourceType,
|
|
2406
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
2196
2407
|
}),
|
|
2197
2408
|
});
|
|
2198
2409
|
if (!response.ok) {
|
|
@@ -2201,7 +2412,7 @@ async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
|
2201
2412
|
}
|
|
2202
2413
|
return parseJsonResponse(response, 'Dataset upload');
|
|
2203
2414
|
}
|
|
2204
|
-
async function uploadJsonlDatasetInChunks(file, project, datasetName) {
|
|
2415
|
+
async function uploadJsonlDatasetInChunks(file, project, datasetName, readmeMarkdown = null) {
|
|
2205
2416
|
let dataset = null;
|
|
2206
2417
|
let totalUploaded = 0;
|
|
2207
2418
|
let chunkIndex = 0;
|
|
@@ -2229,7 +2440,7 @@ async function uploadJsonlDatasetInChunks(file, project, datasetName) {
|
|
|
2229
2440
|
printLine(`Uploading chunk ${chunkIndex} (${chunk.length} rows)...`);
|
|
2230
2441
|
try {
|
|
2231
2442
|
if (!dataset) {
|
|
2232
|
-
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk);
|
|
2443
|
+
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk, readmeMarkdown);
|
|
2233
2444
|
dataset = data.dataset;
|
|
2234
2445
|
totalUploaded = data.dataset.rowCount;
|
|
2235
2446
|
continue;
|
|
@@ -2264,18 +2475,20 @@ async function uploadDataset() {
|
|
|
2264
2475
|
const projectArg = getArg('--project');
|
|
2265
2476
|
const fileArg = getArg('--file');
|
|
2266
2477
|
const name = getArg('--name');
|
|
2478
|
+
const usage = 'Usage: orizu datasets upload --file <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>]';
|
|
2267
2479
|
if (!fileArg) {
|
|
2268
|
-
throw new Error(
|
|
2480
|
+
throw new Error(usage);
|
|
2269
2481
|
}
|
|
2482
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2270
2483
|
const file = expandHomePath(fileArg);
|
|
2271
2484
|
const project = await resolveProjectSlug(projectArg);
|
|
2272
2485
|
const datasetName = name || basename(file);
|
|
2273
2486
|
if (extname(file).toLowerCase() === '.jsonl') {
|
|
2274
|
-
await uploadJsonlDatasetInChunks(file, project, datasetName);
|
|
2487
|
+
await uploadJsonlDatasetInChunks(file, project, datasetName, readmeMarkdown);
|
|
2275
2488
|
return;
|
|
2276
2489
|
}
|
|
2277
2490
|
const { rows, sourceType } = parseDatasetFile(file);
|
|
2278
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows);
|
|
2491
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown);
|
|
2279
2492
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`);
|
|
2280
2493
|
if (data.dataset.url) {
|
|
2281
2494
|
printLine(`View dataset: ${formatTerminalLink(data.dataset.url)}`);
|
|
@@ -2285,20 +2498,26 @@ async function pushDataset() {
|
|
|
2285
2498
|
const projectArg = getArg('--project');
|
|
2286
2499
|
const fileArg = getPositionalArg(2) || getArg('--file');
|
|
2287
2500
|
const name = getArg('--name');
|
|
2501
|
+
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]';
|
|
2288
2502
|
if (!fileArg) {
|
|
2289
|
-
throw new Error(
|
|
2503
|
+
throw new Error(usage);
|
|
2290
2504
|
}
|
|
2505
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2291
2506
|
const file = expandHomePath(fileArg);
|
|
2292
2507
|
const project = await resolveProjectSlug(projectArg);
|
|
2293
2508
|
const datasetName = name || basename(file);
|
|
2294
2509
|
const { rows, sourceType } = parseDatasetFile(file);
|
|
2295
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows);
|
|
2510
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown);
|
|
2296
2511
|
if (hasJsonFlag()) {
|
|
2297
|
-
|
|
2512
|
+
const payload = {
|
|
2298
2513
|
dataset_id: data.dataset.id,
|
|
2299
2514
|
name: data.dataset.name,
|
|
2300
2515
|
row_count: data.dataset.rowCount,
|
|
2301
|
-
}
|
|
2516
|
+
};
|
|
2517
|
+
if (data.readmeVersion?.id) {
|
|
2518
|
+
payload.readme_version_id = data.readmeVersion.id;
|
|
2519
|
+
}
|
|
2520
|
+
printJson(payload);
|
|
2302
2521
|
return;
|
|
2303
2522
|
}
|
|
2304
2523
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`);
|
|
@@ -2306,6 +2525,38 @@ async function pushDataset() {
|
|
|
2306
2525
|
printLine(`View dataset: ${formatTerminalLink(data.dataset.url)}`);
|
|
2307
2526
|
}
|
|
2308
2527
|
}
|
|
2528
|
+
async function setDatasetReadme() {
|
|
2529
|
+
const datasetId = getPositionalArg(3) || getArg('--dataset');
|
|
2530
|
+
const project = getArg('--project');
|
|
2531
|
+
const usage = 'Usage: orizu datasets readme set <datasetId|dataset-name> [--project <team/project>] (--readme-file <README.md> | --readme-text <markdown>) [--json]';
|
|
2532
|
+
if (!datasetId) {
|
|
2533
|
+
throw new Error(usage);
|
|
2534
|
+
}
|
|
2535
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2536
|
+
if (readmeMarkdown === null) {
|
|
2537
|
+
throw new Error(usage);
|
|
2538
|
+
}
|
|
2539
|
+
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project);
|
|
2540
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/readme`, {
|
|
2541
|
+
method: 'POST',
|
|
2542
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2543
|
+
body: JSON.stringify({ markdown: readmeMarkdown }),
|
|
2544
|
+
});
|
|
2545
|
+
if (!response.ok) {
|
|
2546
|
+
throw new Error(`Failed to save dataset README: ${await response.text()}`);
|
|
2547
|
+
}
|
|
2548
|
+
const data = await parseJsonResponse(response, 'Dataset README save');
|
|
2549
|
+
if (hasJsonFlag()) {
|
|
2550
|
+
printJson({
|
|
2551
|
+
dataset_id: data.version.dataset_id,
|
|
2552
|
+
readme_version_id: data.version.id,
|
|
2553
|
+
readme_version_num: data.version.version_num,
|
|
2554
|
+
});
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
printLine(`Saved README v${data.version.version_num} for dataset ` +
|
|
2558
|
+
`${sanitizeTerminalText(data.version.dataset_id)} (${sanitizeTerminalText(data.version.id)}).`);
|
|
2559
|
+
}
|
|
2309
2560
|
function getDatasetReferenceInput() {
|
|
2310
2561
|
const fromFlag = getArg('--dataset');
|
|
2311
2562
|
if (fromFlag) {
|
|
@@ -2937,6 +3188,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
2937
3188
|
await listPrompts();
|
|
2938
3189
|
return;
|
|
2939
3190
|
}
|
|
3191
|
+
if (command === 'prompts' && subcommand === 'comments') {
|
|
3192
|
+
await listPromptComments();
|
|
3193
|
+
return;
|
|
3194
|
+
}
|
|
2940
3195
|
if (command === 'prompts' && subcommand === 'pull') {
|
|
2941
3196
|
await pullPromptArtifact('prompt');
|
|
2942
3197
|
return;
|
|
@@ -3013,6 +3268,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3013
3268
|
await runGepaOptimization();
|
|
3014
3269
|
return;
|
|
3015
3270
|
}
|
|
3271
|
+
if (command === 'optimizations' && subcommand === 'export') {
|
|
3272
|
+
await exportOptimizationRun();
|
|
3273
|
+
return;
|
|
3274
|
+
}
|
|
3016
3275
|
if (command === 'optimizations' &&
|
|
3017
3276
|
(subcommand === 'pause' ||
|
|
3018
3277
|
subcommand === 'resume' ||
|
|
@@ -3042,6 +3301,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3042
3301
|
await appDetail();
|
|
3043
3302
|
return;
|
|
3044
3303
|
}
|
|
3304
|
+
if (command === 'apps' && subcommand === 'export') {
|
|
3305
|
+
await exportAppSource();
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3045
3308
|
if (command === 'tasks' && subcommand === 'list') {
|
|
3046
3309
|
await listTasks();
|
|
3047
3310
|
return;
|
|
@@ -3083,6 +3346,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3083
3346
|
return;
|
|
3084
3347
|
}
|
|
3085
3348
|
const datasetsAction = cliArgs[2];
|
|
3349
|
+
if (command === 'datasets' && subcommand === 'readme' && datasetsAction === 'set') {
|
|
3350
|
+
await setDatasetReadme();
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3086
3353
|
if (command === 'datasets' && subcommand === 'versions' && datasetsAction === 'create') {
|
|
3087
3354
|
await createDatasetVersion();
|
|
3088
3355
|
return;
|