bitfab 0.54.2 → 0.54.4

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.54.2";
54
+ __version__ = "0.54.4";
55
55
  __packageName__ = "bitfab";
56
56
  }
57
57
  });
@@ -66,6 +66,99 @@ var init_constants = __esm({
66
66
  }
67
67
  });
68
68
 
69
+ // src/traceCompletion.ts
70
+ var TraceCompletion;
71
+ var init_traceCompletion = __esm({
72
+ "src/traceCompletion.ts"() {
73
+ "use strict";
74
+ TraceCompletion = class {
75
+ constructor() {
76
+ this.traces = /* @__PURE__ */ new Map();
77
+ this.closed = /* @__PURE__ */ new Map();
78
+ }
79
+ state(traceId) {
80
+ let state = this.traces.get(traceId);
81
+ if (state === void 0) {
82
+ state = { spanIds: /* @__PURE__ */ new Set(), active: /* @__PURE__ */ new Set() };
83
+ this.traces.set(traceId, state);
84
+ }
85
+ return state;
86
+ }
87
+ start(traceId, spanId) {
88
+ if (this.closed.has(traceId)) {
89
+ return;
90
+ }
91
+ const state = this.state(traceId);
92
+ state.spanIds.add(spanId);
93
+ state.active.add(spanId);
94
+ }
95
+ open(traceId) {
96
+ if (!this.closed.has(traceId)) {
97
+ this.state(traceId);
98
+ }
99
+ }
100
+ hold(traceId, key) {
101
+ this.state(traceId).active.add(key);
102
+ }
103
+ record(traceId, spanId) {
104
+ if (!this.closed.has(traceId)) {
105
+ this.state(traceId).spanIds.add(spanId);
106
+ }
107
+ }
108
+ end(traceId, spanId) {
109
+ const state = this.traces.get(traceId);
110
+ if (state !== void 0) {
111
+ state.active.delete(spanId);
112
+ this.drain(traceId, state);
113
+ }
114
+ }
115
+ abort(traceId, spanId) {
116
+ const state = this.traces.get(traceId);
117
+ if (state === void 0) {
118
+ return;
119
+ }
120
+ state.active.delete(spanId);
121
+ state.spanIds.delete(spanId);
122
+ if (state.active.size === 0 && state.spanIds.size === 0 && state.complete === void 0) {
123
+ this.traces.delete(traceId);
124
+ return;
125
+ }
126
+ this.drain(traceId, state);
127
+ }
128
+ close(traceId, complete, dropped = false) {
129
+ const count = this.closed.get(traceId);
130
+ if (count !== void 0) {
131
+ complete(count);
132
+ return;
133
+ }
134
+ const state = this.traces.get(traceId);
135
+ if (state === void 0) {
136
+ return;
137
+ }
138
+ state.complete = complete;
139
+ if (dropped) {
140
+ state.active.clear();
141
+ }
142
+ this.drain(traceId, state);
143
+ }
144
+ drain(traceId, state) {
145
+ if (state.active.size !== 0 || state.complete === void 0) {
146
+ return;
147
+ }
148
+ this.traces.delete(traceId);
149
+ this.closed.set(traceId, state.spanIds.size);
150
+ if (this.closed.size > 1024) {
151
+ const oldest = this.closed.keys().next().value;
152
+ if (oldest !== void 0) {
153
+ this.closed.delete(oldest);
154
+ }
155
+ }
156
+ state.complete(state.spanIds.size);
157
+ }
158
+ };
159
+ }
160
+ });
161
+
69
162
  // src/readEnv.ts
70
163
  function readEnv(name) {
71
164
  if (typeof process !== "undefined" && process.env) {
@@ -2152,6 +2245,7 @@ var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, OTL
2152
2245
  var init_http = __esm({
2153
2246
  "src/http.ts"() {
2154
2247
  "use strict";
2248
+ init_traceCompletion();
2155
2249
  init_compress();
2156
2250
  init_constants();
2157
2251
  init_errors();
@@ -2214,6 +2308,7 @@ var init_http = __esm({
2214
2308
  // must not wait on another client's slow finalize: a false `close()` failure
2215
2309
  // caused by unrelated work is worse than no signal at all.
2216
2310
  this.deferredWork = /* @__PURE__ */ new Set();
2311
+ this.traceCompletion = new TraceCompletion();
2217
2312
  this.closed = false;
2218
2313
  this.apiKey = config.apiKey;
2219
2314
  this.serviceUrl = config.serviceUrl;
@@ -2647,6 +2742,10 @@ var init_http = __esm({
2647
2742
  );
2648
2743
  }
2649
2744
  sendExternalSpan(payload) {
2745
+ const ref2 = carrierRef(payload);
2746
+ if (ref2?.spanId !== void 0) {
2747
+ this.traceCompletion.record(ref2.traceId, ref2.spanId);
2748
+ }
2650
2749
  const gate = this.externalSpanTransform;
2651
2750
  if (gate === void 0) {
2652
2751
  this.submitExternalSpan(payload);
@@ -2678,13 +2777,28 @@ var init_http = __esm({
2678
2777
  */
2679
2778
  sendExternalTrace(rawPayload) {
2680
2779
  const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
2681
- const gate = this.externalTraceTransform;
2682
- if (gate === void 0) {
2683
- this.submitExternalTrace(payload);
2780
+ const traceId = sourceTraceIdOf(payload);
2781
+ if (payload.completed === true && traceId !== void 0) {
2782
+ this.traceCompletion.close(
2783
+ traceId,
2784
+ (expectedSpanCount) => this.sendCountedExternalTrace({ ...payload, expectedSpanCount }),
2785
+ payload.dropped === true
2786
+ );
2684
2787
  return;
2685
2788
  }
2789
+ if (traceId !== void 0) {
2790
+ this.traceCompletion.open(traceId);
2791
+ }
2792
+ this.sendCountedExternalTrace(payload);
2793
+ }
2794
+ sendCountedExternalTrace(payload) {
2686
2795
  try {
2687
- gate(payload, (ready) => this.submitExternalTrace(ready));
2796
+ const gate = this.externalTraceTransform;
2797
+ if (gate === void 0) {
2798
+ this.submitExternalTrace(payload);
2799
+ } else {
2800
+ gate(payload, (ready) => this.submitExternalTrace(ready));
2801
+ }
2688
2802
  } catch (error) {
2689
2803
  warnDroppedExternalTrace(error);
2690
2804
  }
@@ -4503,6 +4617,7 @@ var BitfabClaudeAgentHandler = class {
4503
4617
  spanInfo.dropped = [...inputDropped];
4504
4618
  }
4505
4619
  this.runToSpan.set(spanId, spanInfo);
4620
+ this.httpClient.traceCompletion.start(spanInfo.traceId, spanInfo.spanId);
4506
4621
  return spanInfo;
4507
4622
  }
4508
4623
  completeSpan(spanId, output, error, extraContexts) {
@@ -4524,6 +4639,7 @@ var BitfabClaudeAgentHandler = class {
4524
4639
  spanInfo.contexts.push(extraContexts);
4525
4640
  }
4526
4641
  this.sendSpan(spanInfo);
4642
+ this.httpClient.traceCompletion.end(spanInfo.traceId, spanInfo.spanId);
4527
4643
  }
4528
4644
  sendSpan(spanInfo) {
4529
4645
  const spanData = {
@@ -6181,6 +6297,9 @@ var BitfabLangGraphCallbackHandler = class {
6181
6297
  spanInfo.hidden = true;
6182
6298
  }
6183
6299
  this.runToSpan.set(runId, spanInfo);
6300
+ if (spanInfo.hidden !== true) {
6301
+ this.httpClient.traceCompletion.start(spanInfo.traceId, spanInfo.spanId);
6302
+ }
6184
6303
  if (isRootInvocation) {
6185
6304
  this.sendTraceStart(spanInfo);
6186
6305
  }
@@ -6212,6 +6331,7 @@ var BitfabLangGraphCallbackHandler = class {
6212
6331
  this.sendTraceCompletion(spanInfo, invocation?.activeContext ?? null);
6213
6332
  this.invocations.delete(runId);
6214
6333
  }
6334
+ this.httpClient.traceCompletion.end(spanInfo.traceId, spanInfo.spanId);
6215
6335
  }
6216
6336
  sendSpan(spanInfo) {
6217
6337
  const spanData = {
@@ -6933,6 +7053,8 @@ var BitfabOpenAITracingProcessor = class {
6933
7053
  this.activeTraces = {};
6934
7054
  this.activeSpanMappings = {};
6935
7055
  this.canonicalTraceIds = {};
7056
+ this.startedSpans = /* @__PURE__ */ new Map();
7057
+ this.endedTraces = /* @__PURE__ */ new Set();
6936
7058
  this.ownsHttpClient = config._httpClient === void 0;
6937
7059
  this.httpClient = config._httpClient ?? new HttpClient({
6938
7060
  apiKey: config.apiKey,
@@ -6971,6 +7093,10 @@ var BitfabOpenAITracingProcessor = class {
6971
7093
  }
6972
7094
  const canonicalTraceId = activeContext?.traceId ?? this.getCanonicalTraceId(trace.traceId);
6973
7095
  this.canonicalTraceIds[trace.traceId] = canonicalTraceId;
7096
+ this.httpClient.traceCompletion.hold(
7097
+ activeContext?.traceId ?? trace.traceId,
7098
+ `openai:${trace.traceId}`
7099
+ );
6974
7100
  this.sendTrace(trace, {
6975
7101
  id: canonicalTraceId,
6976
7102
  sourceTraceId: activeContext?.traceId
@@ -6988,16 +7114,33 @@ var BitfabOpenAITracingProcessor = class {
6988
7114
  id: mapping?.traceId ?? this.getCanonicalTraceId(trace.traceId),
6989
7115
  sourceTraceId: mapping?.traceId
6990
7116
  });
6991
- delete this.activeSpanMappings[trace.traceId];
6992
- delete this.canonicalTraceIds[trace.traceId];
6993
- delete this.activeTraces[trace.traceId];
7117
+ this.endedTraces.add(trace.traceId);
7118
+ this.httpClient.traceCompletion.end(
7119
+ mapping?.traceId ?? trace.traceId,
7120
+ `openai:${trace.traceId}`
7121
+ );
7122
+ this.cleanupTrace(trace.traceId);
7123
+ }
7124
+ cleanupTrace(traceId) {
7125
+ if (!this.endedTraces.has(traceId) || [...this.startedSpans.values()].includes(traceId)) {
7126
+ return;
7127
+ }
7128
+ this.endedTraces.delete(traceId);
7129
+ delete this.activeSpanMappings[traceId];
7130
+ delete this.canonicalTraceIds[traceId];
7131
+ delete this.activeTraces[traceId];
6994
7132
  }
6995
7133
  /**
6996
7134
  * Called when a span is started. Span payloads are authoritative completed
6997
7135
  * snapshots, so start notifications do not cross the transport boundary.
6998
7136
  */
6999
7137
  // biome-ignore lint/suspicious/noExplicitAny: OpenAI Agents SDK uses any for span data
7000
- async onSpanStart(_span) {
7138
+ async onSpanStart(span) {
7139
+ if (span.spanId !== void 0 && span.traceId !== null && span.traceId !== void 0) {
7140
+ this.startedSpans.set(span.spanId, span.traceId);
7141
+ const traceId = this.activeSpanMappings[span.traceId]?.traceId ?? span.traceId;
7142
+ this.httpClient.traceCompletion.start(traceId, span.spanId);
7143
+ }
7001
7144
  }
7002
7145
  /**
7003
7146
  * Called when a span is ended.
@@ -7006,7 +7149,17 @@ var BitfabOpenAITracingProcessor = class {
7006
7149
  */
7007
7150
  // biome-ignore lint/suspicious/noExplicitAny: OpenAI Agents SDK uses any for span data
7008
7151
  async onSpanEnd(span) {
7009
- this.sendSpan(span);
7152
+ const originalTraceId = span.traceId ?? "";
7153
+ const traceId = this.activeSpanMappings[originalTraceId]?.traceId ?? originalTraceId;
7154
+ try {
7155
+ this.sendSpan(span);
7156
+ } finally {
7157
+ if (span.spanId !== void 0) {
7158
+ this.httpClient.traceCompletion.end(traceId, span.spanId);
7159
+ this.startedSpans.delete(span.spanId);
7160
+ }
7161
+ this.cleanupTrace(originalTraceId);
7162
+ }
7010
7163
  }
7011
7164
  /**
7012
7165
  * Called when a trace is being flushed.
@@ -8712,6 +8865,7 @@ var Bitfab = class {
8712
8865
  let newStack;
8713
8866
  let executeWithContext;
8714
8867
  let registeredTraceId;
8868
+ let startedSpan;
8715
8869
  try {
8716
8870
  const currentStack = getSpanStack();
8717
8871
  const parentContext = currentStack[currentStack.length - 1];
@@ -8763,6 +8917,8 @@ var Bitfab = class {
8763
8917
  });
8764
8918
  registeredTraceId = traceId;
8765
8919
  }
8920
+ self.httpClient.traceCompletion.start(traceId, spanId);
8921
+ startedSpan = { traceId, spanId };
8766
8922
  const functionName = fn.name !== "" ? fn.name : void 0;
8767
8923
  const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
8768
8924
  const baseSpanParams = {
@@ -8837,6 +8993,8 @@ var Bitfab = class {
8837
8993
  retireTraceMetadata(traceId);
8838
8994
  }
8839
8995
  } catch {
8996
+ } finally {
8997
+ self.httpClient.traceCompletion.end(traceId, spanId);
8840
8998
  }
8841
8999
  };
8842
9000
  const recordSpan = (result) => {
@@ -8853,6 +9011,14 @@ var Bitfab = class {
8853
9011
  void sendSpan({ result });
8854
9012
  }
8855
9013
  };
9014
+ const resolveMockOrAbort = async (resolve) => {
9015
+ try {
9016
+ return await resolve();
9017
+ } catch (error) {
9018
+ self.httpClient.traceCompletion.abort(traceId, spanId);
9019
+ throw error;
9020
+ }
9021
+ };
8856
9022
  executeWithContext = () => {
8857
9023
  let result;
8858
9024
  try {
@@ -8909,7 +9075,7 @@ var Bitfab = class {
8909
9075
  );
8910
9076
  }
8911
9077
  return (async () => {
8912
- const output = await pending;
9078
+ const output = await resolveMockOrAbort(() => pending);
8913
9079
  void sendSpan({
8914
9080
  result: output,
8915
9081
  mocked: true,
@@ -8979,7 +9145,7 @@ var Bitfab = class {
8979
9145
  );
8980
9146
  }
8981
9147
  return runWithSpanStack(newStack, async () => {
8982
- const resolved2 = await resolution;
9148
+ const resolved2 = await resolveMockOrAbort(() => resolution);
8983
9149
  if (resolved2.matched) {
8984
9150
  void sendSpan({
8985
9151
  result: resolved2.output,
@@ -8995,7 +9161,9 @@ var Bitfab = class {
8995
9161
  );
8996
9162
  }
8997
9163
  if (shouldMockWithBaseStrategy) {
8998
- const output = await resolveRecordedOutput();
9164
+ const output = await resolveMockOrAbort(
9165
+ () => resolveRecordedOutput()
9166
+ );
8999
9167
  void sendSpan({
9000
9168
  result: output,
9001
9169
  mocked: true,
@@ -9025,6 +9193,12 @@ var Bitfab = class {
9025
9193
  }
9026
9194
  }
9027
9195
  } catch (setupError) {
9196
+ if (startedSpan) {
9197
+ self.httpClient.traceCompletion.abort(
9198
+ startedSpan.traceId,
9199
+ startedSpan.spanId
9200
+ );
9201
+ }
9028
9202
  if (registeredTraceId) {
9029
9203
  activeTraceStates.delete(registeredTraceId);
9030
9204
  retireTraceMetadata(registeredTraceId);