orizu 0.5.21 → 0.5.23
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 +46 -15
- package/dist/cleanup.d.ts +6 -0
- package/dist/cleanup.d.ts.map +1 -0
- package/dist/cleanup.js +27 -0
- package/dist/gepa-engine-dispatch.d.ts.map +1 -1
- package/dist/gepa-engine-dispatch.js +20 -1
- package/dist/gepa-python-paths.d.ts.map +1 -1
- package/dist/gepa-python-paths.js +25 -6
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +37 -15
- package/dist/hosted-commands.d.ts +13 -0
- package/dist/hosted-commands.d.ts.map +1 -0
- package/dist/hosted-commands.js +10 -0
- package/dist/hosted-gepa-cli.d.ts +11 -0
- package/dist/hosted-gepa-cli.d.ts.map +1 -0
- package/dist/hosted-gepa-cli.js +110 -0
- package/dist/hosted-gepa-numeric-values.d.ts +3 -0
- package/dist/hosted-gepa-numeric-values.d.ts.map +1 -0
- package/dist/hosted-gepa-numeric-values.js +49 -0
- package/dist/hosted-provider-settings.d.ts +3 -0
- package/dist/hosted-provider-settings.d.ts.map +1 -0
- package/dist/hosted-provider-settings.js +69 -0
- package/dist/hosted-session-cli.d.ts.map +1 -1
- package/dist/hosted-session-cli.js +6 -1
- package/dist/http.d.ts +1 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -74
- package/dist/instruction-set-loader.d.ts.map +1 -1
- package/dist/instruction-set-loader.js +57 -5
- package/dist/instruction-sets-cli.d.ts +5 -2
- package/dist/instruction-sets-cli.d.ts.map +1 -1
- package/dist/instruction-sets-cli.js +134 -24
- package/dist/optimizations-promote-cli.d.ts +3 -0
- package/dist/optimizations-promote-cli.d.ts.map +1 -1
- package/dist/optimizations-promote-cli.js +75 -7
- package/dist/runner-dir-verify.d.ts.map +1 -1
- package/dist/runner-dir-verify.js +4 -7
- package/dist/skilled-proposer-bake-report.d.ts +2 -0
- package/dist/skilled-proposer-bake-report.d.ts.map +1 -0
- package/dist/skilled-proposer-bake-report.js +1 -0
- package/dist/skilled-proposer-config.d.ts +2 -0
- package/dist/skilled-proposer-config.d.ts.map +1 -0
- package/dist/skilled-proposer-config.js +212 -0
- package/dist/skilled-proposer-launch.d.ts +26 -0
- package/dist/skilled-proposer-launch.d.ts.map +1 -1
- package/dist/skilled-proposer-launch.js +103 -6
- package/package.json +1 -1
- package/scripts/vendor-gepa-python.mjs +81 -12
- package/src/cleanup.ts +37 -0
- package/src/gepa-engine-dispatch.ts +21 -1
- package/src/gepa-python-paths.ts +24 -6
- package/src/help.ts +37 -15
- package/src/hosted-commands.ts +4 -0
- package/src/hosted-gepa-cli.ts +107 -0
- package/src/hosted-gepa-numeric-values.ts +60 -0
- package/src/hosted-provider-settings.ts +69 -0
- package/src/hosted-session-cli.ts +4 -1
- package/src/http.ts +8 -0
- package/src/index.ts +112 -89
- package/src/instruction-set-loader.ts +55 -5
- package/src/instruction-sets-cli.ts +173 -19
- package/src/optimizations-promote-cli.ts +85 -7
- package/src/runner-dir-verify.ts +11 -6
- package/src/skilled-proposer-bake-report.ts +1 -0
- package/src/skilled-proposer-config.ts +191 -0
- package/src/skilled-proposer-launch.ts +96 -7
- package/src/skilled-proposer-venv-manager.mjs +44 -27
- package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +36 -3
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +31 -3
- package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +7 -2
- package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +354 -5
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +1 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +15 -0
- package/vendor/skills/orizu-cli/SKILL.md +94 -168
- package/vendor/skills/orizu-cli/references/assess-and-plan.md +107 -0
- package/vendor/skills/orizu-cli/references/building-judges.md +442 -180
- package/vendor/skills/orizu-cli/references/cli-reference.md +69 -39
- package/vendor/skills/orizu-cli/references/dataset-design.md +100 -0
- package/vendor/skills/orizu-cli/references/eval-strategy.md +64 -0
- package/vendor/skills/orizu-cli/references/instructions-after-prompts.md +115 -0
- package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +150 -82
- package/vendor/skills/orizu-cli/references/optimization-reports.md +196 -104
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +57 -37
- package/vendor/skills/orizu-cli/references/primer.md +43 -18
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +199 -131
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
interface NumericRule {
|
|
2
|
+
field: string
|
|
3
|
+
flag: string
|
|
4
|
+
grammar: string
|
|
5
|
+
accepts: (value: unknown) => boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const isFiniteNumber = (value: unknown): value is number =>
|
|
9
|
+
typeof value === 'number' && Number.isFinite(value)
|
|
10
|
+
const isInteger = (value: unknown): value is number =>
|
|
11
|
+
isFiniteNumber(value) && Number.isInteger(value)
|
|
12
|
+
const isPositiveInteger = (value: unknown): value is number =>
|
|
13
|
+
isInteger(value) && value > 0
|
|
14
|
+
|
|
15
|
+
const rules: NumericRule[] = [
|
|
16
|
+
...[
|
|
17
|
+
['proposalMaxCalls', '--proposal-max-calls'],
|
|
18
|
+
['proposalMaxTokens', '--proposal-max-tokens'],
|
|
19
|
+
['maxIterations', '--max-iterations'],
|
|
20
|
+
['maxCandidateProposals', '--max-candidate-proposals'],
|
|
21
|
+
['maxFullEvals', '--max-full-evals'],
|
|
22
|
+
['maxMetricCalls', '--max-metric-calls'],
|
|
23
|
+
['minibatchSize', '--minibatch-size'],
|
|
24
|
+
['reflectionMaxTokens', '--reflection-max-tokens'],
|
|
25
|
+
['reflectionRetryAttempts', '--reflection-retry-attempts'],
|
|
26
|
+
['reflectionHttpTimeoutSeconds', '--reflection-http-timeout-seconds'],
|
|
27
|
+
].map(([field, flag]) => ({ field, flag, grammar: 'positive integer', accepts: isPositiveInteger })),
|
|
28
|
+
{
|
|
29
|
+
field: 'numThreads', flag: '--num-threads', grammar: 'auto or positive integer',
|
|
30
|
+
accepts: value => value === 'auto' || isPositiveInteger(value),
|
|
31
|
+
},
|
|
32
|
+
...[
|
|
33
|
+
['epsilon', '--epsilon'],
|
|
34
|
+
['reflectionTemperature', '--reflection-temperature'],
|
|
35
|
+
].map(([field, flag]) => ({ field, flag, grammar: 'finite number', accepts: isFiniteNumber })),
|
|
36
|
+
{ field: 'seed', flag: '--seed', grammar: 'integer', accepts: isInteger },
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
const integerPattern = /^[+-]?\d+$/
|
|
40
|
+
const floatPattern = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i
|
|
41
|
+
|
|
42
|
+
export function parseHostedGepaNumericValue(field: string, rawValue: string): number | 'auto' {
|
|
43
|
+
const rule = rules.find(candidate => candidate.field === field)
|
|
44
|
+
if (!rule) throw new Error(`Unknown hosted numeric field ${field}`)
|
|
45
|
+
const trimmed = rawValue.trim()
|
|
46
|
+
if (field === 'numThreads' && trimmed.toLowerCase() === 'auto') return 'auto'
|
|
47
|
+
const lexicalPattern = rule.grammar === 'finite number' ? floatPattern : integerPattern
|
|
48
|
+
const parsed = lexicalPattern.test(trimmed) ? Number(trimmed) : Number.NaN
|
|
49
|
+
if (!rule.accepts(parsed)) throw new Error(`${rule.flag} must be ${rule.grammar}`)
|
|
50
|
+
return parsed
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function hostedGepaNumericError(values: Record<string, unknown>): string | null {
|
|
54
|
+
for (const rule of rules) {
|
|
55
|
+
if (values[rule.field] !== undefined && !rule.accepts(values[rule.field])) {
|
|
56
|
+
return `${rule.field} must be ${rule.grammar}`
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function hasOnlyKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
|
|
6
|
+
return Object.keys(value).every(key => keys.includes(key))
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isChoice(value: unknown, choices: readonly string[]): boolean {
|
|
10
|
+
return typeof value === 'string' && choices.includes(value)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isProviderObject(value: unknown, keys: readonly string[], choices: Readonly<Record<string, readonly string[]>>): boolean {
|
|
14
|
+
return isRecord(value) && hasOnlyKeys(value, keys)
|
|
15
|
+
&& Object.entries(value).every(([key, nested]) => isChoice(nested, choices[key] ?? []))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isCredentialKey(key: string): boolean {
|
|
19
|
+
const tokens = key.replaceAll(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)
|
|
20
|
+
return tokens.some(token => ['auth', 'authorization', 'token', 'secret', 'password', 'passwd', 'credential', 'credentials', 'key'].includes(token))
|
|
21
|
+
|| /(?:authorization|authheader|bearer|token|secret|password|passwd|credential|credentials|apikey|accesskey(?:id)?|privatekey|clientkey|secretkey|connection(?:string|url)|dsn)$/.test(tokens.join(''))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function containsCredential(value: unknown, root = true): boolean {
|
|
25
|
+
if (Array.isArray(value)) return root || value.some(nested => containsCredential(nested, false))
|
|
26
|
+
if (!isRecord(value)) return root ? value !== undefined
|
|
27
|
+
: typeof value === 'string' && (/(?:authorization|bearer|x-api-key|x-auth-token)\s*[: ]/i.test(value)
|
|
28
|
+
|| /^[a-z]+:\/\/[^/@]*:[^/@]*@/i.test(value))
|
|
29
|
+
return Object.entries(value).some(([key, nested]) => isCredentialKey(key) || containsCredential(nested, false))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function unsupportedHostedSetting(value: unknown, model: string): string | null {
|
|
33
|
+
if (value === undefined) return null
|
|
34
|
+
if (!isRecord(value)) return '<root>'
|
|
35
|
+
const providerKeys = model.startsWith('openai/') ? ['reasoning']
|
|
36
|
+
: model.startsWith('anthropic/') ? ['thinking', 'output_config'] : []
|
|
37
|
+
const unknown = Object.keys(value).find(key => !['top_p', ...providerKeys].includes(key))
|
|
38
|
+
if (unknown) return unknown
|
|
39
|
+
if (value.top_p !== undefined && (typeof value.top_p !== 'number'
|
|
40
|
+
|| !Number.isFinite(value.top_p) || value.top_p < 0 || value.top_p > 1)) return 'top_p'
|
|
41
|
+
if (value.thinking !== undefined && (!isRecord(value.thinking) || value.thinking.type === undefined)) return 'thinking.type'
|
|
42
|
+
for (const [key, nested, keys, choices] of [
|
|
43
|
+
['thinking', value.thinking, ['type'], { type: ['adaptive'] }],
|
|
44
|
+
['output_config', value.output_config, ['effort'], { effort: ['low', 'medium', 'high', 'max'] }],
|
|
45
|
+
['reasoning', value.reasoning, ['effort', 'summary'], { effort: ['minimal', 'low', 'medium', 'high', 'xhigh'], summary: ['auto', 'concise', 'detailed'] }],
|
|
46
|
+
] as Array<[string, unknown, readonly string[], Readonly<Record<string, readonly string[]>>]>) if (nested !== undefined && !isProviderObject(nested, keys, choices)) {
|
|
47
|
+
if (!isRecord(nested)) return key
|
|
48
|
+
return `${key}.${Object.keys(nested).find(child => !keys.includes(child)
|
|
49
|
+
|| !isChoice(nested[child], choices[child] ?? [])) ?? key}`
|
|
50
|
+
}
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function hostedSettingsContainCredentials(value: unknown): boolean { return containsCredential(value) }
|
|
55
|
+
|
|
56
|
+
export function hostedProviderSettingsError(
|
|
57
|
+
value: unknown, model = 'anthropic/', temperature?: unknown
|
|
58
|
+
): string | null {
|
|
59
|
+
if (hostedSettingsContainCredentials(value)) {
|
|
60
|
+
return 'Hosted reflection provider settings must not contain credential-bearing fields.'
|
|
61
|
+
}
|
|
62
|
+
const unsupported = unsupportedHostedSetting(value, model)
|
|
63
|
+
if (unsupported) return `Unsupported hosted setting "${unsupported}".`
|
|
64
|
+
if (model.startsWith('anthropic/') && temperature !== undefined && temperature !== null
|
|
65
|
+
&& isRecord(value) && value.thinking !== undefined) {
|
|
66
|
+
return 'reflection_temperature cannot be combined with Anthropic thinking'
|
|
67
|
+
}
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
@@ -57,6 +57,7 @@ import { buildEgressPolicy, DEFAULT_EGRESS_CANARY_HOST } from './egress-policy.j
|
|
|
57
57
|
import { DEFAULT_HOSTED_MODEL, hostedLoopCommand, type HostedLoopContext } from './hosted-loop.js'
|
|
58
58
|
import { hostedBootCommand } from './hosted-boot.js'
|
|
59
59
|
import { mergeJobCommand } from './merge-job.js'
|
|
60
|
+
import { skilledProposerBakeCommand } from './skilled-proposer-launch.js'
|
|
60
61
|
import { authedFetch } from './http.js'
|
|
61
62
|
import { resolveBaseUrl } from './http.js'
|
|
62
63
|
import { findUnknownOption } from './option-validation.js'
|
|
@@ -1568,7 +1569,9 @@ export async function hostedCommand(
|
|
|
1568
1569
|
if (positional[1] === 'merge-job') {
|
|
1569
1570
|
return mergeJobCommand(io)
|
|
1570
1571
|
}
|
|
1571
|
-
|
|
1572
|
+
if (positional[1] === 'bake-skilled-proposer-venv') return skilledProposerBakeCommand(io)
|
|
1573
|
+
if (positional[1] === 'verify-skilled-proposer-bake') return skilledProposerBakeCommand(io, true)
|
|
1574
|
+
io.printErr?.('Usage: orizu internal <hosted-loop --context <path> | hosted-boot | merge-job | bake-skilled-proposer-venv | verify-skilled-proposer-bake>')
|
|
1572
1575
|
return 1
|
|
1573
1576
|
}
|
|
1574
1577
|
|
package/src/http.ts
CHANGED
|
@@ -93,6 +93,10 @@ function isExpired(expiresAt: number): boolean {
|
|
|
93
93
|
return expiresAt <= nowUnix + 30
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
export function credentialRequestRedirectPolicy(): Pick<RequestInit, 'redirect'> {
|
|
97
|
+
return process.env.ORIZU_CLI_AUTH_REDIRECT === 'error' ? { redirect: 'error' } : {}
|
|
98
|
+
}
|
|
99
|
+
|
|
96
100
|
async function refreshCredentials(baseUrl: string, credentials: ServerCredentials): Promise<ServerCredentials> {
|
|
97
101
|
if (!isSessionCredentials(credentials)) {
|
|
98
102
|
throw new Error('API key credentials do not refresh. Run `orizu login` again if access fails.')
|
|
@@ -103,6 +107,7 @@ async function refreshCredentials(baseUrl: string, credentials: ServerCredential
|
|
|
103
107
|
method: 'POST',
|
|
104
108
|
headers: { 'Content-Type': 'application/json' },
|
|
105
109
|
body: JSON.stringify({ refreshToken: credentials.refreshToken }),
|
|
110
|
+
...credentialRequestRedirectPolicy(),
|
|
106
111
|
})
|
|
107
112
|
|
|
108
113
|
if (!response.ok) {
|
|
@@ -141,6 +146,7 @@ export async function authedFetch(path: string, init: RequestInit = {}) {
|
|
|
141
146
|
...(init.headers || {}),
|
|
142
147
|
Authorization: `Bearer ${envBearer}`,
|
|
143
148
|
},
|
|
149
|
+
...credentialRequestRedirectPolicy(),
|
|
144
150
|
})
|
|
145
151
|
}
|
|
146
152
|
|
|
@@ -160,6 +166,7 @@ export async function authedFetch(path: string, init: RequestInit = {}) {
|
|
|
160
166
|
...(init.headers || {}),
|
|
161
167
|
Authorization: `Bearer ${getAuthorizationToken(activeCredentials)}`,
|
|
162
168
|
},
|
|
169
|
+
...credentialRequestRedirectPolicy(),
|
|
163
170
|
})
|
|
164
171
|
|
|
165
172
|
if (response.status === 401 && isSessionCredentials(activeCredentials)) {
|
|
@@ -170,6 +177,7 @@ export async function authedFetch(path: string, init: RequestInit = {}) {
|
|
|
170
177
|
...(init.headers || {}),
|
|
171
178
|
Authorization: `Bearer ${getAuthorizationToken(activeCredentials)}`,
|
|
172
179
|
},
|
|
180
|
+
...credentialRequestRedirectPolicy(),
|
|
173
181
|
})
|
|
174
182
|
}
|
|
175
183
|
|
package/src/index.ts
CHANGED
|
@@ -36,7 +36,14 @@ import { parseGlobalFlags } from './global-flags.js'
|
|
|
36
36
|
import { getCapabilities, renderHelpForArgs, renderRootHelp } from './help.js'
|
|
37
37
|
import { runLocalAppPreview } from './preview-runtime.js'
|
|
38
38
|
import { readAssignmentManifestJsonlFile } from './task-assignment-manifest.js'
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
assertSecureTokenTransport,
|
|
41
|
+
authedFetch,
|
|
42
|
+
credentialRequestRedirectPolicy,
|
|
43
|
+
getBaseUrl,
|
|
44
|
+
resolveLoginBaseUrl,
|
|
45
|
+
setGlobalFlags,
|
|
46
|
+
} from './http.js'
|
|
40
47
|
import {
|
|
41
48
|
computeSkillContentHash,
|
|
42
49
|
getSkillInstallPath,
|
|
@@ -71,6 +78,7 @@ import { ARTIFACT_MAX_BYTES, runnerOptimizerCommand } from './artifact-pull.js'
|
|
|
71
78
|
import { killSwitchCommand } from './kill-switch-cli.js'
|
|
72
79
|
import { egressAllowlistCommand } from './egress-allowlist-cli.js'
|
|
73
80
|
import { acceptScoreRunCommand } from './scores-accept-cli.js'
|
|
81
|
+
import { cleanupAll, throwWithCleanup } from './cleanup.js'
|
|
74
82
|
import { listOptimizationRunsCommand } from './optimizations-list-cli.js'
|
|
75
83
|
import { promoteOptimizationCommand } from './optimizations-promote-cli.js'
|
|
76
84
|
import { scorersCommand } from './scorers-cli.js'
|
|
@@ -86,7 +94,7 @@ import {
|
|
|
86
94
|
import { teamConnectorsCommand } from './team-connectors-cli.js'
|
|
87
95
|
import { manifestsCommand } from './manifests-cli.js'
|
|
88
96
|
import { workbenchCommand } from './workbench-cli.js'
|
|
89
|
-
import {
|
|
97
|
+
import { dispatchHostedCommands } from './hosted-commands.js'
|
|
90
98
|
import { workspaceSyncCommand } from './workspace-sync.js'
|
|
91
99
|
import { runGitCredentialInvocation } from './git-credential.js'
|
|
92
100
|
import { instructionSetsCommand, syncToDisk } from './instruction-sets-cli.js'; export { loadInstructionSet } from './instruction-set-loader.js'
|
|
@@ -100,7 +108,13 @@ import type {
|
|
|
100
108
|
CliLengthMeasurementUnavailableReason,
|
|
101
109
|
CliLengthStats,
|
|
102
110
|
} from './prompt-length-wire.js'
|
|
103
|
-
import {
|
|
111
|
+
import {
|
|
112
|
+
assertSnapshotManifestConfined,
|
|
113
|
+
extractFlagValue,
|
|
114
|
+
type GepaVerifiedArgs,
|
|
115
|
+
verifyGepaRunnerDirsFromArgs,
|
|
116
|
+
verifyRunnerDirRegistered,
|
|
117
|
+
} from './runner-dir-verify.js'
|
|
104
118
|
import { runScorersRegister } from './scorer-draft-push.js'
|
|
105
119
|
import { runZipArtifactPush } from './zip-draft-push.js'
|
|
106
120
|
import { type GithubLinkResult, runGithubLink, runInteractiveHostedSetup } from './github-setup.js'
|
|
@@ -117,7 +131,7 @@ import { dispatchGepaEngine } from './gepa-engine-dispatch.js'
|
|
|
117
131
|
import { resolveGepaInstructionSetProfileVersion } from './instruction-set-gepa-launch.js'
|
|
118
132
|
import { getGepaPythonCommand } from './gepa-python-command.js'
|
|
119
133
|
import { getGepaPythonPathEntries } from './gepa-python-paths.js'
|
|
120
|
-
import { prepareSkilledProposerLaunch } from './skilled-proposer-launch.js'
|
|
134
|
+
import { prepareSkilledProposerLaunch, spawnSkilledProposerChild } from './skilled-proposer-launch.js'
|
|
121
135
|
|
|
122
136
|
export { parseJsonResponse, sanitizeTerminalText } from './json-response.js'
|
|
123
137
|
|
|
@@ -1287,6 +1301,7 @@ async function logout() {
|
|
|
1287
1301
|
body: 'refreshToken' in credentials
|
|
1288
1302
|
? JSON.stringify({ refreshToken: credentials.refreshToken })
|
|
1289
1303
|
: undefined,
|
|
1304
|
+
...credentialRequestRedirectPolicy(),
|
|
1290
1305
|
})
|
|
1291
1306
|
|
|
1292
1307
|
if (!response.ok) {
|
|
@@ -1767,53 +1782,6 @@ function refusePromptMutation(replacement: string): never {
|
|
|
1767
1782
|
throw new Error(`Use: orizu instructions ${replacement}`)
|
|
1768
1783
|
}
|
|
1769
1784
|
|
|
1770
|
-
async function bindPromptScorer(role: 'headline' | 'tracked') {
|
|
1771
|
-
const promptId = getPositionalArg(3)
|
|
1772
|
-
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
1773
|
-
const scorerVersionId = getArg('--scorer-version')
|
|
1774
|
-
|
|
1775
|
-
if (!promptId || !scorerVersionId) {
|
|
1776
|
-
throw new Error(`Usage: orizu prompts scorers ${role === 'headline' ? 'set-headline' : 'add'} <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]`)
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
const response = await authedFetch(`/api/cli/prompts/scorers?project=${encodeURIComponent(project)}`, {
|
|
1780
|
-
method: 'POST',
|
|
1781
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1782
|
-
body: JSON.stringify({
|
|
1783
|
-
promptId,
|
|
1784
|
-
scorerVersionId,
|
|
1785
|
-
role,
|
|
1786
|
-
datasetVersionId: getArg('--dataset-version') || undefined,
|
|
1787
|
-
splitSetId: getArg('--split-set') || undefined,
|
|
1788
|
-
splitName: getArg('--split') || undefined,
|
|
1789
|
-
}),
|
|
1790
|
-
})
|
|
1791
|
-
|
|
1792
|
-
if (!response.ok) {
|
|
1793
|
-
throw new Error(`Failed to bind prompt scorer: ${await response.text()}`)
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
const data = await parseJsonResponse<{
|
|
1797
|
-
promptScorer: Record<string, unknown>
|
|
1798
|
-
owner?: {
|
|
1799
|
-
instructionSetSlug: string
|
|
1800
|
-
componentKey: string
|
|
1801
|
-
}
|
|
1802
|
-
}>(response, 'Prompt scorer bind')
|
|
1803
|
-
if (hasJsonFlag()) {
|
|
1804
|
-
printJson(data)
|
|
1805
|
-
return
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
|
-
printLine(`${role === 'headline' ? 'Set headline' : 'Added'} scorer ${sanitizeTerminalText(scorerVersionId)} for ${sanitizeTerminalText(promptId)}`)
|
|
1809
|
-
if (data.owner) {
|
|
1810
|
-
printLine(
|
|
1811
|
-
`Owner: ${sanitizeTerminalText(promptId)} -> ` +
|
|
1812
|
-
`${sanitizeTerminalText(data.owner.instructionSetSlug)} / ${sanitizeTerminalText(data.owner.componentKey)}`
|
|
1813
|
-
)
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
1785
|
function getPositionalArg(index: number): string | null {
|
|
1818
1786
|
const value = cliArgs[index]
|
|
1819
1787
|
return value && !value.startsWith('--') ? value : null
|
|
@@ -2497,6 +2465,43 @@ async function updateOptimizationRunLifecycle(action: OptimizationLifecycleActio
|
|
|
2497
2465
|
|
|
2498
2466
|
// ALI-1073 list moved to optimizations-list-cli.ts (line ratchet, ALI-976);
|
|
2499
2467
|
// ALI-1175 labels its best scores as agent-reported evidence there.
|
|
2468
|
+
const GEPA_RUNNER_MATERIALIZATION_PAIRS = [
|
|
2469
|
+
{ versionFlag: '--runner-version-id', dirFlag: '--candidate-runner-dir' },
|
|
2470
|
+
{ versionFlag: '--scorer-runner-version-id', dirFlag: '--scorer-runner-dir' },
|
|
2471
|
+
] as const
|
|
2472
|
+
|
|
2473
|
+
async function materializeMissingGepaRunnerDirs(args: string[]): Promise<{
|
|
2474
|
+
args: string[]
|
|
2475
|
+
cleanup: () => void
|
|
2476
|
+
}> {
|
|
2477
|
+
const rewritten = [...args]
|
|
2478
|
+
const cleanups: Array<() => void> = []
|
|
2479
|
+
const cleanup = () => cleanupAll(
|
|
2480
|
+
cleanups,
|
|
2481
|
+
'Failed to clean materialized runner directories'
|
|
2482
|
+
)
|
|
2483
|
+
|
|
2484
|
+
try {
|
|
2485
|
+
for (const pair of GEPA_RUNNER_MATERIALIZATION_PAIRS) {
|
|
2486
|
+
const runnerVersionId = extractFlagValue(rewritten, pair.versionFlag)
|
|
2487
|
+
const runnerDir = extractFlagValue(rewritten, pair.dirFlag)
|
|
2488
|
+
if (!runnerVersionId || runnerDir) continue
|
|
2489
|
+
|
|
2490
|
+
const materialized = await materializeRunnerVersion(runnerVersionId)
|
|
2491
|
+
cleanups.push(materialized.cleanup)
|
|
2492
|
+
rewritten.push(pair.dirFlag, materialized.runnerDir)
|
|
2493
|
+
}
|
|
2494
|
+
} catch (error) {
|
|
2495
|
+
throwWithCleanup(
|
|
2496
|
+
error,
|
|
2497
|
+
cleanups,
|
|
2498
|
+
'Runner materialization failed and cleanup also failed'
|
|
2499
|
+
)
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
return { args: rewritten, cleanup }
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2500
2505
|
async function runGepaOptimization() {
|
|
2501
2506
|
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
2502
2507
|
const baseUrl = getBaseUrl()
|
|
@@ -2509,11 +2514,12 @@ async function runGepaOptimization() {
|
|
|
2509
2514
|
// itself (both argparse flag forms), fail-closed on half-populated pairs,
|
|
2510
2515
|
// and REWRITES the dir flags to verified snapshots (the exact hashed bytes)
|
|
2511
2516
|
// so a post-hash mutation of the original dirs cannot change what runs.
|
|
2512
|
-
const
|
|
2513
|
-
|
|
2514
|
-
let result
|
|
2517
|
+
const materialized = await materializeMissingGepaRunnerDirs(forwardedArgs)
|
|
2518
|
+
let verified: GepaVerifiedArgs | undefined
|
|
2515
2519
|
let selectedEngine = 'official'
|
|
2516
2520
|
try {
|
|
2521
|
+
verified = await verifyGepaRunnerDirsFromArgs(materialized.args)
|
|
2522
|
+
forwardedArgs = verified.args
|
|
2517
2523
|
// Uniform resolution (ALI-1090): honors ORIZU_TOKEN / ORIZU_TOKEN_FILE before
|
|
2518
2524
|
// credentials.json so hosted sandboxes can run optimizations. Read at spawn
|
|
2519
2525
|
// time — AFTER the verification awaits above, so a hosted token-file
|
|
@@ -2539,18 +2545,29 @@ async function runGepaOptimization() {
|
|
|
2539
2545
|
if (instructionSetProfileVersionId) dispatch.environment.ORIZU_INSTRUCTION_SET_PROFILE_VERSION_ID = instructionSetProfileVersionId
|
|
2540
2546
|
selectedEngine = dispatch.engine
|
|
2541
2547
|
const launch = prepareSkilledProposerLaunch(python, dispatch.engine, dispatch.environment)
|
|
2542
|
-
result =
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2548
|
+
const result = await spawnSkilledProposerChild(
|
|
2549
|
+
launch.python, ['-m', dispatch.module, ...dispatch.args], dispatch.engine, launch.environment,
|
|
2550
|
+
)
|
|
2551
|
+
if (result.error) {
|
|
2552
|
+
throw new Error(`${selectedEngine} GEPA engine failed: ${result.error.message}`, { cause: result.error })
|
|
2553
|
+
}
|
|
2554
|
+
if (result.status !== 0) {
|
|
2555
|
+
throw new Error(`${selectedEngine} GEPA engine failed with exit code ${result.status}`)
|
|
2556
|
+
}
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
throwWithCleanup(
|
|
2559
|
+
error,
|
|
2560
|
+
[
|
|
2561
|
+
...(verified ? [verified.cleanup] : []),
|
|
2562
|
+
materialized.cleanup,
|
|
2563
|
+
],
|
|
2564
|
+
'GEPA launch failed and runner cleanup also failed'
|
|
2565
|
+
)
|
|
2552
2566
|
}
|
|
2553
|
-
|
|
2567
|
+
cleanupAll(
|
|
2568
|
+
[verified.cleanup, materialized.cleanup],
|
|
2569
|
+
'Failed to clean GEPA runner directories'
|
|
2570
|
+
)
|
|
2554
2571
|
if (hasJsonFlag()) {
|
|
2555
2572
|
printJson({ status: 'completed', exitCode: 0 })
|
|
2556
2573
|
}
|
|
@@ -5829,26 +5846,33 @@ export async function materializeRunnerVersion(runnerVersionId: string): Promise
|
|
|
5829
5846
|
const tempDir = mkdtempSync(join(tmpdir(), 'orizu-runner-version-'))
|
|
5830
5847
|
const zipPath = join(tempDir, 'runner.zip')
|
|
5831
5848
|
const runnerDir = join(tempDir, 'runner')
|
|
5832
|
-
const
|
|
5833
|
-
if (zipBytes.byteLength > ARTIFACT_MAX_BYTES) {
|
|
5834
|
-
rmSync(tempDir, { recursive: true, force: true })
|
|
5835
|
-
throw new Error(`Runner artifact exceeds ${ARTIFACT_MAX_BYTES} bytes`)
|
|
5836
|
-
}
|
|
5837
|
-
writeFileSync(zipPath, zipBytes)
|
|
5849
|
+
const cleanup = () => rmSync(tempDir, { recursive: true, force: true })
|
|
5838
5850
|
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
}
|
|
5846
|
-
if (result.status !== 0) {
|
|
5847
|
-
rmSync(tempDir, { recursive: true, force: true })
|
|
5848
|
-
throw new Error(`unzip failed: ${sanitizeTerminalText(result.stderr || result.stdout || '')}`)
|
|
5849
|
-
}
|
|
5851
|
+
try {
|
|
5852
|
+
const zipBytes = new Uint8Array(await response.arrayBuffer())
|
|
5853
|
+
if (zipBytes.byteLength > ARTIFACT_MAX_BYTES) {
|
|
5854
|
+
throw new Error(`Runner artifact exceeds ${ARTIFACT_MAX_BYTES} bytes`)
|
|
5855
|
+
}
|
|
5856
|
+
writeFileSync(zipPath, zipBytes)
|
|
5850
5857
|
|
|
5851
|
-
|
|
5858
|
+
const result = spawnSync('unzip', ['-q', zipPath, '-d', runnerDir], {
|
|
5859
|
+
encoding: 'utf8',
|
|
5860
|
+
})
|
|
5861
|
+
if (result.error) {
|
|
5862
|
+
throw result.error
|
|
5863
|
+
}
|
|
5864
|
+
if (result.status !== 0) {
|
|
5865
|
+
throw new Error(`unzip failed: ${sanitizeTerminalText(result.stderr || result.stdout || '')}`)
|
|
5866
|
+
}
|
|
5867
|
+
|
|
5868
|
+
return { runnerDir, cleanup }
|
|
5869
|
+
} catch (error) {
|
|
5870
|
+
throwWithCleanup(
|
|
5871
|
+
error,
|
|
5872
|
+
[cleanup],
|
|
5873
|
+
`Runner ${runnerVersionId} materialization failed and cleanup also failed`
|
|
5874
|
+
)
|
|
5875
|
+
}
|
|
5852
5876
|
}
|
|
5853
5877
|
|
|
5854
5878
|
async function runnersExec() {
|
|
@@ -6236,13 +6260,11 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6236
6260
|
}
|
|
6237
6261
|
|
|
6238
6262
|
if (command === 'prompts' && subcommand === 'scorers' && cliArgs[2] === 'set-headline') {
|
|
6239
|
-
|
|
6240
|
-
return
|
|
6263
|
+
refusePromptMutation('scorers set-headline <set> --key <component-key> --scorer-version <id> --project <team/project>')
|
|
6241
6264
|
}
|
|
6242
6265
|
|
|
6243
6266
|
if (command === 'prompts' && subcommand === 'scorers' && cliArgs[2] === 'add') {
|
|
6244
|
-
|
|
6245
|
-
return
|
|
6267
|
+
refusePromptMutation('scorers add <set> --key <component-key> --scorer-version <id> --project <team/project>')
|
|
6246
6268
|
}
|
|
6247
6269
|
|
|
6248
6270
|
if (command === 'judges' && subcommand === 'list') {
|
|
@@ -6324,6 +6346,7 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6324
6346
|
return
|
|
6325
6347
|
}
|
|
6326
6348
|
|
|
6349
|
+
if (await dispatchHostedCommands(cliArgs, { environment: process.env, getArg, json: hasJsonFlag(), printJson, printLine, printErr: printError, resolveProjectSlug, setExitCode: code => { process.exitCode = code } })) return
|
|
6327
6350
|
if (command === 'optimizations' && subcommand === 'start') {
|
|
6328
6351
|
await startOptimizationRun()
|
|
6329
6352
|
return
|
|
@@ -6355,7 +6378,8 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6355
6378
|
}
|
|
6356
6379
|
if (command === 'optimizations' && subcommand === 'promote') {
|
|
6357
6380
|
await promoteOptimizationCommand(cliArgs.slice(2), {
|
|
6358
|
-
json: hasJsonFlag(), print: printLine,
|
|
6381
|
+
json: hasJsonFlag(), origin: parsed.flags, print: printLine, printErr: printError,
|
|
6382
|
+
resolveProjectSlug, fetcher: authedFetch,
|
|
6359
6383
|
})
|
|
6360
6384
|
return
|
|
6361
6385
|
}
|
|
@@ -6511,7 +6535,6 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6511
6535
|
if (command === 'datasets' && subcommand === 'lock') { await lockDataset(); return }
|
|
6512
6536
|
if (command === 'datasets' && subcommand === 'clone') { await cloneDataset(); return }
|
|
6513
6537
|
if (command === 'tasks' && subcommand === 'export') { await downloadAnnotations(); return }
|
|
6514
|
-
if (command === 'internal' || (command === 'session' && subcommand === 'start' && hasArg('--hosted'))) { process.exitCode = await hostedCommand(cliArgs, { json: hasJsonFlag(), print: printLine, printErr: printError }); return }
|
|
6515
6538
|
if (command === 'session' || command === 'run') {
|
|
6516
6539
|
process.exitCode = await workbenchCommand(cliArgs, { json: hasJsonFlag(), print: printLine })
|
|
6517
6540
|
return
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, realpathSync, readFileSync, statSync } from 'node:fs'
|
|
1
|
+
import { existsSync, realpathSync, readFileSync, readdirSync, statSync } from 'node:fs'
|
|
2
2
|
import { resolve, sep } from 'node:path'
|
|
3
3
|
|
|
4
4
|
export class InstructionSetLoaderError extends Error {
|
|
@@ -6,14 +6,64 @@ export class InstructionSetLoaderError extends Error {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
interface Material { files: Record<string, string>; pinnedComponents?: Record<string, unknown> }
|
|
9
|
-
interface Manifest { manifestVersion: number; name: string; shape: string[]; default: Material; profiles: Array<{ modelConfigIdentity: string; production: Material | null }>; filteredTo?: string[] }
|
|
9
|
+
interface Manifest { manifestVersion: number; projectId?: string; instructionSetId?: string; name: string; slug?: string; shape: string[]; default: Material; profiles: Array<{ modelConfigIdentity: string; production: Material | null }>; filteredTo?: string[] }
|
|
10
10
|
|
|
11
11
|
function inside(root: string, candidate: string) { return candidate === root || candidate.startsWith(`${root}${sep}`) }
|
|
12
12
|
|
|
13
|
+
function manifestMatchesReference(candidate: string, reference: string): boolean {
|
|
14
|
+
try {
|
|
15
|
+
const manifest = JSON.parse(
|
|
16
|
+
readFileSync(resolve(candidate, 'manifest.json'), 'utf8')
|
|
17
|
+
) as Pick<Manifest, 'name' | 'slug'>
|
|
18
|
+
return manifest.name === reference || manifest.slug === reference
|
|
19
|
+
} catch {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveSetRoot(directory: string, reference: string): string {
|
|
25
|
+
let directoryRoot: string
|
|
26
|
+
try { directoryRoot = realpathSync(directory) } catch { throw new InstructionSetLoaderError('instruction_set_not_synced') }
|
|
27
|
+
const direct = resolve(directoryRoot, reference)
|
|
28
|
+
try {
|
|
29
|
+
if (inside(directoryRoot, direct) && existsSync(direct) && statSync(direct).isDirectory()) {
|
|
30
|
+
const realDirect = realpathSync(direct)
|
|
31
|
+
if (inside(directoryRoot, realDirect)) {
|
|
32
|
+
try {
|
|
33
|
+
const manifest = JSON.parse(
|
|
34
|
+
readFileSync(resolve(realDirect, 'manifest.json'), 'utf8')
|
|
35
|
+
) as Pick<Manifest, 'name' | 'slug'>
|
|
36
|
+
if (manifest.name === reference || manifest.slug === reference) {
|
|
37
|
+
return realDirect
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
// Preserve the exact-path loader's specific missing/invalid manifest error.
|
|
41
|
+
return realDirect
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
// A stale or unreadable direct-path candidate must not shadow a valid set.
|
|
47
|
+
}
|
|
48
|
+
const matches = readdirSync(directoryRoot, { withFileTypes: true }).flatMap(entry => {
|
|
49
|
+
try {
|
|
50
|
+
if (entry.name.startsWith('.') || !entry.isDirectory()) return []
|
|
51
|
+
const candidate = realpathSync(resolve(directoryRoot, entry.name))
|
|
52
|
+
return inside(directoryRoot, candidate) && manifestMatchesReference(candidate, reference)
|
|
53
|
+
? [candidate]
|
|
54
|
+
: []
|
|
55
|
+
} catch {
|
|
56
|
+
return []
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
if (matches.length !== 1) throw new InstructionSetLoaderError(
|
|
60
|
+
matches.length > 1 ? 'instruction_set_reference_ambiguous' : 'instruction_set_not_synced'
|
|
61
|
+
)
|
|
62
|
+
return matches[0]!
|
|
63
|
+
}
|
|
64
|
+
|
|
13
65
|
export function loadInstructionSet(directory: string, name: string, modelConfigIdentity: string): Record<string, string> {
|
|
14
|
-
const
|
|
15
|
-
if (!existsSync(unresolvedRoot) || !statSync(unresolvedRoot).isDirectory()) throw new InstructionSetLoaderError('instruction_set_not_synced')
|
|
16
|
-
const setRoot = realpathSync(unresolvedRoot)
|
|
66
|
+
const setRoot = resolveSetRoot(directory, name)
|
|
17
67
|
const manifestPath = resolve(setRoot, 'manifest.json')
|
|
18
68
|
if (!existsSync(manifestPath)) throw new InstructionSetLoaderError('instruction_set_manifest_missing')
|
|
19
69
|
let manifest: Manifest
|