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/node.cjs
CHANGED
|
@@ -97,7 +97,7 @@ var __version__, __packageName__;
|
|
|
97
97
|
var init_version_generated = __esm({
|
|
98
98
|
"src/version.generated.ts"() {
|
|
99
99
|
"use strict";
|
|
100
|
-
__version__ = "0.61.
|
|
100
|
+
__version__ = "0.61.3";
|
|
101
101
|
__packageName__ = "bitfab";
|
|
102
102
|
}
|
|
103
103
|
});
|
|
@@ -1084,6 +1084,7 @@ var init_simulationPlan = __esm({
|
|
|
1084
1084
|
"input_meta",
|
|
1085
1085
|
"output",
|
|
1086
1086
|
"output_meta",
|
|
1087
|
+
"replay_recording",
|
|
1087
1088
|
"prompt"
|
|
1088
1089
|
];
|
|
1089
1090
|
SimulationPlan = class {
|
|
@@ -2898,8 +2899,11 @@ var init_http = __esm({
|
|
|
2898
2899
|
* Start a replay session by fetching historical traces.
|
|
2899
2900
|
* Blocking call - creates an experiment and returns lightweight item references.
|
|
2900
2901
|
*/
|
|
2901
|
-
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions) {
|
|
2902
|
+
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions, experimentalSelectiveReplay) {
|
|
2902
2903
|
const payload = { traceFunctionKey };
|
|
2904
|
+
if (experimentalSelectiveReplay !== void 0) {
|
|
2905
|
+
payload.experimentalSelectiveReplay = experimentalSelectiveReplay;
|
|
2906
|
+
}
|
|
2903
2907
|
if (limit !== void 0) {
|
|
2904
2908
|
payload.limit = limit;
|
|
2905
2909
|
}
|
|
@@ -3623,6 +3627,291 @@ var init_integrationFixture = __esm({
|
|
|
3623
3627
|
}
|
|
3624
3628
|
});
|
|
3625
3629
|
|
|
3630
|
+
// src/selectiveReplay.ts
|
|
3631
|
+
function replayNodeKey(node) {
|
|
3632
|
+
return JSON.stringify([node.traceFunctionKey, node.spanName]);
|
|
3633
|
+
}
|
|
3634
|
+
function jsonFingerprint(value) {
|
|
3635
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3636
|
+
function encode(input) {
|
|
3637
|
+
if (input === null || typeof input === "boolean") {
|
|
3638
|
+
return JSON.stringify(input);
|
|
3639
|
+
}
|
|
3640
|
+
if (typeof input === "string" && !input.startsWith("<unserializable")) {
|
|
3641
|
+
return JSON.stringify(input);
|
|
3642
|
+
}
|
|
3643
|
+
if (typeof input === "number" && Number.isFinite(input) && !Object.is(input, -0)) {
|
|
3644
|
+
return JSON.stringify(input);
|
|
3645
|
+
}
|
|
3646
|
+
if (typeof input !== "object" || input === null || seen.has(input)) {
|
|
3647
|
+
throw new Error("Not a lossless JSON value");
|
|
3648
|
+
}
|
|
3649
|
+
seen.add(input);
|
|
3650
|
+
const prototype = Object.getPrototypeOf(input);
|
|
3651
|
+
if (prototype !== (Array.isArray(input) ? Array.prototype : Object.prototype)) {
|
|
3652
|
+
throw new Error("Not a plain JSON object");
|
|
3653
|
+
}
|
|
3654
|
+
if (Object.getOwnPropertySymbols(input).length > 0) {
|
|
3655
|
+
throw new Error("Symbol properties are not recorded");
|
|
3656
|
+
}
|
|
3657
|
+
const keys = Object.keys(input);
|
|
3658
|
+
if (Object.getOwnPropertyNames(input).length !== keys.length + (Array.isArray(input) ? 1 : 0)) {
|
|
3659
|
+
throw new Error("Non-enumerable properties are not recorded");
|
|
3660
|
+
}
|
|
3661
|
+
if (Array.isArray(input) && (keys.length !== input.length || keys.some((key, index) => key !== String(index)))) {
|
|
3662
|
+
throw new Error("Not a dense JSON array");
|
|
3663
|
+
}
|
|
3664
|
+
const properties = keys.map((key) => {
|
|
3665
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
3666
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
3667
|
+
throw new Error("Accessor properties are not replay inputs");
|
|
3668
|
+
}
|
|
3669
|
+
return `${Array.isArray(input) ? "" : `${JSON.stringify(key)}:`}${encode(descriptor.value)}`;
|
|
3670
|
+
});
|
|
3671
|
+
return Array.isArray(input) ? `[${properties.join(",")}]` : `{${properties.join(",")}}`;
|
|
3672
|
+
}
|
|
3673
|
+
try {
|
|
3674
|
+
return encode(value);
|
|
3675
|
+
} catch {
|
|
3676
|
+
return void 0;
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
var init_selectiveReplay = __esm({
|
|
3680
|
+
"src/selectiveReplay.ts"() {
|
|
3681
|
+
"use strict";
|
|
3682
|
+
}
|
|
3683
|
+
});
|
|
3684
|
+
|
|
3685
|
+
// src/selectiveReplayRuntime.ts
|
|
3686
|
+
function validateSelectiveReplayOptions(options) {
|
|
3687
|
+
const validNodes = (nodes) => Array.isArray(nodes) && nodes.length <= 100 && nodes.every(
|
|
3688
|
+
(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
|
|
3689
|
+
);
|
|
3690
|
+
if (!options || !validNodes(options.mustRun) || options.mustRun.length === 0 || Object.keys(options).some((key) => key !== "mustRun")) {
|
|
3691
|
+
throw new BitfabError(
|
|
3692
|
+
"experimentalSelectiveReplay accepts only 1\u2013100 mustRun with traceFunctionKey and spanName. Declare replayReusable on eligible spans, not reusableNodes in the replay request."
|
|
3693
|
+
);
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
function policyKey(options) {
|
|
3697
|
+
return JSON.stringify(options.mustRun.map(replayNodeKey).sort());
|
|
3698
|
+
}
|
|
3699
|
+
var SelectiveReplayRuntime;
|
|
3700
|
+
var init_selectiveReplayRuntime = __esm({
|
|
3701
|
+
"src/selectiveReplayRuntime.ts"() {
|
|
3702
|
+
"use strict";
|
|
3703
|
+
init_errors();
|
|
3704
|
+
init_selectiveReplay();
|
|
3705
|
+
SelectiveReplayRuntime = class {
|
|
3706
|
+
constructor(plan, options) {
|
|
3707
|
+
this.plan = plan;
|
|
3708
|
+
this.options = options;
|
|
3709
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
3710
|
+
this.children = /* @__PURE__ */ new Map();
|
|
3711
|
+
this.runtimeParents = /* @__PURE__ */ new Map();
|
|
3712
|
+
this.consumed = /* @__PURE__ */ new Map();
|
|
3713
|
+
this.requiredScopes = /* @__PURE__ */ new Set();
|
|
3714
|
+
this.recordings = /* @__PURE__ */ new Map();
|
|
3715
|
+
this.reached = /* @__PURE__ */ new Set();
|
|
3716
|
+
this.decisions = [];
|
|
3717
|
+
this.assertionNames = /* @__PURE__ */ new Set();
|
|
3718
|
+
this.assertionScopes = /* @__PURE__ */ new Set();
|
|
3719
|
+
this.observedNames = /* @__PURE__ */ new Map();
|
|
3720
|
+
this.unresolvedAssertions = [];
|
|
3721
|
+
validateSelectiveReplayOptions(options);
|
|
3722
|
+
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) {
|
|
3723
|
+
throw new BitfabError(
|
|
3724
|
+
"Server did not return a compatible selective replay plan. Update the server or disable experimentalSelectiveReplay."
|
|
3725
|
+
);
|
|
3726
|
+
}
|
|
3727
|
+
validateSelectiveReplayOptions(plan.options);
|
|
3728
|
+
if (policyKey(plan.options) !== policyKey(options)) {
|
|
3729
|
+
throw new BitfabError(
|
|
3730
|
+
"Selective replay plan does not match the requested mustRun nodes."
|
|
3731
|
+
);
|
|
3732
|
+
}
|
|
3733
|
+
this.required = new Set(options.mustRun.map(replayNodeKey));
|
|
3734
|
+
for (const node of plan.nodes) {
|
|
3735
|
+
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") {
|
|
3736
|
+
throw new BitfabError("Invalid selective replay node.");
|
|
3737
|
+
}
|
|
3738
|
+
this.nodes.set(node.id, node);
|
|
3739
|
+
if (node.recording !== void 0) {
|
|
3740
|
+
this.recordings.set(node.id, {
|
|
3741
|
+
input: node.recording.kwargs === void 0 ? jsonFingerprint(node.recording.input) : void 0,
|
|
3742
|
+
output: jsonFingerprint(node.recording.output)
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
if (node.parentId !== null) {
|
|
3746
|
+
const siblings = this.children.get(node.parentId) ?? [];
|
|
3747
|
+
siblings.push(node);
|
|
3748
|
+
this.children.set(node.parentId, siblings);
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
const root = this.nodes.get(plan.rootId);
|
|
3752
|
+
if (!root || root.parentId !== null || !root.mustRun) {
|
|
3753
|
+
throw new BitfabError("Selective replay requires a live recorded root.");
|
|
3754
|
+
}
|
|
3755
|
+
const assertionIds = /* @__PURE__ */ new Set();
|
|
3756
|
+
let wholeTrace = false;
|
|
3757
|
+
for (const assertion of plan.assertions) {
|
|
3758
|
+
const target = assertion?.target;
|
|
3759
|
+
if (typeof assertion?.id !== "string" || assertion.id.length === 0 || assertionIds.has(assertion.id) || target !== null && (typeof target !== "object" || target.kind !== "output" && target.kind !== "span")) {
|
|
3760
|
+
throw new BitfabError(
|
|
3761
|
+
"Invalid selective replay assertion requirements."
|
|
3762
|
+
);
|
|
3763
|
+
}
|
|
3764
|
+
assertionIds.add(assertion.id);
|
|
3765
|
+
if (target === null) {
|
|
3766
|
+
wholeTrace = true;
|
|
3767
|
+
} else if (target.kind === "span") {
|
|
3768
|
+
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)) {
|
|
3769
|
+
throw new BitfabError("Invalid selective replay assertion target.");
|
|
3770
|
+
}
|
|
3771
|
+
this.assertionNames.add(target.name);
|
|
3772
|
+
const count = plan.nodes.filter(
|
|
3773
|
+
(node) => node.spanName === target.name
|
|
3774
|
+
).length;
|
|
3775
|
+
if (count <= (typeof target.occurrence === "number" ? target.occurrence : 0)) {
|
|
3776
|
+
this.unresolvedAssertions.push(assertion.id);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
this.fullTraceForAssertions = wholeTrace || this.unresolvedAssertions.length > 0;
|
|
3781
|
+
const recordedKeys = new Set(plan.nodes.map(replayNodeKey));
|
|
3782
|
+
const unresolved = options.mustRun.filter(
|
|
3783
|
+
(node) => !recordedKeys.has(replayNodeKey(node))
|
|
3784
|
+
);
|
|
3785
|
+
if (JSON.stringify(unresolved.map(replayNodeKey).sort()) !== JSON.stringify(plan.unresolvedMustRun.map(replayNodeKey).sort())) {
|
|
3786
|
+
throw new BitfabError(
|
|
3787
|
+
"Selective replay plan has incomplete mustRun coverage."
|
|
3788
|
+
);
|
|
3789
|
+
}
|
|
3790
|
+
for (const node of plan.nodes) {
|
|
3791
|
+
const ancestors = /* @__PURE__ */ new Set([node.id]);
|
|
3792
|
+
let requiredByManifest = this.required.has(replayNodeKey(node));
|
|
3793
|
+
let requiredForAssertion = this.fullTraceForAssertions || this.assertionNames.has(node.spanName);
|
|
3794
|
+
let parentId = node.parentId;
|
|
3795
|
+
while (parentId !== null) {
|
|
3796
|
+
const parent = this.nodes.get(parentId);
|
|
3797
|
+
if (!parent || ancestors.has(parentId) || node.mustRun && !parent.mustRun) {
|
|
3798
|
+
throw new BitfabError("Invalid selective replay ancestry.");
|
|
3799
|
+
}
|
|
3800
|
+
ancestors.add(parentId);
|
|
3801
|
+
requiredByManifest || (requiredByManifest = this.required.has(replayNodeKey(parent)));
|
|
3802
|
+
requiredForAssertion || (requiredForAssertion = this.assertionNames.has(parent.spanName));
|
|
3803
|
+
parentId = parent.parentId;
|
|
3804
|
+
}
|
|
3805
|
+
if (!ancestors.has(plan.rootId) || (requiredByManifest || unresolved.length > 0) && !node.mustRun) {
|
|
3806
|
+
throw new BitfabError("Selective replay plan could hide required code.");
|
|
3807
|
+
}
|
|
3808
|
+
if (requiredForAssertion && !node.mustRun) {
|
|
3809
|
+
throw new BitfabError(
|
|
3810
|
+
"Selective replay plan could hide assertion evidence."
|
|
3811
|
+
);
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
enter(params) {
|
|
3816
|
+
const identity = {
|
|
3817
|
+
traceFunctionKey: params.traceFunctionKey,
|
|
3818
|
+
spanName: params.spanName
|
|
3819
|
+
};
|
|
3820
|
+
const key = replayNodeKey(identity);
|
|
3821
|
+
const fingerprint = jsonFingerprint(params.inputs);
|
|
3822
|
+
let node;
|
|
3823
|
+
let matchReason = "unmatched-call";
|
|
3824
|
+
if (params.parentSpanId === null) {
|
|
3825
|
+
node = this.nodes.get(this.plan.rootId);
|
|
3826
|
+
} else {
|
|
3827
|
+
const recordedParent = this.runtimeParents.get(params.parentSpanId);
|
|
3828
|
+
const consumed = this.consumed.get(params.parentSpanId) ?? /* @__PURE__ */ new Set();
|
|
3829
|
+
this.consumed.set(params.parentSpanId, consumed);
|
|
3830
|
+
const candidates = recordedParent ? (this.children.get(recordedParent) ?? []).filter(
|
|
3831
|
+
(candidate) => replayNodeKey(candidate) === key && !consumed.has(candidate.id)
|
|
3832
|
+
) : [];
|
|
3833
|
+
const exact = fingerprint === void 0 ? [] : candidates.filter(
|
|
3834
|
+
(candidate) => this.recordings.get(candidate.id)?.input === fingerprint
|
|
3835
|
+
);
|
|
3836
|
+
node = exact.length === 1 ? exact[0] : exact.length === 0 && candidates.length === 1 ? candidates[0] : void 0;
|
|
3837
|
+
if (node) {
|
|
3838
|
+
consumed.add(node.id);
|
|
3839
|
+
} else if (candidates.length > 0) {
|
|
3840
|
+
matchReason = "ambiguous-call";
|
|
3841
|
+
for (const candidate of candidates) {
|
|
3842
|
+
consumed.add(candidate.id);
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
this.runtimeParents.set(params.spanId, node?.id ?? null);
|
|
3847
|
+
const requiredByManifest = this.required.has(key) || params.parentSpanId !== null && this.requiredScopes.has(params.parentSpanId);
|
|
3848
|
+
if (requiredByManifest) {
|
|
3849
|
+
this.requiredScopes.add(params.spanId);
|
|
3850
|
+
}
|
|
3851
|
+
const requiredForAssertion = this.fullTraceForAssertions || this.assertionNames.has(params.spanName) || params.parentSpanId !== null && this.assertionScopes.has(params.parentSpanId);
|
|
3852
|
+
if (requiredForAssertion) {
|
|
3853
|
+
this.assertionScopes.add(params.spanId);
|
|
3854
|
+
}
|
|
3855
|
+
const mustRun = params.parentSpanId === null || requiredByManifest || node?.mustRun === true || requiredForAssertion || this.plan.unresolvedMustRun.length > 0;
|
|
3856
|
+
const recording = node === void 0 ? void 0 : this.recordings.get(node.id);
|
|
3857
|
+
const equalInputs = fingerprint !== void 0 && recording?.input === fingerprint;
|
|
3858
|
+
const reusable = recording?.output !== void 0 && equalInputs && params.canReuseOutput;
|
|
3859
|
+
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;
|
|
3860
|
+
const decision = (action, why) => {
|
|
3861
|
+
this.decisions.push({
|
|
3862
|
+
...identity,
|
|
3863
|
+
originalSpanId: node?.id ?? null,
|
|
3864
|
+
action,
|
|
3865
|
+
reason: why
|
|
3866
|
+
});
|
|
3867
|
+
};
|
|
3868
|
+
if (params.safetyMock && (mustRun || !reusable)) {
|
|
3869
|
+
decision("blocked", `safety-conflict:${reason}`);
|
|
3870
|
+
this.failure = new BitfabError(
|
|
3871
|
+
`Selective replay cannot execute or safely reuse safety-mocked node ${params.spanName} (${reason}).`
|
|
3872
|
+
);
|
|
3873
|
+
throw this.failure;
|
|
3874
|
+
}
|
|
3875
|
+
if (!mustRun && reusable && (params.safetyMock || node?.reusable === true && params.reuseAllowed)) {
|
|
3876
|
+
decision("mock", params.safetyMock ? "safety-mock" : "unchanged-inputs");
|
|
3877
|
+
return {
|
|
3878
|
+
mock: true,
|
|
3879
|
+
output: JSON.parse(recording.output)
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
decision("run", reason);
|
|
3883
|
+
this.observedNames.set(
|
|
3884
|
+
params.spanName,
|
|
3885
|
+
(this.observedNames.get(params.spanName) ?? 0) + 1
|
|
3886
|
+
);
|
|
3887
|
+
if (this.required.has(key)) {
|
|
3888
|
+
this.reached.add(key);
|
|
3889
|
+
}
|
|
3890
|
+
return { mock: false };
|
|
3891
|
+
}
|
|
3892
|
+
assertNoSafetyConflict() {
|
|
3893
|
+
if (this.failure) {
|
|
3894
|
+
throw this.failure;
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
report() {
|
|
3898
|
+
return {
|
|
3899
|
+
decisions: this.decisions,
|
|
3900
|
+
mustRunNotReached: this.options.mustRun.filter(
|
|
3901
|
+
(node) => !this.reached.has(replayNodeKey(node))
|
|
3902
|
+
),
|
|
3903
|
+
unresolvedMustRun: this.plan.unresolvedMustRun,
|
|
3904
|
+
assertionIds: this.plan.assertions.map((assertion) => assertion.id),
|
|
3905
|
+
unresolvedAssertions: this.unresolvedAssertions,
|
|
3906
|
+
assertionTargetsNotReached: this.plan.assertions.flatMap(
|
|
3907
|
+
({ id, target }) => target?.kind === "span" && (this.observedNames.get(target.name) ?? 0) <= (typeof target.occurrence === "number" ? target.occurrence : 0) ? [id] : []
|
|
3908
|
+
)
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
});
|
|
3914
|
+
|
|
3626
3915
|
// src/replay.ts
|
|
3627
3916
|
var replay_exports = {};
|
|
3628
3917
|
__export(replay_exports, {
|
|
@@ -3878,7 +4167,7 @@ function buildMockTree(rootNode) {
|
|
|
3878
4167
|
}
|
|
3879
4168
|
return { spans };
|
|
3880
4169
|
}
|
|
3881
|
-
async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
|
|
4170
|
+
async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun, selectiveOptions) {
|
|
3882
4171
|
let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
|
|
3883
4172
|
let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
|
|
3884
4173
|
let dbSnapshotRef = serverItem.dbSnapshotRef;
|
|
@@ -3891,6 +4180,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3891
4180
|
let replayError = null;
|
|
3892
4181
|
let replayDurationMs = null;
|
|
3893
4182
|
let replayStarted = null;
|
|
4183
|
+
let selectiveReplay;
|
|
3894
4184
|
const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
|
|
3895
4185
|
const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
|
|
3896
4186
|
try {
|
|
@@ -3923,6 +4213,17 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3923
4213
|
originalTraceId
|
|
3924
4214
|
);
|
|
3925
4215
|
}
|
|
4216
|
+
if (selectiveOptions !== void 0) {
|
|
4217
|
+
if (serverItem.selectiveReplayPlan === void 0) {
|
|
4218
|
+
throw new BitfabError(
|
|
4219
|
+
"Server did not return a selective replay plan. Update the server or disable experimentalSelectiveReplay."
|
|
4220
|
+
);
|
|
4221
|
+
}
|
|
4222
|
+
selectiveReplay = new SelectiveReplayRuntime(
|
|
4223
|
+
serverItem.selectiveReplayPlan,
|
|
4224
|
+
selectiveOptions
|
|
4225
|
+
);
|
|
4226
|
+
}
|
|
3926
4227
|
const span = await httpClient.getExternalSpan(originalSpanId, {
|
|
3927
4228
|
view: "replay"
|
|
3928
4229
|
});
|
|
@@ -3942,7 +4243,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3942
4243
|
});
|
|
3943
4244
|
}
|
|
3944
4245
|
const hasOverrides = resolvedOverrides.length > 0;
|
|
3945
|
-
const needTree = mockStrategy === "all" || mockStrategy === "marked" || hasOverrides;
|
|
4246
|
+
const needTree = selectiveReplay === void 0 && (mockStrategy === "all" || mockStrategy === "marked" || hasOverrides);
|
|
3946
4247
|
const includeOutputs = mockStrategy === "all";
|
|
3947
4248
|
let mockTree;
|
|
3948
4249
|
if (needTree) {
|
|
@@ -4017,6 +4318,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4017
4318
|
sourceBitfabTraceId: originalTraceId,
|
|
4018
4319
|
replayAttempt: attempt,
|
|
4019
4320
|
mockTree,
|
|
4321
|
+
selectiveReplay,
|
|
4020
4322
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
4021
4323
|
mockStrategy,
|
|
4022
4324
|
mockOverrides: hasOverrides ? resolvedOverrides : void 0,
|
|
@@ -4028,6 +4330,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4028
4330
|
() => fn(...inputs)
|
|
4029
4331
|
);
|
|
4030
4332
|
result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
|
|
4333
|
+
selectiveReplay?.assertNoSafetyConflict();
|
|
4031
4334
|
replayDurationMs = Math.round(performance.now() - replayStarted);
|
|
4032
4335
|
} catch (e) {
|
|
4033
4336
|
if (replayStarted !== null) {
|
|
@@ -4079,6 +4382,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4079
4382
|
traceError,
|
|
4080
4383
|
replayError,
|
|
4081
4384
|
durationMs: replayDurationMs,
|
|
4385
|
+
...selectiveReplay === void 0 ? {} : { selectiveReplay: selectiveReplay.report() },
|
|
4082
4386
|
originalDurationMs,
|
|
4083
4387
|
originalTokens: serverItem.originalTokens ?? serverItem.tokens ?? null,
|
|
4084
4388
|
originalModel,
|
|
@@ -4183,6 +4487,14 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4183
4487
|
"force only applies when resuming an experiment. Pass it together with resume."
|
|
4184
4488
|
);
|
|
4185
4489
|
}
|
|
4490
|
+
if (options?.experimentalSelectiveReplay !== void 0) {
|
|
4491
|
+
validateSelectiveReplayOptions(options.experimentalSelectiveReplay);
|
|
4492
|
+
if ((options.mock ?? "marked") !== "marked" || normalizeMockOverrides(options.mockOverride).length > 0 || registeredOverrides.length > 0) {
|
|
4493
|
+
throw new BitfabError(
|
|
4494
|
+
"experimentalSelectiveReplay requires mock: 'marked' and cannot be combined with mock overrides."
|
|
4495
|
+
);
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4186
4498
|
if (options?.traceIds !== void 0) {
|
|
4187
4499
|
if (options.traceIds.length === 0) {
|
|
4188
4500
|
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
@@ -4244,7 +4556,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4244
4556
|
requestedAttempts,
|
|
4245
4557
|
options?.adaptInputs !== void 0,
|
|
4246
4558
|
options?.onlyWithAssertions,
|
|
4247
|
-
options?.judgeAssertions
|
|
4559
|
+
options?.judgeAssertions,
|
|
4560
|
+
options?.experimentalSelectiveReplay
|
|
4248
4561
|
);
|
|
4249
4562
|
if (started2.items.length === 0) {
|
|
4250
4563
|
try {
|
|
@@ -4306,7 +4619,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4306
4619
|
session.includeDbBranchLease,
|
|
4307
4620
|
session.dbBranchSettings,
|
|
4308
4621
|
options?.adaptInputs,
|
|
4309
|
-
options?.dryRun === true
|
|
4622
|
+
options?.dryRun === true,
|
|
4623
|
+
options?.experimentalSelectiveReplay
|
|
4310
4624
|
)
|
|
4311
4625
|
);
|
|
4312
4626
|
const total = tasks.length + carriedOverItems.length;
|
|
@@ -4379,6 +4693,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4379
4693
|
traceError: item.traceError,
|
|
4380
4694
|
replayError: item.replayError,
|
|
4381
4695
|
durationMs: item.durationMs,
|
|
4696
|
+
...item.selectiveReplay === void 0 ? {} : { selectiveReplay: item.selectiveReplay },
|
|
4382
4697
|
originalDurationMs: item.originalDurationMs,
|
|
4383
4698
|
originalTokens: item.originalTokens,
|
|
4384
4699
|
originalModel: item.originalModel,
|
|
@@ -4557,6 +4872,7 @@ var init_replay = __esm({
|
|
|
4557
4872
|
init_mockOverride();
|
|
4558
4873
|
init_randomUuid();
|
|
4559
4874
|
init_replayContext();
|
|
4875
|
+
init_selectiveReplayRuntime();
|
|
4560
4876
|
init_serialize();
|
|
4561
4877
|
REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
|
|
4562
4878
|
MAX_REPLAY_ATTEMPTS = 100;
|
|
@@ -4568,7 +4884,8 @@ var init_replay = __esm({
|
|
|
4568
4884
|
"attempts",
|
|
4569
4885
|
"graderIds",
|
|
4570
4886
|
"onlyWithAssertions",
|
|
4571
|
-
"dryRun"
|
|
4887
|
+
"dryRun",
|
|
4888
|
+
"experimentalSelectiveReplay"
|
|
4572
4889
|
];
|
|
4573
4890
|
CARRIED_OVER_ERROR = "This item errored in an earlier run of this experiment, before it was resumed.";
|
|
4574
4891
|
BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
|
|
@@ -9269,6 +9586,9 @@ var Bitfab = class {
|
|
|
9269
9586
|
...options.mockOnReplay !== void 0 && {
|
|
9270
9587
|
mockOnReplay: options.mockOnReplay
|
|
9271
9588
|
},
|
|
9589
|
+
...options.replayReusable !== void 0 && {
|
|
9590
|
+
replayReusable: options.replayReusable
|
|
9591
|
+
},
|
|
9272
9592
|
...options.finalize !== void 0 && { finalize: options.finalize }
|
|
9273
9593
|
};
|
|
9274
9594
|
}
|
|
@@ -9417,6 +9737,9 @@ var Bitfab = class {
|
|
|
9417
9737
|
...mockOnReplay !== void 0 && {
|
|
9418
9738
|
mockOnReplay
|
|
9419
9739
|
},
|
|
9740
|
+
...nodeConfiguration?.replayReusable !== void 0 && {
|
|
9741
|
+
replayReusable: nodeConfiguration.replayReusable
|
|
9742
|
+
},
|
|
9420
9743
|
...nodeConfiguration?.finalize !== void 0 && {
|
|
9421
9744
|
finalize: nodeConfiguration.finalize
|
|
9422
9745
|
}
|
|
@@ -10214,6 +10537,7 @@ var Bitfab = class {
|
|
|
10214
10537
|
instrumentation: options.instrumentation ?? "span",
|
|
10215
10538
|
functionId: options.functionId,
|
|
10216
10539
|
captureContent: options.captureContent ?? true,
|
|
10540
|
+
replayReusable: options.replayReusable,
|
|
10217
10541
|
autoTraceDefinition: options.autoTraceDefinition,
|
|
10218
10542
|
declaredNode: options.declaredNode === true,
|
|
10219
10543
|
nestedTrace: options.nestedTrace,
|
|
@@ -10333,6 +10657,27 @@ var Bitfab = class {
|
|
|
10333
10657
|
return result;
|
|
10334
10658
|
};
|
|
10335
10659
|
const replayCtxForMock = getReplayContext();
|
|
10660
|
+
if (replayCtxForMock?.selectiveReplay) {
|
|
10661
|
+
const decision = replayCtxForMock.selectiveReplay.enter({
|
|
10662
|
+
traceFunctionKey,
|
|
10663
|
+
spanName: baseSpanParams.spanName ?? traceFunctionKey,
|
|
10664
|
+
spanId,
|
|
10665
|
+
parentSpanId,
|
|
10666
|
+
inputs: args,
|
|
10667
|
+
safetyMock: !isRootSpan && options.mockOnReplay === true,
|
|
10668
|
+
reuseAllowed: options.replayReusable === true,
|
|
10669
|
+
canReuseOutput: options.finalize === void 0 && options.replayOutput === void 0 && !fn.constructor.name.includes("Generator")
|
|
10670
|
+
});
|
|
10671
|
+
if (decision.mock) {
|
|
10672
|
+
void sendSpan({
|
|
10673
|
+
result: decision.output,
|
|
10674
|
+
mocked: true,
|
|
10675
|
+
mockTarget: "output",
|
|
10676
|
+
mockSource: "recorded"
|
|
10677
|
+
});
|
|
10678
|
+
return fnReturnsPromise ? Promise.resolve(decision.output) : decision.output;
|
|
10679
|
+
}
|
|
10680
|
+
}
|
|
10336
10681
|
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
10337
10682
|
const counters = replayCtxForMock.callCounters;
|
|
10338
10683
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
@@ -10756,6 +11101,9 @@ var Bitfab = class {
|
|
|
10756
11101
|
span_origin: makeSpanOrigin(params.instrumentation),
|
|
10757
11102
|
span_data: {
|
|
10758
11103
|
name: params.spanName,
|
|
11104
|
+
...params.replayReusable !== void 0 && {
|
|
11105
|
+
replay_reusable: params.replayReusable
|
|
11106
|
+
},
|
|
10759
11107
|
type: params.spanType,
|
|
10760
11108
|
...params.functionId !== void 0 && {
|
|
10761
11109
|
function_id: params.functionId,
|