bitfab 0.11.5 → 0.12.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.d.cts CHANGED
@@ -258,6 +258,7 @@ declare class BitfabLangGraphCallbackHandler {
258
258
  * 3. Complete — marks the test run as completed on the server
259
259
  */
260
260
 
261
+ type MockStrategy = "none" | "all" | "marked";
261
262
  interface ReplayOptions {
262
263
  /** Maximum number of traces to replay (1–100, default 5). */
263
264
  limit?: number;
@@ -277,6 +278,13 @@ interface ReplayOptions {
277
278
  * files (`before`) or deleted files (`after`).
278
279
  */
279
280
  codeChangeFiles?: CodeChangeFile[];
281
+ /**
282
+ * Mock strategy for child spans during replay.
283
+ * - "none": everything runs real code (default)
284
+ * - "all": every child withSpan returns historical output
285
+ * - "marked": only spans tagged with { mockOnReplay: true } in SpanOptions are mocked
286
+ */
287
+ mock?: MockStrategy;
280
288
  }
281
289
  interface ReplayItem<T> {
282
290
  /** Deserialized inputs from the original trace. */
@@ -533,6 +541,16 @@ interface SpanOptions {
533
541
  * The type of span. Defaults to "custom" if not specified.
534
542
  */
535
543
  type?: SpanType;
544
+ /**
545
+ * When true, replay will reuse this span's historical output instead of
546
+ * executing the wrapped function. Read by the "marked" replay strategy;
547
+ * ignored outside replay and under the "all"/"none" strategies.
548
+ *
549
+ * Use this for child spans that are expensive (paid LLM/API calls),
550
+ * slow, or non-deterministic — the root function still runs real code,
551
+ * only the marked descendants return their recorded output.
552
+ */
553
+ mockOnReplay?: boolean;
536
554
  }
537
555
 
538
556
  /**
@@ -740,6 +758,7 @@ declare class Bitfab {
740
758
  maxConcurrency?: number;
741
759
  codeChangeDescription?: string;
742
760
  codeChangeFiles?: CodeChangeFile[];
761
+ mock?: "none" | "all" | "marked";
743
762
  }): Promise<ReplayResult<TReturn>>;
744
763
  }
745
764
  /**
@@ -803,7 +822,7 @@ declare class BitfabFunction {
803
822
  /**
804
823
  * SDK version from package.json (injected at build time)
805
824
  */
806
- declare const __version__ = "0.11.5";
825
+ declare const __version__ = "0.12.0";
807
826
 
808
827
  /**
809
828
  * Constants for the Bitfab SDK.
@@ -813,4 +832,4 @@ declare const __version__ = "0.11.5";
813
832
  */
814
833
  declare const DEFAULT_SERVICE_URL = "https://bitfab.ai";
815
834
 
816
- export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
835
+ export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type MockStrategy, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
package/dist/index.d.ts CHANGED
@@ -258,6 +258,7 @@ declare class BitfabLangGraphCallbackHandler {
258
258
  * 3. Complete — marks the test run as completed on the server
259
259
  */
260
260
 
261
+ type MockStrategy = "none" | "all" | "marked";
261
262
  interface ReplayOptions {
262
263
  /** Maximum number of traces to replay (1–100, default 5). */
263
264
  limit?: number;
@@ -277,6 +278,13 @@ interface ReplayOptions {
277
278
  * files (`before`) or deleted files (`after`).
278
279
  */
279
280
  codeChangeFiles?: CodeChangeFile[];
281
+ /**
282
+ * Mock strategy for child spans during replay.
283
+ * - "none": everything runs real code (default)
284
+ * - "all": every child withSpan returns historical output
285
+ * - "marked": only spans tagged with { mockOnReplay: true } in SpanOptions are mocked
286
+ */
287
+ mock?: MockStrategy;
280
288
  }
281
289
  interface ReplayItem<T> {
282
290
  /** Deserialized inputs from the original trace. */
@@ -533,6 +541,16 @@ interface SpanOptions {
533
541
  * The type of span. Defaults to "custom" if not specified.
534
542
  */
535
543
  type?: SpanType;
544
+ /**
545
+ * When true, replay will reuse this span's historical output instead of
546
+ * executing the wrapped function. Read by the "marked" replay strategy;
547
+ * ignored outside replay and under the "all"/"none" strategies.
548
+ *
549
+ * Use this for child spans that are expensive (paid LLM/API calls),
550
+ * slow, or non-deterministic — the root function still runs real code,
551
+ * only the marked descendants return their recorded output.
552
+ */
553
+ mockOnReplay?: boolean;
536
554
  }
537
555
 
538
556
  /**
@@ -740,6 +758,7 @@ declare class Bitfab {
740
758
  maxConcurrency?: number;
741
759
  codeChangeDescription?: string;
742
760
  codeChangeFiles?: CodeChangeFile[];
761
+ mock?: "none" | "all" | "marked";
743
762
  }): Promise<ReplayResult<TReturn>>;
744
763
  }
745
764
  /**
@@ -803,7 +822,7 @@ declare class BitfabFunction {
803
822
  /**
804
823
  * SDK version from package.json (injected at build time)
805
824
  */
806
- declare const __version__ = "0.11.5";
825
+ declare const __version__ = "0.12.0";
807
826
 
808
827
  /**
809
828
  * Constants for the Bitfab SDK.
@@ -813,4 +832,4 @@ declare const __version__ = "0.11.5";
813
832
  */
814
833
  declare const DEFAULT_SERVICE_URL = "https://bitfab.ai";
815
834
 
816
- export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
835
+ export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type MockStrategy, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
package/dist/index.js CHANGED
@@ -6,13 +6,13 @@ import {
6
6
  BitfabOpenAITracingProcessor,
7
7
  getCurrentSpan,
8
8
  getCurrentTrace
9
- } from "./chunk-GWHHP5PL.js";
9
+ } from "./chunk-QUCK3IU2.js";
10
10
  import {
11
11
  BitfabError,
12
12
  DEFAULT_SERVICE_URL,
13
13
  __version__,
14
14
  flushTraces
15
- } from "./chunk-KSUI74KL.js";
15
+ } from "./chunk-VMJPNYAG.js";
16
16
  export {
17
17
  Bitfab,
18
18
  BitfabClaudeAgentHandler,
package/dist/node.cjs CHANGED
@@ -81,7 +81,7 @@ var __version__;
81
81
  var init_version_generated = __esm({
82
82
  "src/version.generated.ts"() {
83
83
  "use strict";
84
- __version__ = "0.11.5";
84
+ __version__ = "0.12.0";
85
85
  }
86
86
  });
87
87
 
@@ -336,6 +336,42 @@ var init_http = __esm({
336
336
  clearTimeout(timeoutId);
337
337
  }
338
338
  }
339
+ /**
340
+ * Fetch the span tree for a root span.
341
+ * Blocking GET request.
342
+ */
343
+ async getSpanTree(externalSpanId) {
344
+ const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}`;
345
+ const controller = new AbortController();
346
+ const timeoutId = setTimeout(() => controller.abort(), 3e4);
347
+ try {
348
+ const response = await fetch(url, {
349
+ method: "GET",
350
+ headers: { Authorization: `Bearer ${this.apiKey}` },
351
+ signal: controller.signal
352
+ });
353
+ if (!response.ok) {
354
+ const errorText = await response.text();
355
+ throw new BitfabError(
356
+ `HTTP ${response.status}: ${errorText.slice(0, 500)}`
357
+ );
358
+ }
359
+ return await response.json();
360
+ } catch (error) {
361
+ if (error instanceof BitfabError) {
362
+ throw error;
363
+ }
364
+ if (error instanceof Error) {
365
+ if (error.name === "AbortError") {
366
+ throw new BitfabError("Request timed out after 30000ms");
367
+ }
368
+ throw new BitfabError(error.message);
369
+ }
370
+ throw new BitfabError("Unknown error occurred");
371
+ } finally {
372
+ clearTimeout(timeoutId);
373
+ }
374
+ }
339
375
  /**
340
376
  * Mark a replay test run as completed.
341
377
  * Blocking call.
@@ -431,16 +467,51 @@ function deserializeOutput(spanData) {
431
467
  }
432
468
  return rawOutput;
433
469
  }
434
- async function processItem(httpClient, serverItem, fn, testRunId) {
470
+ function buildMockTree(rootNode) {
471
+ const spans = /* @__PURE__ */ new Map();
472
+ const counters = /* @__PURE__ */ new Map();
473
+ function walk(node) {
474
+ const key = node.traceFunctionKey;
475
+ if (key) {
476
+ const index = counters.get(key) ?? 0;
477
+ counters.set(key, index + 1);
478
+ spans.set(`${key}:${index}`, {
479
+ sourceSpanId: node.sourceSpanId,
480
+ output: node.output,
481
+ outputMeta: node.outputMeta
482
+ });
483
+ }
484
+ for (const child of node.children) {
485
+ walk(child);
486
+ }
487
+ }
488
+ for (const child of rootNode.children) {
489
+ walk(child);
490
+ }
491
+ return { spans };
492
+ }
493
+ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy) {
435
494
  const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
436
495
  const spanData = span.rawData?.span_data ?? {};
437
496
  const inputs = deserializeInputs(spanData);
438
497
  const originalOutput = deserializeOutput(spanData);
498
+ let mockTree;
499
+ if (mockStrategy === "all" || mockStrategy === "marked") {
500
+ const treeResponse = await httpClient.getSpanTree(serverItem.externalSpanId);
501
+ mockTree = buildMockTree(treeResponse.root);
502
+ }
439
503
  let result;
440
504
  let error = null;
441
505
  try {
442
506
  const maybePromise = runWithReplayContext(
443
- { testRunId, inputSourceSpanId: span.id },
507
+ {
508
+ testRunId,
509
+ inputSourceSpanId: span.id,
510
+ inputSourceTraceId: span.externalTraceId,
511
+ mockTree,
512
+ callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
513
+ mockStrategy
514
+ },
444
515
  () => fn(...inputs)
445
516
  );
446
517
  result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
@@ -486,9 +557,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
486
557
  options?.codeChangeDescription,
487
558
  options?.codeChangeFiles
488
559
  );
560
+ const mockStrategy = options?.mock ?? "none";
489
561
  const maxConcurrency = options?.maxConcurrency ?? 10;
490
562
  const tasks = serverItems.map(
491
- (serverItem) => () => processItem(httpClient, serverItem, fn, testRunId)
563
+ (serverItem) => () => processItem(httpClient, serverItem, fn, testRunId, mockStrategy)
492
564
  );
493
565
  const resultItems = await mapWithConcurrency(tasks, maxConcurrency);
494
566
  await flushTraces();
@@ -2569,6 +2641,9 @@ var Bitfab = class {
2569
2641
  contexts: [],
2570
2642
  ...replayCtxAtRoot?.testRunId && {
2571
2643
  testRunId: replayCtxAtRoot.testRunId
2644
+ },
2645
+ ...replayCtxAtRoot?.inputSourceTraceId && {
2646
+ inputSourceTraceId: replayCtxAtRoot.inputSourceTraceId
2572
2647
  }
2573
2648
  });
2574
2649
  pendingSpanPromises.set(traceId, []);
@@ -2617,7 +2692,8 @@ var Bitfab = class {
2617
2692
  sessionId: traceState?.sessionId,
2618
2693
  metadata: traceState?.metadata,
2619
2694
  contexts: traceState?.contexts ?? [],
2620
- testRunId: traceState?.testRunId
2695
+ testRunId: traceState?.testRunId,
2696
+ inputSourceTraceId: traceState?.inputSourceTraceId
2621
2697
  });
2622
2698
  activeTraceStates.delete(traceId);
2623
2699
  } else {
@@ -2631,6 +2707,28 @@ var Bitfab = class {
2631
2707
  } catch {
2632
2708
  }
2633
2709
  };
2710
+ const replayCtxForMock = getReplayContext();
2711
+ if (replayCtxForMock?.mockTree && !isRootSpan) {
2712
+ const counters = replayCtxForMock.callCounters;
2713
+ const callIndex = counters.get(traceFunctionKey) ?? 0;
2714
+ counters.set(traceFunctionKey, callIndex + 1);
2715
+ const shouldMock = replayCtxForMock.mockStrategy === "all" || replayCtxForMock.mockStrategy === "marked" && options.mockOnReplay === true;
2716
+ if (shouldMock) {
2717
+ const mockKey = `${traceFunctionKey}:${callIndex}`;
2718
+ const mockSpan = replayCtxForMock.mockTree.spans.get(mockKey);
2719
+ if (mockSpan) {
2720
+ let output = mockSpan.output;
2721
+ if (mockSpan.outputMeta !== void 0 && mockSpan.outputMeta !== null) {
2722
+ output = deserializeValue({
2723
+ json: mockSpan.output,
2724
+ meta: mockSpan.outputMeta
2725
+ });
2726
+ }
2727
+ void sendSpan({ result: output });
2728
+ return output;
2729
+ }
2730
+ }
2731
+ }
2634
2732
  const executeWithContext = () => {
2635
2733
  const result = fn(...args);
2636
2734
  if (result instanceof Promise) {
@@ -2694,6 +2792,9 @@ var Bitfab = class {
2694
2792
  if (params.contexts && params.contexts.length > 0) {
2695
2793
  rawTrace.contexts = params.contexts;
2696
2794
  }
2795
+ if (params.inputSourceTraceId) {
2796
+ rawTrace.input_source_trace_id = params.inputSourceTraceId;
2797
+ }
2697
2798
  this.httpClient.sendExternalTrace({
2698
2799
  type: "sdk-function",
2699
2800
  source: "typescript-sdk-function",