bitfab 0.38.9 → 0.39.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/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.38.9";
54
+ __version__ = "0.39.0";
55
55
  __packageName__ = "bitfab";
56
56
  }
57
57
  });
@@ -1207,6 +1207,16 @@ var init_transport = __esm({
1207
1207
  });
1208
1208
 
1209
1209
  // src/http.ts
1210
+ var http_exports = {};
1211
+ __export(http_exports, {
1212
+ BitfabError: () => BitfabError,
1213
+ HttpClient: () => HttpClient,
1214
+ awaitOnExit: () => awaitOnExit,
1215
+ awaitPendingRequests: () => awaitPendingRequests,
1216
+ flushTraces: () => flushTraces,
1217
+ parseRetryAfterMs: () => parseRetryAfterMs,
1218
+ serializePayloadBody: () => serializePayloadBody
1219
+ });
1210
1220
  function awaitOnExit(promise) {
1211
1221
  pendingTracePromises.add(promise);
1212
1222
  void promise.finally(() => {
@@ -2551,7 +2561,7 @@ function buildMockTree(rootNode) {
2551
2561
  }
2552
2562
  return { spans };
2553
2563
  }
2554
- async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, includeDbBranchLease, dbBranchSettings, adaptInputs) {
2564
+ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
2555
2565
  let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
2556
2566
  let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
2557
2567
  let dbSnapshotRef = serverItem.dbSnapshotRef;
@@ -2646,6 +2656,32 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2646
2656
  }
2647
2657
  return pending;
2648
2658
  } : void 0;
2659
+ if (dryRun) {
2660
+ return {
2661
+ traceId: null,
2662
+ originalTraceId,
2663
+ originalSpanId,
2664
+ sourceTraceId: originalTraceId,
2665
+ sourceSpanId: originalSpanId,
2666
+ input: inputs,
2667
+ result: void 0,
2668
+ originalOutput,
2669
+ ...serverItem.ingestionType && {
2670
+ ingestionType: serverItem.ingestionType
2671
+ },
2672
+ error: null,
2673
+ traceError: null,
2674
+ replayError: null,
2675
+ durationMs: null,
2676
+ originalDurationMs: serverItem.originalDurationMs ?? serverItem.durationMs ?? null,
2677
+ originalTokens: serverItem.originalTokens ?? serverItem.tokens ?? null,
2678
+ originalModel: serverItem.originalModel ?? serverItem.model ?? null,
2679
+ tokens: null,
2680
+ model: serverItem.originalModel ?? serverItem.model ?? null,
2681
+ dbSnapshotRef: dbSnapshotRef ?? null,
2682
+ dbBranchTimings: dbBranchTimings ?? null
2683
+ };
2684
+ }
2649
2685
  try {
2650
2686
  replayStarted = performance.now();
2651
2687
  const maybePromise = runWithReplayContext(
@@ -2709,6 +2745,9 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2709
2745
  input: inputs,
2710
2746
  result,
2711
2747
  originalOutput,
2748
+ ...serverItem.ingestionType && {
2749
+ ingestionType: serverItem.ingestionType
2750
+ },
2712
2751
  error,
2713
2752
  traceError,
2714
2753
  replayError,
@@ -2856,13 +2895,23 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2856
2895
  options?.name,
2857
2896
  codeChangeDescription,
2858
2897
  codeChangeFiles,
2859
- dbBranchEnabled(options?.dbBranch),
2860
- // includeDbBranchLease
2898
+ // A dry run executes nothing, so a branch would be provisioned (and billed)
2899
+ // for code that never runs, and a seeded source's refusal would fail the item
2900
+ // before it could report its resolved inputs.
2901
+ dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true,
2861
2902
  options?.experimentGroupId,
2862
2903
  options?.datasetId,
2863
2904
  options?.graderIds,
2864
2905
  resolveDbBranchSettings(options?.dbBranch)
2865
2906
  );
2907
+ if (serverItems.length === 0) {
2908
+ try {
2909
+ console.warn(
2910
+ `Bitfab: no traces matched "${traceFunctionKey}", so this replay ran nothing. Capture a trace, or seed one with seedTrace, before replaying.`
2911
+ );
2912
+ } catch {
2913
+ }
2914
+ }
2866
2915
  const mockStrategy = options?.mock ?? "marked";
2867
2916
  const maxConcurrency = options?.maxConcurrency ?? 10;
2868
2917
  const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
@@ -2881,9 +2930,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2881
2930
  mockStrategy,
2882
2931
  resolvedOverrides,
2883
2932
  replayedTraceIds[index],
2884
- dbBranchEnabled(options?.dbBranch),
2933
+ dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true,
2885
2934
  resolveDbBranchSettings(options?.dbBranch),
2886
- options?.adaptInputs
2935
+ options?.adaptInputs,
2936
+ options?.dryRun === true
2887
2937
  )
2888
2938
  );
2889
2939
  const total = tasks.length;
@@ -2991,6 +3041,16 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2991
3041
  }
2992
3042
  } : void 0
2993
3043
  );
3044
+ if (options?.dryRun === true) {
3045
+ await httpClient.completeReplay(testRunId).catch(() => void 0);
3046
+ const dryResult = {
3047
+ items: resultItems,
3048
+ testRunId,
3049
+ testRunUrl: fullTestRunUrl
3050
+ };
3051
+ await writeReplayResultFile(dryResult);
3052
+ return dryResult;
3053
+ }
2994
3054
  const deliveredTraceIds = await preserveReplayFailure(
2995
3055
  () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
2996
3056
  resultItems,
@@ -3159,6 +3219,7 @@ __export(index_exports, {
3159
3219
  getCurrentSpan: () => getCurrentSpan,
3160
3220
  getCurrentTrace: () => getCurrentTrace,
3161
3221
  reportReplayProgress: () => reportReplayProgress,
3222
+ seedFromRegistry: () => seedFromRegistry,
3162
3223
  serializeReplayResult: () => serializeReplayResult
3163
3224
  });
3164
3225
  module.exports = __toCommonJS(index_exports);
@@ -6171,6 +6232,7 @@ var Bitfab = class {
6171
6232
  return invokeWithoutNode();
6172
6233
  }
6173
6234
  spansUsed += 1;
6235
+ const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
6174
6236
  const childOptions = {
6175
6237
  name: nodeConfiguration?.name ?? definition.name,
6176
6238
  type: nodeConfiguration?.type ?? "function",
@@ -6181,8 +6243,8 @@ var Bitfab = class {
6181
6243
  ...nodeConfiguration?.testRunId !== void 0 && {
6182
6244
  testRunId: nodeConfiguration.testRunId
6183
6245
  },
6184
- ...nodeConfiguration?.mockOnReplay !== void 0 && {
6185
- mockOnReplay: nodeConfiguration.mockOnReplay
6246
+ ...mockOnReplay !== void 0 && {
6247
+ mockOnReplay
6186
6248
  },
6187
6249
  ...nodeConfiguration?.finalize !== void 0 && {
6188
6250
  finalize: nodeConfiguration.finalize
@@ -6868,6 +6930,7 @@ var Bitfab = class {
6868
6930
  inputSourceTraceId: traceState?.inputSourceTraceId,
6869
6931
  dbSnapshotRef: traceState?.dbSnapshotRef,
6870
6932
  dropped: traceState?.dropped,
6933
+ ingestionType: traceState?.ingestionType,
6871
6934
  // Built AFTER the wrapped fn finished, so `accessed` reflects
6872
6935
  // whether customer code obtained the branch URL during this
6873
6936
  // item. Omitted entirely when no lease was attached, so the
@@ -7091,6 +7154,7 @@ var Bitfab = class {
7091
7154
  Object.defineProperty(wrappedFn, "_bitfabTraceFunctionKey", {
7092
7155
  value: traceFunctionKey
7093
7156
  });
7157
+ Object.defineProperty(wrappedFn, "_bitfabWrappedFn", { value: fn });
7094
7158
  return wrappedFn;
7095
7159
  }
7096
7160
  /**
@@ -7253,6 +7317,9 @@ var Bitfab = class {
7253
7317
  if (params.dbSnapshotRef) {
7254
7318
  rawTrace.db_snapshot_ref = params.dbSnapshotRef;
7255
7319
  }
7320
+ if (params.ingestionType) {
7321
+ rawTrace.ingestion_type = params.ingestionType;
7322
+ }
7256
7323
  if (params.dbSnapshotUsage) {
7257
7324
  rawTrace.db_snapshot_usage = {
7258
7325
  neon_branch_id: params.dbSnapshotUsage.neonBranchId,
@@ -7399,6 +7466,68 @@ var Bitfab = class {
7399
7466
  clearMockOverrides() {
7400
7467
  this.mockOverrides.length = 0;
7401
7468
  }
7469
+ /**
7470
+ * Write a replayable trace from a case, without running anything.
7471
+ *
7472
+ * Use this to turn a corpus you already hold (a Braintrust dataset, a
7473
+ * spreadsheet, hand-written cases) into traces that {@link replay} can
7474
+ * select. The recorded root span carries `input` as its input and `expected`
7475
+ * as its output, so replay reports each item against the value you expected
7476
+ * rather than against a previous run.
7477
+ *
7478
+ * A seeded trace has no child spans and no database pin, so replay mocking
7479
+ * has nothing recorded to substitute and `dbBranch` refuses it. Pass
7480
+ * `mockOverride` at replay time for calls that must not run.
7481
+ *
7482
+ * @returns The trace ID, usable with `replay({ traceIds: [...] })`.
7483
+ */
7484
+ seedTrace(traceFunctionKey, options) {
7485
+ const { input } = options;
7486
+ const fn = options.fn?._bitfabWrappedFn ?? options.fn;
7487
+ if (fn && input.length < fn.length) {
7488
+ throw new BitfabError(
7489
+ `Seeded case supplies ${input.length} argument(s) but ${fn.name === "" ? "the function" : fn.name} requires ${fn.length}. Fix the case, or omit fn to seed it anyway.`
7490
+ );
7491
+ }
7492
+ const traceId = randomUuid();
7493
+ const startedAt = nowIsoTimestamp();
7494
+ activeTraceStates.set(traceId, {
7495
+ traceId,
7496
+ startedAt,
7497
+ contexts: [],
7498
+ ingestionType: "seeded",
7499
+ ...options.sessionId !== void 0 && { sessionId: options.sessionId },
7500
+ ...options.metadata !== void 0 && { metadata: options.metadata }
7501
+ });
7502
+ try {
7503
+ this.sendWrapperSpan({
7504
+ traceFunctionKey,
7505
+ spanName: options.spanName ?? traceFunctionKey,
7506
+ traceId,
7507
+ spanId: randomUuid(),
7508
+ parentSpanId: null,
7509
+ inputs: input,
7510
+ result: options.expected,
7511
+ startedAt,
7512
+ endedAt: startedAt,
7513
+ spanType: options.spanType ?? "agent",
7514
+ captureContent: true
7515
+ });
7516
+ this.sendTraceCompletion({
7517
+ traceFunctionKey,
7518
+ traceId,
7519
+ startedAt,
7520
+ endedAt: startedAt,
7521
+ sessionId: options.sessionId,
7522
+ metadata: options.metadata,
7523
+ contexts: [],
7524
+ ingestionType: "seeded"
7525
+ });
7526
+ } finally {
7527
+ activeTraceStates.delete(traceId);
7528
+ }
7529
+ return traceId;
7530
+ }
7402
7531
  async replay(traceFunctionKey, fn, options) {
7403
7532
  const wrappedKey = fn._bitfabTraceFunctionKey;
7404
7533
  let replayFn = fn;
@@ -7659,6 +7788,37 @@ init_replay();
7659
7788
  function defineReplayRegistry(registry) {
7660
7789
  return registry;
7661
7790
  }
7791
+ async function seedFromRegistry(registry, pipeline, cases) {
7792
+ const registration = registry[pipeline];
7793
+ if (registration === void 0) {
7794
+ throw new BitfabError(
7795
+ `Unknown pipeline '${pipeline}'. Registered: ${Object.keys(registry).join(", ")}`
7796
+ );
7797
+ }
7798
+ const traceFunctionKey = resolveTraceFunctionKey(registration);
7799
+ const traceIds = cases.map(
7800
+ (seedCase) => registration.client.seedTrace(traceFunctionKey, {
7801
+ input: seedCase.input,
7802
+ expected: seedCase.expected,
7803
+ fn: registration.fn,
7804
+ metadata: seedCase.metadata,
7805
+ sessionId: seedCase.sessionId
7806
+ })
7807
+ );
7808
+ const { flushTraces: flushTraces2 } = await Promise.resolve().then(() => (init_http(), http_exports));
7809
+ await flushTraces2(3e4);
7810
+ return { pipeline, traceFunctionKey, traceIds };
7811
+ }
7812
+ function resolveTraceFunctionKey(registration) {
7813
+ const wrappedKey = registration.fn._bitfabTraceFunctionKey;
7814
+ const key = registration.traceFunctionKey ?? wrappedKey;
7815
+ if (key === void 0) {
7816
+ throw new BitfabError(
7817
+ "Replay registry entry uses a plain function. Set traceFunctionKey to the key its production handler records."
7818
+ );
7819
+ }
7820
+ return key;
7821
+ }
7662
7822
  // Annotate the CommonJS export names for ESM import in node:
7663
7823
  0 && (module.exports = {
7664
7824
  BITFAB_PROGRESS_PREFIX,
@@ -7686,6 +7846,7 @@ function defineReplayRegistry(registry) {
7686
7846
  getCurrentSpan,
7687
7847
  getCurrentTrace,
7688
7848
  reportReplayProgress,
7849
+ seedFromRegistry,
7689
7850
  serializeReplayResult
7690
7851
  });
7691
7852
  //# sourceMappingURL=index.cjs.map