bitfab 0.61.2 → 0.61.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/dist/autoTrace.cjs.map +1 -1
- package/dist/autoTrace.d.cts +1 -0
- package/dist/autoTrace.d.ts +1 -0
- package/dist/autoTrace.js +1 -1
- package/dist/{chunk-INA6W7OB.js → chunk-6IMUONDN.js} +7 -3
- package/dist/chunk-6IMUONDN.js.map +1 -0
- package/dist/{chunk-ONZFMRRF.js → chunk-HVEZHLVQ.js} +6 -3
- package/dist/chunk-HVEZHLVQ.js.map +1 -0
- package/dist/{chunk-A4UVO6BQ.js → chunk-IZUZ5NBB.js} +306 -7
- package/dist/chunk-IZUZ5NBB.js.map +1 -0
- package/dist/{chunk-BMJNDIVA.js → chunk-K4XCWQBC.js} +1 -1
- package/dist/chunk-K4XCWQBC.js.map +1 -0
- package/dist/{chunk-LUOCT4HG.js → chunk-PAUXHTNF.js} +38 -7
- package/dist/chunk-PAUXHTNF.js.map +1 -0
- package/dist/{chunk-MTK6PZHA.js → chunk-WOHIAKZF.js} +3 -3
- package/dist/chunk-WOHIAKZF.js.map +1 -0
- package/dist/{http-KM6QWKAP.js → http-3IYU5Z54.js} +2 -2
- package/dist/{http-2R6MSRVA.js → http-BYICBOWP.js} +2 -2
- package/dist/index.cjs +355 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -3
- package/dist/index.d.ts +120 -3
- package/dist/index.js +4 -4
- package/dist/node.cjs +355 -7
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +4 -4
- package/dist/{replay-6UUBJBLK.js → replay-SXHX5BBU.js} +3 -3
- package/dist/replayCli.js +2 -2
- package/dist/seedCli.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-A4UVO6BQ.js.map +0 -1
- package/dist/chunk-BMJNDIVA.js.map +0 -1
- package/dist/chunk-INA6W7OB.js.map +0 -1
- package/dist/chunk-LUOCT4HG.js.map +0 -1
- package/dist/chunk-MTK6PZHA.js.map +0 -1
- package/dist/chunk-ONZFMRRF.js.map +0 -1
- /package/dist/{http-2R6MSRVA.js.map → http-3IYU5Z54.js.map} +0 -0
- /package/dist/{http-KM6QWKAP.js.map → http-BYICBOWP.js.map} +0 -0
- /package/dist/{replay-6UUBJBLK.js.map → replay-SXHX5BBU.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -51,7 +51,7 @@ var __version__, __packageName__;
|
|
|
51
51
|
var init_version_generated = __esm({
|
|
52
52
|
"src/version.generated.ts"() {
|
|
53
53
|
"use strict";
|
|
54
|
-
__version__ = "0.61.
|
|
54
|
+
__version__ = "0.61.3";
|
|
55
55
|
__packageName__ = "bitfab";
|
|
56
56
|
}
|
|
57
57
|
});
|
|
@@ -1077,6 +1077,7 @@ var init_simulationPlan = __esm({
|
|
|
1077
1077
|
"input_meta",
|
|
1078
1078
|
"output",
|
|
1079
1079
|
"output_meta",
|
|
1080
|
+
"replay_recording",
|
|
1080
1081
|
"prompt"
|
|
1081
1082
|
];
|
|
1082
1083
|
SimulationPlan = class {
|
|
@@ -2891,8 +2892,11 @@ var init_http = __esm({
|
|
|
2891
2892
|
* Start a replay session by fetching historical traces.
|
|
2892
2893
|
* Blocking call - creates an experiment and returns lightweight item references.
|
|
2893
2894
|
*/
|
|
2894
|
-
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions) {
|
|
2895
|
+
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions, experimentalSelectiveReplay) {
|
|
2895
2896
|
const payload = { traceFunctionKey };
|
|
2897
|
+
if (experimentalSelectiveReplay !== void 0) {
|
|
2898
|
+
payload.experimentalSelectiveReplay = experimentalSelectiveReplay;
|
|
2899
|
+
}
|
|
2896
2900
|
if (limit !== void 0) {
|
|
2897
2901
|
payload.limit = limit;
|
|
2898
2902
|
}
|
|
@@ -3616,6 +3620,291 @@ var init_integrationFixture = __esm({
|
|
|
3616
3620
|
}
|
|
3617
3621
|
});
|
|
3618
3622
|
|
|
3623
|
+
// src/selectiveReplay.ts
|
|
3624
|
+
function replayNodeKey(node) {
|
|
3625
|
+
return JSON.stringify([node.traceFunctionKey, node.spanName]);
|
|
3626
|
+
}
|
|
3627
|
+
function jsonFingerprint(value) {
|
|
3628
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3629
|
+
function encode(input) {
|
|
3630
|
+
if (input === null || typeof input === "boolean") {
|
|
3631
|
+
return JSON.stringify(input);
|
|
3632
|
+
}
|
|
3633
|
+
if (typeof input === "string" && !input.startsWith("<unserializable")) {
|
|
3634
|
+
return JSON.stringify(input);
|
|
3635
|
+
}
|
|
3636
|
+
if (typeof input === "number" && Number.isFinite(input) && !Object.is(input, -0)) {
|
|
3637
|
+
return JSON.stringify(input);
|
|
3638
|
+
}
|
|
3639
|
+
if (typeof input !== "object" || input === null || seen.has(input)) {
|
|
3640
|
+
throw new Error("Not a lossless JSON value");
|
|
3641
|
+
}
|
|
3642
|
+
seen.add(input);
|
|
3643
|
+
const prototype = Object.getPrototypeOf(input);
|
|
3644
|
+
if (prototype !== (Array.isArray(input) ? Array.prototype : Object.prototype)) {
|
|
3645
|
+
throw new Error("Not a plain JSON object");
|
|
3646
|
+
}
|
|
3647
|
+
if (Object.getOwnPropertySymbols(input).length > 0) {
|
|
3648
|
+
throw new Error("Symbol properties are not recorded");
|
|
3649
|
+
}
|
|
3650
|
+
const keys = Object.keys(input);
|
|
3651
|
+
if (Object.getOwnPropertyNames(input).length !== keys.length + (Array.isArray(input) ? 1 : 0)) {
|
|
3652
|
+
throw new Error("Non-enumerable properties are not recorded");
|
|
3653
|
+
}
|
|
3654
|
+
if (Array.isArray(input) && (keys.length !== input.length || keys.some((key, index) => key !== String(index)))) {
|
|
3655
|
+
throw new Error("Not a dense JSON array");
|
|
3656
|
+
}
|
|
3657
|
+
const properties = keys.map((key) => {
|
|
3658
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
3659
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
3660
|
+
throw new Error("Accessor properties are not replay inputs");
|
|
3661
|
+
}
|
|
3662
|
+
return `${Array.isArray(input) ? "" : `${JSON.stringify(key)}:`}${encode(descriptor.value)}`;
|
|
3663
|
+
});
|
|
3664
|
+
return Array.isArray(input) ? `[${properties.join(",")}]` : `{${properties.join(",")}}`;
|
|
3665
|
+
}
|
|
3666
|
+
try {
|
|
3667
|
+
return encode(value);
|
|
3668
|
+
} catch {
|
|
3669
|
+
return void 0;
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
var init_selectiveReplay = __esm({
|
|
3673
|
+
"src/selectiveReplay.ts"() {
|
|
3674
|
+
"use strict";
|
|
3675
|
+
}
|
|
3676
|
+
});
|
|
3677
|
+
|
|
3678
|
+
// src/selectiveReplayRuntime.ts
|
|
3679
|
+
function validateSelectiveReplayOptions(options) {
|
|
3680
|
+
const validNodes = (nodes) => Array.isArray(nodes) && nodes.length <= 100 && nodes.every(
|
|
3681
|
+
(node) => typeof node?.traceFunctionKey === "string" && node.traceFunctionKey.length > 0 && node.traceFunctionKey.length <= 500 && typeof node.spanName === "string" && node.spanName.length > 0 && node.spanName.length <= 500
|
|
3682
|
+
);
|
|
3683
|
+
if (!options || !validNodes(options.mustRun) || options.mustRun.length === 0 || Object.keys(options).some((key) => key !== "mustRun")) {
|
|
3684
|
+
throw new BitfabError(
|
|
3685
|
+
"experimentalSelectiveReplay accepts only 1\u2013100 mustRun with traceFunctionKey and spanName. Declare replayReusable on eligible spans, not reusableNodes in the replay request."
|
|
3686
|
+
);
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
function policyKey(options) {
|
|
3690
|
+
return JSON.stringify(options.mustRun.map(replayNodeKey).sort());
|
|
3691
|
+
}
|
|
3692
|
+
var SelectiveReplayRuntime;
|
|
3693
|
+
var init_selectiveReplayRuntime = __esm({
|
|
3694
|
+
"src/selectiveReplayRuntime.ts"() {
|
|
3695
|
+
"use strict";
|
|
3696
|
+
init_errors();
|
|
3697
|
+
init_selectiveReplay();
|
|
3698
|
+
SelectiveReplayRuntime = class {
|
|
3699
|
+
constructor(plan, options) {
|
|
3700
|
+
this.plan = plan;
|
|
3701
|
+
this.options = options;
|
|
3702
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
3703
|
+
this.children = /* @__PURE__ */ new Map();
|
|
3704
|
+
this.runtimeParents = /* @__PURE__ */ new Map();
|
|
3705
|
+
this.consumed = /* @__PURE__ */ new Map();
|
|
3706
|
+
this.requiredScopes = /* @__PURE__ */ new Set();
|
|
3707
|
+
this.recordings = /* @__PURE__ */ new Map();
|
|
3708
|
+
this.reached = /* @__PURE__ */ new Set();
|
|
3709
|
+
this.decisions = [];
|
|
3710
|
+
this.assertionNames = /* @__PURE__ */ new Set();
|
|
3711
|
+
this.assertionScopes = /* @__PURE__ */ new Set();
|
|
3712
|
+
this.observedNames = /* @__PURE__ */ new Map();
|
|
3713
|
+
this.unresolvedAssertions = [];
|
|
3714
|
+
validateSelectiveReplayOptions(options);
|
|
3715
|
+
if (plan?.version !== 4 || !Array.isArray(plan.nodes) || plan.nodes.length === 0 || plan.nodes.length > 500 || !Array.isArray(plan.unresolvedMustRun) || !Array.isArray(plan.assertions) || !plan.options) {
|
|
3716
|
+
throw new BitfabError(
|
|
3717
|
+
"Server did not return a compatible selective replay plan. Update the server or disable experimentalSelectiveReplay."
|
|
3718
|
+
);
|
|
3719
|
+
}
|
|
3720
|
+
validateSelectiveReplayOptions(plan.options);
|
|
3721
|
+
if (policyKey(plan.options) !== policyKey(options)) {
|
|
3722
|
+
throw new BitfabError(
|
|
3723
|
+
"Selective replay plan does not match the requested mustRun nodes."
|
|
3724
|
+
);
|
|
3725
|
+
}
|
|
3726
|
+
this.required = new Set(options.mustRun.map(replayNodeKey));
|
|
3727
|
+
for (const node of plan.nodes) {
|
|
3728
|
+
if (typeof node.id !== "string" || this.nodes.has(node.id) || typeof node.traceFunctionKey !== "string" || typeof node.spanName !== "string" || typeof node.mustRun !== "boolean" || typeof node.reusable !== "boolean") {
|
|
3729
|
+
throw new BitfabError("Invalid selective replay node.");
|
|
3730
|
+
}
|
|
3731
|
+
this.nodes.set(node.id, node);
|
|
3732
|
+
if (node.recording !== void 0) {
|
|
3733
|
+
this.recordings.set(node.id, {
|
|
3734
|
+
input: node.recording.kwargs === void 0 ? jsonFingerprint(node.recording.input) : void 0,
|
|
3735
|
+
output: jsonFingerprint(node.recording.output)
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3738
|
+
if (node.parentId !== null) {
|
|
3739
|
+
const siblings = this.children.get(node.parentId) ?? [];
|
|
3740
|
+
siblings.push(node);
|
|
3741
|
+
this.children.set(node.parentId, siblings);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
const root = this.nodes.get(plan.rootId);
|
|
3745
|
+
if (!root || root.parentId !== null || !root.mustRun) {
|
|
3746
|
+
throw new BitfabError("Selective replay requires a live recorded root.");
|
|
3747
|
+
}
|
|
3748
|
+
const assertionIds = /* @__PURE__ */ new Set();
|
|
3749
|
+
let wholeTrace = false;
|
|
3750
|
+
for (const assertion of plan.assertions) {
|
|
3751
|
+
const target = assertion?.target;
|
|
3752
|
+
if (typeof assertion?.id !== "string" || assertion.id.length === 0 || assertionIds.has(assertion.id) || target !== null && (typeof target !== "object" || target.kind !== "output" && target.kind !== "span")) {
|
|
3753
|
+
throw new BitfabError(
|
|
3754
|
+
"Invalid selective replay assertion requirements."
|
|
3755
|
+
);
|
|
3756
|
+
}
|
|
3757
|
+
assertionIds.add(assertion.id);
|
|
3758
|
+
if (target === null) {
|
|
3759
|
+
wholeTrace = true;
|
|
3760
|
+
} else if (target.kind === "span") {
|
|
3761
|
+
if (typeof target.name !== "string" || target.name.length === 0 || target.occurrence !== void 0 && target.occurrence !== "first" && target.occurrence !== "last" && !(Number.isInteger(target.occurrence) && typeof target.occurrence === "number" && target.occurrence >= 0)) {
|
|
3762
|
+
throw new BitfabError("Invalid selective replay assertion target.");
|
|
3763
|
+
}
|
|
3764
|
+
this.assertionNames.add(target.name);
|
|
3765
|
+
const count = plan.nodes.filter(
|
|
3766
|
+
(node) => node.spanName === target.name
|
|
3767
|
+
).length;
|
|
3768
|
+
if (count <= (typeof target.occurrence === "number" ? target.occurrence : 0)) {
|
|
3769
|
+
this.unresolvedAssertions.push(assertion.id);
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
this.fullTraceForAssertions = wholeTrace || this.unresolvedAssertions.length > 0;
|
|
3774
|
+
const recordedKeys = new Set(plan.nodes.map(replayNodeKey));
|
|
3775
|
+
const unresolved = options.mustRun.filter(
|
|
3776
|
+
(node) => !recordedKeys.has(replayNodeKey(node))
|
|
3777
|
+
);
|
|
3778
|
+
if (JSON.stringify(unresolved.map(replayNodeKey).sort()) !== JSON.stringify(plan.unresolvedMustRun.map(replayNodeKey).sort())) {
|
|
3779
|
+
throw new BitfabError(
|
|
3780
|
+
"Selective replay plan has incomplete mustRun coverage."
|
|
3781
|
+
);
|
|
3782
|
+
}
|
|
3783
|
+
for (const node of plan.nodes) {
|
|
3784
|
+
const ancestors = /* @__PURE__ */ new Set([node.id]);
|
|
3785
|
+
let requiredByManifest = this.required.has(replayNodeKey(node));
|
|
3786
|
+
let requiredForAssertion = this.fullTraceForAssertions || this.assertionNames.has(node.spanName);
|
|
3787
|
+
let parentId = node.parentId;
|
|
3788
|
+
while (parentId !== null) {
|
|
3789
|
+
const parent = this.nodes.get(parentId);
|
|
3790
|
+
if (!parent || ancestors.has(parentId) || node.mustRun && !parent.mustRun) {
|
|
3791
|
+
throw new BitfabError("Invalid selective replay ancestry.");
|
|
3792
|
+
}
|
|
3793
|
+
ancestors.add(parentId);
|
|
3794
|
+
requiredByManifest || (requiredByManifest = this.required.has(replayNodeKey(parent)));
|
|
3795
|
+
requiredForAssertion || (requiredForAssertion = this.assertionNames.has(parent.spanName));
|
|
3796
|
+
parentId = parent.parentId;
|
|
3797
|
+
}
|
|
3798
|
+
if (!ancestors.has(plan.rootId) || (requiredByManifest || unresolved.length > 0) && !node.mustRun) {
|
|
3799
|
+
throw new BitfabError("Selective replay plan could hide required code.");
|
|
3800
|
+
}
|
|
3801
|
+
if (requiredForAssertion && !node.mustRun) {
|
|
3802
|
+
throw new BitfabError(
|
|
3803
|
+
"Selective replay plan could hide assertion evidence."
|
|
3804
|
+
);
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
enter(params) {
|
|
3809
|
+
const identity = {
|
|
3810
|
+
traceFunctionKey: params.traceFunctionKey,
|
|
3811
|
+
spanName: params.spanName
|
|
3812
|
+
};
|
|
3813
|
+
const key = replayNodeKey(identity);
|
|
3814
|
+
const fingerprint = jsonFingerprint(params.inputs);
|
|
3815
|
+
let node;
|
|
3816
|
+
let matchReason = "unmatched-call";
|
|
3817
|
+
if (params.parentSpanId === null) {
|
|
3818
|
+
node = this.nodes.get(this.plan.rootId);
|
|
3819
|
+
} else {
|
|
3820
|
+
const recordedParent = this.runtimeParents.get(params.parentSpanId);
|
|
3821
|
+
const consumed = this.consumed.get(params.parentSpanId) ?? /* @__PURE__ */ new Set();
|
|
3822
|
+
this.consumed.set(params.parentSpanId, consumed);
|
|
3823
|
+
const candidates = recordedParent ? (this.children.get(recordedParent) ?? []).filter(
|
|
3824
|
+
(candidate) => replayNodeKey(candidate) === key && !consumed.has(candidate.id)
|
|
3825
|
+
) : [];
|
|
3826
|
+
const exact = fingerprint === void 0 ? [] : candidates.filter(
|
|
3827
|
+
(candidate) => this.recordings.get(candidate.id)?.input === fingerprint
|
|
3828
|
+
);
|
|
3829
|
+
node = exact.length === 1 ? exact[0] : exact.length === 0 && candidates.length === 1 ? candidates[0] : void 0;
|
|
3830
|
+
if (node) {
|
|
3831
|
+
consumed.add(node.id);
|
|
3832
|
+
} else if (candidates.length > 0) {
|
|
3833
|
+
matchReason = "ambiguous-call";
|
|
3834
|
+
for (const candidate of candidates) {
|
|
3835
|
+
consumed.add(candidate.id);
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
this.runtimeParents.set(params.spanId, node?.id ?? null);
|
|
3840
|
+
const requiredByManifest = this.required.has(key) || params.parentSpanId !== null && this.requiredScopes.has(params.parentSpanId);
|
|
3841
|
+
if (requiredByManifest) {
|
|
3842
|
+
this.requiredScopes.add(params.spanId);
|
|
3843
|
+
}
|
|
3844
|
+
const requiredForAssertion = this.fullTraceForAssertions || this.assertionNames.has(params.spanName) || params.parentSpanId !== null && this.assertionScopes.has(params.parentSpanId);
|
|
3845
|
+
if (requiredForAssertion) {
|
|
3846
|
+
this.assertionScopes.add(params.spanId);
|
|
3847
|
+
}
|
|
3848
|
+
const mustRun = params.parentSpanId === null || requiredByManifest || node?.mustRun === true || requiredForAssertion || this.plan.unresolvedMustRun.length > 0;
|
|
3849
|
+
const recording = node === void 0 ? void 0 : this.recordings.get(node.id);
|
|
3850
|
+
const equalInputs = fingerprint !== void 0 && recording?.input === fingerprint;
|
|
3851
|
+
const reusable = recording?.output !== void 0 && equalInputs && params.canReuseOutput;
|
|
3852
|
+
const reason = mustRun ? requiredByManifest ? "must-run" : requiredForAssertion ? "assertion" : node?.reason ?? "unresolved-must-run" : !node ? matchReason : !equalInputs ? "input-drift-or-missing-recording" : !params.canReuseOutput ? "unsupported-output-boundary" : !params.safetyMock && !params.reuseAllowed ? "reuse-not-declared" : node.reason;
|
|
3853
|
+
const decision = (action, why) => {
|
|
3854
|
+
this.decisions.push({
|
|
3855
|
+
...identity,
|
|
3856
|
+
originalSpanId: node?.id ?? null,
|
|
3857
|
+
action,
|
|
3858
|
+
reason: why
|
|
3859
|
+
});
|
|
3860
|
+
};
|
|
3861
|
+
if (params.safetyMock && (mustRun || !reusable)) {
|
|
3862
|
+
decision("blocked", `safety-conflict:${reason}`);
|
|
3863
|
+
this.failure = new BitfabError(
|
|
3864
|
+
`Selective replay cannot execute or safely reuse safety-mocked node ${params.spanName} (${reason}).`
|
|
3865
|
+
);
|
|
3866
|
+
throw this.failure;
|
|
3867
|
+
}
|
|
3868
|
+
if (!mustRun && reusable && (params.safetyMock || node?.reusable === true && params.reuseAllowed)) {
|
|
3869
|
+
decision("mock", params.safetyMock ? "safety-mock" : "unchanged-inputs");
|
|
3870
|
+
return {
|
|
3871
|
+
mock: true,
|
|
3872
|
+
output: JSON.parse(recording.output)
|
|
3873
|
+
};
|
|
3874
|
+
}
|
|
3875
|
+
decision("run", reason);
|
|
3876
|
+
this.observedNames.set(
|
|
3877
|
+
params.spanName,
|
|
3878
|
+
(this.observedNames.get(params.spanName) ?? 0) + 1
|
|
3879
|
+
);
|
|
3880
|
+
if (this.required.has(key)) {
|
|
3881
|
+
this.reached.add(key);
|
|
3882
|
+
}
|
|
3883
|
+
return { mock: false };
|
|
3884
|
+
}
|
|
3885
|
+
assertNoSafetyConflict() {
|
|
3886
|
+
if (this.failure) {
|
|
3887
|
+
throw this.failure;
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
report() {
|
|
3891
|
+
return {
|
|
3892
|
+
decisions: this.decisions,
|
|
3893
|
+
mustRunNotReached: this.options.mustRun.filter(
|
|
3894
|
+
(node) => !this.reached.has(replayNodeKey(node))
|
|
3895
|
+
),
|
|
3896
|
+
unresolvedMustRun: this.plan.unresolvedMustRun,
|
|
3897
|
+
assertionIds: this.plan.assertions.map((assertion) => assertion.id),
|
|
3898
|
+
unresolvedAssertions: this.unresolvedAssertions,
|
|
3899
|
+
assertionTargetsNotReached: this.plan.assertions.flatMap(
|
|
3900
|
+
({ id, target }) => target?.kind === "span" && (this.observedNames.get(target.name) ?? 0) <= (typeof target.occurrence === "number" ? target.occurrence : 0) ? [id] : []
|
|
3901
|
+
)
|
|
3902
|
+
};
|
|
3903
|
+
}
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
});
|
|
3907
|
+
|
|
3619
3908
|
// src/replay.ts
|
|
3620
3909
|
var replay_exports = {};
|
|
3621
3910
|
__export(replay_exports, {
|
|
@@ -3871,7 +4160,7 @@ function buildMockTree(rootNode) {
|
|
|
3871
4160
|
}
|
|
3872
4161
|
return { spans };
|
|
3873
4162
|
}
|
|
3874
|
-
async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
|
|
4163
|
+
async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun, selectiveOptions) {
|
|
3875
4164
|
let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
|
|
3876
4165
|
let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
|
|
3877
4166
|
let dbSnapshotRef = serverItem.dbSnapshotRef;
|
|
@@ -3884,6 +4173,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3884
4173
|
let replayError = null;
|
|
3885
4174
|
let replayDurationMs = null;
|
|
3886
4175
|
let replayStarted = null;
|
|
4176
|
+
let selectiveReplay;
|
|
3887
4177
|
const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
|
|
3888
4178
|
const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
|
|
3889
4179
|
try {
|
|
@@ -3916,6 +4206,17 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3916
4206
|
originalTraceId
|
|
3917
4207
|
);
|
|
3918
4208
|
}
|
|
4209
|
+
if (selectiveOptions !== void 0) {
|
|
4210
|
+
if (serverItem.selectiveReplayPlan === void 0) {
|
|
4211
|
+
throw new BitfabError(
|
|
4212
|
+
"Server did not return a selective replay plan. Update the server or disable experimentalSelectiveReplay."
|
|
4213
|
+
);
|
|
4214
|
+
}
|
|
4215
|
+
selectiveReplay = new SelectiveReplayRuntime(
|
|
4216
|
+
serverItem.selectiveReplayPlan,
|
|
4217
|
+
selectiveOptions
|
|
4218
|
+
);
|
|
4219
|
+
}
|
|
3919
4220
|
const span = await httpClient.getExternalSpan(originalSpanId, {
|
|
3920
4221
|
view: "replay"
|
|
3921
4222
|
});
|
|
@@ -3935,7 +4236,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3935
4236
|
});
|
|
3936
4237
|
}
|
|
3937
4238
|
const hasOverrides = resolvedOverrides.length > 0;
|
|
3938
|
-
const needTree = mockStrategy === "all" || mockStrategy === "marked" || hasOverrides;
|
|
4239
|
+
const needTree = selectiveReplay === void 0 && (mockStrategy === "all" || mockStrategy === "marked" || hasOverrides);
|
|
3939
4240
|
const includeOutputs = mockStrategy === "all";
|
|
3940
4241
|
let mockTree;
|
|
3941
4242
|
if (needTree) {
|
|
@@ -4010,6 +4311,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4010
4311
|
sourceBitfabTraceId: originalTraceId,
|
|
4011
4312
|
replayAttempt: attempt,
|
|
4012
4313
|
mockTree,
|
|
4314
|
+
selectiveReplay,
|
|
4013
4315
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
4014
4316
|
mockStrategy,
|
|
4015
4317
|
mockOverrides: hasOverrides ? resolvedOverrides : void 0,
|
|
@@ -4021,6 +4323,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4021
4323
|
() => fn(...inputs)
|
|
4022
4324
|
);
|
|
4023
4325
|
result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
|
|
4326
|
+
selectiveReplay?.assertNoSafetyConflict();
|
|
4024
4327
|
replayDurationMs = Math.round(performance.now() - replayStarted);
|
|
4025
4328
|
} catch (e) {
|
|
4026
4329
|
if (replayStarted !== null) {
|
|
@@ -4072,6 +4375,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4072
4375
|
traceError,
|
|
4073
4376
|
replayError,
|
|
4074
4377
|
durationMs: replayDurationMs,
|
|
4378
|
+
...selectiveReplay === void 0 ? {} : { selectiveReplay: selectiveReplay.report() },
|
|
4075
4379
|
originalDurationMs,
|
|
4076
4380
|
originalTokens: serverItem.originalTokens ?? serverItem.tokens ?? null,
|
|
4077
4381
|
originalModel,
|
|
@@ -4176,6 +4480,14 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4176
4480
|
"force only applies when resuming an experiment. Pass it together with resume."
|
|
4177
4481
|
);
|
|
4178
4482
|
}
|
|
4483
|
+
if (options?.experimentalSelectiveReplay !== void 0) {
|
|
4484
|
+
validateSelectiveReplayOptions(options.experimentalSelectiveReplay);
|
|
4485
|
+
if ((options.mock ?? "marked") !== "marked" || normalizeMockOverrides(options.mockOverride).length > 0 || registeredOverrides.length > 0) {
|
|
4486
|
+
throw new BitfabError(
|
|
4487
|
+
"experimentalSelectiveReplay requires mock: 'marked' and cannot be combined with mock overrides."
|
|
4488
|
+
);
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4179
4491
|
if (options?.traceIds !== void 0) {
|
|
4180
4492
|
if (options.traceIds.length === 0) {
|
|
4181
4493
|
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
@@ -4237,7 +4549,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4237
4549
|
requestedAttempts,
|
|
4238
4550
|
options?.adaptInputs !== void 0,
|
|
4239
4551
|
options?.onlyWithAssertions,
|
|
4240
|
-
options?.judgeAssertions
|
|
4552
|
+
options?.judgeAssertions,
|
|
4553
|
+
options?.experimentalSelectiveReplay
|
|
4241
4554
|
);
|
|
4242
4555
|
if (started2.items.length === 0) {
|
|
4243
4556
|
try {
|
|
@@ -4299,7 +4612,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4299
4612
|
session.includeDbBranchLease,
|
|
4300
4613
|
session.dbBranchSettings,
|
|
4301
4614
|
options?.adaptInputs,
|
|
4302
|
-
options?.dryRun === true
|
|
4615
|
+
options?.dryRun === true,
|
|
4616
|
+
options?.experimentalSelectiveReplay
|
|
4303
4617
|
)
|
|
4304
4618
|
);
|
|
4305
4619
|
const total = tasks.length + carriedOverItems.length;
|
|
@@ -4372,6 +4686,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4372
4686
|
traceError: item.traceError,
|
|
4373
4687
|
replayError: item.replayError,
|
|
4374
4688
|
durationMs: item.durationMs,
|
|
4689
|
+
...item.selectiveReplay === void 0 ? {} : { selectiveReplay: item.selectiveReplay },
|
|
4375
4690
|
originalDurationMs: item.originalDurationMs,
|
|
4376
4691
|
originalTokens: item.originalTokens,
|
|
4377
4692
|
originalModel: item.originalModel,
|
|
@@ -4550,6 +4865,7 @@ var init_replay = __esm({
|
|
|
4550
4865
|
init_mockOverride();
|
|
4551
4866
|
init_randomUuid();
|
|
4552
4867
|
init_replayContext();
|
|
4868
|
+
init_selectiveReplayRuntime();
|
|
4553
4869
|
init_serialize();
|
|
4554
4870
|
REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
|
|
4555
4871
|
MAX_REPLAY_ATTEMPTS = 100;
|
|
@@ -4561,7 +4877,8 @@ var init_replay = __esm({
|
|
|
4561
4877
|
"attempts",
|
|
4562
4878
|
"graderIds",
|
|
4563
4879
|
"onlyWithAssertions",
|
|
4564
|
-
"dryRun"
|
|
4880
|
+
"dryRun",
|
|
4881
|
+
"experimentalSelectiveReplay"
|
|
4565
4882
|
];
|
|
4566
4883
|
CARRIED_OVER_ERROR = "This item errored in an earlier run of this experiment, before it was resumed.";
|
|
4567
4884
|
BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
|
|
@@ -9255,6 +9572,9 @@ var Bitfab = class {
|
|
|
9255
9572
|
...options.mockOnReplay !== void 0 && {
|
|
9256
9573
|
mockOnReplay: options.mockOnReplay
|
|
9257
9574
|
},
|
|
9575
|
+
...options.replayReusable !== void 0 && {
|
|
9576
|
+
replayReusable: options.replayReusable
|
|
9577
|
+
},
|
|
9258
9578
|
...options.finalize !== void 0 && { finalize: options.finalize }
|
|
9259
9579
|
};
|
|
9260
9580
|
}
|
|
@@ -9403,6 +9723,9 @@ var Bitfab = class {
|
|
|
9403
9723
|
...mockOnReplay !== void 0 && {
|
|
9404
9724
|
mockOnReplay
|
|
9405
9725
|
},
|
|
9726
|
+
...nodeConfiguration?.replayReusable !== void 0 && {
|
|
9727
|
+
replayReusable: nodeConfiguration.replayReusable
|
|
9728
|
+
},
|
|
9406
9729
|
...nodeConfiguration?.finalize !== void 0 && {
|
|
9407
9730
|
finalize: nodeConfiguration.finalize
|
|
9408
9731
|
}
|
|
@@ -10200,6 +10523,7 @@ var Bitfab = class {
|
|
|
10200
10523
|
instrumentation: options.instrumentation ?? "span",
|
|
10201
10524
|
functionId: options.functionId,
|
|
10202
10525
|
captureContent: options.captureContent ?? true,
|
|
10526
|
+
replayReusable: options.replayReusable,
|
|
10203
10527
|
autoTraceDefinition: options.autoTraceDefinition,
|
|
10204
10528
|
declaredNode: options.declaredNode === true,
|
|
10205
10529
|
nestedTrace: options.nestedTrace,
|
|
@@ -10319,6 +10643,27 @@ var Bitfab = class {
|
|
|
10319
10643
|
return result;
|
|
10320
10644
|
};
|
|
10321
10645
|
const replayCtxForMock = getReplayContext();
|
|
10646
|
+
if (replayCtxForMock?.selectiveReplay) {
|
|
10647
|
+
const decision = replayCtxForMock.selectiveReplay.enter({
|
|
10648
|
+
traceFunctionKey,
|
|
10649
|
+
spanName: baseSpanParams.spanName ?? traceFunctionKey,
|
|
10650
|
+
spanId,
|
|
10651
|
+
parentSpanId,
|
|
10652
|
+
inputs: args,
|
|
10653
|
+
safetyMock: !isRootSpan && options.mockOnReplay === true,
|
|
10654
|
+
reuseAllowed: options.replayReusable === true,
|
|
10655
|
+
canReuseOutput: options.finalize === void 0 && options.replayOutput === void 0 && !fn.constructor.name.includes("Generator")
|
|
10656
|
+
});
|
|
10657
|
+
if (decision.mock) {
|
|
10658
|
+
void sendSpan({
|
|
10659
|
+
result: decision.output,
|
|
10660
|
+
mocked: true,
|
|
10661
|
+
mockTarget: "output",
|
|
10662
|
+
mockSource: "recorded"
|
|
10663
|
+
});
|
|
10664
|
+
return fnReturnsPromise ? Promise.resolve(decision.output) : decision.output;
|
|
10665
|
+
}
|
|
10666
|
+
}
|
|
10322
10667
|
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
10323
10668
|
const counters = replayCtxForMock.callCounters;
|
|
10324
10669
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
@@ -10742,6 +11087,9 @@ var Bitfab = class {
|
|
|
10742
11087
|
span_origin: makeSpanOrigin(params.instrumentation),
|
|
10743
11088
|
span_data: {
|
|
10744
11089
|
name: params.spanName,
|
|
11090
|
+
...params.replayReusable !== void 0 && {
|
|
11091
|
+
replay_reusable: params.replayReusable
|
|
11092
|
+
},
|
|
10745
11093
|
type: params.spanType,
|
|
10746
11094
|
...params.functionId !== void 0 && {
|
|
10747
11095
|
function_id: params.functionId,
|