rightmodeler 0.2.0 → 0.3.0
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 +9 -2
- package/dist-bundle/cli.js +9432 -5081
- package/dist-bundle/provenance.js +89 -0
- package/dist-bundle/proxy/container-supervisor.mjs +6 -0
- package/dist-bundle/proxy/proxy-runtime.mjs +105 -13
- package/dist-bundle/transport/stream.js +8 -2
- package/docs/commands.md +105 -18
- package/docs/evaluators.md +88 -9
- package/docs/exit-codes.md +41 -1
- package/docs/gateways.md +109 -0
- package/docs/getting-started.md +131 -6
- package/docs/github-actions.md +328 -0
- package/docs/github.md +110 -0
- package/docs/modeb.md +34 -0
- package/package.json +6 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const DATED_SNAPSHOT = /^\d{2,4}(?:-?\d{2}){1,2}$/;
|
|
2
|
+
const PORTKEY_CACHE_HITS = new Set(["hit", "semantic hit"]);
|
|
3
|
+
function objectOf(value) {
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
5
|
+
? value
|
|
6
|
+
: undefined;
|
|
7
|
+
}
|
|
8
|
+
function nonEmptyString(value) {
|
|
9
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
10
|
+
}
|
|
11
|
+
export function servedModel(body) {
|
|
12
|
+
return nonEmptyString(objectOf(body)?.model);
|
|
13
|
+
}
|
|
14
|
+
function modelParts(id) {
|
|
15
|
+
const segments = id.toLowerCase().split("/");
|
|
16
|
+
return {
|
|
17
|
+
name: segments[segments.length - 1],
|
|
18
|
+
...(segments.length > 1 ? { vendor: segments[segments.length - 2] } : {}),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function sameModel(requested, served) {
|
|
22
|
+
const want = modelParts(requested);
|
|
23
|
+
const got = modelParts(served);
|
|
24
|
+
if (want.vendor !== undefined &&
|
|
25
|
+
got.vendor !== undefined &&
|
|
26
|
+
want.vendor !== got.vendor) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (got.name === want.name)
|
|
30
|
+
return true;
|
|
31
|
+
const prefix = `${want.name}-`;
|
|
32
|
+
return (got.name.startsWith(prefix) &&
|
|
33
|
+
DATED_SNAPSHOT.test(got.name.slice(prefix.length)));
|
|
34
|
+
}
|
|
35
|
+
function header(headers, name) {
|
|
36
|
+
if (headers instanceof Headers)
|
|
37
|
+
return headers.get(name) ?? undefined;
|
|
38
|
+
const value = headers[name];
|
|
39
|
+
return typeof value === "string" ? value : value?.join(", ");
|
|
40
|
+
}
|
|
41
|
+
function substitution(kind, evidence) {
|
|
42
|
+
return { kind, evidence: evidence.slice(0, 200) };
|
|
43
|
+
}
|
|
44
|
+
export function responseSubstitution(input) {
|
|
45
|
+
const body = objectOf(input.body);
|
|
46
|
+
const extra = objectOf(body?.extra_fields);
|
|
47
|
+
const served = servedModel(body);
|
|
48
|
+
if (served !== undefined && !sameModel(input.requestedModel, served)) {
|
|
49
|
+
return substitution("model", `served ${served} for requested ${input.requestedModel}`);
|
|
50
|
+
}
|
|
51
|
+
const fallback = nonEmptyString(objectOf(extra?.routing_info)?.server_side_fallback_model);
|
|
52
|
+
if (fallback !== undefined) {
|
|
53
|
+
return substitution("model", `bifrost server-side fallback served ${fallback}`);
|
|
54
|
+
}
|
|
55
|
+
const cacheStatus = header(input.headers, "x-portkey-cache-status");
|
|
56
|
+
if (cacheStatus !== undefined &&
|
|
57
|
+
PORTKEY_CACHE_HITS.has(cacheStatus.toLowerCase())) {
|
|
58
|
+
return substitution("cache", `x-portkey-cache-status: ${cacheStatus}`);
|
|
59
|
+
}
|
|
60
|
+
const cacheDebug = objectOf(extra?.cache_debug);
|
|
61
|
+
if (cacheDebug?.cache_hit === true) {
|
|
62
|
+
const hitType = cacheDebug.hit_type;
|
|
63
|
+
return substitution("cache", `bifrost cache hit${typeof hitType === "string" ? ` (${hitType})` : ""}`);
|
|
64
|
+
}
|
|
65
|
+
const hookResults = objectOf(body?.hook_results);
|
|
66
|
+
const transformed = [
|
|
67
|
+
hookResults?.before_request_hooks,
|
|
68
|
+
hookResults?.after_request_hooks,
|
|
69
|
+
]
|
|
70
|
+
.flatMap((hooks) => (Array.isArray(hooks) ? hooks : []))
|
|
71
|
+
.map(objectOf)
|
|
72
|
+
.find((hook) => hook?.transformed === true);
|
|
73
|
+
if (transformed !== undefined) {
|
|
74
|
+
const id = nonEmptyString(transformed.id) ?? "unnamed";
|
|
75
|
+
return substitution("request", `portkey hook ${id} transformed the call`);
|
|
76
|
+
}
|
|
77
|
+
const dropped = [
|
|
78
|
+
extra?.dropped_compat_plugin_params,
|
|
79
|
+
extra?.dropped_unsupported_tools,
|
|
80
|
+
].flatMap((items) => (Array.isArray(items) ? items : []));
|
|
81
|
+
if (dropped.length > 0) {
|
|
82
|
+
return substitution("request", `bifrost dropped ${dropped.join(", ")}`);
|
|
83
|
+
}
|
|
84
|
+
const converted = nonEmptyString(extra?.converted_request_type);
|
|
85
|
+
if (converted !== undefined) {
|
|
86
|
+
return substitution("request", `bifrost converted the request to ${converted}`);
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
@@ -30,6 +30,11 @@ const heartbeat = setInterval(
|
|
|
30
30
|
500,
|
|
31
31
|
);
|
|
32
32
|
heartbeat.unref();
|
|
33
|
+
const deadlineMs = Number(process.env.RM_DEADLINE_MS ?? 0);
|
|
34
|
+
let active = null;
|
|
35
|
+
if (deadlineMs > 0) {
|
|
36
|
+
setTimeout(() => active?.kill("SIGKILL"), deadlineMs).unref();
|
|
37
|
+
}
|
|
33
38
|
|
|
34
39
|
function start(command, stdoutPath, stderrPath, flags = "w") {
|
|
35
40
|
const stdout = createWriteStream(stdoutPath, { flags });
|
|
@@ -38,6 +43,7 @@ function start(command, stdoutPath, stderrPath, flags = "w") {
|
|
|
38
43
|
cwd: APP_ROOT,
|
|
39
44
|
stdio: ["ignore", "pipe", "pipe"],
|
|
40
45
|
});
|
|
46
|
+
active = child;
|
|
41
47
|
child.stdout.pipe(stdout);
|
|
42
48
|
child.stderr.pipe(stderr);
|
|
43
49
|
const closed = new Promise((resolve, reject) => {
|
|
@@ -12,6 +12,7 @@ import { join } from "node:path";
|
|
|
12
12
|
import { PassThrough } from "node:stream";
|
|
13
13
|
|
|
14
14
|
import { hopByHopHeaders } from "./headers.js";
|
|
15
|
+
import { responseSubstitution, servedModel } from "../provenance.js";
|
|
15
16
|
import { classifyStream } from "../transport/stream.js";
|
|
16
17
|
|
|
17
18
|
const maxRequestBytes = 10 * 1024 * 1024;
|
|
@@ -69,7 +70,32 @@ function parseConfig() {
|
|
|
69
70
|
"RM_PRICING_TABLE values must contain non-negative input and output prices",
|
|
70
71
|
);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
if (
|
|
74
|
+
pricing.maxOutputTokens !== undefined &&
|
|
75
|
+
(!Number.isSafeInteger(pricing.maxOutputTokens) ||
|
|
76
|
+
pricing.maxOutputTokens <= 0)
|
|
77
|
+
) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"RM_PRICING_TABLE maxOutputTokens must be a positive integer",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
pricingTable[model] = {
|
|
83
|
+
input: pricing.input,
|
|
84
|
+
output: pricing.output,
|
|
85
|
+
...(pricing.maxOutputTokens === undefined
|
|
86
|
+
? {}
|
|
87
|
+
: { maxOutputTokens: pricing.maxOutputTokens }),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const defaultMaxOutputTokens = Number(
|
|
92
|
+
requiredEnv("RM_DEFAULT_MAX_OUTPUT_TOKENS"),
|
|
93
|
+
);
|
|
94
|
+
if (
|
|
95
|
+
!Number.isSafeInteger(defaultMaxOutputTokens) ||
|
|
96
|
+
defaultMaxOutputTokens <= 0
|
|
97
|
+
) {
|
|
98
|
+
throw new Error("RM_DEFAULT_MAX_OUTPUT_TOKENS must be a positive integer");
|
|
73
99
|
}
|
|
74
100
|
|
|
75
101
|
const lease = jsonEnv("RM_BUDGET_LEASE");
|
|
@@ -88,6 +114,21 @@ function parseConfig() {
|
|
|
88
114
|
throw new Error("RM_EGRESS_URL must use http or https");
|
|
89
115
|
}
|
|
90
116
|
|
|
117
|
+
const requestHeaders =
|
|
118
|
+
process.env.RM_REQUEST_HEADERS === undefined
|
|
119
|
+
? {}
|
|
120
|
+
: jsonEnv("RM_REQUEST_HEADERS");
|
|
121
|
+
if (
|
|
122
|
+
!isObject(requestHeaders) ||
|
|
123
|
+
Object.entries(requestHeaders).some(
|
|
124
|
+
([name, value]) => name.length === 0 || typeof value !== "string",
|
|
125
|
+
)
|
|
126
|
+
) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"RM_REQUEST_HEADERS must map header names to string values",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
91
132
|
return {
|
|
92
133
|
runId: requiredEnv("RM_RUN_ID"),
|
|
93
134
|
caseId: requiredEnv("RM_CASE_ID"),
|
|
@@ -98,7 +139,9 @@ function parseConfig() {
|
|
|
98
139
|
egressUrl,
|
|
99
140
|
swapPolicy,
|
|
100
141
|
pricingTable,
|
|
142
|
+
defaultMaxOutputTokens,
|
|
101
143
|
lease,
|
|
144
|
+
requestHeaders,
|
|
102
145
|
};
|
|
103
146
|
}
|
|
104
147
|
|
|
@@ -222,12 +265,13 @@ function responseHeaders(headers) {
|
|
|
222
265
|
return forwarded;
|
|
223
266
|
}
|
|
224
267
|
|
|
225
|
-
function requestHeaders(headers, bodyLength) {
|
|
268
|
+
function requestHeaders(headers, bodyLength, configured) {
|
|
226
269
|
const forwarded = {};
|
|
227
270
|
for (const [name, value] of Object.entries(headers)) {
|
|
228
271
|
if (
|
|
229
272
|
value !== undefined &&
|
|
230
273
|
!hopByHopHeaders.has(name) &&
|
|
274
|
+
name !== "accept-encoding" &&
|
|
231
275
|
name !== "authorization" &&
|
|
232
276
|
name !== "host" &&
|
|
233
277
|
name !== "content-length"
|
|
@@ -235,6 +279,8 @@ function requestHeaders(headers, bodyLength) {
|
|
|
235
279
|
forwarded[name] = value;
|
|
236
280
|
}
|
|
237
281
|
}
|
|
282
|
+
Object.assign(forwarded, configured);
|
|
283
|
+
forwarded["accept-encoding"] = "identity";
|
|
238
284
|
forwarded["content-length"] = String(bodyLength);
|
|
239
285
|
return forwarded;
|
|
240
286
|
}
|
|
@@ -288,7 +334,11 @@ function requestUpstream(
|
|
|
288
334
|
return new Promise((resolve, reject) => {
|
|
289
335
|
const request = send(
|
|
290
336
|
url,
|
|
291
|
-
{
|
|
337
|
+
{
|
|
338
|
+
method,
|
|
339
|
+
path: `${url.pathname.replace(/\/$/, "")}${requestTarget}`,
|
|
340
|
+
headers,
|
|
341
|
+
},
|
|
292
342
|
(response) => {
|
|
293
343
|
clearTimeout(deadline);
|
|
294
344
|
resolve(response);
|
|
@@ -437,6 +487,7 @@ async function forwardStreaming(upstream, outgoing, status, spoolSink) {
|
|
|
437
487
|
spoolPath: result.spoolPath ?? null,
|
|
438
488
|
finishedWithoutSentinel: result.finishedWithoutSentinel === true,
|
|
439
489
|
upstreamFailed,
|
|
490
|
+
model: result.model ?? null,
|
|
440
491
|
};
|
|
441
492
|
}
|
|
442
493
|
|
|
@@ -489,6 +540,7 @@ async function forwardNonStreaming(upstream, outgoing, status) {
|
|
|
489
540
|
streamOutcome: "completed",
|
|
490
541
|
usage: isObject(body) ? (body.usage ?? null) : null,
|
|
491
542
|
upstreamFailed: false,
|
|
543
|
+
body,
|
|
492
544
|
};
|
|
493
545
|
} catch {
|
|
494
546
|
return { streamOutcome: "truncated", usage: null, upstreamFailed: false };
|
|
@@ -624,13 +676,14 @@ async function main() {
|
|
|
624
676
|
return;
|
|
625
677
|
}
|
|
626
678
|
|
|
627
|
-
const
|
|
679
|
+
const requestedLimit =
|
|
680
|
+
parsed?.max_completion_tokens ?? parsed?.max_tokens ?? undefined;
|
|
628
681
|
if (
|
|
629
682
|
!isObject(parsed) ||
|
|
630
683
|
typeof parsed.model !== "string" ||
|
|
631
684
|
parsed.model.length === 0 ||
|
|
632
|
-
|
|
633
|
-
|
|
685
|
+
(requestedLimit !== undefined &&
|
|
686
|
+
(!Number.isSafeInteger(requestedLimit) || requestedLimit < 0))
|
|
634
687
|
) {
|
|
635
688
|
recordLost({
|
|
636
689
|
attemptGroup,
|
|
@@ -640,13 +693,26 @@ async function main() {
|
|
|
640
693
|
startedAt,
|
|
641
694
|
});
|
|
642
695
|
sendJson(outgoing, 400, {
|
|
643
|
-
error:
|
|
696
|
+
error:
|
|
697
|
+
"Request body requires model; max_completion_tokens or max_tokens must be a non-negative integer when present.",
|
|
644
698
|
});
|
|
645
699
|
return;
|
|
646
700
|
}
|
|
647
701
|
|
|
702
|
+
const swapped = stepId in config.swapPolicy;
|
|
648
703
|
const model = config.swapPolicy[stepId] ?? parsed.model;
|
|
649
|
-
const rewritten = {
|
|
704
|
+
const rewritten = {
|
|
705
|
+
...parsed,
|
|
706
|
+
model,
|
|
707
|
+
...(parsed.stream === true
|
|
708
|
+
? {
|
|
709
|
+
stream_options: {
|
|
710
|
+
...(isObject(parsed.stream_options) ? parsed.stream_options : {}),
|
|
711
|
+
include_usage: true,
|
|
712
|
+
},
|
|
713
|
+
}
|
|
714
|
+
: {}),
|
|
715
|
+
};
|
|
650
716
|
const forwardedBody = Buffer.from(JSON.stringify(rewritten));
|
|
651
717
|
const pricing = config.pricingTable[model];
|
|
652
718
|
if (pricing === undefined) {
|
|
@@ -662,6 +728,10 @@ async function main() {
|
|
|
662
728
|
});
|
|
663
729
|
return;
|
|
664
730
|
}
|
|
731
|
+
const maxTokens =
|
|
732
|
+
requestedLimit ??
|
|
733
|
+
pricing.maxOutputTokens ??
|
|
734
|
+
config.defaultMaxOutputTokens;
|
|
665
735
|
|
|
666
736
|
const estimatedInputTokens = forwardedBody.length;
|
|
667
737
|
const estimatedWorstCaseUsd =
|
|
@@ -726,21 +796,25 @@ async function main() {
|
|
|
726
796
|
upstreamSource: null,
|
|
727
797
|
upstreamFailed: false,
|
|
728
798
|
};
|
|
799
|
+
let provenance = {};
|
|
729
800
|
try {
|
|
730
801
|
const upstream = await requestUpstream(
|
|
731
802
|
config.egressUrl,
|
|
732
803
|
incoming.url ?? "/",
|
|
733
804
|
incoming.method ?? "POST",
|
|
734
|
-
requestHeaders(
|
|
805
|
+
requestHeaders(
|
|
806
|
+
incoming.headers,
|
|
807
|
+
forwardedBody.length,
|
|
808
|
+
config.requestHeaders,
|
|
809
|
+
),
|
|
735
810
|
forwardedBody,
|
|
736
811
|
streamHardDeadlineMs,
|
|
737
812
|
);
|
|
738
813
|
const status = upstream.statusCode ?? 502;
|
|
739
|
-
const declaredSource = upstream.headers[egressSourceHeader];
|
|
740
814
|
const upstreamSource =
|
|
741
|
-
|
|
742
|
-
?
|
|
743
|
-
:
|
|
815
|
+
upstream.headers[egressSourceHeader] === "egress"
|
|
816
|
+
? "egress"
|
|
817
|
+
: "provider";
|
|
744
818
|
const forwarded =
|
|
745
819
|
rewritten.stream === true && status < 400
|
|
746
820
|
? await forwardStreaming(upstream, outgoing, status, {
|
|
@@ -755,6 +829,23 @@ async function main() {
|
|
|
755
829
|
upstreamStatus: status,
|
|
756
830
|
upstreamSource: forwarded.upstreamFailed ? "egress" : upstreamSource,
|
|
757
831
|
};
|
|
832
|
+
if (swapped && status < 400) {
|
|
833
|
+
const answered =
|
|
834
|
+
forwarded.body ??
|
|
835
|
+
(typeof forwarded.model === "string"
|
|
836
|
+
? { model: forwarded.model }
|
|
837
|
+
: undefined);
|
|
838
|
+
const served = servedModel(answered);
|
|
839
|
+
const substitution = responseSubstitution({
|
|
840
|
+
requestedModel: model,
|
|
841
|
+
headers: upstream.headers,
|
|
842
|
+
body: answered,
|
|
843
|
+
});
|
|
844
|
+
provenance = {
|
|
845
|
+
...(served === undefined ? {} : { servedModel: served }),
|
|
846
|
+
...(substitution === undefined ? {} : { substitution }),
|
|
847
|
+
};
|
|
848
|
+
}
|
|
758
849
|
} catch {
|
|
759
850
|
if (!outgoing.headersSent) {
|
|
760
851
|
sendJson(outgoing, 502, { error: "Egress request failed." });
|
|
@@ -790,6 +881,7 @@ async function main() {
|
|
|
790
881
|
...(result.finishedWithoutSentinel
|
|
791
882
|
? { finishedWithoutSentinel: true }
|
|
792
883
|
: {}),
|
|
884
|
+
...provenance,
|
|
793
885
|
usage,
|
|
794
886
|
responseSpoolPath: result.spoolPath,
|
|
795
887
|
costUsd: leaseChargeUsd,
|
|
@@ -65,13 +65,13 @@ function parseUsage(value) {
|
|
|
65
65
|
if (!isRecord(value) ||
|
|
66
66
|
typeof value.prompt_tokens !== "number" ||
|
|
67
67
|
typeof value.completion_tokens !== "number" ||
|
|
68
|
-
typeof value.total_tokens !== "number") {
|
|
68
|
+
(value.total_tokens !== undefined && typeof value.total_tokens !== "number")) {
|
|
69
69
|
return null;
|
|
70
70
|
}
|
|
71
71
|
return {
|
|
72
72
|
inputTokens: value.prompt_tokens,
|
|
73
73
|
outputTokens: value.completion_tokens,
|
|
74
|
-
totalTokens: value.total_tokens,
|
|
74
|
+
totalTokens: value.total_tokens ?? value.prompt_tokens + value.completion_tokens,
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
function parseEvent(data) {
|
|
@@ -121,6 +121,9 @@ function parseEvent(data) {
|
|
|
121
121
|
usage: value.usage === undefined || value.usage === null
|
|
122
122
|
? null
|
|
123
123
|
: parseUsage(value.usage),
|
|
124
|
+
...(typeof value.model === "string" && value.model.length > 0
|
|
125
|
+
? { model: value.model }
|
|
126
|
+
: {}),
|
|
124
127
|
};
|
|
125
128
|
}
|
|
126
129
|
function eventData(event) {
|
|
@@ -144,6 +147,7 @@ export async function classifyStream(byteStream, options) {
|
|
|
144
147
|
const collector = new ContentCollector(options.spoolSink, processingController.signal);
|
|
145
148
|
let chunks = 0;
|
|
146
149
|
let usage = null;
|
|
150
|
+
let model;
|
|
147
151
|
let sawFinish = false;
|
|
148
152
|
let selectedResult;
|
|
149
153
|
const result = (outcome, reason, finishedWithoutSentinel = false) => {
|
|
@@ -154,6 +158,7 @@ export async function classifyStream(byteStream, options) {
|
|
|
154
158
|
usage,
|
|
155
159
|
chunks,
|
|
156
160
|
...(finishedWithoutSentinel ? { finishedWithoutSentinel: true } : {}),
|
|
161
|
+
...(model === undefined ? {} : { model }),
|
|
157
162
|
};
|
|
158
163
|
return selectedResult;
|
|
159
164
|
};
|
|
@@ -283,6 +288,7 @@ export async function classifyStream(byteStream, options) {
|
|
|
283
288
|
throw error;
|
|
284
289
|
}
|
|
285
290
|
usage = event.usage ?? usage;
|
|
291
|
+
model ??= event.model;
|
|
286
292
|
sawFinish ||= event.finished;
|
|
287
293
|
}
|
|
288
294
|
}
|
package/docs/commands.md
CHANGED
|
@@ -38,8 +38,9 @@ Commands:
|
|
|
38
38
|
rollback [options] open a draft pull request restoring a prior model swap
|
|
39
39
|
drift [options] detect drift against the active replay corpus
|
|
40
40
|
watch [options] reconcile one open model-swap pull request
|
|
41
|
-
report
|
|
41
|
+
report [options] write report.md and report.json
|
|
42
42
|
status [options] summarize the current store
|
|
43
|
+
docs [name] print documentation packaged with this CLI
|
|
43
44
|
help [command] display help for command
|
|
44
45
|
```
|
|
45
46
|
|
|
@@ -51,7 +52,8 @@ Usage: rightmodeler init [options]
|
|
|
51
52
|
run the resumable Phase A pipeline
|
|
52
53
|
|
|
53
54
|
Options:
|
|
54
|
-
--traces <path> trace input file
|
|
55
|
+
--traces <path> trace input file or directory
|
|
56
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
55
57
|
--include-free include zero-priced models in candidate
|
|
56
58
|
shortlists
|
|
57
59
|
--modeb-config <path> versioned Mode B runtime configuration
|
|
@@ -62,6 +64,17 @@ Options:
|
|
|
62
64
|
--max-cost-usd <amount> optional hard spend cap in USD; omit to
|
|
63
65
|
run uncapped so every case and judge cell
|
|
64
66
|
completes
|
|
67
|
+
--max-concurrency <n> maximum concurrent provider requests
|
|
68
|
+
--pricing-file <path> JSON map from model id to per-token input
|
|
69
|
+
and output USD, for catalogs without
|
|
70
|
+
pricing
|
|
71
|
+
--header <header> extra HTTP header for every provider
|
|
72
|
+
request, as 'name: value' (repeatable)
|
|
73
|
+
--catalog-reference <url-or-path> upstream /models URL or file that fills
|
|
74
|
+
pricing, context and capabilities the
|
|
75
|
+
provider catalog lacks
|
|
76
|
+
--policy <path> release policy JSON file: quality floor,
|
|
77
|
+
shortlist size, model allow and deny lists
|
|
65
78
|
--evaluator <provider> external evaluator provider (choices:
|
|
66
79
|
"braintrust", "langfuse", "langsmith",
|
|
67
80
|
"promptfoo")
|
|
@@ -84,6 +97,8 @@ Options:
|
|
|
84
97
|
"ingest", "reconcile", "scrub", "corpus",
|
|
85
98
|
"audit-sample", "shortlist", "replay",
|
|
86
99
|
"aggregate", "confirm", "report")
|
|
100
|
+
--code-graph <path> Graphify graph.json for static code
|
|
101
|
+
context in the report; never evidence
|
|
87
102
|
--yes accept the newest discovered trace without
|
|
88
103
|
prompting
|
|
89
104
|
-h, --help display help for command
|
|
@@ -97,7 +112,8 @@ Usage: rightmodeler estimate [options]
|
|
|
97
112
|
project replay spend before paid model calls
|
|
98
113
|
|
|
99
114
|
Options:
|
|
100
|
-
--traces <path> trace input file
|
|
115
|
+
--traces <path> trace input file or directory
|
|
116
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
101
117
|
--include-free include zero-priced models in candidate
|
|
102
118
|
shortlists
|
|
103
119
|
--modeb-config <path> versioned Mode B runtime configuration
|
|
@@ -108,6 +124,17 @@ Options:
|
|
|
108
124
|
--max-cost-usd <amount> optional hard spend cap in USD; omit to
|
|
109
125
|
run uncapped so every case and judge cell
|
|
110
126
|
completes
|
|
127
|
+
--max-concurrency <n> maximum concurrent provider requests
|
|
128
|
+
--pricing-file <path> JSON map from model id to per-token input
|
|
129
|
+
and output USD, for catalogs without
|
|
130
|
+
pricing
|
|
131
|
+
--header <header> extra HTTP header for every provider
|
|
132
|
+
request, as 'name: value' (repeatable)
|
|
133
|
+
--catalog-reference <url-or-path> upstream /models URL or file that fills
|
|
134
|
+
pricing, context and capabilities the
|
|
135
|
+
provider catalog lacks
|
|
136
|
+
--policy <path> release policy JSON file: quality floor,
|
|
137
|
+
shortlist size, model allow and deny lists
|
|
111
138
|
--evaluator <provider> external evaluator provider (choices:
|
|
112
139
|
"braintrust", "langfuse", "langsmith",
|
|
113
140
|
"promptfoo")
|
|
@@ -140,7 +167,8 @@ Usage: rightmodeler scan [options]
|
|
|
140
167
|
run through the scan stage
|
|
141
168
|
|
|
142
169
|
Options:
|
|
143
|
-
--traces <path> trace input file
|
|
170
|
+
--traces <path> trace input file or directory
|
|
171
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
144
172
|
--include-free include zero-priced models in candidate shortlists
|
|
145
173
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
146
174
|
-h, --help display help for command
|
|
@@ -154,7 +182,8 @@ Usage: rightmodeler ingest [options]
|
|
|
154
182
|
run through the ingest stage
|
|
155
183
|
|
|
156
184
|
Options:
|
|
157
|
-
--traces <path> trace input file
|
|
185
|
+
--traces <path> trace input file or directory
|
|
186
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
158
187
|
--include-free include zero-priced models in candidate shortlists
|
|
159
188
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
160
189
|
-h, --help display help for command
|
|
@@ -168,7 +197,8 @@ Usage: rightmodeler reconcile [options]
|
|
|
168
197
|
run through the reconcile stage
|
|
169
198
|
|
|
170
199
|
Options:
|
|
171
|
-
--traces <path> trace input file
|
|
200
|
+
--traces <path> trace input file or directory
|
|
201
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
172
202
|
--include-free include zero-priced models in candidate shortlists
|
|
173
203
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
174
204
|
-h, --help display help for command
|
|
@@ -182,7 +212,8 @@ Usage: rightmodeler scrub [options]
|
|
|
182
212
|
run through the scrub stage
|
|
183
213
|
|
|
184
214
|
Options:
|
|
185
|
-
--traces <path> trace input file
|
|
215
|
+
--traces <path> trace input file or directory
|
|
216
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
186
217
|
--include-free include zero-priced models in candidate shortlists
|
|
187
218
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
188
219
|
-h, --help display help for command
|
|
@@ -196,7 +227,8 @@ Usage: rightmodeler shortlist [options]
|
|
|
196
227
|
run through the shortlist stage
|
|
197
228
|
|
|
198
229
|
Options:
|
|
199
|
-
--traces <path> trace input file
|
|
230
|
+
--traces <path> trace input file or directory
|
|
231
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
200
232
|
--include-free include zero-priced models in candidate shortlists
|
|
201
233
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
202
234
|
-h, --help display help for command
|
|
@@ -210,7 +242,8 @@ Usage: rightmodeler replay [options]
|
|
|
210
242
|
run through the replay stage
|
|
211
243
|
|
|
212
244
|
Options:
|
|
213
|
-
--traces <path> trace input file
|
|
245
|
+
--traces <path> trace input file or directory
|
|
246
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
214
247
|
--include-free include zero-priced models in candidate
|
|
215
248
|
shortlists
|
|
216
249
|
--modeb-config <path> versioned Mode B runtime configuration
|
|
@@ -221,6 +254,17 @@ Options:
|
|
|
221
254
|
--max-cost-usd <amount> optional hard spend cap in USD; omit to
|
|
222
255
|
run uncapped so every case and judge cell
|
|
223
256
|
completes
|
|
257
|
+
--max-concurrency <n> maximum concurrent provider requests
|
|
258
|
+
--pricing-file <path> JSON map from model id to per-token input
|
|
259
|
+
and output USD, for catalogs without
|
|
260
|
+
pricing
|
|
261
|
+
--header <header> extra HTTP header for every provider
|
|
262
|
+
request, as 'name: value' (repeatable)
|
|
263
|
+
--catalog-reference <url-or-path> upstream /models URL or file that fills
|
|
264
|
+
pricing, context and capabilities the
|
|
265
|
+
provider catalog lacks
|
|
266
|
+
--policy <path> release policy JSON file: quality floor,
|
|
267
|
+
shortlist size, model allow and deny lists
|
|
224
268
|
--evaluator <provider> external evaluator provider (choices:
|
|
225
269
|
"braintrust", "langfuse", "langsmith",
|
|
226
270
|
"promptfoo")
|
|
@@ -252,7 +296,8 @@ Usage: rightmodeler aggregate [options]
|
|
|
252
296
|
run through the aggregate stage
|
|
253
297
|
|
|
254
298
|
Options:
|
|
255
|
-
--traces <path> trace input file
|
|
299
|
+
--traces <path> trace input file or directory
|
|
300
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
256
301
|
--include-free include zero-priced models in candidate shortlists
|
|
257
302
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
258
303
|
-h, --help display help for command
|
|
@@ -266,7 +311,8 @@ Usage: rightmodeler confirm [options]
|
|
|
266
311
|
run through the confirm stage
|
|
267
312
|
|
|
268
313
|
Options:
|
|
269
|
-
--traces <path> trace input file
|
|
314
|
+
--traces <path> trace input file or directory
|
|
315
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
270
316
|
--include-free include zero-priced models in candidate
|
|
271
317
|
shortlists
|
|
272
318
|
--modeb-config <path> versioned Mode B runtime configuration
|
|
@@ -277,6 +323,17 @@ Options:
|
|
|
277
323
|
--max-cost-usd <amount> optional hard spend cap in USD; omit to
|
|
278
324
|
run uncapped so every case and judge cell
|
|
279
325
|
completes
|
|
326
|
+
--max-concurrency <n> maximum concurrent provider requests
|
|
327
|
+
--pricing-file <path> JSON map from model id to per-token input
|
|
328
|
+
and output USD, for catalogs without
|
|
329
|
+
pricing
|
|
330
|
+
--header <header> extra HTTP header for every provider
|
|
331
|
+
request, as 'name: value' (repeatable)
|
|
332
|
+
--catalog-reference <url-or-path> upstream /models URL or file that fills
|
|
333
|
+
pricing, context and capabilities the
|
|
334
|
+
provider catalog lacks
|
|
335
|
+
--policy <path> release policy JSON file: quality floor,
|
|
336
|
+
shortlist size, model allow and deny lists
|
|
280
337
|
--evaluator <provider> external evaluator provider (choices:
|
|
281
338
|
"braintrust", "langfuse", "langsmith",
|
|
282
339
|
"promptfoo")
|
|
@@ -305,7 +362,8 @@ Usage: rightmodeler corpus [options] [command]
|
|
|
305
362
|
build or import the replay corpus
|
|
306
363
|
|
|
307
364
|
Options:
|
|
308
|
-
--traces <path> trace input file
|
|
365
|
+
--traces <path> trace input file or directory
|
|
366
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
309
367
|
--include-free include zero-priced models in candidate shortlists
|
|
310
368
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
311
369
|
-h, --help display help for command
|
|
@@ -375,7 +433,8 @@ Usage: rightmodeler audit sample [options]
|
|
|
375
433
|
write the audit worksheet without blocking
|
|
376
434
|
|
|
377
435
|
Options:
|
|
378
|
-
--traces <path> trace input file
|
|
436
|
+
--traces <path> trace input file or directory
|
|
437
|
+
--matchers <path> declarative matcher definitions JSON file
|
|
379
438
|
--include-free include zero-priced models in candidate shortlists
|
|
380
439
|
--modeb-config <path> versioned Mode B runtime configuration JSON file
|
|
381
440
|
-h, --help display help for command
|
|
@@ -402,9 +461,14 @@ open a draft pull request for proven model swaps
|
|
|
402
461
|
|
|
403
462
|
Options:
|
|
404
463
|
--owner <owner> GitHub repository owner
|
|
405
|
-
--github-
|
|
464
|
+
--github-repo <repo> GitHub repository name (default: the repository
|
|
465
|
+
directory name)
|
|
466
|
+
--github-base-url <url> GitHub API base URL (default:
|
|
467
|
+
"https://api.github.com")
|
|
406
468
|
--github-token-env <name> environment variable containing the GitHub token
|
|
407
469
|
--dry-run run all machine gates without writing GitHub state
|
|
470
|
+
--code-graph <path> Graphify graph.json for static code context in the
|
|
471
|
+
pull request body; never evidence
|
|
408
472
|
-h, --help display help for command
|
|
409
473
|
```
|
|
410
474
|
|
|
@@ -417,8 +481,11 @@ open a draft pull request restoring a prior model swap
|
|
|
417
481
|
|
|
418
482
|
Options:
|
|
419
483
|
--owner <owner> GitHub repository owner
|
|
484
|
+
--github-repo <repo> GitHub repository name (default: the repository
|
|
485
|
+
directory name)
|
|
420
486
|
--pr <number> merged pull request number
|
|
421
|
-
--github-base-url <url> GitHub API base URL
|
|
487
|
+
--github-base-url <url> GitHub API base URL (default:
|
|
488
|
+
"https://api.github.com")
|
|
422
489
|
--github-token-env <name> environment variable containing the GitHub token
|
|
423
490
|
-h, --help display help for command
|
|
424
491
|
```
|
|
@@ -474,9 +541,11 @@ reconcile one open model-swap pull request
|
|
|
474
541
|
|
|
475
542
|
Options:
|
|
476
543
|
--owner <owner> GitHub repository owner
|
|
477
|
-
--github-repo <repo> GitHub repository name
|
|
544
|
+
--github-repo <repo> GitHub repository name (default: the repository
|
|
545
|
+
directory name)
|
|
478
546
|
--pr <number> pull request number
|
|
479
|
-
--github-base-url <url> GitHub API base URL
|
|
547
|
+
--github-base-url <url> GitHub API base URL (default:
|
|
548
|
+
"https://api.github.com")
|
|
480
549
|
--github-token-env <name> environment variable containing the GitHub token
|
|
481
550
|
-h, --help display help for command
|
|
482
551
|
```
|
|
@@ -489,7 +558,9 @@ Usage: rightmodeler report [options]
|
|
|
489
558
|
write report.md and report.json
|
|
490
559
|
|
|
491
560
|
Options:
|
|
492
|
-
-
|
|
561
|
+
--code-graph <path> Graphify graph.json for static code context in the
|
|
562
|
+
report; never evidence
|
|
563
|
+
-h, --help display help for command
|
|
493
564
|
```
|
|
494
565
|
|
|
495
566
|
## `rightmodeler status`
|
|
@@ -503,3 +574,19 @@ Options:
|
|
|
503
574
|
--run <runId> report one detached replay run
|
|
504
575
|
-h, --help display help for command
|
|
505
576
|
```
|
|
577
|
+
|
|
578
|
+
## `rightmodeler docs`
|
|
579
|
+
|
|
580
|
+
```text
|
|
581
|
+
Usage: rightmodeler docs [options] [name]
|
|
582
|
+
|
|
583
|
+
print documentation packaged with this CLI
|
|
584
|
+
|
|
585
|
+
Arguments:
|
|
586
|
+
name packaged document name (choices: "commands", "evaluators",
|
|
587
|
+
"exit-codes", "gateways", "getting-started", "github",
|
|
588
|
+
"github-actions", "modeb")
|
|
589
|
+
|
|
590
|
+
Options:
|
|
591
|
+
-h, --help display help for command
|
|
592
|
+
```
|