bitfab 0.9.2 → 0.11.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/README.md +14 -14
- package/dist/{chunk-WUJR72QY.js → chunk-I3OKZ2TF.js} +1085 -6
- package/dist/chunk-I3OKZ2TF.js.map +1 -0
- package/dist/{chunk-VGALEXKQ.js → chunk-KAX2QQPS.js} +47 -47
- package/dist/chunk-KAX2QQPS.js.map +1 -0
- package/dist/index.cjs +1139 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +271 -49
- package/dist/index.d.ts +271 -49
- package/dist/index.js +6 -2
- package/dist/node.cjs +1100 -5
- 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 +6 -2
- package/dist/node.js.map +1 -1
- package/dist/{replay-DJ2HXTHQ.js → replay-IIBLVODH.js} +11 -3
- package/dist/replay-IIBLVODH.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-VGALEXKQ.js.map +0 -1
- package/dist/chunk-WUJR72QY.js.map +0 -1
- package/dist/replay-DJ2HXTHQ.js.map +0 -1
|
@@ -7,7 +7,481 @@ import {
|
|
|
7
7
|
getReplayContext,
|
|
8
8
|
isAsyncStorageInitDone,
|
|
9
9
|
serializeValue
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KAX2QQPS.js";
|
|
11
|
+
|
|
12
|
+
// src/claudeAgentSdk.ts
|
|
13
|
+
function nowIso() {
|
|
14
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
15
|
+
}
|
|
16
|
+
function safeSerialize(value) {
|
|
17
|
+
if (value === null || value === void 0) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return value.map(safeSerialize);
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === "object") {
|
|
27
|
+
if (typeof value.toJSON === "function") {
|
|
28
|
+
return value.toJSON();
|
|
29
|
+
}
|
|
30
|
+
const result = {};
|
|
31
|
+
for (const [k, v] of Object.entries(value)) {
|
|
32
|
+
if (!k.startsWith("_")) {
|
|
33
|
+
result[k] = safeSerialize(v);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
return String(value);
|
|
39
|
+
}
|
|
40
|
+
function extractContentBlocks(content) {
|
|
41
|
+
if (!Array.isArray(content)) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
return content.map((block) => safeSerialize(block));
|
|
45
|
+
}
|
|
46
|
+
function extractUsage(message) {
|
|
47
|
+
const usageInfo = {};
|
|
48
|
+
const usage = message.usage;
|
|
49
|
+
if (!usage) {
|
|
50
|
+
return usageInfo;
|
|
51
|
+
}
|
|
52
|
+
const mapping = {
|
|
53
|
+
input_tokens: "inputTokens",
|
|
54
|
+
output_tokens: "outputTokens",
|
|
55
|
+
cache_read_input_tokens: "cacheReadTokens",
|
|
56
|
+
cache_creation_input_tokens: "cacheCreationTokens"
|
|
57
|
+
};
|
|
58
|
+
for (const [srcKey, dstKey] of Object.entries(mapping)) {
|
|
59
|
+
const val = usage[srcKey];
|
|
60
|
+
if (val !== void 0 && val !== null) {
|
|
61
|
+
usageInfo[dstKey] = val;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return usageInfo;
|
|
65
|
+
}
|
|
66
|
+
var BitfabClaudeAgentHandler = class {
|
|
67
|
+
constructor(config) {
|
|
68
|
+
// Span tracking
|
|
69
|
+
this.runToSpan = /* @__PURE__ */ new Map();
|
|
70
|
+
this.traceId = null;
|
|
71
|
+
this.rootSpanId = null;
|
|
72
|
+
this.activeContext = null;
|
|
73
|
+
this.traceStartedAt = null;
|
|
74
|
+
// LLM turn tracking
|
|
75
|
+
this.conversationHistory = [];
|
|
76
|
+
this.pendingMessages = [];
|
|
77
|
+
this.currentLlmSpanId = null;
|
|
78
|
+
this.currentLlmMessageId = null;
|
|
79
|
+
this.currentLlmContent = [];
|
|
80
|
+
this.currentLlmModel = null;
|
|
81
|
+
this.currentLlmUsage = {};
|
|
82
|
+
this.currentLlmStartedAt = null;
|
|
83
|
+
this.currentLlmHistorySnapshot = [];
|
|
84
|
+
// Subagent tracking
|
|
85
|
+
this.activeSubagentSpans = /* @__PURE__ */ new Map();
|
|
86
|
+
this.httpClient = new HttpClient({
|
|
87
|
+
apiKey: config.apiKey,
|
|
88
|
+
serviceUrl: config.serviceUrl ?? DEFAULT_SERVICE_URL,
|
|
89
|
+
timeout: config.timeout ?? 1e4
|
|
90
|
+
});
|
|
91
|
+
this.traceFunctionKey = config.traceFunctionKey;
|
|
92
|
+
this.getActiveSpanContext = config.getActiveSpanContext ?? null;
|
|
93
|
+
this.preToolUseHook = this.preToolUseHook.bind(this);
|
|
94
|
+
this.postToolUseHook = this.postToolUseHook.bind(this);
|
|
95
|
+
this.postToolUseFailureHook = this.postToolUseFailureHook.bind(this);
|
|
96
|
+
this.subagentStartHook = this.subagentStartHook.bind(this);
|
|
97
|
+
this.subagentStopHook = this.subagentStopHook.bind(this);
|
|
98
|
+
}
|
|
99
|
+
// ── trace lifecycle ──────────────────────────────────────────
|
|
100
|
+
ensureTrace() {
|
|
101
|
+
if (this.traceId !== null) {
|
|
102
|
+
return this.traceId;
|
|
103
|
+
}
|
|
104
|
+
this.activeContext = this.getActiveSpanContext?.() ?? null;
|
|
105
|
+
if (this.activeContext) {
|
|
106
|
+
this.traceId = this.activeContext.traceId;
|
|
107
|
+
} else {
|
|
108
|
+
this.traceId = crypto.randomUUID();
|
|
109
|
+
}
|
|
110
|
+
this.traceStartedAt = nowIso();
|
|
111
|
+
return this.traceId;
|
|
112
|
+
}
|
|
113
|
+
getParentId(agentId) {
|
|
114
|
+
if (agentId) {
|
|
115
|
+
const subagentSpanId = this.activeSubagentSpans.get(agentId);
|
|
116
|
+
if (subagentSpanId) {
|
|
117
|
+
return subagentSpanId;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return this.activeContext?.spanId ?? this.rootSpanId ?? null;
|
|
121
|
+
}
|
|
122
|
+
// ── span helpers ─────────────────────────────────────────────
|
|
123
|
+
startSpan(spanId, name, spanType, inputData, parentId) {
|
|
124
|
+
const traceId = this.ensureTrace();
|
|
125
|
+
const spanInfo = {
|
|
126
|
+
spanId,
|
|
127
|
+
traceId,
|
|
128
|
+
parentId: parentId ?? null,
|
|
129
|
+
startedAt: nowIso(),
|
|
130
|
+
name,
|
|
131
|
+
type: spanType,
|
|
132
|
+
input: safeSerialize(inputData),
|
|
133
|
+
contexts: []
|
|
134
|
+
};
|
|
135
|
+
this.runToSpan.set(spanId, spanInfo);
|
|
136
|
+
return spanInfo;
|
|
137
|
+
}
|
|
138
|
+
completeSpan(spanId, output, error, extraContexts) {
|
|
139
|
+
const spanInfo = this.runToSpan.get(spanId);
|
|
140
|
+
if (!spanInfo) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.runToSpan.delete(spanId);
|
|
144
|
+
spanInfo.endedAt = nowIso();
|
|
145
|
+
spanInfo.output = safeSerialize(output);
|
|
146
|
+
if (error !== void 0) {
|
|
147
|
+
spanInfo.error = error;
|
|
148
|
+
}
|
|
149
|
+
if (extraContexts) {
|
|
150
|
+
spanInfo.contexts.push(extraContexts);
|
|
151
|
+
}
|
|
152
|
+
this.sendSpan(spanInfo);
|
|
153
|
+
}
|
|
154
|
+
sendSpan(spanInfo) {
|
|
155
|
+
const spanData = {
|
|
156
|
+
name: spanInfo.name,
|
|
157
|
+
type: spanInfo.type
|
|
158
|
+
};
|
|
159
|
+
if (spanInfo.input !== void 0) {
|
|
160
|
+
spanData.input = spanInfo.input;
|
|
161
|
+
}
|
|
162
|
+
if (spanInfo.output !== void 0) {
|
|
163
|
+
spanData.output = spanInfo.output;
|
|
164
|
+
}
|
|
165
|
+
if (spanInfo.error !== void 0) {
|
|
166
|
+
spanData.error = spanInfo.error;
|
|
167
|
+
}
|
|
168
|
+
if (spanInfo.contexts.length > 0) {
|
|
169
|
+
spanData.contexts = spanInfo.contexts;
|
|
170
|
+
}
|
|
171
|
+
const rawSpan = {
|
|
172
|
+
id: spanInfo.spanId,
|
|
173
|
+
trace_id: spanInfo.traceId,
|
|
174
|
+
started_at: spanInfo.startedAt,
|
|
175
|
+
ended_at: spanInfo.endedAt ?? nowIso(),
|
|
176
|
+
span_data: spanData
|
|
177
|
+
};
|
|
178
|
+
if (spanInfo.parentId !== null) {
|
|
179
|
+
rawSpan.parent_id = spanInfo.parentId;
|
|
180
|
+
}
|
|
181
|
+
const payload = {
|
|
182
|
+
type: "sdk-function",
|
|
183
|
+
source: "typescript-sdk-claude-agent-sdk",
|
|
184
|
+
traceFunctionKey: this.traceFunctionKey,
|
|
185
|
+
sourceTraceId: spanInfo.traceId,
|
|
186
|
+
rawSpan
|
|
187
|
+
};
|
|
188
|
+
try {
|
|
189
|
+
this.httpClient.sendExternalSpan(payload);
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
sendTraceCompletion(endedAt, metadata) {
|
|
194
|
+
if (this.traceId === null) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const completed = this.activeContext === null;
|
|
198
|
+
const traceId = this.traceId;
|
|
199
|
+
this.traceId = null;
|
|
200
|
+
const externalTrace = {
|
|
201
|
+
id: traceId,
|
|
202
|
+
started_at: this.traceStartedAt ?? nowIso(),
|
|
203
|
+
ended_at: endedAt ?? nowIso(),
|
|
204
|
+
workflow_name: this.traceFunctionKey
|
|
205
|
+
};
|
|
206
|
+
if (metadata) {
|
|
207
|
+
externalTrace.metadata = metadata;
|
|
208
|
+
}
|
|
209
|
+
const traceData = {
|
|
210
|
+
type: "sdk-function",
|
|
211
|
+
source: "typescript-sdk-claude-agent-sdk",
|
|
212
|
+
traceFunctionKey: this.traceFunctionKey,
|
|
213
|
+
externalTrace,
|
|
214
|
+
completed
|
|
215
|
+
};
|
|
216
|
+
try {
|
|
217
|
+
this.httpClient.sendExternalTrace(traceData);
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// ── hook callbacks ───────────────────────────────────────────
|
|
222
|
+
async preToolUseHook(inputData, toolUseId, _context) {
|
|
223
|
+
try {
|
|
224
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? crypto.randomUUID();
|
|
225
|
+
const toolName = inputData.tool_name ?? "tool";
|
|
226
|
+
const toolInput = inputData.tool_input ?? {};
|
|
227
|
+
const agentId = inputData.agent_id;
|
|
228
|
+
const parentId = this.getParentId(agentId);
|
|
229
|
+
this.startSpan(sid, toolName, "function", toolInput, parentId);
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
return {};
|
|
233
|
+
}
|
|
234
|
+
async postToolUseHook(inputData, toolUseId, _context) {
|
|
235
|
+
try {
|
|
236
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? "";
|
|
237
|
+
const toolResponse = inputData.tool_response;
|
|
238
|
+
this.completeSpan(sid, toolResponse);
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
return {};
|
|
242
|
+
}
|
|
243
|
+
async postToolUseFailureHook(inputData, toolUseId, _context) {
|
|
244
|
+
try {
|
|
245
|
+
const sid = inputData.tool_use_id ?? toolUseId ?? "";
|
|
246
|
+
const error = String(inputData.error ?? "Unknown error");
|
|
247
|
+
this.completeSpan(sid, void 0, error);
|
|
248
|
+
} catch {
|
|
249
|
+
}
|
|
250
|
+
return {};
|
|
251
|
+
}
|
|
252
|
+
async subagentStartHook(inputData, _toolUseId, _context) {
|
|
253
|
+
try {
|
|
254
|
+
const agentId = inputData.agent_id ?? crypto.randomUUID();
|
|
255
|
+
const agentType = inputData.agent_type ?? "subagent";
|
|
256
|
+
const parentId = this.getParentId();
|
|
257
|
+
const spanId = crypto.randomUUID();
|
|
258
|
+
this.activeSubagentSpans.set(agentId, spanId);
|
|
259
|
+
this.startSpan(
|
|
260
|
+
spanId,
|
|
261
|
+
`Agent: ${agentType}`,
|
|
262
|
+
"agent",
|
|
263
|
+
void 0,
|
|
264
|
+
parentId
|
|
265
|
+
);
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
return {};
|
|
269
|
+
}
|
|
270
|
+
async subagentStopHook(inputData, _toolUseId, _context) {
|
|
271
|
+
try {
|
|
272
|
+
const agentId = inputData.agent_id ?? "";
|
|
273
|
+
const spanId = this.activeSubagentSpans.get(agentId);
|
|
274
|
+
if (spanId) {
|
|
275
|
+
this.activeSubagentSpans.delete(agentId);
|
|
276
|
+
this.completeSpan(spanId);
|
|
277
|
+
}
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
return {};
|
|
281
|
+
}
|
|
282
|
+
// ── public API ───────────────────────────────────────────────
|
|
283
|
+
/**
|
|
284
|
+
* Inject Bitfab tracing hooks into Claude Agent SDK options.
|
|
285
|
+
*
|
|
286
|
+
* Modifies the options object and returns it for convenience.
|
|
287
|
+
* The SDK's `HookMatcher` is constructed as a plain object
|
|
288
|
+
* (`{ matcher: null, hooks: [callback] }`) to avoid requiring
|
|
289
|
+
* `@anthropic-ai/claude-agent-sdk` as a dependency.
|
|
290
|
+
*
|
|
291
|
+
* @param options - Options object with a `hooks` property
|
|
292
|
+
* @returns The modified options object with Bitfab hooks injected
|
|
293
|
+
*/
|
|
294
|
+
instrumentOptions(options) {
|
|
295
|
+
const hooks = options.hooks ?? {};
|
|
296
|
+
if (!options.hooks) {
|
|
297
|
+
;
|
|
298
|
+
options.hooks = hooks;
|
|
299
|
+
}
|
|
300
|
+
const hookConfig = [
|
|
301
|
+
["PreToolUse", this.preToolUseHook],
|
|
302
|
+
["PostToolUse", this.postToolUseHook],
|
|
303
|
+
["PostToolUseFailure", this.postToolUseFailureHook],
|
|
304
|
+
["SubagentStart", this.subagentStartHook],
|
|
305
|
+
["SubagentStop", this.subagentStopHook]
|
|
306
|
+
];
|
|
307
|
+
for (const [event, callback] of hookConfig) {
|
|
308
|
+
if (!hooks[event]) {
|
|
309
|
+
hooks[event] = [];
|
|
310
|
+
}
|
|
311
|
+
hooks[event].push({ matcher: null, hooks: [callback] });
|
|
312
|
+
}
|
|
313
|
+
return options;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Wrap a `ClaudeSDKClient.receiveResponse()` stream to capture LLM turns.
|
|
317
|
+
*
|
|
318
|
+
* Yields every message unchanged while capturing AssistantMessage
|
|
319
|
+
* content as LLM turn spans.
|
|
320
|
+
*/
|
|
321
|
+
async *wrapResponse(stream) {
|
|
322
|
+
yield* this.processStream(stream);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Wrap a `query()` async iterator to capture LLM turns.
|
|
326
|
+
*
|
|
327
|
+
* Same as `wrapResponse` but for the simpler `query()` API
|
|
328
|
+
* which does not support hooks (no tool/subagent spans).
|
|
329
|
+
*/
|
|
330
|
+
async *wrapQuery(stream) {
|
|
331
|
+
yield* this.processStream(stream);
|
|
332
|
+
}
|
|
333
|
+
// ── stream processing ────────────────────────────────────────
|
|
334
|
+
async *processStream(stream) {
|
|
335
|
+
try {
|
|
336
|
+
for await (const message of stream) {
|
|
337
|
+
try {
|
|
338
|
+
this.processMessage(message);
|
|
339
|
+
} catch {
|
|
340
|
+
}
|
|
341
|
+
yield message;
|
|
342
|
+
}
|
|
343
|
+
} finally {
|
|
344
|
+
try {
|
|
345
|
+
this.flushLlmTurn();
|
|
346
|
+
this.sendTraceCompletion();
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
this.resetState();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
processMessage(message) {
|
|
353
|
+
const typeName = message.constructor?.name ?? "";
|
|
354
|
+
if (typeName === "AssistantMessage") {
|
|
355
|
+
this.handleAssistantMessage(message);
|
|
356
|
+
} else if (typeName === "UserMessage") {
|
|
357
|
+
this.handleUserMessage(message);
|
|
358
|
+
} else if (typeName === "ResultMessage") {
|
|
359
|
+
this.handleResultMessage(message);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
handleAssistantMessage(message) {
|
|
363
|
+
this.ensureTrace();
|
|
364
|
+
const messageId = message.message_id;
|
|
365
|
+
if (messageId !== this.currentLlmMessageId) {
|
|
366
|
+
this.flushLlmTurn();
|
|
367
|
+
this.conversationHistory.push(...this.pendingMessages);
|
|
368
|
+
this.pendingMessages = [];
|
|
369
|
+
this.currentLlmSpanId = crypto.randomUUID();
|
|
370
|
+
this.currentLlmMessageId = messageId ?? null;
|
|
371
|
+
this.currentLlmContent = [];
|
|
372
|
+
this.currentLlmModel = message.model ?? null;
|
|
373
|
+
this.currentLlmUsage = {};
|
|
374
|
+
this.currentLlmStartedAt = nowIso();
|
|
375
|
+
this.currentLlmHistorySnapshot = [...this.conversationHistory];
|
|
376
|
+
}
|
|
377
|
+
const content = message.content;
|
|
378
|
+
if (Array.isArray(content)) {
|
|
379
|
+
this.currentLlmContent.push(...extractContentBlocks(content));
|
|
380
|
+
}
|
|
381
|
+
const usage = extractUsage(message);
|
|
382
|
+
if (Object.keys(usage).length > 0) {
|
|
383
|
+
Object.assign(this.currentLlmUsage, usage);
|
|
384
|
+
}
|
|
385
|
+
const model = message.model;
|
|
386
|
+
if (model) {
|
|
387
|
+
this.currentLlmModel = model;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
handleUserMessage(message) {
|
|
391
|
+
const content = message.content;
|
|
392
|
+
const toolUseResult = message.tool_use_result;
|
|
393
|
+
if (toolUseResult !== void 0) {
|
|
394
|
+
this.pendingMessages.push({
|
|
395
|
+
role: "tool",
|
|
396
|
+
content: safeSerialize(content),
|
|
397
|
+
tool_result: safeSerialize(toolUseResult)
|
|
398
|
+
});
|
|
399
|
+
} else {
|
|
400
|
+
this.pendingMessages.push({
|
|
401
|
+
role: "user",
|
|
402
|
+
content: safeSerialize(content)
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
handleResultMessage(message) {
|
|
407
|
+
this.flushLlmTurn();
|
|
408
|
+
const metadata = {};
|
|
409
|
+
for (const attr of [
|
|
410
|
+
"num_turns",
|
|
411
|
+
"total_cost_usd",
|
|
412
|
+
"duration_ms",
|
|
413
|
+
"duration_api_ms",
|
|
414
|
+
"session_id"
|
|
415
|
+
]) {
|
|
416
|
+
const val = message[attr];
|
|
417
|
+
if (val !== void 0 && val !== null) {
|
|
418
|
+
metadata[attr] = val;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const usage = message.usage;
|
|
422
|
+
if (usage && typeof usage === "object") {
|
|
423
|
+
metadata.usage = safeSerialize(usage);
|
|
424
|
+
}
|
|
425
|
+
this.sendTraceCompletion(
|
|
426
|
+
void 0,
|
|
427
|
+
Object.keys(metadata).length > 0 ? metadata : void 0
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
flushLlmTurn() {
|
|
431
|
+
if (this.currentLlmSpanId === null) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const spanId = this.currentLlmSpanId;
|
|
435
|
+
const traceId = this.ensureTrace();
|
|
436
|
+
const parentId = this.getParentId();
|
|
437
|
+
const llmContext = {};
|
|
438
|
+
if (this.currentLlmModel) {
|
|
439
|
+
llmContext.model = this.currentLlmModel;
|
|
440
|
+
}
|
|
441
|
+
Object.assign(llmContext, this.currentLlmUsage);
|
|
442
|
+
const spanInfo = {
|
|
443
|
+
spanId,
|
|
444
|
+
traceId,
|
|
445
|
+
parentId,
|
|
446
|
+
startedAt: this.currentLlmStartedAt ?? nowIso(),
|
|
447
|
+
endedAt: nowIso(),
|
|
448
|
+
name: this.currentLlmModel ?? "llm",
|
|
449
|
+
type: "llm",
|
|
450
|
+
input: this.currentLlmHistorySnapshot,
|
|
451
|
+
output: this.currentLlmContent,
|
|
452
|
+
contexts: Object.keys(llmContext).length > 0 ? [llmContext] : []
|
|
453
|
+
};
|
|
454
|
+
this.sendSpan(spanInfo);
|
|
455
|
+
this.conversationHistory.push({
|
|
456
|
+
role: "assistant",
|
|
457
|
+
content: this.currentLlmContent
|
|
458
|
+
});
|
|
459
|
+
this.currentLlmSpanId = null;
|
|
460
|
+
this.currentLlmMessageId = null;
|
|
461
|
+
this.currentLlmContent = [];
|
|
462
|
+
this.currentLlmModel = null;
|
|
463
|
+
this.currentLlmUsage = {};
|
|
464
|
+
this.currentLlmStartedAt = null;
|
|
465
|
+
this.currentLlmHistorySnapshot = [];
|
|
466
|
+
}
|
|
467
|
+
resetState() {
|
|
468
|
+
this.runToSpan.clear();
|
|
469
|
+
this.traceId = null;
|
|
470
|
+
this.rootSpanId = null;
|
|
471
|
+
this.activeContext = null;
|
|
472
|
+
this.traceStartedAt = null;
|
|
473
|
+
this.conversationHistory = [];
|
|
474
|
+
this.pendingMessages = [];
|
|
475
|
+
this.currentLlmSpanId = null;
|
|
476
|
+
this.currentLlmMessageId = null;
|
|
477
|
+
this.currentLlmContent = [];
|
|
478
|
+
this.currentLlmModel = null;
|
|
479
|
+
this.currentLlmUsage = {};
|
|
480
|
+
this.currentLlmStartedAt = null;
|
|
481
|
+
this.currentLlmHistorySnapshot = [];
|
|
482
|
+
this.activeSubagentSpans.clear();
|
|
483
|
+
}
|
|
484
|
+
};
|
|
11
485
|
|
|
12
486
|
// src/baml.ts
|
|
13
487
|
var cachedBaml = null;
|
|
@@ -297,6 +771,480 @@ async function runFunctionWithBaml(bamlSource, inputs, providers, envVars) {
|
|
|
297
771
|
};
|
|
298
772
|
}
|
|
299
773
|
|
|
774
|
+
// src/langgraph.ts
|
|
775
|
+
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
776
|
+
var LANGGRAPH_METADATA_KEYS = [
|
|
777
|
+
"langgraph_step",
|
|
778
|
+
"langgraph_node",
|
|
779
|
+
"langgraph_triggers",
|
|
780
|
+
"langgraph_path",
|
|
781
|
+
"langgraph_checkpoint_ns"
|
|
782
|
+
];
|
|
783
|
+
function nowIso2() {
|
|
784
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
785
|
+
}
|
|
786
|
+
var MAX_SERIALIZE_DEPTH = 6;
|
|
787
|
+
function safeSerialize2(value) {
|
|
788
|
+
return safeSerializeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
789
|
+
}
|
|
790
|
+
function safeSerializeInner(value, depth, seen) {
|
|
791
|
+
if (value === null || value === void 0) {
|
|
792
|
+
return value;
|
|
793
|
+
}
|
|
794
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
795
|
+
return value;
|
|
796
|
+
}
|
|
797
|
+
const className = value?.constructor?.name ?? typeof value;
|
|
798
|
+
if (depth > MAX_SERIALIZE_DEPTH) {
|
|
799
|
+
return `<${className}>`;
|
|
800
|
+
}
|
|
801
|
+
if (typeof value === "object") {
|
|
802
|
+
if (seen.has(value)) {
|
|
803
|
+
return `<cycle ${className}>`;
|
|
804
|
+
}
|
|
805
|
+
seen.add(value);
|
|
806
|
+
}
|
|
807
|
+
if (Array.isArray(value)) {
|
|
808
|
+
return value.map((item) => safeSerializeInner(item, depth + 1, seen));
|
|
809
|
+
}
|
|
810
|
+
if (typeof value === "object") {
|
|
811
|
+
if (typeof value.toJSON === "function") {
|
|
812
|
+
try {
|
|
813
|
+
return value.toJSON();
|
|
814
|
+
} catch {
|
|
815
|
+
return `<${className}>`;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
try {
|
|
819
|
+
const result = {};
|
|
820
|
+
for (const [k, v] of Object.entries(value)) {
|
|
821
|
+
if (!k.startsWith("_")) {
|
|
822
|
+
result[k] = safeSerializeInner(v, depth + 1, seen);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return result;
|
|
826
|
+
} catch {
|
|
827
|
+
return `<${className}>`;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
try {
|
|
831
|
+
return String(value);
|
|
832
|
+
} catch {
|
|
833
|
+
return `<${className}>`;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
function convertMessage(message) {
|
|
837
|
+
if (typeof message !== "object" || message === null) {
|
|
838
|
+
return { role: "unknown", content: String(message) };
|
|
839
|
+
}
|
|
840
|
+
const msg = message;
|
|
841
|
+
if (typeof msg.toDict === "function") {
|
|
842
|
+
return msg.toDict();
|
|
843
|
+
}
|
|
844
|
+
const typeToRole = {
|
|
845
|
+
human: "user",
|
|
846
|
+
ai: "assistant",
|
|
847
|
+
system: "system",
|
|
848
|
+
tool: "tool",
|
|
849
|
+
function: "function"
|
|
850
|
+
};
|
|
851
|
+
const result = {};
|
|
852
|
+
const msgType = msg._getType ? String(msg._getType()) : msg.type;
|
|
853
|
+
result.role = (msgType ? typeToRole[msgType] : void 0) ?? msg.role ?? "unknown";
|
|
854
|
+
result.content = msg.content ?? "";
|
|
855
|
+
if (msg.tool_calls) {
|
|
856
|
+
result.tool_calls = msg.tool_calls;
|
|
857
|
+
}
|
|
858
|
+
if (msg.tool_call_id) {
|
|
859
|
+
result.tool_call_id = msg.tool_call_id;
|
|
860
|
+
}
|
|
861
|
+
if (msg.name) {
|
|
862
|
+
result.name = msg.name;
|
|
863
|
+
}
|
|
864
|
+
return result;
|
|
865
|
+
}
|
|
866
|
+
function extractModelName(serialized, metadata) {
|
|
867
|
+
if (serialized) {
|
|
868
|
+
const kwargs = serialized.kwargs;
|
|
869
|
+
if (kwargs) {
|
|
870
|
+
const model = kwargs.model_name ?? kwargs.model ?? kwargs.model_id;
|
|
871
|
+
if (model) {
|
|
872
|
+
return String(model);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
if (metadata) {
|
|
877
|
+
const lsModel = metadata.ls_model_name;
|
|
878
|
+
if (lsModel) {
|
|
879
|
+
return String(lsModel);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return void 0;
|
|
883
|
+
}
|
|
884
|
+
function extractUsage2(output) {
|
|
885
|
+
const usage = {};
|
|
886
|
+
const llmOutput = output.llmOutput;
|
|
887
|
+
const tokenUsage = llmOutput?.tokenUsage ?? llmOutput?.token_usage ?? llmOutput?.usage ?? {};
|
|
888
|
+
const inputTokens = tokenUsage.promptTokens ?? tokenUsage.prompt_tokens ?? tokenUsage.input_tokens;
|
|
889
|
+
const outputTokens = tokenUsage.completionTokens ?? tokenUsage.completion_tokens ?? tokenUsage.output_tokens;
|
|
890
|
+
const totalTokens = tokenUsage.totalTokens ?? tokenUsage.total_tokens;
|
|
891
|
+
if (inputTokens !== void 0 && inputTokens !== null) {
|
|
892
|
+
usage.inputTokens = inputTokens;
|
|
893
|
+
}
|
|
894
|
+
if (outputTokens !== void 0 && outputTokens !== null) {
|
|
895
|
+
usage.outputTokens = outputTokens;
|
|
896
|
+
}
|
|
897
|
+
if (totalTokens !== void 0 && totalTokens !== null) {
|
|
898
|
+
usage.totalTokens = totalTokens;
|
|
899
|
+
}
|
|
900
|
+
return usage;
|
|
901
|
+
}
|
|
902
|
+
function extractLangGraphMetadata(metadata) {
|
|
903
|
+
if (!metadata) {
|
|
904
|
+
return {};
|
|
905
|
+
}
|
|
906
|
+
const result = {};
|
|
907
|
+
for (const key of LANGGRAPH_METADATA_KEYS) {
|
|
908
|
+
if (key in metadata) {
|
|
909
|
+
result[key] = metadata[key];
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return result;
|
|
913
|
+
}
|
|
914
|
+
var BitfabLangGraphCallbackHandler = class {
|
|
915
|
+
constructor(config) {
|
|
916
|
+
this.name = "BitfabLangGraphCallbackHandler";
|
|
917
|
+
this.ignoreRetriever = true;
|
|
918
|
+
this.ignoreRetry = true;
|
|
919
|
+
this.ignoreCustomEvent = true;
|
|
920
|
+
this.runToSpan = /* @__PURE__ */ new Map();
|
|
921
|
+
this.invocations = /* @__PURE__ */ new Map();
|
|
922
|
+
this.httpClient = new HttpClient({
|
|
923
|
+
apiKey: config.apiKey,
|
|
924
|
+
serviceUrl: config.serviceUrl ?? DEFAULT_SERVICE_URL,
|
|
925
|
+
timeout: config.timeout ?? 1e4
|
|
926
|
+
});
|
|
927
|
+
this.traceFunctionKey = config.traceFunctionKey;
|
|
928
|
+
this.getActiveSpanContext = config.getActiveSpanContext ?? null;
|
|
929
|
+
}
|
|
930
|
+
// ── lifecycle helpers ──────────────────────────────────────────
|
|
931
|
+
startSpan(runId, parentRunId, name, spanType, inputData, metadata, tags) {
|
|
932
|
+
const parentSpan = parentRunId ? this.runToSpan.get(parentRunId) : void 0;
|
|
933
|
+
const willHide = tags?.includes(LANGSMITH_HIDDEN_TAG) === true;
|
|
934
|
+
let invocation;
|
|
935
|
+
let effectiveParentId;
|
|
936
|
+
if (parentSpan) {
|
|
937
|
+
const existing = this.invocations.get(parentSpan.rootRunId);
|
|
938
|
+
if (existing) {
|
|
939
|
+
invocation = existing;
|
|
940
|
+
} else {
|
|
941
|
+
invocation = {
|
|
942
|
+
traceId: parentSpan.traceId,
|
|
943
|
+
activeContext: null,
|
|
944
|
+
rootRunId: parentSpan.rootRunId
|
|
945
|
+
};
|
|
946
|
+
this.invocations.set(invocation.rootRunId, invocation);
|
|
947
|
+
}
|
|
948
|
+
if (!willHide) {
|
|
949
|
+
let resolved = parentSpan;
|
|
950
|
+
while (resolved?.hidden === true) {
|
|
951
|
+
resolved = resolved.parentId ? this.runToSpan.get(resolved.parentId) : void 0;
|
|
952
|
+
}
|
|
953
|
+
effectiveParentId = resolved ? resolved.spanId : invocation.activeContext?.spanId ?? null;
|
|
954
|
+
} else {
|
|
955
|
+
effectiveParentId = parentRunId ?? null;
|
|
956
|
+
}
|
|
957
|
+
} else {
|
|
958
|
+
const activeContext = this.getActiveSpanContext?.() ?? null;
|
|
959
|
+
invocation = {
|
|
960
|
+
traceId: activeContext ? activeContext.traceId : crypto.randomUUID(),
|
|
961
|
+
activeContext,
|
|
962
|
+
rootRunId: runId
|
|
963
|
+
};
|
|
964
|
+
this.invocations.set(runId, invocation);
|
|
965
|
+
effectiveParentId = activeContext?.spanId ?? null;
|
|
966
|
+
}
|
|
967
|
+
const lgMetadata = extractLangGraphMetadata(metadata);
|
|
968
|
+
const contexts = Object.keys(lgMetadata).length > 0 ? [lgMetadata] : [];
|
|
969
|
+
const spanInfo = {
|
|
970
|
+
spanId: runId,
|
|
971
|
+
traceId: invocation.traceId,
|
|
972
|
+
rootRunId: invocation.rootRunId,
|
|
973
|
+
parentId: effectiveParentId,
|
|
974
|
+
startedAt: nowIso2(),
|
|
975
|
+
name,
|
|
976
|
+
type: spanType,
|
|
977
|
+
input: safeSerialize2(inputData),
|
|
978
|
+
contexts
|
|
979
|
+
};
|
|
980
|
+
if (willHide) {
|
|
981
|
+
spanInfo.hidden = true;
|
|
982
|
+
}
|
|
983
|
+
this.runToSpan.set(runId, spanInfo);
|
|
984
|
+
return spanInfo;
|
|
985
|
+
}
|
|
986
|
+
completeSpan(runId, output, error, extraContexts) {
|
|
987
|
+
const spanInfo = this.runToSpan.get(runId);
|
|
988
|
+
if (!spanInfo) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
this.runToSpan.delete(runId);
|
|
992
|
+
spanInfo.endedAt = nowIso2();
|
|
993
|
+
spanInfo.output = safeSerialize2(output);
|
|
994
|
+
if (error !== void 0) {
|
|
995
|
+
spanInfo.error = error;
|
|
996
|
+
}
|
|
997
|
+
if (extraContexts && Object.keys(extraContexts).length > 0) {
|
|
998
|
+
spanInfo.contexts.push(extraContexts);
|
|
999
|
+
}
|
|
1000
|
+
this.sendSpan(spanInfo);
|
|
1001
|
+
if (runId === spanInfo.rootRunId) {
|
|
1002
|
+
const invocation = this.invocations.get(runId);
|
|
1003
|
+
this.sendTraceCompletion(spanInfo, invocation?.activeContext ?? null);
|
|
1004
|
+
this.invocations.delete(runId);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
sendSpan(spanInfo) {
|
|
1008
|
+
const spanData = {
|
|
1009
|
+
name: spanInfo.name,
|
|
1010
|
+
type: spanInfo.type
|
|
1011
|
+
};
|
|
1012
|
+
if (spanInfo.input !== void 0) {
|
|
1013
|
+
spanData.input = spanInfo.input;
|
|
1014
|
+
}
|
|
1015
|
+
if (spanInfo.output !== void 0) {
|
|
1016
|
+
spanData.output = spanInfo.output;
|
|
1017
|
+
}
|
|
1018
|
+
if (spanInfo.error !== void 0) {
|
|
1019
|
+
spanData.error = spanInfo.error;
|
|
1020
|
+
}
|
|
1021
|
+
if (spanInfo.contexts.length > 0) {
|
|
1022
|
+
spanData.contexts = spanInfo.contexts;
|
|
1023
|
+
}
|
|
1024
|
+
if (spanInfo.hidden) {
|
|
1025
|
+
spanData.hidden = true;
|
|
1026
|
+
}
|
|
1027
|
+
const rawSpan = {
|
|
1028
|
+
id: spanInfo.spanId,
|
|
1029
|
+
trace_id: spanInfo.traceId,
|
|
1030
|
+
started_at: spanInfo.startedAt,
|
|
1031
|
+
ended_at: spanInfo.endedAt ?? nowIso2(),
|
|
1032
|
+
span_data: spanData
|
|
1033
|
+
};
|
|
1034
|
+
if (spanInfo.parentId !== null) {
|
|
1035
|
+
rawSpan.parent_id = spanInfo.parentId;
|
|
1036
|
+
}
|
|
1037
|
+
const payload = {
|
|
1038
|
+
type: "sdk-function",
|
|
1039
|
+
source: "typescript-sdk-langgraph",
|
|
1040
|
+
traceFunctionKey: this.traceFunctionKey,
|
|
1041
|
+
sourceTraceId: spanInfo.traceId,
|
|
1042
|
+
rawSpan
|
|
1043
|
+
};
|
|
1044
|
+
try {
|
|
1045
|
+
this.httpClient.sendExternalSpan(payload);
|
|
1046
|
+
} catch {
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
sendTraceCompletion(rootSpan, activeContext) {
|
|
1050
|
+
const completed = activeContext === null;
|
|
1051
|
+
const traceData = {
|
|
1052
|
+
type: "sdk-function",
|
|
1053
|
+
source: "typescript-sdk-langgraph",
|
|
1054
|
+
traceFunctionKey: this.traceFunctionKey,
|
|
1055
|
+
externalTrace: {
|
|
1056
|
+
id: rootSpan.traceId,
|
|
1057
|
+
started_at: rootSpan.startedAt,
|
|
1058
|
+
ended_at: rootSpan.endedAt ?? nowIso2(),
|
|
1059
|
+
workflow_name: this.traceFunctionKey
|
|
1060
|
+
},
|
|
1061
|
+
completed
|
|
1062
|
+
};
|
|
1063
|
+
try {
|
|
1064
|
+
this.httpClient.sendExternalTrace(traceData);
|
|
1065
|
+
} catch {
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
// ── chain callbacks (graph nodes) ─────────────────────────────
|
|
1069
|
+
async handleChainStart(chain, inputs, runId, parentRunId, tags, metadata) {
|
|
1070
|
+
try {
|
|
1071
|
+
const idArr = chain.id;
|
|
1072
|
+
const name = chain.name ?? idArr?.[idArr.length - 1] ?? "chain";
|
|
1073
|
+
this.startSpan(
|
|
1074
|
+
runId,
|
|
1075
|
+
parentRunId,
|
|
1076
|
+
String(name),
|
|
1077
|
+
"agent",
|
|
1078
|
+
inputs,
|
|
1079
|
+
metadata,
|
|
1080
|
+
tags
|
|
1081
|
+
);
|
|
1082
|
+
} catch {
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
async handleChainEnd(outputs, runId) {
|
|
1086
|
+
try {
|
|
1087
|
+
this.completeSpan(runId, outputs);
|
|
1088
|
+
} catch {
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
async handleChainError(error, runId) {
|
|
1092
|
+
try {
|
|
1093
|
+
const errorObj = error;
|
|
1094
|
+
if (errorObj?.constructor?.name === "GraphBubbleUp") {
|
|
1095
|
+
this.completeSpan(runId, void 0, void 0);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
this.completeSpan(
|
|
1099
|
+
runId,
|
|
1100
|
+
void 0,
|
|
1101
|
+
error instanceof Error ? error.message : String(error)
|
|
1102
|
+
);
|
|
1103
|
+
} catch {
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
// ── LLM callbacks ─────────────────────────────────────────────
|
|
1107
|
+
async handleChatModelStart(llm, messages, runId, parentRunId, _extraParams, tags, metadata) {
|
|
1108
|
+
try {
|
|
1109
|
+
const model = extractModelName(llm, metadata);
|
|
1110
|
+
const idArr = llm.id;
|
|
1111
|
+
const name = model ?? idArr?.[idArr.length - 1] ?? "llm";
|
|
1112
|
+
const converted = messages.map((batch) => batch.map(convertMessage));
|
|
1113
|
+
const spanInfo = this.startSpan(
|
|
1114
|
+
runId,
|
|
1115
|
+
parentRunId,
|
|
1116
|
+
String(name),
|
|
1117
|
+
"llm",
|
|
1118
|
+
converted,
|
|
1119
|
+
metadata,
|
|
1120
|
+
tags
|
|
1121
|
+
);
|
|
1122
|
+
spanInfo.model = model;
|
|
1123
|
+
} catch {
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
async handleLLMStart(llm, prompts, runId, parentRunId, _extraParams, tags, metadata) {
|
|
1127
|
+
try {
|
|
1128
|
+
const model = extractModelName(llm, metadata);
|
|
1129
|
+
const idArr = llm.id;
|
|
1130
|
+
const name = model ?? idArr?.[idArr.length - 1] ?? "llm";
|
|
1131
|
+
const spanInfo = this.startSpan(
|
|
1132
|
+
runId,
|
|
1133
|
+
parentRunId,
|
|
1134
|
+
String(name),
|
|
1135
|
+
"llm",
|
|
1136
|
+
prompts,
|
|
1137
|
+
metadata,
|
|
1138
|
+
tags
|
|
1139
|
+
);
|
|
1140
|
+
spanInfo.model = model;
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
async handleLLMEnd(output, runId) {
|
|
1145
|
+
try {
|
|
1146
|
+
let llmOutput;
|
|
1147
|
+
const generations = output.generations;
|
|
1148
|
+
if (generations?.length && generations[generations.length - 1]?.length) {
|
|
1149
|
+
const gen = generations[generations.length - 1][generations[generations.length - 1].length - 1];
|
|
1150
|
+
const msg = gen.message;
|
|
1151
|
+
llmOutput = msg ? convertMessage(msg) : gen.text ?? String(gen);
|
|
1152
|
+
}
|
|
1153
|
+
const usage = extractUsage2(output);
|
|
1154
|
+
const spanInfo = this.runToSpan.get(runId);
|
|
1155
|
+
const model = spanInfo?.model;
|
|
1156
|
+
const llmContext = {};
|
|
1157
|
+
if (model) {
|
|
1158
|
+
llmContext.model = model;
|
|
1159
|
+
}
|
|
1160
|
+
Object.assign(llmContext, usage);
|
|
1161
|
+
this.completeSpan(
|
|
1162
|
+
runId,
|
|
1163
|
+
llmOutput,
|
|
1164
|
+
void 0,
|
|
1165
|
+
Object.keys(llmContext).length > 0 ? llmContext : void 0
|
|
1166
|
+
);
|
|
1167
|
+
} catch {
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
async handleLLMError(error, runId) {
|
|
1171
|
+
try {
|
|
1172
|
+
this.completeSpan(
|
|
1173
|
+
runId,
|
|
1174
|
+
void 0,
|
|
1175
|
+
error instanceof Error ? error.message : String(error)
|
|
1176
|
+
);
|
|
1177
|
+
} catch {
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
async handleLLMNewToken() {
|
|
1181
|
+
}
|
|
1182
|
+
// ── tool callbacks ────────────────────────────────────────────
|
|
1183
|
+
async handleToolStart(tool, input, runId, parentRunId, tags, metadata) {
|
|
1184
|
+
try {
|
|
1185
|
+
const name = tool.name ?? "tool";
|
|
1186
|
+
this.startSpan(
|
|
1187
|
+
runId,
|
|
1188
|
+
parentRunId,
|
|
1189
|
+
String(name),
|
|
1190
|
+
"function",
|
|
1191
|
+
input,
|
|
1192
|
+
metadata,
|
|
1193
|
+
tags
|
|
1194
|
+
);
|
|
1195
|
+
} catch {
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
async handleToolEnd(output, runId) {
|
|
1199
|
+
try {
|
|
1200
|
+
this.completeSpan(runId, output);
|
|
1201
|
+
} catch {
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
async handleToolError(error, runId) {
|
|
1205
|
+
try {
|
|
1206
|
+
this.completeSpan(
|
|
1207
|
+
runId,
|
|
1208
|
+
void 0,
|
|
1209
|
+
error instanceof Error ? error.message : String(error)
|
|
1210
|
+
);
|
|
1211
|
+
} catch {
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
// ── retriever callbacks ───────────────────────────────────────
|
|
1215
|
+
async handleRetrieverStart(retriever, query, runId, parentRunId, tags, metadata) {
|
|
1216
|
+
try {
|
|
1217
|
+
const name = retriever.name ?? "retriever";
|
|
1218
|
+
this.startSpan(
|
|
1219
|
+
runId,
|
|
1220
|
+
parentRunId,
|
|
1221
|
+
String(name),
|
|
1222
|
+
"function",
|
|
1223
|
+
query,
|
|
1224
|
+
metadata,
|
|
1225
|
+
tags
|
|
1226
|
+
);
|
|
1227
|
+
} catch {
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
async handleRetrieverEnd(documents, runId) {
|
|
1231
|
+
try {
|
|
1232
|
+
this.completeSpan(runId, documents);
|
|
1233
|
+
} catch {
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
async handleRetrieverError(error, runId) {
|
|
1237
|
+
try {
|
|
1238
|
+
this.completeSpan(
|
|
1239
|
+
runId,
|
|
1240
|
+
void 0,
|
|
1241
|
+
error instanceof Error ? error.message : String(error)
|
|
1242
|
+
);
|
|
1243
|
+
} catch {
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
|
|
300
1248
|
// src/tracing.ts
|
|
301
1249
|
var BitfabOpenAITracingProcessor = class {
|
|
302
1250
|
/**
|
|
@@ -516,6 +1464,65 @@ function runWithSpanStack(stack, fn) {
|
|
|
516
1464
|
throw error;
|
|
517
1465
|
}
|
|
518
1466
|
}
|
|
1467
|
+
function isAsyncGenerator(value) {
|
|
1468
|
+
if (value === null || typeof value !== "object") {
|
|
1469
|
+
return false;
|
|
1470
|
+
}
|
|
1471
|
+
const candidate = value;
|
|
1472
|
+
return typeof candidate.next === "function" && typeof candidate.return === "function" && typeof candidate.throw === "function" && typeof candidate[Symbol.asyncIterator] === "function";
|
|
1473
|
+
}
|
|
1474
|
+
function wrapAsyncGenerator(source, spanStack, sendSpan) {
|
|
1475
|
+
const yielded = [];
|
|
1476
|
+
let returnValue;
|
|
1477
|
+
let finalized = false;
|
|
1478
|
+
const finalize = (errorMsg) => {
|
|
1479
|
+
if (finalized) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
finalized = true;
|
|
1483
|
+
void sendSpan({
|
|
1484
|
+
result: { yielded, return: returnValue },
|
|
1485
|
+
...errorMsg && { error: errorMsg }
|
|
1486
|
+
});
|
|
1487
|
+
};
|
|
1488
|
+
const step = (method, arg) => runWithSpanStack(spanStack, () => {
|
|
1489
|
+
const op = source[method];
|
|
1490
|
+
return op.call(source, arg);
|
|
1491
|
+
});
|
|
1492
|
+
const handle = async (method, arg) => {
|
|
1493
|
+
try {
|
|
1494
|
+
const result = await step(method, arg);
|
|
1495
|
+
if (result.done) {
|
|
1496
|
+
returnValue = result.value;
|
|
1497
|
+
finalize();
|
|
1498
|
+
} else {
|
|
1499
|
+
yielded.push(result.value);
|
|
1500
|
+
}
|
|
1501
|
+
return result;
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
finalize(error instanceof Error ? error.message : String(error));
|
|
1504
|
+
throw error;
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
const wrapped = {
|
|
1508
|
+
next(arg) {
|
|
1509
|
+
return handle("next", arg);
|
|
1510
|
+
},
|
|
1511
|
+
return(value) {
|
|
1512
|
+
return handle("return", value);
|
|
1513
|
+
},
|
|
1514
|
+
throw(err) {
|
|
1515
|
+
return handle("throw", err);
|
|
1516
|
+
},
|
|
1517
|
+
[Symbol.asyncIterator]() {
|
|
1518
|
+
return wrapped;
|
|
1519
|
+
},
|
|
1520
|
+
[Symbol.asyncDispose]() {
|
|
1521
|
+
return handle("return", void 0).then(() => void 0);
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
return wrapped;
|
|
1525
|
+
}
|
|
519
1526
|
var cachedCollectorClass;
|
|
520
1527
|
async function loadCollectorClass() {
|
|
521
1528
|
if (cachedCollectorClass !== void 0) {
|
|
@@ -805,6 +1812,67 @@ var Bitfab = class {
|
|
|
805
1812
|
}
|
|
806
1813
|
});
|
|
807
1814
|
}
|
|
1815
|
+
/**
|
|
1816
|
+
* Get a LangGraph/LangChain callback handler for tracing.
|
|
1817
|
+
*
|
|
1818
|
+
* The handler captures graph node execution, LLM calls, and tool
|
|
1819
|
+
* invocations as Bitfab spans with proper parent-child hierarchy.
|
|
1820
|
+
*
|
|
1821
|
+
* ```typescript
|
|
1822
|
+
* const handler = client.getLangGraphCallbackHandler("my-agent");
|
|
1823
|
+
* const result = await agent.invoke(
|
|
1824
|
+
* { messages: [...] },
|
|
1825
|
+
* { callbacks: [handler] },
|
|
1826
|
+
* );
|
|
1827
|
+
* ```
|
|
1828
|
+
*
|
|
1829
|
+
* @param traceFunctionKey - Groups traces under this key in Bitfab
|
|
1830
|
+
* @returns A BitfabLangGraphCallbackHandler configured for this client
|
|
1831
|
+
*/
|
|
1832
|
+
getLangGraphCallbackHandler(traceFunctionKey) {
|
|
1833
|
+
return new BitfabLangGraphCallbackHandler({
|
|
1834
|
+
apiKey: this.apiKey,
|
|
1835
|
+
traceFunctionKey,
|
|
1836
|
+
serviceUrl: this.serviceUrl,
|
|
1837
|
+
getActiveSpanContext: () => {
|
|
1838
|
+
const stack = getSpanStack();
|
|
1839
|
+
return stack[stack.length - 1] ?? null;
|
|
1840
|
+
}
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Get a Claude Agent SDK handler for tracing.
|
|
1845
|
+
*
|
|
1846
|
+
* The handler captures LLM turns, tool invocations, and subagent
|
|
1847
|
+
* execution as Bitfab spans with proper parent-child hierarchy.
|
|
1848
|
+
*
|
|
1849
|
+
* ```typescript
|
|
1850
|
+
* const handler = client.getClaudeAgentHandler("my-agent");
|
|
1851
|
+
* const options = handler.instrumentOptions({
|
|
1852
|
+
* model: "claude-sonnet-4-5-...",
|
|
1853
|
+
* });
|
|
1854
|
+
* const sdkClient = new ClaudeSDKClient(options);
|
|
1855
|
+
* await sdkClient.connect();
|
|
1856
|
+
* await sdkClient.query("Do something");
|
|
1857
|
+
* for await (const msg of handler.wrapResponse(sdkClient.receiveResponse())) {
|
|
1858
|
+
* // process messages
|
|
1859
|
+
* }
|
|
1860
|
+
* ```
|
|
1861
|
+
*
|
|
1862
|
+
* @param traceFunctionKey - Groups traces under this key in Bitfab
|
|
1863
|
+
* @returns A BitfabClaudeAgentHandler configured for this client
|
|
1864
|
+
*/
|
|
1865
|
+
getClaudeAgentHandler(traceFunctionKey) {
|
|
1866
|
+
return new BitfabClaudeAgentHandler({
|
|
1867
|
+
apiKey: this.apiKey,
|
|
1868
|
+
traceFunctionKey,
|
|
1869
|
+
serviceUrl: this.serviceUrl,
|
|
1870
|
+
getActiveSpanContext: () => {
|
|
1871
|
+
const stack = getSpanStack();
|
|
1872
|
+
return stack[stack.length - 1] ?? null;
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
808
1876
|
/**
|
|
809
1877
|
* Wrap a BAML client method to automatically capture prompt and LLM metadata.
|
|
810
1878
|
*
|
|
@@ -947,10 +2015,14 @@ var Bitfab = class {
|
|
|
947
2015
|
const inputs = args;
|
|
948
2016
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
949
2017
|
if (isRootSpan && !activeTraceStates.has(traceId)) {
|
|
2018
|
+
const replayCtxAtRoot = getReplayContext();
|
|
950
2019
|
activeTraceStates.set(traceId, {
|
|
951
2020
|
traceId,
|
|
952
2021
|
startedAt,
|
|
953
|
-
contexts: []
|
|
2022
|
+
contexts: [],
|
|
2023
|
+
...replayCtxAtRoot?.testRunId && {
|
|
2024
|
+
testRunId: replayCtxAtRoot.testRunId
|
|
2025
|
+
}
|
|
954
2026
|
});
|
|
955
2027
|
pendingSpanPromises.set(traceId, []);
|
|
956
2028
|
}
|
|
@@ -997,7 +2069,8 @@ var Bitfab = class {
|
|
|
997
2069
|
endedAt,
|
|
998
2070
|
sessionId: traceState?.sessionId,
|
|
999
2071
|
metadata: traceState?.metadata,
|
|
1000
|
-
contexts: traceState?.contexts ?? []
|
|
2072
|
+
contexts: traceState?.contexts ?? [],
|
|
2073
|
+
testRunId: traceState?.testRunId
|
|
1001
2074
|
});
|
|
1002
2075
|
activeTraceStates.delete(traceId);
|
|
1003
2076
|
} else {
|
|
@@ -1025,6 +2098,9 @@ var Bitfab = class {
|
|
|
1025
2098
|
throw error;
|
|
1026
2099
|
});
|
|
1027
2100
|
}
|
|
2101
|
+
if (isAsyncGenerator(result)) {
|
|
2102
|
+
return wrapAsyncGenerator(result, newStack, sendSpan);
|
|
2103
|
+
}
|
|
1028
2104
|
void sendSpan({ result });
|
|
1029
2105
|
return result;
|
|
1030
2106
|
};
|
|
@@ -1077,7 +2153,8 @@ var Bitfab = class {
|
|
|
1077
2153
|
traceFunctionKey: params.traceFunctionKey,
|
|
1078
2154
|
externalTrace: rawTrace,
|
|
1079
2155
|
completed: true,
|
|
1080
|
-
...params.sessionId && { sessionId: params.sessionId }
|
|
2156
|
+
...params.sessionId && { sessionId: params.sessionId },
|
|
2157
|
+
...params.testRunId && { testRunId: params.testRunId }
|
|
1081
2158
|
});
|
|
1082
2159
|
}
|
|
1083
2160
|
/**
|
|
@@ -1145,7 +2222,7 @@ var Bitfab = class {
|
|
|
1145
2222
|
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
1146
2223
|
*/
|
|
1147
2224
|
async replay(traceFunctionKey, fn, options) {
|
|
1148
|
-
const { replay: doReplay } = await import("./replay-
|
|
2225
|
+
const { replay: doReplay } = await import("./replay-IIBLVODH.js");
|
|
1149
2226
|
return doReplay(
|
|
1150
2227
|
this.httpClient,
|
|
1151
2228
|
this.serviceUrl,
|
|
@@ -1205,10 +2282,12 @@ var BitfabFunction = class {
|
|
|
1205
2282
|
};
|
|
1206
2283
|
|
|
1207
2284
|
export {
|
|
2285
|
+
BitfabClaudeAgentHandler,
|
|
2286
|
+
BitfabLangGraphCallbackHandler,
|
|
1208
2287
|
BitfabOpenAITracingProcessor,
|
|
1209
2288
|
getCurrentSpan,
|
|
1210
2289
|
getCurrentTrace,
|
|
1211
2290
|
Bitfab,
|
|
1212
2291
|
BitfabFunction
|
|
1213
2292
|
};
|
|
1214
|
-
//# sourceMappingURL=chunk-
|
|
2293
|
+
//# sourceMappingURL=chunk-I3OKZ2TF.js.map
|