bitfab 0.38.6 → 0.38.7
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/{chunk-O75EYGVO.js → chunk-2OJZPS6C.js} +2 -2
- package/dist/{chunk-O75EYGVO.js.map → chunk-2OJZPS6C.js.map} +1 -1
- package/dist/{chunk-YCTQONNS.js → chunk-DXM6GTLL.js} +303 -3
- package/dist/chunk-DXM6GTLL.js.map +1 -0
- package/dist/index.cjs +307 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -2
- package/dist/index.d.ts +97 -2
- package/dist/index.js +4 -2
- package/dist/node.cjs +307 -4
- 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 +4 -2
- package/dist/node.js.map +1 -1
- package/dist/{replay-K3UMQAKO.js → replay-KDIILV5R.js} +2 -2
- package/package.json +11 -1
- package/dist/chunk-YCTQONNS.js.map +0 -1
- /package/dist/{replay-K3UMQAKO.js.map → replay-KDIILV5R.js.map} +0 -0
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.
|
|
54
|
+
__version__ = "0.38.7";
|
|
55
55
|
__packageName__ = "bitfab";
|
|
56
56
|
}
|
|
57
57
|
});
|
|
@@ -507,8 +507,8 @@ function encodePayloadBody(payload) {
|
|
|
507
507
|
const marker = { error: `payload_serialize_failed: ${message}` };
|
|
508
508
|
return { body: JSON.stringify(marker), dropped, value: marker };
|
|
509
509
|
}
|
|
510
|
-
const
|
|
511
|
-
if (dropped.length > 0 &&
|
|
510
|
+
const isRecord2 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
|
|
511
|
+
if (dropped.length > 0 && isRecord2) {
|
|
512
512
|
const obj = sanitized;
|
|
513
513
|
const existing = Array.isArray(obj.errors) ? obj.errors : [];
|
|
514
514
|
obj.errors = [
|
|
@@ -525,7 +525,7 @@ function encodePayloadBody(payload) {
|
|
|
525
525
|
return {
|
|
526
526
|
body: JSON.stringify(sanitized),
|
|
527
527
|
dropped,
|
|
528
|
-
value:
|
|
528
|
+
value: isRecord2 ? sanitized : void 0
|
|
529
529
|
};
|
|
530
530
|
}
|
|
531
531
|
}
|
|
@@ -3141,6 +3141,7 @@ __export(index_exports, {
|
|
|
3141
3141
|
BitfabFunction: () => BitfabFunction,
|
|
3142
3142
|
BitfabLangChainCallbackHandler: () => BitfabLangGraphCallbackHandler,
|
|
3143
3143
|
BitfabLangGraphCallbackHandler: () => BitfabLangGraphCallbackHandler,
|
|
3144
|
+
BitfabLangGraphIntegration: () => BitfabLangGraphIntegration,
|
|
3144
3145
|
BitfabOpenAIAgentHandler: () => BitfabOpenAIAgentHandler,
|
|
3145
3146
|
BitfabOpenAITracingProcessor: () => BitfabOpenAITracingProcessor,
|
|
3146
3147
|
BitfabVercelAiHandler: () => BitfabVercelAiHandler,
|
|
@@ -4465,6 +4466,7 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
4465
4466
|
});
|
|
4466
4467
|
this.traceFunctionKey = config.traceFunctionKey;
|
|
4467
4468
|
this.getActiveSpanContext = config.getActiveSpanContext ?? null;
|
|
4469
|
+
this.captureTools = config.captureTools ?? true;
|
|
4468
4470
|
}
|
|
4469
4471
|
/**
|
|
4470
4472
|
* Flush and release the span transport this handler started. A no-op when
|
|
@@ -4773,6 +4775,9 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
4773
4775
|
}
|
|
4774
4776
|
// ── tool callbacks ────────────────────────────────────────────
|
|
4775
4777
|
async handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName) {
|
|
4778
|
+
if (!this.captureTools) {
|
|
4779
|
+
return;
|
|
4780
|
+
}
|
|
4776
4781
|
try {
|
|
4777
4782
|
const serialized = tool ?? {};
|
|
4778
4783
|
const name = runName ?? serialized.name ?? "tool";
|
|
@@ -4789,12 +4794,18 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
4789
4794
|
}
|
|
4790
4795
|
}
|
|
4791
4796
|
async handleToolEnd(output, runId) {
|
|
4797
|
+
if (!this.captureTools) {
|
|
4798
|
+
return;
|
|
4799
|
+
}
|
|
4792
4800
|
try {
|
|
4793
4801
|
this.completeSpan(runId, output);
|
|
4794
4802
|
} catch {
|
|
4795
4803
|
}
|
|
4796
4804
|
}
|
|
4797
4805
|
async handleToolError(error, runId) {
|
|
4806
|
+
if (!this.captureTools) {
|
|
4807
|
+
return;
|
|
4808
|
+
}
|
|
4798
4809
|
try {
|
|
4799
4810
|
this.completeSpan(
|
|
4800
4811
|
runId,
|
|
@@ -4839,6 +4850,258 @@ var BitfabLangGraphCallbackHandler = class {
|
|
|
4839
4850
|
}
|
|
4840
4851
|
};
|
|
4841
4852
|
|
|
4853
|
+
// src/langgraphIntegration.ts
|
|
4854
|
+
init_http();
|
|
4855
|
+
init_replayContext();
|
|
4856
|
+
var TOOL_RESULT_TAG = "__bitfabLangGraphToolResult";
|
|
4857
|
+
function isRecord(value) {
|
|
4858
|
+
return typeof value === "object" && value !== null;
|
|
4859
|
+
}
|
|
4860
|
+
function isToolMessage(value) {
|
|
4861
|
+
return isRecord(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
|
|
4862
|
+
}
|
|
4863
|
+
function isCommand(value) {
|
|
4864
|
+
return isRecord(value) && value.lg_name === "Command";
|
|
4865
|
+
}
|
|
4866
|
+
function encodeNested(value) {
|
|
4867
|
+
if (isToolMessage(value) || isCommand(value)) {
|
|
4868
|
+
return encodeNativeToolResult(value);
|
|
4869
|
+
}
|
|
4870
|
+
if (Array.isArray(value)) {
|
|
4871
|
+
return value.map(encodeNested);
|
|
4872
|
+
}
|
|
4873
|
+
if (isRecord(value)) {
|
|
4874
|
+
return Object.fromEntries(
|
|
4875
|
+
Object.entries(value).map(([key, entry]) => [key, encodeNested(entry)])
|
|
4876
|
+
);
|
|
4877
|
+
}
|
|
4878
|
+
return value;
|
|
4879
|
+
}
|
|
4880
|
+
function encodeNativeToolResult(value) {
|
|
4881
|
+
if (isToolMessage(value)) {
|
|
4882
|
+
return {
|
|
4883
|
+
[TOOL_RESULT_TAG]: "tool-message",
|
|
4884
|
+
content: value.content,
|
|
4885
|
+
name: typeof value.name === "string" ? value.name : void 0,
|
|
4886
|
+
id: typeof value.id === "string" ? value.id : void 0,
|
|
4887
|
+
status: value.status === "success" || value.status === "error" ? value.status : void 0,
|
|
4888
|
+
artifact: encodeNested(value.artifact),
|
|
4889
|
+
metadata: encodeNested(value.metadata),
|
|
4890
|
+
additionalKwargs: encodeNested(value.additional_kwargs),
|
|
4891
|
+
responseMetadata: encodeNested(value.response_metadata)
|
|
4892
|
+
};
|
|
4893
|
+
}
|
|
4894
|
+
return {
|
|
4895
|
+
[TOOL_RESULT_TAG]: "command",
|
|
4896
|
+
graph: isRecord(value) && typeof value.graph === "string" ? value.graph : void 0,
|
|
4897
|
+
update: isRecord(value) ? encodeNested(value.update) : void 0,
|
|
4898
|
+
resume: isRecord(value) ? encodeNested(value.resume) : void 0,
|
|
4899
|
+
goto: isRecord(value) ? encodeNested(value.goto) : void 0
|
|
4900
|
+
};
|
|
4901
|
+
}
|
|
4902
|
+
function finalizeToolResult(value) {
|
|
4903
|
+
return isToolMessage(value) || isCommand(value) ? encodeNativeToolResult(value) : value;
|
|
4904
|
+
}
|
|
4905
|
+
function isEncodedToolResult(value) {
|
|
4906
|
+
return isRecord(value) && typeof value[TOOL_RESULT_TAG] === "string";
|
|
4907
|
+
}
|
|
4908
|
+
async function loadLangChainCore() {
|
|
4909
|
+
try {
|
|
4910
|
+
return await importOptionalPeer([
|
|
4911
|
+
"@langchain",
|
|
4912
|
+
"core",
|
|
4913
|
+
"messages"
|
|
4914
|
+
]);
|
|
4915
|
+
} catch {
|
|
4916
|
+
throw new BitfabError(
|
|
4917
|
+
"LangGraph tool replay requires @langchain/core. Install @langchain/langgraph before using getLangGraphIntegration().",
|
|
4918
|
+
"https://docs.bitfab.ai/frameworks/langgraph"
|
|
4919
|
+
);
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
async function loadLangGraph() {
|
|
4923
|
+
try {
|
|
4924
|
+
return await importOptionalPeer([
|
|
4925
|
+
"@langchain",
|
|
4926
|
+
"langgraph"
|
|
4927
|
+
]);
|
|
4928
|
+
} catch {
|
|
4929
|
+
throw new BitfabError(
|
|
4930
|
+
"Replaying a LangGraph Command requires @langchain/langgraph.",
|
|
4931
|
+
"https://docs.bitfab.ai/frameworks/langgraph"
|
|
4932
|
+
);
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
async function reviveNested(value, toolCallId) {
|
|
4936
|
+
if (isEncodedToolResult(value)) {
|
|
4937
|
+
return reviveToolResult(value, toolCallId);
|
|
4938
|
+
}
|
|
4939
|
+
if (Array.isArray(value)) {
|
|
4940
|
+
return Promise.all(value.map((entry) => reviveNested(entry, toolCallId)));
|
|
4941
|
+
}
|
|
4942
|
+
if (isRecord(value)) {
|
|
4943
|
+
const entries = await Promise.all(
|
|
4944
|
+
Object.entries(value).map(async ([key, entry]) => [
|
|
4945
|
+
key,
|
|
4946
|
+
await reviveNested(entry, toolCallId)
|
|
4947
|
+
])
|
|
4948
|
+
);
|
|
4949
|
+
return Object.fromEntries(entries);
|
|
4950
|
+
}
|
|
4951
|
+
return value;
|
|
4952
|
+
}
|
|
4953
|
+
async function reviveToolResult(value, toolCallId) {
|
|
4954
|
+
if (value[TOOL_RESULT_TAG] === "tool-message") {
|
|
4955
|
+
const { ToolMessage } = await loadLangChainCore();
|
|
4956
|
+
return new ToolMessage({
|
|
4957
|
+
content: value.content,
|
|
4958
|
+
tool_call_id: toolCallId,
|
|
4959
|
+
...typeof value.name === "string" && { name: value.name },
|
|
4960
|
+
...typeof value.id === "string" && { id: value.id },
|
|
4961
|
+
...(value.status === "success" || value.status === "error") && {
|
|
4962
|
+
status: value.status
|
|
4963
|
+
},
|
|
4964
|
+
...value.artifact !== void 0 && {
|
|
4965
|
+
artifact: await reviveNested(value.artifact, toolCallId)
|
|
4966
|
+
},
|
|
4967
|
+
...isRecord(value.metadata) && {
|
|
4968
|
+
metadata: await reviveNested(value.metadata, toolCallId)
|
|
4969
|
+
},
|
|
4970
|
+
...isRecord(value.additionalKwargs) && {
|
|
4971
|
+
additional_kwargs: await reviveNested(
|
|
4972
|
+
value.additionalKwargs,
|
|
4973
|
+
toolCallId
|
|
4974
|
+
)
|
|
4975
|
+
},
|
|
4976
|
+
...isRecord(value.responseMetadata) && {
|
|
4977
|
+
response_metadata: await reviveNested(
|
|
4978
|
+
value.responseMetadata,
|
|
4979
|
+
toolCallId
|
|
4980
|
+
)
|
|
4981
|
+
}
|
|
4982
|
+
});
|
|
4983
|
+
}
|
|
4984
|
+
const { Command } = await loadLangGraph();
|
|
4985
|
+
return new Command({
|
|
4986
|
+
...typeof value.graph === "string" && { graph: value.graph },
|
|
4987
|
+
...value.update !== void 0 && {
|
|
4988
|
+
update: await reviveNested(value.update, toolCallId)
|
|
4989
|
+
},
|
|
4990
|
+
...value.resume !== void 0 && {
|
|
4991
|
+
resume: await reviveNested(value.resume, toolCallId)
|
|
4992
|
+
},
|
|
4993
|
+
...value.goto !== void 0 && {
|
|
4994
|
+
goto: await reviveNested(value.goto, toolCallId)
|
|
4995
|
+
}
|
|
4996
|
+
});
|
|
4997
|
+
}
|
|
4998
|
+
var BitfabLangGraphIntegration = class {
|
|
4999
|
+
constructor(config) {
|
|
5000
|
+
this.client = config.client;
|
|
5001
|
+
this.traceFunctionKey = config.traceFunctionKey;
|
|
5002
|
+
this.callbackHandler = config.callbackHandler;
|
|
5003
|
+
this.mockToolsOnReplay = config.options?.mockToolsOnReplay ?? true;
|
|
5004
|
+
}
|
|
5005
|
+
/**
|
|
5006
|
+
* Wrap tools before passing the same returned array to both
|
|
5007
|
+
* `model.bindTools()` and `new ToolNode()`. Each invocation becomes an
|
|
5008
|
+
* independently mockable child span.
|
|
5009
|
+
*/
|
|
5010
|
+
wrapTools(tools) {
|
|
5011
|
+
return tools.map((tool) => this.wrapTool(tool));
|
|
5012
|
+
}
|
|
5013
|
+
/**
|
|
5014
|
+
* Create the normal graph entry point. The returned function adds Bitfab's
|
|
5015
|
+
* callback handler, preserves invocation config, and records only the graph
|
|
5016
|
+
* input as the replayable root input.
|
|
5017
|
+
*
|
|
5018
|
+
* @experimental This API may change before it is stable.
|
|
5019
|
+
*/
|
|
5020
|
+
createInvoker(graph) {
|
|
5021
|
+
const configuredGraph = graph.withConfig({
|
|
5022
|
+
callbacks: [this.callbackHandler]
|
|
5023
|
+
});
|
|
5024
|
+
return (input, config) => {
|
|
5025
|
+
const invoke = this.wrapInvoke(
|
|
5026
|
+
(rootInput) => configuredGraph.invoke(rootInput, config)
|
|
5027
|
+
);
|
|
5028
|
+
return invoke(input);
|
|
5029
|
+
};
|
|
5030
|
+
}
|
|
5031
|
+
wrapTool(tool) {
|
|
5032
|
+
const toolName = tool.name;
|
|
5033
|
+
if (typeof toolName !== "string" || toolName.length === 0) {
|
|
5034
|
+
throw new BitfabError(
|
|
5035
|
+
"LangGraph replayable tools must have a name.",
|
|
5036
|
+
"https://docs.bitfab.ai/frameworks/langgraph"
|
|
5037
|
+
);
|
|
5038
|
+
}
|
|
5039
|
+
const mockToolsOnReplay = this.mockToolsOnReplay;
|
|
5040
|
+
const shouldMock = typeof mockToolsOnReplay === "boolean" ? mockToolsOnReplay : mockToolsOnReplay.includes(toolName);
|
|
5041
|
+
const originalInvoke = tool.invoke.bind(tool);
|
|
5042
|
+
return new Proxy(tool, {
|
|
5043
|
+
get: (target, property) => {
|
|
5044
|
+
if (property === "invoke") {
|
|
5045
|
+
return async (input, ...rest) => {
|
|
5046
|
+
const toolCallId = isRecord(input) && typeof input.id === "string" ? input.id : "";
|
|
5047
|
+
const args = isRecord(input) && "args" in input ? input.args : input;
|
|
5048
|
+
this.assertReplayToolResultExists(toolName, shouldMock);
|
|
5049
|
+
const execute = this.client.withSpan(
|
|
5050
|
+
this.traceFunctionKey,
|
|
5051
|
+
{
|
|
5052
|
+
name: toolName,
|
|
5053
|
+
type: "function",
|
|
5054
|
+
captureWhen: "nested",
|
|
5055
|
+
mockOnReplay: shouldMock,
|
|
5056
|
+
finalize: finalizeToolResult
|
|
5057
|
+
},
|
|
5058
|
+
async (_args) => await originalInvoke(input, ...rest)
|
|
5059
|
+
);
|
|
5060
|
+
const result = await execute(args);
|
|
5061
|
+
return isEncodedToolResult(result) ? await reviveToolResult(result, toolCallId) : result;
|
|
5062
|
+
};
|
|
5063
|
+
}
|
|
5064
|
+
const value = Reflect.get(target, property, target);
|
|
5065
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
5066
|
+
}
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5069
|
+
assertReplayToolResultExists(toolName, shouldMock) {
|
|
5070
|
+
const replayContext = getReplayContext();
|
|
5071
|
+
if (!replayContext?.mockTree) {
|
|
5072
|
+
return;
|
|
5073
|
+
}
|
|
5074
|
+
const counterKey = `${this.traceFunctionKey}:${toolName}`;
|
|
5075
|
+
const callIndex = replayContext.callCounters?.get(counterKey) ?? 0;
|
|
5076
|
+
const mockSpan = replayContext.mockTree.spans.get(
|
|
5077
|
+
`${counterKey}:${callIndex}`
|
|
5078
|
+
);
|
|
5079
|
+
const hasMatchingOverride = replayContext.mockOverrides?.some(
|
|
5080
|
+
(override) => override.match({
|
|
5081
|
+
traceFunctionKey: this.traceFunctionKey,
|
|
5082
|
+
spanName: toolName,
|
|
5083
|
+
type: "function",
|
|
5084
|
+
originalSpanId: mockSpan?.sourceSpanId
|
|
5085
|
+
})
|
|
5086
|
+
);
|
|
5087
|
+
const expectsRecordedOutput = replayContext.mockStrategy === "all" || replayContext.mockStrategy === "marked" && shouldMock;
|
|
5088
|
+
if (hasMatchingOverride !== true && expectsRecordedOutput && !mockSpan) {
|
|
5089
|
+
throw new BitfabError(
|
|
5090
|
+
`No recorded LangGraph tool result for "${toolName}" at call ${callIndex + 1}; refusing to execute the live tool during replay.`,
|
|
5091
|
+
"https://docs.bitfab.ai/frameworks/langgraph"
|
|
5092
|
+
);
|
|
5093
|
+
}
|
|
5094
|
+
}
|
|
5095
|
+
/** Wrap the function that invokes the compiled graph as the replay root. */
|
|
5096
|
+
wrapInvoke(fn) {
|
|
5097
|
+
return this.client.withSpan(
|
|
5098
|
+
this.traceFunctionKey,
|
|
5099
|
+
{ name: this.traceFunctionKey, type: "agent" },
|
|
5100
|
+
fn
|
|
5101
|
+
);
|
|
5102
|
+
}
|
|
5103
|
+
};
|
|
5104
|
+
|
|
4842
5105
|
// src/client.ts
|
|
4843
5106
|
init_mockOverride();
|
|
4844
5107
|
|
|
@@ -6229,6 +6492,37 @@ var Bitfab = class {
|
|
|
6229
6492
|
getLangChainCallbackHandler(traceFunctionKey) {
|
|
6230
6493
|
return this.getLangGraphCallbackHandler(traceFunctionKey);
|
|
6231
6494
|
}
|
|
6495
|
+
/**
|
|
6496
|
+
* Get the first-class LangGraph integration for tracing and replaying tools
|
|
6497
|
+
* executed by `ToolNode`.
|
|
6498
|
+
*
|
|
6499
|
+
* The integration combines `wrapTools()` for per-tool replay interception
|
|
6500
|
+
* with `createInvoker()` for a callback-configured replayable graph entry
|
|
6501
|
+
* point. Lower-level callback and root wrappers remain available.
|
|
6502
|
+
*
|
|
6503
|
+
* @param traceFunctionKey - Groups traces under this key in Bitfab
|
|
6504
|
+
* @param options - Controls which tools are marked for replay mocking
|
|
6505
|
+
* @experimental This API may change before it is stable.
|
|
6506
|
+
*/
|
|
6507
|
+
getLangGraphIntegration(traceFunctionKey, options) {
|
|
6508
|
+
const callbackHandler = new BitfabLangGraphCallbackHandler({
|
|
6509
|
+
apiKey: this.resolveApiKey(),
|
|
6510
|
+
traceFunctionKey,
|
|
6511
|
+
serviceUrl: this.serviceUrl,
|
|
6512
|
+
getActiveSpanContext: () => {
|
|
6513
|
+
const stack = getSpanStack();
|
|
6514
|
+
return stack[stack.length - 1] ?? null;
|
|
6515
|
+
},
|
|
6516
|
+
captureTools: false,
|
|
6517
|
+
_httpClient: this.httpClient
|
|
6518
|
+
});
|
|
6519
|
+
return new BitfabLangGraphIntegration({
|
|
6520
|
+
client: this,
|
|
6521
|
+
traceFunctionKey,
|
|
6522
|
+
callbackHandler,
|
|
6523
|
+
options
|
|
6524
|
+
});
|
|
6525
|
+
}
|
|
6232
6526
|
/**
|
|
6233
6527
|
* Get a Claude Agent SDK handler for tracing.
|
|
6234
6528
|
*
|
|
@@ -7259,6 +7553,14 @@ var BitfabFunction = class {
|
|
|
7259
7553
|
getLangChainCallbackHandler() {
|
|
7260
7554
|
return this.client.getLangChainCallbackHandler(this.traceFunctionKey);
|
|
7261
7555
|
}
|
|
7556
|
+
/**
|
|
7557
|
+
* Get the first-class LangGraph tool replay integration bound to this key.
|
|
7558
|
+
*
|
|
7559
|
+
* @experimental This API may change before it is stable.
|
|
7560
|
+
*/
|
|
7561
|
+
getLangGraphIntegration(options) {
|
|
7562
|
+
return this.client.getLangGraphIntegration(this.traceFunctionKey, options);
|
|
7563
|
+
}
|
|
7262
7564
|
/**
|
|
7263
7565
|
* Wrap a BAML client method to automatically capture prompt and LLM metadata.
|
|
7264
7566
|
* Delegates to the parent client's wrapBAML method.
|
|
@@ -7354,6 +7656,7 @@ function defineReplayRegistry(registry) {
|
|
|
7354
7656
|
BitfabFunction,
|
|
7355
7657
|
BitfabLangChainCallbackHandler,
|
|
7356
7658
|
BitfabLangGraphCallbackHandler,
|
|
7659
|
+
BitfabLangGraphIntegration,
|
|
7357
7660
|
BitfabOpenAIAgentHandler,
|
|
7358
7661
|
BitfabOpenAITracingProcessor,
|
|
7359
7662
|
BitfabVercelAiHandler,
|