orizu 0.3.1 → 0.3.3
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 +11 -0
- package/dist/index.js +171 -11
- package/package.json +12 -2
- package/scripts/vendor-gepa-python.mjs +54 -0
- package/src/index.ts +203 -4
- package/vendor/orizu-gepa-python/manifest.json +9 -0
- package/vendor/orizu-gepa-python/pyproject.toml +16 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/__init__.py +19 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +206 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +360 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +123 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +1276 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +177 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -0
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>
|
package/dist/index.js
CHANGED
|
@@ -30,10 +30,10 @@ function printVersion() {
|
|
|
30
30
|
printLine(`orizu ${getCliVersion()}`);
|
|
31
31
|
}
|
|
32
32
|
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>]`);
|
|
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>]`);
|
|
34
34
|
}
|
|
35
35
|
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]`);
|
|
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]`);
|
|
37
37
|
}
|
|
38
38
|
let cliArgs = process.argv.slice(2);
|
|
39
39
|
function getArg(name) {
|
|
@@ -480,6 +480,63 @@ function printTaskStatusSummary(data) {
|
|
|
480
480
|
});
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
|
+
function compactTerminalText(value, maxLength = 160) {
|
|
484
|
+
const compact = sanitizeTerminalText(value).replace(/\s+/g, ' ').trim();
|
|
485
|
+
if (compact.length <= maxLength) {
|
|
486
|
+
return compact;
|
|
487
|
+
}
|
|
488
|
+
return `${compact.slice(0, Math.max(0, maxLength - 1)).trim()}…`;
|
|
489
|
+
}
|
|
490
|
+
function printIndentedBody(value, indent) {
|
|
491
|
+
const lines = sanitizeTerminalText(value || '').split(/\r?\n/);
|
|
492
|
+
for (const line of lines) {
|
|
493
|
+
printLine(`${indent}${line}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function formatPromptCommentAnchor(anchor) {
|
|
497
|
+
if (!anchor) {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
const lineLabel = anchor.startLine && anchor.endLine
|
|
501
|
+
? anchor.startLine === anchor.endLine
|
|
502
|
+
? `line ${anchor.startLine}`
|
|
503
|
+
: `lines ${anchor.startLine}-${anchor.endLine}`
|
|
504
|
+
: null;
|
|
505
|
+
const selectedText = anchor.text ? `"${compactTerminalText(anchor.text, 180)}"` : null;
|
|
506
|
+
if (lineLabel && selectedText) {
|
|
507
|
+
return `${lineLabel}: ${selectedText}`;
|
|
508
|
+
}
|
|
509
|
+
return lineLabel || selectedText;
|
|
510
|
+
}
|
|
511
|
+
function printPromptComments(data) {
|
|
512
|
+
const versionLabel = data.version.versionLabel || (data.version.versionNumber ? `v${data.version.versionNumber}` : data.version.id);
|
|
513
|
+
printLine(`Prompt: ${sanitizeTerminalText(data.prompt.name)} (${sanitizeTerminalText(data.prompt.id)})`);
|
|
514
|
+
printLine(`Version: ${sanitizeTerminalText(String(versionLabel))} (${sanitizeTerminalText(data.version.id)})`);
|
|
515
|
+
printLine(`Comments: ${data.summary.threadCount} thread${data.summary.threadCount === 1 ? '' : 's'} ` +
|
|
516
|
+
`(${data.summary.openThreadCount} open, ${data.summary.resolvedThreadCount} resolved, ${data.summary.replyCount} replies)`);
|
|
517
|
+
if (data.comments.length === 0) {
|
|
518
|
+
printLine('\nNo comments found for this prompt version.');
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
for (const [index, thread] of data.comments.entries()) {
|
|
522
|
+
const authorName = thread.author?.name || 'Unknown';
|
|
523
|
+
const anchor = formatPromptCommentAnchor(thread.anchor);
|
|
524
|
+
printLine('');
|
|
525
|
+
printLine(`${index + 1}. [${thread.status}] ${sanitizeTerminalText(authorName)} · ${sanitizeTerminalText(thread.createdAt)}`);
|
|
526
|
+
if (anchor) {
|
|
527
|
+
printLine(` Selection: ${anchor}`);
|
|
528
|
+
}
|
|
529
|
+
printIndentedBody(thread.body, ' ');
|
|
530
|
+
if (thread.replies.length > 0) {
|
|
531
|
+
printLine(` Replies (${thread.replies.length})`);
|
|
532
|
+
for (const reply of thread.replies) {
|
|
533
|
+
const replyAuthor = reply.author?.name || 'Unknown';
|
|
534
|
+
printLine(` - ${sanitizeTerminalText(replyAuthor)} · ${sanitizeTerminalText(reply.createdAt)}`);
|
|
535
|
+
printIndentedBody(reply.body, ' ');
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
483
540
|
const DEFAULT_AUTH_CALLBACK_PORT = 43123;
|
|
484
541
|
function resolveAuthCallbackPort() {
|
|
485
542
|
const envPort = process.env.ORIZU_AUTH_PORT;
|
|
@@ -788,6 +845,33 @@ async function listJudges() {
|
|
|
788
845
|
const data = await parseJsonResponse(response, 'Judges list');
|
|
789
846
|
printPromptSummaries(data.judges, 'No judges found.');
|
|
790
847
|
}
|
|
848
|
+
async function listPromptComments() {
|
|
849
|
+
const promptRef = getPositionalArg(2);
|
|
850
|
+
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
851
|
+
const label = getArg('--label');
|
|
852
|
+
const version = getArg('--version');
|
|
853
|
+
if (!promptRef) {
|
|
854
|
+
throw new Error('Usage: orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <version-id>] [--json]');
|
|
855
|
+
}
|
|
856
|
+
if (label && version) {
|
|
857
|
+
throw new Error('Use either --label or --version, not both');
|
|
858
|
+
}
|
|
859
|
+
const params = new URLSearchParams({ project });
|
|
860
|
+
if (label)
|
|
861
|
+
params.set('label', label);
|
|
862
|
+
if (version)
|
|
863
|
+
params.set('version', version);
|
|
864
|
+
const response = await authedFetch(`/api/cli/prompts/${encodeURIComponent(promptRef)}/comments?${params.toString()}`);
|
|
865
|
+
if (!response.ok) {
|
|
866
|
+
throw new Error(`Failed to fetch prompt comments: ${await response.text()}`);
|
|
867
|
+
}
|
|
868
|
+
const data = await parseJsonResponse(response, 'Prompt comments');
|
|
869
|
+
if (hasJsonFlag()) {
|
|
870
|
+
printJson(data);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
printPromptComments(data);
|
|
874
|
+
}
|
|
791
875
|
async function listScorers() {
|
|
792
876
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
793
877
|
const response = await authedFetch(`/api/cli/scorers?project=${encodeURIComponent(project)}`);
|
|
@@ -1473,6 +1557,27 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1473
1557
|
printLine(`Cancelled optimization run ${id}`);
|
|
1474
1558
|
}
|
|
1475
1559
|
}
|
|
1560
|
+
async function exportOptimizationRun() {
|
|
1561
|
+
const runId = getPositionalArg(2) || getArg('--run-id');
|
|
1562
|
+
const outPathArg = getArg('--out');
|
|
1563
|
+
if (!runId) {
|
|
1564
|
+
throw new Error('Usage: orizu optimizations export <run-id> [--out <path>] [--json]');
|
|
1565
|
+
}
|
|
1566
|
+
const response = await authedFetch(`/api/cli/optimization-runs/${encodeURIComponent(runId)}/export`);
|
|
1567
|
+
if (!response.ok) {
|
|
1568
|
+
throw new Error(`Failed to export optimization run: ${await response.text()}`);
|
|
1569
|
+
}
|
|
1570
|
+
const data = await parseJsonResponse(response, 'Optimization export');
|
|
1571
|
+
if (hasJsonFlag() && !outPathArg) {
|
|
1572
|
+
printJson(data);
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
const filename = outPathArg
|
|
1576
|
+
? expandHomePath(outPathArg)
|
|
1577
|
+
: `${runId}.optimization.json`;
|
|
1578
|
+
writeTextFileEnsuringDir(filename, `${JSON.stringify(data, null, 2)}\n`);
|
|
1579
|
+
printLine(`Saved optimization export to ${sanitizeTerminalText(filename)}`);
|
|
1580
|
+
}
|
|
1476
1581
|
function removeFlagWithValue(args, flag) {
|
|
1477
1582
|
const filtered = [];
|
|
1478
1583
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -1485,8 +1590,11 @@ function removeFlagWithValue(args, flag) {
|
|
|
1485
1590
|
return filtered;
|
|
1486
1591
|
}
|
|
1487
1592
|
function bundledOrizuGepaPythonPath() {
|
|
1488
|
-
const
|
|
1489
|
-
|
|
1593
|
+
const candidates = [
|
|
1594
|
+
fileURLToPath(new URL('../vendor/orizu-gepa-python/src', import.meta.url)),
|
|
1595
|
+
fileURLToPath(new URL('../../orizu-gepa-python/src', import.meta.url)),
|
|
1596
|
+
];
|
|
1597
|
+
return candidates.find(candidate => existsSync(candidate)) ?? null;
|
|
1490
1598
|
}
|
|
1491
1599
|
async function runGepaOptimization() {
|
|
1492
1600
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
@@ -2664,6 +2772,47 @@ function readRunnerManifest(runnerDir) {
|
|
|
2664
2772
|
supports_body_kind: supportedBodyKinds,
|
|
2665
2773
|
};
|
|
2666
2774
|
}
|
|
2775
|
+
const RUNNER_TIMEOUT_MS = 120_000;
|
|
2776
|
+
const RUNNER_OUTPUT_MAX_BYTES = 2 * 1024 * 1024;
|
|
2777
|
+
const RUNNER_ARTIFACT_MAX_BYTES = 25 * 1024 * 1024;
|
|
2778
|
+
const RUNNER_ENV_ALLOWLIST = new Set([
|
|
2779
|
+
'PATH',
|
|
2780
|
+
'SystemRoot',
|
|
2781
|
+
'WINDIR',
|
|
2782
|
+
'HOME',
|
|
2783
|
+
'TMPDIR',
|
|
2784
|
+
'TEMP',
|
|
2785
|
+
'TMP',
|
|
2786
|
+
'LANG',
|
|
2787
|
+
'LC_ALL',
|
|
2788
|
+
'PYTHONPATH',
|
|
2789
|
+
'NODE_PATH',
|
|
2790
|
+
'ANTHROPIC_API_KEY',
|
|
2791
|
+
'OPENAI_API_KEY',
|
|
2792
|
+
'GEMINI_API_KEY',
|
|
2793
|
+
'GOOGLE_API_KEY',
|
|
2794
|
+
]);
|
|
2795
|
+
function runnerSubprocessEnv(inputPath, outputPath) {
|
|
2796
|
+
const env = {};
|
|
2797
|
+
for (const key of RUNNER_ENV_ALLOWLIST) {
|
|
2798
|
+
const value = process.env[key];
|
|
2799
|
+
if (value !== undefined) {
|
|
2800
|
+
env[key] = value;
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
env.ORIZU_RUNNER_INPUT_PATH = inputPath;
|
|
2804
|
+
env.ORIZU_RUNNER_OUTPUT_PATH = outputPath;
|
|
2805
|
+
return env;
|
|
2806
|
+
}
|
|
2807
|
+
function boundedRunnerOutput(value) {
|
|
2808
|
+
if (!value)
|
|
2809
|
+
return '';
|
|
2810
|
+
const buffer = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
2811
|
+
if (buffer.byteLength <= RUNNER_OUTPUT_MAX_BYTES) {
|
|
2812
|
+
return buffer.toString('utf8');
|
|
2813
|
+
}
|
|
2814
|
+
return `${buffer.subarray(0, RUNNER_OUTPUT_MAX_BYTES).toString('utf8')}\n[truncated]`;
|
|
2815
|
+
}
|
|
2667
2816
|
async function materializeRunnerVersion(runnerVersionId) {
|
|
2668
2817
|
const response = await authedFetch(`/api/cli/runner-versions/${encodeURIComponent(runnerVersionId)}/download`);
|
|
2669
2818
|
if (!response.ok) {
|
|
@@ -2672,7 +2821,12 @@ async function materializeRunnerVersion(runnerVersionId) {
|
|
|
2672
2821
|
const tempDir = mkdtempSync(join(tmpdir(), 'orizu-runner-version-'));
|
|
2673
2822
|
const zipPath = join(tempDir, 'runner.zip');
|
|
2674
2823
|
const runnerDir = join(tempDir, 'runner');
|
|
2675
|
-
|
|
2824
|
+
const zipBytes = new Uint8Array(await response.arrayBuffer());
|
|
2825
|
+
if (zipBytes.byteLength > RUNNER_ARTIFACT_MAX_BYTES) {
|
|
2826
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
2827
|
+
throw new Error(`Runner artifact exceeds ${RUNNER_ARTIFACT_MAX_BYTES} bytes`);
|
|
2828
|
+
}
|
|
2829
|
+
writeFileSync(zipPath, zipBytes);
|
|
2676
2830
|
const result = spawnSync('unzip', ['-q', zipPath, '-d', runnerDir], {
|
|
2677
2831
|
encoding: 'utf8',
|
|
2678
2832
|
});
|
|
@@ -2778,18 +2932,16 @@ async function runnersExec() {
|
|
|
2778
2932
|
}));
|
|
2779
2933
|
const result = spawnSync(manifest.command[0], manifest.command.slice(1), {
|
|
2780
2934
|
cwd: runnerDir,
|
|
2781
|
-
env:
|
|
2782
|
-
...process.env,
|
|
2783
|
-
ORIZU_RUNNER_INPUT_PATH: inputPath,
|
|
2784
|
-
ORIZU_RUNNER_OUTPUT_PATH: outputPath,
|
|
2785
|
-
},
|
|
2935
|
+
env: runnerSubprocessEnv(inputPath, outputPath),
|
|
2786
2936
|
encoding: 'utf8',
|
|
2937
|
+
maxBuffer: RUNNER_OUTPUT_MAX_BYTES,
|
|
2938
|
+
timeout: RUNNER_TIMEOUT_MS,
|
|
2787
2939
|
});
|
|
2788
2940
|
if (result.error) {
|
|
2789
2941
|
throw result.error;
|
|
2790
2942
|
}
|
|
2791
2943
|
if (result.status !== 0) {
|
|
2792
|
-
throw new Error(`Runner failed for row ${row.id} with exit code ${result.status}: ${sanitizeTerminalText(result.stderr || result.stdout
|
|
2944
|
+
throw new Error(`Runner failed for row ${row.id} with exit code ${result.status}: ${sanitizeTerminalText(boundedRunnerOutput(result.stderr || result.stdout))}`);
|
|
2793
2945
|
}
|
|
2794
2946
|
const runnerOutput = JSON.parse(readFileSync(outputPath, 'utf8'));
|
|
2795
2947
|
resultLines.push(JSON.stringify({
|
|
@@ -2890,6 +3042,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
2890
3042
|
await listPrompts();
|
|
2891
3043
|
return;
|
|
2892
3044
|
}
|
|
3045
|
+
if (command === 'prompts' && subcommand === 'comments') {
|
|
3046
|
+
await listPromptComments();
|
|
3047
|
+
return;
|
|
3048
|
+
}
|
|
2893
3049
|
if (command === 'prompts' && subcommand === 'pull') {
|
|
2894
3050
|
await pullPromptArtifact('prompt');
|
|
2895
3051
|
return;
|
|
@@ -2966,6 +3122,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
2966
3122
|
await runGepaOptimization();
|
|
2967
3123
|
return;
|
|
2968
3124
|
}
|
|
3125
|
+
if (command === 'optimizations' && subcommand === 'export') {
|
|
3126
|
+
await exportOptimizationRun();
|
|
3127
|
+
return;
|
|
3128
|
+
}
|
|
2969
3129
|
if (command === 'optimizations' &&
|
|
2970
3130
|
(subcommand === 'pause' ||
|
|
2971
3131
|
subcommand === 'resume' ||
|
package/package.json
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orizu",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"orizu": "dist/index.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"README.md",
|
|
11
|
+
"dist",
|
|
12
|
+
"scripts",
|
|
13
|
+
"src",
|
|
14
|
+
"tsconfig.json",
|
|
15
|
+
"vendor"
|
|
16
|
+
],
|
|
9
17
|
"scripts": {
|
|
10
|
-
"build": "tsc -p tsconfig.json"
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"prepack": "node scripts/vendor-gepa-python.mjs",
|
|
20
|
+
"postpack": "node scripts/vendor-gepa-python.mjs --clean"
|
|
11
21
|
}
|
|
12
22
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
const cliRoot = resolve(scriptDir, '..')
|
|
7
|
+
const repoRoot = resolve(cliRoot, '..', '..')
|
|
8
|
+
const sourceRoot = resolve(repoRoot, 'packages', 'orizu-gepa-python')
|
|
9
|
+
const vendorRoot = resolve(cliRoot, 'vendor', 'orizu-gepa-python')
|
|
10
|
+
const shouldClean = process.argv.includes('--clean')
|
|
11
|
+
const sourceSrc = resolve(sourceRoot, 'src')
|
|
12
|
+
const sourcePyproject = resolve(sourceRoot, 'pyproject.toml')
|
|
13
|
+
const sourceManifest = resolve(sourceRoot, 'manifest.json')
|
|
14
|
+
const vendoredSrc = resolve(vendorRoot, 'src')
|
|
15
|
+
const vendoredPyproject = resolve(vendorRoot, 'pyproject.toml')
|
|
16
|
+
const vendoredManifest = resolve(vendorRoot, 'manifest.json')
|
|
17
|
+
const hasSourcePackage = existsSync(sourceSrc)
|
|
18
|
+
&& existsSync(sourcePyproject)
|
|
19
|
+
&& existsSync(sourceManifest)
|
|
20
|
+
const hasVendoredPackage = existsSync(vendoredSrc)
|
|
21
|
+
&& existsSync(vendoredPyproject)
|
|
22
|
+
&& existsSync(vendoredManifest)
|
|
23
|
+
|
|
24
|
+
if (shouldClean) {
|
|
25
|
+
if (hasSourcePackage) {
|
|
26
|
+
rmSync(vendorRoot, { recursive: true, force: true })
|
|
27
|
+
}
|
|
28
|
+
process.exit(0)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!hasSourcePackage) {
|
|
32
|
+
if (hasVendoredPackage) {
|
|
33
|
+
process.exit(0)
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Unable to vendor orizu-gepa-python: source package not found at ${sourceRoot}`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
rmSync(vendorRoot, { recursive: true, force: true })
|
|
39
|
+
mkdirSync(vendorRoot, { recursive: true })
|
|
40
|
+
cpSync(sourcePyproject, resolve(vendorRoot, 'pyproject.toml'))
|
|
41
|
+
cpSync(sourceManifest, resolve(vendorRoot, 'manifest.json'))
|
|
42
|
+
cpSync(sourceSrc, resolve(vendorRoot, 'src'), {
|
|
43
|
+
recursive: true,
|
|
44
|
+
filter: sourcePath => {
|
|
45
|
+
const normalizedPath = sourcePath.replace(/\\/g, '/')
|
|
46
|
+
return !normalizedPath.endsWith('/__pycache__')
|
|
47
|
+
&& !normalizedPath.includes('/__pycache__/')
|
|
48
|
+
&& !normalizedPath.endsWith('.pyc')
|
|
49
|
+
&& !normalizedPath.endsWith('.pyo')
|
|
50
|
+
&& !normalizedPath.endsWith('/.pytest_cache')
|
|
51
|
+
&& !normalizedPath.includes('/.pytest_cache/')
|
|
52
|
+
&& !normalizedPath.endsWith('/.DS_Store')
|
|
53
|
+
},
|
|
54
|
+
})
|