@bitfab/sdk 0.27.2 → 0.28.1

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.js CHANGED
@@ -14,12 +14,12 @@ import {
14
14
  flushTraces,
15
15
  getCurrentSpan,
16
16
  getCurrentTrace
17
- } from "./chunk-DW246LVL.js";
17
+ } from "./chunk-3YNAPGPA.js";
18
18
  import {
19
19
  BITFAB_PROGRESS_PREFIX,
20
20
  BitfabError,
21
21
  reportReplayProgress
22
- } from "./chunk-MD4XQGAF.js";
22
+ } from "./chunk-FTXZWRTG.js";
23
23
  export {
24
24
  BITFAB_PROGRESS_PREFIX,
25
25
  Bitfab,
package/dist/node.cjs CHANGED
@@ -536,6 +536,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
536
536
  }
537
537
  try {
538
538
  options?.onProgress?.({
539
+ testRunId,
539
540
  completed,
540
541
  total,
541
542
  succeeded,
@@ -545,8 +546,15 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
545
546
  // that just settled. The item's own traceId is the new replay
546
547
  // trace and is assigned later (below), so use the server item.
547
548
  traceId: serverItems[index]?.traceId ?? null,
549
+ replayTraceId: item.traceId,
550
+ input: item.input,
551
+ result: item.result,
552
+ originalOutput: item.originalOutput,
548
553
  error: item.error,
549
- durationMs: item.durationMs
554
+ durationMs: item.durationMs,
555
+ tokens: item.tokens,
556
+ model: item.model,
557
+ dbSnapshotRef: item.dbSnapshotRef
550
558
  }
551
559
  });
552
560
  } catch {
@@ -650,7 +658,7 @@ registerAsyncLocalStorageClass(
650
658
  );
651
659
 
652
660
  // src/version.generated.ts
653
- var __version__ = "0.27.2";
661
+ var __version__ = "0.28.1";
654
662
 
655
663
  // src/constants.ts
656
664
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -809,6 +817,13 @@ var HttpClient = class {
809
817
  this.serviceUrl = config.serviceUrl;
810
818
  this.timeout = config.timeout ?? 12e4;
811
819
  }
820
+ /**
821
+ * Resolve the API key at the moment it is needed (request time), invoking
822
+ * the function form if one was supplied. Never read at construction.
823
+ */
824
+ resolveApiKey() {
825
+ return typeof this.apiKey === "function" ? this.apiKey() : this.apiKey;
826
+ }
812
827
  /**
813
828
  * Make an HTTP request to the Bitfab API. Defaults to POST; pass
814
829
  * `options.method` to use a different verb (e.g. "PATCH").
@@ -839,7 +854,7 @@ var HttpClient = class {
839
854
  method,
840
855
  headers: {
841
856
  "Content-Type": "application/json",
842
- Authorization: `Bearer ${this.apiKey}`
857
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
843
858
  },
844
859
  body,
845
860
  signal: controller.signal
@@ -1000,7 +1015,7 @@ var HttpClient = class {
1000
1015
  try {
1001
1016
  const response = await fetch(url, {
1002
1017
  method: "GET",
1003
- headers: { Authorization: `Bearer ${this.apiKey}` },
1018
+ headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
1004
1019
  signal: controller.signal
1005
1020
  });
1006
1021
  if (!response.ok) {
@@ -1036,7 +1051,7 @@ var HttpClient = class {
1036
1051
  try {
1037
1052
  const response = await fetch(url, {
1038
1053
  method: "GET",
1039
- headers: { Authorization: `Bearer ${this.apiKey}` },
1054
+ headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
1040
1055
  signal: controller.signal
1041
1056
  });
1042
1057
  if (!response.ok) {
@@ -3366,6 +3381,12 @@ function getCurrentTrace() {
3366
3381
  }
3367
3382
  };
3368
3383
  }
3384
+ function readEnv(name) {
3385
+ if (typeof process !== "undefined" && process.env) {
3386
+ return process.env[name];
3387
+ }
3388
+ return void 0;
3389
+ }
3369
3390
  var Bitfab = class {
3370
3391
  /**
3371
3392
  * Initialize the Bitfab client.
@@ -3373,30 +3394,75 @@ var Bitfab = class {
3373
3394
  * @param config - Configuration options for the client
3374
3395
  */
3375
3396
  constructor(config) {
3376
- this.apiKey = config.apiKey;
3397
+ /** Gate the empty-key warning to fire at most once. */
3398
+ this.apiKeyWarned = false;
3399
+ this.apiKeyConfig = config.apiKey;
3377
3400
  this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
3378
3401
  this.timeout = config.timeout ?? 12e4;
3379
3402
  this.envVars = config.envVars ?? {};
3380
- const enabled = config.enabled ?? true;
3381
- if (enabled && (!config.apiKey || config.apiKey.trim() === "")) {
3382
- console.warn(
3383
- "Bitfab: apiKey is empty \u2014 tracing is disabled. Provide a valid API key to enable tracing."
3384
- );
3385
- this.enabled = false;
3386
- } else {
3387
- this.enabled = enabled;
3388
- }
3403
+ this.explicitlyEnabled = config.enabled ?? true;
3404
+ this.strict = config.strict ?? false;
3389
3405
  this.bamlClient = config.bamlClient ?? null;
3390
3406
  if (config.dbSnapshot) {
3391
3407
  validateDbSnapshotConfig(config.dbSnapshot);
3392
3408
  }
3393
3409
  this.dbSnapshot = config.dbSnapshot;
3394
3410
  this.httpClient = new HttpClient({
3395
- apiKey: this.apiKey,
3411
+ apiKey: () => this.resolveApiKey(),
3396
3412
  serviceUrl: this.serviceUrl,
3397
3413
  timeout: this.timeout
3398
3414
  });
3399
3415
  }
3416
+ /**
3417
+ * Resolve the API key lazily, the first time a span actually needs it.
3418
+ *
3419
+ * The key is intentionally NOT read at construction. In ESM, a shim that
3420
+ * does `new Bitfab({ apiKey: process.env.BITFAB_API_KEY })` is hoisted and
3421
+ * evaluated before the importing script's body runs `dotenv.config()`, so
3422
+ * the key would be empty at construction even though it is set moments
3423
+ * later. Resolving here (at first `withSpan` call / first request) reads
3424
+ * the key after env loading has run.
3425
+ *
3426
+ * Resolution order: the configured value (string, or function called each
3427
+ * time it is still unresolved), then a fallback read of `BITFAB_API_KEY`
3428
+ * from the environment. Once a non-empty key is found it is cached, so an
3429
+ * early resolve that found nothing never poisons a later one.
3430
+ */
3431
+ resolveApiKey() {
3432
+ if (this.resolvedApiKey !== void 0) {
3433
+ return this.resolvedApiKey;
3434
+ }
3435
+ const fromConfig = typeof this.apiKeyConfig === "function" ? this.apiKeyConfig() : this.apiKeyConfig;
3436
+ const candidate = fromConfig && fromConfig.trim() !== "" ? fromConfig : readEnv("BITFAB_API_KEY");
3437
+ const key = candidate && candidate.trim() !== "" ? candidate : void 0;
3438
+ if (key) {
3439
+ this.resolvedApiKey = key;
3440
+ return key;
3441
+ }
3442
+ if (this.strict) {
3443
+ throw new BitfabError(
3444
+ "Bitfab: no API key resolved. Set BITFAB_API_KEY or pass apiKey to new Bitfab(). If a script loads env with dotenv, load it before the module that constructs the client is imported (e.g. `node --env-file=.env script.ts`), or pass `apiKey: () => process.env.BITFAB_API_KEY`."
3445
+ );
3446
+ }
3447
+ if (this.explicitlyEnabled && !this.apiKeyWarned) {
3448
+ this.apiKeyWarned = true;
3449
+ console.warn(
3450
+ "Bitfab: apiKey is empty \u2014 tracing is disabled. Provide a valid API key to enable tracing."
3451
+ );
3452
+ }
3453
+ return void 0;
3454
+ }
3455
+ /**
3456
+ * Whether tracing should run, decided lazily at call time (not frozen at
3457
+ * construction). An explicit `enabled: false` short-circuits without ever
3458
+ * touching the key.
3459
+ */
3460
+ isTracingEnabled() {
3461
+ if (!this.explicitlyEnabled) {
3462
+ return false;
3463
+ }
3464
+ return this.resolveApiKey() !== void 0;
3465
+ }
3400
3466
  /**
3401
3467
  * Fetch the function with its current version and BAML prompt from the server.
3402
3468
  *
@@ -3489,7 +3555,9 @@ var Bitfab = class {
3489
3555
  */
3490
3556
  getOpenAiTracingProcessor() {
3491
3557
  return new BitfabOpenAITracingProcessor({
3492
- apiKey: this.apiKey,
3558
+ // Resolved at getter-call time (framework handlers are set up after env
3559
+ // loads); the withSpan path stays lazy via the constructor HttpClient thunk.
3560
+ apiKey: this.resolveApiKey(),
3493
3561
  serviceUrl: this.serviceUrl,
3494
3562
  getActiveSpanContext: () => {
3495
3563
  const stack = getSpanStack();
@@ -3548,7 +3616,7 @@ var Bitfab = class {
3548
3616
  */
3549
3617
  getLangGraphCallbackHandler(traceFunctionKey) {
3550
3618
  return new BitfabLangGraphCallbackHandler({
3551
- apiKey: this.apiKey,
3619
+ apiKey: this.resolveApiKey(),
3552
3620
  traceFunctionKey,
3553
3621
  serviceUrl: this.serviceUrl,
3554
3622
  getActiveSpanContext: () => {
@@ -3600,7 +3668,7 @@ var Bitfab = class {
3600
3668
  */
3601
3669
  getClaudeAgentHandler(traceFunctionKey) {
3602
3670
  return new BitfabClaudeAgentHandler({
3603
- apiKey: this.apiKey,
3671
+ apiKey: this.resolveApiKey(),
3604
3672
  traceFunctionKey,
3605
3673
  serviceUrl: this.serviceUrl,
3606
3674
  getActiveSpanContext: () => {
@@ -3772,9 +3840,8 @@ var Bitfab = class {
3772
3840
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
3773
3841
  */
3774
3842
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
3775
- if (!this.enabled) {
3776
- const fn2 = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
3777
- return fn2;
3843
+ if (!this.explicitlyEnabled) {
3844
+ return typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
3778
3845
  }
3779
3846
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
3780
3847
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
@@ -3789,6 +3856,9 @@ var Bitfab = class {
3789
3856
  }
3790
3857
  })();
3791
3858
  const wrappedFn = function(...args) {
3859
+ if (!self.isTracingEnabled()) {
3860
+ return fn.apply(this, args);
3861
+ }
3792
3862
  if (!asyncLocalStorage && !isAsyncStorageInitDone()) {
3793
3863
  return asyncLocalStorageReady.then(
3794
3864
  () => wrappedFn.apply(this, args)
@@ -4049,7 +4119,7 @@ var Bitfab = class {
4049
4119
  return {
4050
4120
  traceId,
4051
4121
  addContext: (context) => {
4052
- if (!this.enabled) {
4122
+ if (!this.isTracingEnabled()) {
4053
4123
  return Promise.resolve();
4054
4124
  }
4055
4125
  if (typeof context !== "object" || context === null) {
@@ -4060,7 +4130,7 @@ var Bitfab = class {
4060
4130
  });
4061
4131
  },
4062
4132
  setMetadata: (metadata) => {
4063
- if (!this.enabled) {
4133
+ if (!this.isTracingEnabled()) {
4064
4134
  return Promise.resolve();
4065
4135
  }
4066
4136
  if (typeof metadata !== "object" || metadata === null) {
@@ -4069,7 +4139,7 @@ var Bitfab = class {
4069
4139
  return this.httpClient.patchTrace(traceId, { mergeMetadata: metadata });
4070
4140
  },
4071
4141
  setSessionId: (sessionId) => {
4072
- if (!this.enabled) {
4142
+ if (!this.isTracingEnabled()) {
4073
4143
  return Promise.resolve();
4074
4144
  }
4075
4145
  if (typeof sessionId !== "string" || sessionId.length === 0) {