@bitfab/sdk 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-X5DRPZJJ.js → chunk-34UFYAE6.js} +306 -7
- package/dist/chunk-34UFYAE6.js.map +1 -0
- package/dist/{chunk-SJPMWVJ3.js → chunk-65QKCHI2.js} +7 -3
- package/dist/chunk-65QKCHI2.js.map +1 -0
- package/dist/{chunk-IR5KCKHF.js → chunk-7BU5LUMR.js} +6 -3
- package/dist/chunk-7BU5LUMR.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-DSVQPSAL.js → chunk-OWXP3YXA.js} +3 -3
- package/dist/chunk-OWXP3YXA.js.map +1 -0
- package/dist/{chunk-HNEKBDI7.js → chunk-TZSTKVZF.js} +38 -7
- package/dist/chunk-TZSTKVZF.js.map +1 -0
- package/dist/{http-OEABHRHY.js → http-VTIALK4P.js} +2 -2
- package/dist/{http-RNI6PKQ3.js → http-ZWENQXQA.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-3ERB6W6N.js → replay-YQG3OGDN.js} +3 -3
- package/dist/replayCli.js +2 -2
- package/dist/seedCli.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-BMJNDIVA.js.map +0 -1
- package/dist/chunk-DSVQPSAL.js.map +0 -1
- package/dist/chunk-HNEKBDI7.js.map +0 -1
- package/dist/chunk-IR5KCKHF.js.map +0 -1
- package/dist/chunk-SJPMWVJ3.js.map +0 -1
- package/dist/chunk-X5DRPZJJ.js.map +0 -1
- /package/dist/{http-OEABHRHY.js.map → http-VTIALK4P.js.map} +0 -0
- /package/dist/{http-RNI6PKQ3.js.map → http-ZWENQXQA.js.map} +0 -0
- /package/dist/{replay-3ERB6W6N.js.map → replay-YQG3OGDN.js.map} +0 -0
package/dist/node.cjs
CHANGED
|
@@ -88,7 +88,7 @@ var __version__, __packageName__;
|
|
|
88
88
|
var init_version_generated = __esm({
|
|
89
89
|
"src/version.generated.ts"() {
|
|
90
90
|
"use strict";
|
|
91
|
-
__version__ = "0.61.
|
|
91
|
+
__version__ = "0.61.3";
|
|
92
92
|
__packageName__ = "@bitfab/sdk";
|
|
93
93
|
}
|
|
94
94
|
});
|
|
@@ -1075,6 +1075,7 @@ var init_simulationPlan = __esm({
|
|
|
1075
1075
|
"input_meta",
|
|
1076
1076
|
"output",
|
|
1077
1077
|
"output_meta",
|
|
1078
|
+
"replay_recording",
|
|
1078
1079
|
"prompt"
|
|
1079
1080
|
];
|
|
1080
1081
|
SimulationPlan = class {
|
|
@@ -2889,8 +2890,11 @@ var init_http = __esm({
|
|
|
2889
2890
|
* Start a replay session by fetching historical traces.
|
|
2890
2891
|
* Blocking call - creates an experiment and returns lightweight item references.
|
|
2891
2892
|
*/
|
|
2892
|
-
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions) {
|
|
2893
|
+
async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions, experimentalSelectiveReplay) {
|
|
2893
2894
|
const payload = { traceFunctionKey };
|
|
2895
|
+
if (experimentalSelectiveReplay !== void 0) {
|
|
2896
|
+
payload.experimentalSelectiveReplay = experimentalSelectiveReplay;
|
|
2897
|
+
}
|
|
2894
2898
|
if (limit !== void 0) {
|
|
2895
2899
|
payload.limit = limit;
|
|
2896
2900
|
}
|
|
@@ -3614,6 +3618,291 @@ var init_integrationFixture = __esm({
|
|
|
3614
3618
|
}
|
|
3615
3619
|
});
|
|
3616
3620
|
|
|
3621
|
+
// src/selectiveReplay.ts
|
|
3622
|
+
function replayNodeKey(node) {
|
|
3623
|
+
return JSON.stringify([node.traceFunctionKey, node.spanName]);
|
|
3624
|
+
}
|
|
3625
|
+
function jsonFingerprint(value) {
|
|
3626
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3627
|
+
function encode(input) {
|
|
3628
|
+
if (input === null || typeof input === "boolean") {
|
|
3629
|
+
return JSON.stringify(input);
|
|
3630
|
+
}
|
|
3631
|
+
if (typeof input === "string" && !input.startsWith("<unserializable")) {
|
|
3632
|
+
return JSON.stringify(input);
|
|
3633
|
+
}
|
|
3634
|
+
if (typeof input === "number" && Number.isFinite(input) && !Object.is(input, -0)) {
|
|
3635
|
+
return JSON.stringify(input);
|
|
3636
|
+
}
|
|
3637
|
+
if (typeof input !== "object" || input === null || seen.has(input)) {
|
|
3638
|
+
throw new Error("Not a lossless JSON value");
|
|
3639
|
+
}
|
|
3640
|
+
seen.add(input);
|
|
3641
|
+
const prototype = Object.getPrototypeOf(input);
|
|
3642
|
+
if (prototype !== (Array.isArray(input) ? Array.prototype : Object.prototype)) {
|
|
3643
|
+
throw new Error("Not a plain JSON object");
|
|
3644
|
+
}
|
|
3645
|
+
if (Object.getOwnPropertySymbols(input).length > 0) {
|
|
3646
|
+
throw new Error("Symbol properties are not recorded");
|
|
3647
|
+
}
|
|
3648
|
+
const keys = Object.keys(input);
|
|
3649
|
+
if (Object.getOwnPropertyNames(input).length !== keys.length + (Array.isArray(input) ? 1 : 0)) {
|
|
3650
|
+
throw new Error("Non-enumerable properties are not recorded");
|
|
3651
|
+
}
|
|
3652
|
+
if (Array.isArray(input) && (keys.length !== input.length || keys.some((key, index) => key !== String(index)))) {
|
|
3653
|
+
throw new Error("Not a dense JSON array");
|
|
3654
|
+
}
|
|
3655
|
+
const properties = keys.map((key) => {
|
|
3656
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
3657
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
3658
|
+
throw new Error("Accessor properties are not replay inputs");
|
|
3659
|
+
}
|
|
3660
|
+
return `${Array.isArray(input) ? "" : `${JSON.stringify(key)}:`}${encode(descriptor.value)}`;
|
|
3661
|
+
});
|
|
3662
|
+
return Array.isArray(input) ? `[${properties.join(",")}]` : `{${properties.join(",")}}`;
|
|
3663
|
+
}
|
|
3664
|
+
try {
|
|
3665
|
+
return encode(value);
|
|
3666
|
+
} catch {
|
|
3667
|
+
return void 0;
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
var init_selectiveReplay = __esm({
|
|
3671
|
+
"src/selectiveReplay.ts"() {
|
|
3672
|
+
"use strict";
|
|
3673
|
+
}
|
|
3674
|
+
});
|
|
3675
|
+
|
|
3676
|
+
// src/selectiveReplayRuntime.ts
|
|
3677
|
+
function validateSelectiveReplayOptions(options) {
|
|
3678
|
+
const validNodes = (nodes) => Array.isArray(nodes) && nodes.length <= 100 && nodes.every(
|
|
3679
|
+
(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
|
|
3680
|
+
);
|
|
3681
|
+
if (!options || !validNodes(options.mustRun) || options.mustRun.length === 0 || Object.keys(options).some((key) => key !== "mustRun")) {
|
|
3682
|
+
throw new BitfabError(
|
|
3683
|
+
"experimentalSelectiveReplay accepts only 1\u2013100 mustRun with traceFunctionKey and spanName. Declare replayReusable on eligible spans, not reusableNodes in the replay request."
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
function policyKey(options) {
|
|
3688
|
+
return JSON.stringify(options.mustRun.map(replayNodeKey).sort());
|
|
3689
|
+
}
|
|
3690
|
+
var SelectiveReplayRuntime;
|
|
3691
|
+
var init_selectiveReplayRuntime = __esm({
|
|
3692
|
+
"src/selectiveReplayRuntime.ts"() {
|
|
3693
|
+
"use strict";
|
|
3694
|
+
init_errors();
|
|
3695
|
+
init_selectiveReplay();
|
|
3696
|
+
SelectiveReplayRuntime = class {
|
|
3697
|
+
constructor(plan, options) {
|
|
3698
|
+
this.plan = plan;
|
|
3699
|
+
this.options = options;
|
|
3700
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
3701
|
+
this.children = /* @__PURE__ */ new Map();
|
|
3702
|
+
this.runtimeParents = /* @__PURE__ */ new Map();
|
|
3703
|
+
this.consumed = /* @__PURE__ */ new Map();
|
|
3704
|
+
this.requiredScopes = /* @__PURE__ */ new Set();
|
|
3705
|
+
this.recordings = /* @__PURE__ */ new Map();
|
|
3706
|
+
this.reached = /* @__PURE__ */ new Set();
|
|
3707
|
+
this.decisions = [];
|
|
3708
|
+
this.assertionNames = /* @__PURE__ */ new Set();
|
|
3709
|
+
this.assertionScopes = /* @__PURE__ */ new Set();
|
|
3710
|
+
this.observedNames = /* @__PURE__ */ new Map();
|
|
3711
|
+
this.unresolvedAssertions = [];
|
|
3712
|
+
validateSelectiveReplayOptions(options);
|
|
3713
|
+
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) {
|
|
3714
|
+
throw new BitfabError(
|
|
3715
|
+
"Server did not return a compatible selective replay plan. Update the server or disable experimentalSelectiveReplay."
|
|
3716
|
+
);
|
|
3717
|
+
}
|
|
3718
|
+
validateSelectiveReplayOptions(plan.options);
|
|
3719
|
+
if (policyKey(plan.options) !== policyKey(options)) {
|
|
3720
|
+
throw new BitfabError(
|
|
3721
|
+
"Selective replay plan does not match the requested mustRun nodes."
|
|
3722
|
+
);
|
|
3723
|
+
}
|
|
3724
|
+
this.required = new Set(options.mustRun.map(replayNodeKey));
|
|
3725
|
+
for (const node of plan.nodes) {
|
|
3726
|
+
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") {
|
|
3727
|
+
throw new BitfabError("Invalid selective replay node.");
|
|
3728
|
+
}
|
|
3729
|
+
this.nodes.set(node.id, node);
|
|
3730
|
+
if (node.recording !== void 0) {
|
|
3731
|
+
this.recordings.set(node.id, {
|
|
3732
|
+
input: node.recording.kwargs === void 0 ? jsonFingerprint(node.recording.input) : void 0,
|
|
3733
|
+
output: jsonFingerprint(node.recording.output)
|
|
3734
|
+
});
|
|
3735
|
+
}
|
|
3736
|
+
if (node.parentId !== null) {
|
|
3737
|
+
const siblings = this.children.get(node.parentId) ?? [];
|
|
3738
|
+
siblings.push(node);
|
|
3739
|
+
this.children.set(node.parentId, siblings);
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
const root = this.nodes.get(plan.rootId);
|
|
3743
|
+
if (!root || root.parentId !== null || !root.mustRun) {
|
|
3744
|
+
throw new BitfabError("Selective replay requires a live recorded root.");
|
|
3745
|
+
}
|
|
3746
|
+
const assertionIds = /* @__PURE__ */ new Set();
|
|
3747
|
+
let wholeTrace = false;
|
|
3748
|
+
for (const assertion of plan.assertions) {
|
|
3749
|
+
const target = assertion?.target;
|
|
3750
|
+
if (typeof assertion?.id !== "string" || assertion.id.length === 0 || assertionIds.has(assertion.id) || target !== null && (typeof target !== "object" || target.kind !== "output" && target.kind !== "span")) {
|
|
3751
|
+
throw new BitfabError(
|
|
3752
|
+
"Invalid selective replay assertion requirements."
|
|
3753
|
+
);
|
|
3754
|
+
}
|
|
3755
|
+
assertionIds.add(assertion.id);
|
|
3756
|
+
if (target === null) {
|
|
3757
|
+
wholeTrace = true;
|
|
3758
|
+
} else if (target.kind === "span") {
|
|
3759
|
+
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)) {
|
|
3760
|
+
throw new BitfabError("Invalid selective replay assertion target.");
|
|
3761
|
+
}
|
|
3762
|
+
this.assertionNames.add(target.name);
|
|
3763
|
+
const count = plan.nodes.filter(
|
|
3764
|
+
(node) => node.spanName === target.name
|
|
3765
|
+
).length;
|
|
3766
|
+
if (count <= (typeof target.occurrence === "number" ? target.occurrence : 0)) {
|
|
3767
|
+
this.unresolvedAssertions.push(assertion.id);
|
|
3768
|
+
}
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
this.fullTraceForAssertions = wholeTrace || this.unresolvedAssertions.length > 0;
|
|
3772
|
+
const recordedKeys = new Set(plan.nodes.map(replayNodeKey));
|
|
3773
|
+
const unresolved = options.mustRun.filter(
|
|
3774
|
+
(node) => !recordedKeys.has(replayNodeKey(node))
|
|
3775
|
+
);
|
|
3776
|
+
if (JSON.stringify(unresolved.map(replayNodeKey).sort()) !== JSON.stringify(plan.unresolvedMustRun.map(replayNodeKey).sort())) {
|
|
3777
|
+
throw new BitfabError(
|
|
3778
|
+
"Selective replay plan has incomplete mustRun coverage."
|
|
3779
|
+
);
|
|
3780
|
+
}
|
|
3781
|
+
for (const node of plan.nodes) {
|
|
3782
|
+
const ancestors = /* @__PURE__ */ new Set([node.id]);
|
|
3783
|
+
let requiredByManifest = this.required.has(replayNodeKey(node));
|
|
3784
|
+
let requiredForAssertion = this.fullTraceForAssertions || this.assertionNames.has(node.spanName);
|
|
3785
|
+
let parentId = node.parentId;
|
|
3786
|
+
while (parentId !== null) {
|
|
3787
|
+
const parent = this.nodes.get(parentId);
|
|
3788
|
+
if (!parent || ancestors.has(parentId) || node.mustRun && !parent.mustRun) {
|
|
3789
|
+
throw new BitfabError("Invalid selective replay ancestry.");
|
|
3790
|
+
}
|
|
3791
|
+
ancestors.add(parentId);
|
|
3792
|
+
requiredByManifest || (requiredByManifest = this.required.has(replayNodeKey(parent)));
|
|
3793
|
+
requiredForAssertion || (requiredForAssertion = this.assertionNames.has(parent.spanName));
|
|
3794
|
+
parentId = parent.parentId;
|
|
3795
|
+
}
|
|
3796
|
+
if (!ancestors.has(plan.rootId) || (requiredByManifest || unresolved.length > 0) && !node.mustRun) {
|
|
3797
|
+
throw new BitfabError("Selective replay plan could hide required code.");
|
|
3798
|
+
}
|
|
3799
|
+
if (requiredForAssertion && !node.mustRun) {
|
|
3800
|
+
throw new BitfabError(
|
|
3801
|
+
"Selective replay plan could hide assertion evidence."
|
|
3802
|
+
);
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
enter(params) {
|
|
3807
|
+
const identity = {
|
|
3808
|
+
traceFunctionKey: params.traceFunctionKey,
|
|
3809
|
+
spanName: params.spanName
|
|
3810
|
+
};
|
|
3811
|
+
const key = replayNodeKey(identity);
|
|
3812
|
+
const fingerprint = jsonFingerprint(params.inputs);
|
|
3813
|
+
let node;
|
|
3814
|
+
let matchReason = "unmatched-call";
|
|
3815
|
+
if (params.parentSpanId === null) {
|
|
3816
|
+
node = this.nodes.get(this.plan.rootId);
|
|
3817
|
+
} else {
|
|
3818
|
+
const recordedParent = this.runtimeParents.get(params.parentSpanId);
|
|
3819
|
+
const consumed = this.consumed.get(params.parentSpanId) ?? /* @__PURE__ */ new Set();
|
|
3820
|
+
this.consumed.set(params.parentSpanId, consumed);
|
|
3821
|
+
const candidates = recordedParent ? (this.children.get(recordedParent) ?? []).filter(
|
|
3822
|
+
(candidate) => replayNodeKey(candidate) === key && !consumed.has(candidate.id)
|
|
3823
|
+
) : [];
|
|
3824
|
+
const exact = fingerprint === void 0 ? [] : candidates.filter(
|
|
3825
|
+
(candidate) => this.recordings.get(candidate.id)?.input === fingerprint
|
|
3826
|
+
);
|
|
3827
|
+
node = exact.length === 1 ? exact[0] : exact.length === 0 && candidates.length === 1 ? candidates[0] : void 0;
|
|
3828
|
+
if (node) {
|
|
3829
|
+
consumed.add(node.id);
|
|
3830
|
+
} else if (candidates.length > 0) {
|
|
3831
|
+
matchReason = "ambiguous-call";
|
|
3832
|
+
for (const candidate of candidates) {
|
|
3833
|
+
consumed.add(candidate.id);
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
this.runtimeParents.set(params.spanId, node?.id ?? null);
|
|
3838
|
+
const requiredByManifest = this.required.has(key) || params.parentSpanId !== null && this.requiredScopes.has(params.parentSpanId);
|
|
3839
|
+
if (requiredByManifest) {
|
|
3840
|
+
this.requiredScopes.add(params.spanId);
|
|
3841
|
+
}
|
|
3842
|
+
const requiredForAssertion = this.fullTraceForAssertions || this.assertionNames.has(params.spanName) || params.parentSpanId !== null && this.assertionScopes.has(params.parentSpanId);
|
|
3843
|
+
if (requiredForAssertion) {
|
|
3844
|
+
this.assertionScopes.add(params.spanId);
|
|
3845
|
+
}
|
|
3846
|
+
const mustRun = params.parentSpanId === null || requiredByManifest || node?.mustRun === true || requiredForAssertion || this.plan.unresolvedMustRun.length > 0;
|
|
3847
|
+
const recording = node === void 0 ? void 0 : this.recordings.get(node.id);
|
|
3848
|
+
const equalInputs = fingerprint !== void 0 && recording?.input === fingerprint;
|
|
3849
|
+
const reusable = recording?.output !== void 0 && equalInputs && params.canReuseOutput;
|
|
3850
|
+
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;
|
|
3851
|
+
const decision = (action, why) => {
|
|
3852
|
+
this.decisions.push({
|
|
3853
|
+
...identity,
|
|
3854
|
+
originalSpanId: node?.id ?? null,
|
|
3855
|
+
action,
|
|
3856
|
+
reason: why
|
|
3857
|
+
});
|
|
3858
|
+
};
|
|
3859
|
+
if (params.safetyMock && (mustRun || !reusable)) {
|
|
3860
|
+
decision("blocked", `safety-conflict:${reason}`);
|
|
3861
|
+
this.failure = new BitfabError(
|
|
3862
|
+
`Selective replay cannot execute or safely reuse safety-mocked node ${params.spanName} (${reason}).`
|
|
3863
|
+
);
|
|
3864
|
+
throw this.failure;
|
|
3865
|
+
}
|
|
3866
|
+
if (!mustRun && reusable && (params.safetyMock || node?.reusable === true && params.reuseAllowed)) {
|
|
3867
|
+
decision("mock", params.safetyMock ? "safety-mock" : "unchanged-inputs");
|
|
3868
|
+
return {
|
|
3869
|
+
mock: true,
|
|
3870
|
+
output: JSON.parse(recording.output)
|
|
3871
|
+
};
|
|
3872
|
+
}
|
|
3873
|
+
decision("run", reason);
|
|
3874
|
+
this.observedNames.set(
|
|
3875
|
+
params.spanName,
|
|
3876
|
+
(this.observedNames.get(params.spanName) ?? 0) + 1
|
|
3877
|
+
);
|
|
3878
|
+
if (this.required.has(key)) {
|
|
3879
|
+
this.reached.add(key);
|
|
3880
|
+
}
|
|
3881
|
+
return { mock: false };
|
|
3882
|
+
}
|
|
3883
|
+
assertNoSafetyConflict() {
|
|
3884
|
+
if (this.failure) {
|
|
3885
|
+
throw this.failure;
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
report() {
|
|
3889
|
+
return {
|
|
3890
|
+
decisions: this.decisions,
|
|
3891
|
+
mustRunNotReached: this.options.mustRun.filter(
|
|
3892
|
+
(node) => !this.reached.has(replayNodeKey(node))
|
|
3893
|
+
),
|
|
3894
|
+
unresolvedMustRun: this.plan.unresolvedMustRun,
|
|
3895
|
+
assertionIds: this.plan.assertions.map((assertion) => assertion.id),
|
|
3896
|
+
unresolvedAssertions: this.unresolvedAssertions,
|
|
3897
|
+
assertionTargetsNotReached: this.plan.assertions.flatMap(
|
|
3898
|
+
({ id, target }) => target?.kind === "span" && (this.observedNames.get(target.name) ?? 0) <= (typeof target.occurrence === "number" ? target.occurrence : 0) ? [id] : []
|
|
3899
|
+
)
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
};
|
|
3903
|
+
}
|
|
3904
|
+
});
|
|
3905
|
+
|
|
3617
3906
|
// src/replay.ts
|
|
3618
3907
|
var replay_exports = {};
|
|
3619
3908
|
__export(replay_exports, {
|
|
@@ -3869,7 +4158,7 @@ function buildMockTree(rootNode) {
|
|
|
3869
4158
|
}
|
|
3870
4159
|
return { spans };
|
|
3871
4160
|
}
|
|
3872
|
-
async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
|
|
4161
|
+
async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun, selectiveOptions) {
|
|
3873
4162
|
let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
|
|
3874
4163
|
let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
|
|
3875
4164
|
let dbSnapshotRef = serverItem.dbSnapshotRef;
|
|
@@ -3882,6 +4171,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3882
4171
|
let replayError = null;
|
|
3883
4172
|
let replayDurationMs = null;
|
|
3884
4173
|
let replayStarted = null;
|
|
4174
|
+
let selectiveReplay;
|
|
3885
4175
|
const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
|
|
3886
4176
|
const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
|
|
3887
4177
|
try {
|
|
@@ -3914,6 +4204,17 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3914
4204
|
originalTraceId
|
|
3915
4205
|
);
|
|
3916
4206
|
}
|
|
4207
|
+
if (selectiveOptions !== void 0) {
|
|
4208
|
+
if (serverItem.selectiveReplayPlan === void 0) {
|
|
4209
|
+
throw new BitfabError(
|
|
4210
|
+
"Server did not return a selective replay plan. Update the server or disable experimentalSelectiveReplay."
|
|
4211
|
+
);
|
|
4212
|
+
}
|
|
4213
|
+
selectiveReplay = new SelectiveReplayRuntime(
|
|
4214
|
+
serverItem.selectiveReplayPlan,
|
|
4215
|
+
selectiveOptions
|
|
4216
|
+
);
|
|
4217
|
+
}
|
|
3917
4218
|
const span = await httpClient.getExternalSpan(originalSpanId, {
|
|
3918
4219
|
view: "replay"
|
|
3919
4220
|
});
|
|
@@ -3933,7 +4234,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
3933
4234
|
});
|
|
3934
4235
|
}
|
|
3935
4236
|
const hasOverrides = resolvedOverrides.length > 0;
|
|
3936
|
-
const needTree = mockStrategy === "all" || mockStrategy === "marked" || hasOverrides;
|
|
4237
|
+
const needTree = selectiveReplay === void 0 && (mockStrategy === "all" || mockStrategy === "marked" || hasOverrides);
|
|
3937
4238
|
const includeOutputs = mockStrategy === "all";
|
|
3938
4239
|
let mockTree;
|
|
3939
4240
|
if (needTree) {
|
|
@@ -4008,6 +4309,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4008
4309
|
sourceBitfabTraceId: originalTraceId,
|
|
4009
4310
|
replayAttempt: attempt,
|
|
4010
4311
|
mockTree,
|
|
4312
|
+
selectiveReplay,
|
|
4011
4313
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
4012
4314
|
mockStrategy,
|
|
4013
4315
|
mockOverrides: hasOverrides ? resolvedOverrides : void 0,
|
|
@@ -4019,6 +4321,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4019
4321
|
() => fn(...inputs)
|
|
4020
4322
|
);
|
|
4021
4323
|
result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
|
|
4324
|
+
selectiveReplay?.assertNoSafetyConflict();
|
|
4022
4325
|
replayDurationMs = Math.round(performance.now() - replayStarted);
|
|
4023
4326
|
} catch (e) {
|
|
4024
4327
|
if (replayStarted !== null) {
|
|
@@ -4070,6 +4373,7 @@ async function processItem(httpClient, serverItem, fn, experimentId, mockStrateg
|
|
|
4070
4373
|
traceError,
|
|
4071
4374
|
replayError,
|
|
4072
4375
|
durationMs: replayDurationMs,
|
|
4376
|
+
...selectiveReplay === void 0 ? {} : { selectiveReplay: selectiveReplay.report() },
|
|
4073
4377
|
originalDurationMs,
|
|
4074
4378
|
originalTokens: serverItem.originalTokens ?? serverItem.tokens ?? null,
|
|
4075
4379
|
originalModel,
|
|
@@ -4174,6 +4478,14 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4174
4478
|
"force only applies when resuming an experiment. Pass it together with resume."
|
|
4175
4479
|
);
|
|
4176
4480
|
}
|
|
4481
|
+
if (options?.experimentalSelectiveReplay !== void 0) {
|
|
4482
|
+
validateSelectiveReplayOptions(options.experimentalSelectiveReplay);
|
|
4483
|
+
if ((options.mock ?? "marked") !== "marked" || normalizeMockOverrides(options.mockOverride).length > 0 || registeredOverrides.length > 0) {
|
|
4484
|
+
throw new BitfabError(
|
|
4485
|
+
"experimentalSelectiveReplay requires mock: 'marked' and cannot be combined with mock overrides."
|
|
4486
|
+
);
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4177
4489
|
if (options?.traceIds !== void 0) {
|
|
4178
4490
|
if (options.traceIds.length === 0) {
|
|
4179
4491
|
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
@@ -4235,7 +4547,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4235
4547
|
requestedAttempts,
|
|
4236
4548
|
options?.adaptInputs !== void 0,
|
|
4237
4549
|
options?.onlyWithAssertions,
|
|
4238
|
-
options?.judgeAssertions
|
|
4550
|
+
options?.judgeAssertions,
|
|
4551
|
+
options?.experimentalSelectiveReplay
|
|
4239
4552
|
);
|
|
4240
4553
|
if (started2.items.length === 0) {
|
|
4241
4554
|
try {
|
|
@@ -4297,7 +4610,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4297
4610
|
session.includeDbBranchLease,
|
|
4298
4611
|
session.dbBranchSettings,
|
|
4299
4612
|
options?.adaptInputs,
|
|
4300
|
-
options?.dryRun === true
|
|
4613
|
+
options?.dryRun === true,
|
|
4614
|
+
options?.experimentalSelectiveReplay
|
|
4301
4615
|
)
|
|
4302
4616
|
);
|
|
4303
4617
|
const total = tasks.length + carriedOverItems.length;
|
|
@@ -4370,6 +4684,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
|
|
|
4370
4684
|
traceError: item.traceError,
|
|
4371
4685
|
replayError: item.replayError,
|
|
4372
4686
|
durationMs: item.durationMs,
|
|
4687
|
+
...item.selectiveReplay === void 0 ? {} : { selectiveReplay: item.selectiveReplay },
|
|
4373
4688
|
originalDurationMs: item.originalDurationMs,
|
|
4374
4689
|
originalTokens: item.originalTokens,
|
|
4375
4690
|
originalModel: item.originalModel,
|
|
@@ -4548,6 +4863,7 @@ var init_replay = __esm({
|
|
|
4548
4863
|
init_mockOverride();
|
|
4549
4864
|
init_randomUuid();
|
|
4550
4865
|
init_replayContext();
|
|
4866
|
+
init_selectiveReplayRuntime();
|
|
4551
4867
|
init_serialize();
|
|
4552
4868
|
REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
|
|
4553
4869
|
MAX_REPLAY_ATTEMPTS = 100;
|
|
@@ -4559,7 +4875,8 @@ var init_replay = __esm({
|
|
|
4559
4875
|
"attempts",
|
|
4560
4876
|
"graderIds",
|
|
4561
4877
|
"onlyWithAssertions",
|
|
4562
|
-
"dryRun"
|
|
4878
|
+
"dryRun",
|
|
4879
|
+
"experimentalSelectiveReplay"
|
|
4563
4880
|
];
|
|
4564
4881
|
CARRIED_OVER_ERROR = "This item errored in an earlier run of this experiment, before it was resumed.";
|
|
4565
4882
|
BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
|
|
@@ -9260,6 +9577,9 @@ var Bitfab = class {
|
|
|
9260
9577
|
...options.mockOnReplay !== void 0 && {
|
|
9261
9578
|
mockOnReplay: options.mockOnReplay
|
|
9262
9579
|
},
|
|
9580
|
+
...options.replayReusable !== void 0 && {
|
|
9581
|
+
replayReusable: options.replayReusable
|
|
9582
|
+
},
|
|
9263
9583
|
...options.finalize !== void 0 && { finalize: options.finalize }
|
|
9264
9584
|
};
|
|
9265
9585
|
}
|
|
@@ -9408,6 +9728,9 @@ var Bitfab = class {
|
|
|
9408
9728
|
...mockOnReplay !== void 0 && {
|
|
9409
9729
|
mockOnReplay
|
|
9410
9730
|
},
|
|
9731
|
+
...nodeConfiguration?.replayReusable !== void 0 && {
|
|
9732
|
+
replayReusable: nodeConfiguration.replayReusable
|
|
9733
|
+
},
|
|
9411
9734
|
...nodeConfiguration?.finalize !== void 0 && {
|
|
9412
9735
|
finalize: nodeConfiguration.finalize
|
|
9413
9736
|
}
|
|
@@ -10205,6 +10528,7 @@ var Bitfab = class {
|
|
|
10205
10528
|
instrumentation: options.instrumentation ?? "span",
|
|
10206
10529
|
functionId: options.functionId,
|
|
10207
10530
|
captureContent: options.captureContent ?? true,
|
|
10531
|
+
replayReusable: options.replayReusable,
|
|
10208
10532
|
autoTraceDefinition: options.autoTraceDefinition,
|
|
10209
10533
|
declaredNode: options.declaredNode === true,
|
|
10210
10534
|
nestedTrace: options.nestedTrace,
|
|
@@ -10324,6 +10648,27 @@ var Bitfab = class {
|
|
|
10324
10648
|
return result;
|
|
10325
10649
|
};
|
|
10326
10650
|
const replayCtxForMock = getReplayContext();
|
|
10651
|
+
if (replayCtxForMock?.selectiveReplay) {
|
|
10652
|
+
const decision = replayCtxForMock.selectiveReplay.enter({
|
|
10653
|
+
traceFunctionKey,
|
|
10654
|
+
spanName: baseSpanParams.spanName ?? traceFunctionKey,
|
|
10655
|
+
spanId,
|
|
10656
|
+
parentSpanId,
|
|
10657
|
+
inputs: args,
|
|
10658
|
+
safetyMock: !isRootSpan && options.mockOnReplay === true,
|
|
10659
|
+
reuseAllowed: options.replayReusable === true,
|
|
10660
|
+
canReuseOutput: options.finalize === void 0 && options.replayOutput === void 0 && !fn.constructor.name.includes("Generator")
|
|
10661
|
+
});
|
|
10662
|
+
if (decision.mock) {
|
|
10663
|
+
void sendSpan({
|
|
10664
|
+
result: decision.output,
|
|
10665
|
+
mocked: true,
|
|
10666
|
+
mockTarget: "output",
|
|
10667
|
+
mockSource: "recorded"
|
|
10668
|
+
});
|
|
10669
|
+
return fnReturnsPromise ? Promise.resolve(decision.output) : decision.output;
|
|
10670
|
+
}
|
|
10671
|
+
}
|
|
10327
10672
|
if (replayCtxForMock?.mockTree && !isRootSpan) {
|
|
10328
10673
|
const counters = replayCtxForMock.callCounters;
|
|
10329
10674
|
const counterKey = `${traceFunctionKey}:${baseSpanParams.spanName}`;
|
|
@@ -10747,6 +11092,9 @@ var Bitfab = class {
|
|
|
10747
11092
|
span_origin: makeSpanOrigin(params.instrumentation),
|
|
10748
11093
|
span_data: {
|
|
10749
11094
|
name: params.spanName,
|
|
11095
|
+
...params.replayReusable !== void 0 && {
|
|
11096
|
+
replay_reusable: params.replayReusable
|
|
11097
|
+
},
|
|
10750
11098
|
type: params.spanType,
|
|
10751
11099
|
...params.functionId !== void 0 && {
|
|
10752
11100
|
function_id: params.functionId,
|