langsmith 0.7.0 → 0.7.2

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.
Files changed (39) hide show
  1. package/dist/client.cjs +1 -1
  2. package/dist/client.js +1 -1
  3. package/dist/experimental/vercel/index.cjs +18 -637
  4. package/dist/experimental/vercel/index.d.ts +3 -257
  5. package/dist/experimental/vercel/index.js +2 -635
  6. package/dist/experimental/vercel/middleware.cjs +2 -78
  7. package/dist/experimental/vercel/middleware.js +1 -77
  8. package/dist/experimental/vercel/telemetry.cjs +462 -0
  9. package/dist/experimental/vercel/telemetry.d.ts +89 -0
  10. package/dist/experimental/vercel/telemetry.js +459 -0
  11. package/dist/experimental/vercel/utils.cjs +142 -35
  12. package/dist/experimental/vercel/utils.d.ts +28 -3
  13. package/dist/experimental/vercel/utils.js +140 -34
  14. package/dist/experimental/vercel/wrap.cjs +639 -0
  15. package/dist/experimental/vercel/wrap.d.ts +257 -0
  16. package/dist/experimental/vercel/wrap.js +635 -0
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/sandbox/client.cjs +21 -0
  21. package/dist/sandbox/client.d.ts +1 -0
  22. package/dist/sandbox/client.js +21 -0
  23. package/dist/sandbox/sandbox.cjs +12 -1
  24. package/dist/sandbox/sandbox.js +12 -1
  25. package/dist/sandbox/types.d.ts +12 -0
  26. package/dist/sandbox/ws_execute.cjs +11 -7
  27. package/dist/sandbox/ws_execute.d.ts +2 -1
  28. package/dist/sandbox/ws_execute.js +11 -7
  29. package/dist/utils/types.cjs +13 -0
  30. package/dist/utils/types.d.ts +2 -0
  31. package/dist/utils/types.js +8 -0
  32. package/dist/utils/vercel.cjs +68 -10
  33. package/dist/utils/vercel.d.ts +17 -2
  34. package/dist/utils/vercel.js +68 -10
  35. package/experimental/sandbox.cjs +1 -0
  36. package/experimental/sandbox.d.cts +1 -0
  37. package/experimental/sandbox.d.ts +1 -0
  38. package/experimental/sandbox.js +1 -0
  39. package/package.json +22 -7
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LangSmithMiddleware = LangSmithMiddleware;
4
4
  const traceable_js_1 = require("../../traceable.cjs");
5
- const vercel_js_1 = require("../../utils/vercel.cjs");
6
5
  const utils_js_1 = require("./utils.cjs");
7
6
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
7
  const _formatTracedInputs = (params) => {
@@ -34,81 +33,6 @@ const _formatTracedOutputs = (outputs, includeHttpDetails = false) => {
34
33
  }
35
34
  return (0, utils_js_1.convertMessageToTracedFormat)(formattedOutputs);
36
35
  };
37
- const setUsageMetadataOnRunTree = (result, runTree) => {
38
- if (result.usage == null || typeof result.usage !== "object") {
39
- return;
40
- }
41
- const usage = result.usage;
42
- let inputTokens;
43
- let outputTokens;
44
- let totalTokens;
45
- // AI SDK 6: Check for object-based token structures first
46
- if (typeof usage.inputTokens === "object" &&
47
- usage.inputTokens?.total != null) {
48
- // AI SDK 6 detected
49
- inputTokens = usage.inputTokens.total;
50
- if (typeof usage.outputTokens === "object" &&
51
- usage.outputTokens?.total != null) {
52
- outputTokens = usage.outputTokens.total;
53
- }
54
- totalTokens = result.usage?.totalTokens;
55
- if (typeof totalTokens !== "number" &&
56
- typeof inputTokens === "number" &&
57
- typeof outputTokens === "number") {
58
- totalTokens = inputTokens + outputTokens;
59
- }
60
- }
61
- else if (typeof usage.inputTokens === "number") {
62
- // AI SDK 5 detected
63
- inputTokens = usage.inputTokens;
64
- if (typeof usage.outputTokens === "number") {
65
- outputTokens = usage.outputTokens;
66
- }
67
- totalTokens = result.usage?.totalTokens;
68
- if (typeof totalTokens !== "number" &&
69
- typeof inputTokens === "number" &&
70
- typeof outputTokens === "number") {
71
- totalTokens = inputTokens + outputTokens;
72
- }
73
- }
74
- else {
75
- // AI SDK 4 fallback
76
- if (typeof usage.promptTokens === "number") {
77
- inputTokens = usage.promptTokens;
78
- }
79
- if (typeof usage.completionTokens === "number") {
80
- outputTokens = usage.completionTokens;
81
- }
82
- totalTokens = result.usage?.totalTokens;
83
- if (typeof totalTokens !== "number" &&
84
- typeof inputTokens === "number" &&
85
- typeof outputTokens === "number") {
86
- totalTokens = inputTokens + outputTokens;
87
- }
88
- }
89
- const langsmithUsage = {
90
- input_tokens: inputTokens,
91
- output_tokens: outputTokens,
92
- total_tokens: totalTokens,
93
- };
94
- const inputTokenDetails = (0, vercel_js_1.extractInputTokenDetails)(result.usage, result.providerMetadata);
95
- const outputTokenDetails = (0, vercel_js_1.extractOutputTokenDetails)(result.usage, result.providerMetadata);
96
- runTree.extra = {
97
- ...runTree.extra,
98
- metadata: {
99
- ...runTree.extra?.metadata,
100
- usage_metadata: {
101
- ...langsmithUsage,
102
- input_token_details: {
103
- ...inputTokenDetails,
104
- },
105
- output_token_details: {
106
- ...outputTokenDetails,
107
- },
108
- },
109
- },
110
- };
111
- };
112
36
  /**
113
37
  * AI SDK middleware that wraps an AI SDK 6 or 5 model and adds LangSmith tracing.
114
38
  */
@@ -120,7 +44,7 @@ function LangSmithMiddleware(config) {
120
44
  const result = await doGenerate();
121
45
  const currentRunTree = (0, traceable_js_1.getCurrentRunTree)(true);
122
46
  if (currentRunTree !== undefined) {
123
- setUsageMetadataOnRunTree(result, currentRunTree);
47
+ (0, utils_js_1.setUsageMetadataOnRunTree)(result, currentRunTree);
124
48
  }
125
49
  return result;
126
50
  }, {
@@ -250,7 +174,7 @@ function LangSmithMiddleware(config) {
250
174
  }
251
175
  else if (chunk.type === "finish") {
252
176
  if (runTree != null) {
253
- setUsageMetadataOnRunTree(chunk, runTree);
177
+ (0, utils_js_1.setUsageMetadataOnRunTree)(chunk, runTree);
254
178
  }
255
179
  return {
256
180
  ...aggregated,
@@ -1,6 +1,5 @@
1
1
  import { getCurrentRunTree, traceable } from "../../traceable.js";
2
- import { extractInputTokenDetails, extractOutputTokenDetails, } from "../../utils/vercel.js";
3
- import { convertMessageToTracedFormat } from "./utils.js";
2
+ import { convertMessageToTracedFormat, setUsageMetadataOnRunTree, } from "./utils.js";
4
3
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
4
  const _formatTracedInputs = (params) => {
6
5
  const { prompt, ...rest } = params;
@@ -31,81 +30,6 @@ const _formatTracedOutputs = (outputs, includeHttpDetails = false) => {
31
30
  }
32
31
  return convertMessageToTracedFormat(formattedOutputs);
33
32
  };
34
- const setUsageMetadataOnRunTree = (result, runTree) => {
35
- if (result.usage == null || typeof result.usage !== "object") {
36
- return;
37
- }
38
- const usage = result.usage;
39
- let inputTokens;
40
- let outputTokens;
41
- let totalTokens;
42
- // AI SDK 6: Check for object-based token structures first
43
- if (typeof usage.inputTokens === "object" &&
44
- usage.inputTokens?.total != null) {
45
- // AI SDK 6 detected
46
- inputTokens = usage.inputTokens.total;
47
- if (typeof usage.outputTokens === "object" &&
48
- usage.outputTokens?.total != null) {
49
- outputTokens = usage.outputTokens.total;
50
- }
51
- totalTokens = result.usage?.totalTokens;
52
- if (typeof totalTokens !== "number" &&
53
- typeof inputTokens === "number" &&
54
- typeof outputTokens === "number") {
55
- totalTokens = inputTokens + outputTokens;
56
- }
57
- }
58
- else if (typeof usage.inputTokens === "number") {
59
- // AI SDK 5 detected
60
- inputTokens = usage.inputTokens;
61
- if (typeof usage.outputTokens === "number") {
62
- outputTokens = usage.outputTokens;
63
- }
64
- totalTokens = result.usage?.totalTokens;
65
- if (typeof totalTokens !== "number" &&
66
- typeof inputTokens === "number" &&
67
- typeof outputTokens === "number") {
68
- totalTokens = inputTokens + outputTokens;
69
- }
70
- }
71
- else {
72
- // AI SDK 4 fallback
73
- if (typeof usage.promptTokens === "number") {
74
- inputTokens = usage.promptTokens;
75
- }
76
- if (typeof usage.completionTokens === "number") {
77
- outputTokens = usage.completionTokens;
78
- }
79
- totalTokens = result.usage?.totalTokens;
80
- if (typeof totalTokens !== "number" &&
81
- typeof inputTokens === "number" &&
82
- typeof outputTokens === "number") {
83
- totalTokens = inputTokens + outputTokens;
84
- }
85
- }
86
- const langsmithUsage = {
87
- input_tokens: inputTokens,
88
- output_tokens: outputTokens,
89
- total_tokens: totalTokens,
90
- };
91
- const inputTokenDetails = extractInputTokenDetails(result.usage, result.providerMetadata);
92
- const outputTokenDetails = extractOutputTokenDetails(result.usage, result.providerMetadata);
93
- runTree.extra = {
94
- ...runTree.extra,
95
- metadata: {
96
- ...runTree.extra?.metadata,
97
- usage_metadata: {
98
- ...langsmithUsage,
99
- input_token_details: {
100
- ...inputTokenDetails,
101
- },
102
- output_token_details: {
103
- ...outputTokenDetails,
104
- },
105
- },
106
- },
107
- };
108
- };
109
33
  /**
110
34
  * AI SDK middleware that wraps an AI SDK 6 or 5 model and adds LangSmith tracing.
111
35
  */
@@ -0,0 +1,462 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LangSmithTelemetry = LangSmithTelemetry;
4
+ const run_trees_js_1 = require("../../run_trees.cjs");
5
+ const traceable_js_1 = require("../../singletons/traceable.cjs");
6
+ const env_js_1 = require("../../env.cjs");
7
+ const utils_js_1 = require("./utils.cjs");
8
+ const utils_js_2 = require("./utils.cjs");
9
+ const types_js_1 = require("../../utils/types.cjs");
10
+ function _formatMessages(messages) {
11
+ if (!Array.isArray(messages))
12
+ return messages;
13
+ return messages.map((msg) => (0, utils_js_1.convertMessageToTracedFormat)(msg));
14
+ }
15
+ // oxlint-disable-next-line typescript/no-explicit-any
16
+ function _formatToolCalls(toolCalls) {
17
+ return toolCalls.map((tc) => ({
18
+ id: tc.toolCallId,
19
+ type: "function",
20
+ function: {
21
+ name: tc.toolName,
22
+ arguments: (0, types_js_1.isPrimitive)(tc.input)
23
+ ? String(tc.input)
24
+ : JSON.stringify(tc.input),
25
+ },
26
+ }));
27
+ }
28
+ function _formatStepOutput(
29
+ // oxlint-disable-next-line typescript/no-explicit-any
30
+ event, traceRawHttp) {
31
+ // Build an assistant-style message from the step result
32
+ const output = { role: "assistant" };
33
+ // Text content
34
+ if (event.content != null) {
35
+ output.content = event.content;
36
+ }
37
+ else if (event.text != null) {
38
+ output.content = event.text;
39
+ }
40
+ // Tool calls
41
+ if (Array.isArray(event.toolCalls) && event.toolCalls.length > 0) {
42
+ output.tool_calls = _formatToolCalls(event.toolCalls);
43
+ }
44
+ if (event.finishReason != null) {
45
+ output.finish_reason = event.finishReason;
46
+ }
47
+ if (traceRawHttp) {
48
+ if (event.request != null)
49
+ output.request = event.request;
50
+ if (event.response != null)
51
+ output.response = event.response;
52
+ }
53
+ return (0, utils_js_1.convertMessageToTracedFormat)(output);
54
+ }
55
+ function _getLsAgentType(parentRunTree) {
56
+ if ((0, run_trees_js_1.isRunTree)(parentRunTree) && parentRunTree.run_type === "tool") {
57
+ return "subagent";
58
+ }
59
+ return "root";
60
+ }
61
+ /**
62
+ * Creates a LangSmith `Telemetry` for the Vercel AI SDK.
63
+ *
64
+ * This adapter implements the Vercel AI SDK's `Telemetry` interface
65
+ * and maps lifecycle events to LangSmith traces. It creates a root span for
66
+ * the entire generation, child LLM spans for each step, and tool spans for
67
+ * tool calls.
68
+ *
69
+ * ```ts
70
+ * import { generateText, registerTelemetry } from "ai";
71
+ * import { LangSmithTelemetry } from "langsmith/experimental/vercel";
72
+ *
73
+ * registerTelemetry(LangSmithTelemetry());
74
+ *
75
+ * const result = await generateText({
76
+ * model: openai("gpt-4o"),
77
+ * prompt: "Hello!",
78
+ * });
79
+ * ```
80
+ *
81
+ * @experimental Only available in Vercel AI SDK 7.
82
+ */
83
+ function LangSmithTelemetry(config) {
84
+ const { name: customName, runType = "chain", metadata: customMetadata, tags: customTags, client, projectName, processInputs, processOutputs, processChildLLMRunInputs, processChildLLMRunOutputs, traceResponseMetadata, traceRawHttp, tracingEnabled, extra: customExtra, } = config ?? {};
85
+ function getOpenStepOrRoot(state) {
86
+ let openStep;
87
+ state.stepRunTrees.forEach((stepRt) => {
88
+ if (stepRt.end_time == null) {
89
+ openStep = stepRt;
90
+ }
91
+ });
92
+ return openStep ?? state.rootRunTree;
93
+ }
94
+ async function finalizeOpenToolRuns(state, opts) {
95
+ const entries = Array.from(state.toolRunTrees.entries());
96
+ for (let i = 0; i < entries.length; i++) {
97
+ const [, toolRt] = entries[i];
98
+ if (toolRt.end_time == null) {
99
+ if (opts?.error != null) {
100
+ await toolRt.end(undefined, opts.error);
101
+ }
102
+ else {
103
+ await toolRt.end(opts?.note != null ? { note: opts.note } : undefined);
104
+ }
105
+ await toolRt.patchRun({ excludeInputs: true });
106
+ }
107
+ }
108
+ state.toolRunTrees.clear();
109
+ }
110
+ /** Per-generation state keyed by AI SDK `callId` (stable across nested calls). */
111
+ const invocationsByCallId = new Map();
112
+ const onStart = async (event) => {
113
+ if (!(0, env_js_1.isTracingEnabled)(tracingEnabled))
114
+ return;
115
+ if (!("callId" in event) || typeof event.callId !== "string")
116
+ return;
117
+ // If called within an existing traceable context, nest under it
118
+ const parentRunTree = (0, traceable_js_1.getCurrentRunTree)(true);
119
+ let inputs = {};
120
+ if (event.recordInputs !== false) {
121
+ if ("messages" in event && event.messages != null) {
122
+ inputs.messages = _formatMessages(event.messages);
123
+ }
124
+ if ("prompt" in event && event.prompt != null) {
125
+ inputs.prompt = event.prompt;
126
+ }
127
+ if ("instructions" in event && event.instructions != null) {
128
+ inputs.instructions = event.instructions;
129
+ }
130
+ if ("system" in event && event.system != null) {
131
+ inputs.system = event.system;
132
+ }
133
+ if ("tools" in event && event.tools != null) {
134
+ inputs.tools = Object.keys(event.tools);
135
+ }
136
+ if ("runtimeContext" in event && event.runtimeContext != null) {
137
+ inputs.runtimeContext = event.runtimeContext;
138
+ }
139
+ if ("toolsContext" in event && event.toolsContext != null) {
140
+ inputs.toolsContext = event.toolsContext;
141
+ }
142
+ // Apply user-provided input processing
143
+ if (processInputs) {
144
+ try {
145
+ inputs = processInputs(inputs);
146
+ }
147
+ catch (e) {
148
+ console.error("Error in processInputs, using raw inputs:", e);
149
+ }
150
+ }
151
+ }
152
+ const runTreeConfig = {
153
+ name: customName ?? event.functionId ?? event.provider,
154
+ run_type: runType,
155
+ inputs,
156
+ tracingEnabled: true,
157
+ extra: {
158
+ ...customExtra,
159
+ metadata: {
160
+ ...customMetadata,
161
+ ai_sdk_method: event.operationId,
162
+ ls_agent_type: _getLsAgentType(parentRunTree),
163
+ ls_model_name: event.modelId,
164
+ ls_provider: event.provider,
165
+ ls_integration: "vercel-ai-sdk-telemetry",
166
+ },
167
+ },
168
+ tags: customTags,
169
+ ...(client ? { client } : {}),
170
+ ...(projectName ? { project_name: projectName } : {}),
171
+ };
172
+ let rootRunTree;
173
+ if ((0, run_trees_js_1.isRunTree)(parentRunTree)) {
174
+ rootRunTree = parentRunTree.createChild(runTreeConfig);
175
+ }
176
+ else {
177
+ rootRunTree = new run_trees_js_1.RunTree(runTreeConfig);
178
+ }
179
+ await rootRunTree.postRun();
180
+ invocationsByCallId.set(event.callId, {
181
+ rootRunTree,
182
+ stepRunTrees: new Map(),
183
+ toolRunTrees: new Map(),
184
+ });
185
+ };
186
+ const onStepStart = async (event) => {
187
+ const state = invocationsByCallId.get(event.callId);
188
+ if (!state)
189
+ return;
190
+ const stepNumber = event.stepNumber ?? 0;
191
+ let inputs = {};
192
+ if (event.recordInputs !== false) {
193
+ if ("messages" in event && event.messages != null) {
194
+ inputs.messages = _formatMessages(event.messages);
195
+ }
196
+ if ("runtimeContext" in event && event.runtimeContext != null) {
197
+ inputs.runtimeContext = event.runtimeContext;
198
+ }
199
+ if ("toolsContext" in event && event.toolsContext != null) {
200
+ inputs.toolsContext = event.toolsContext;
201
+ }
202
+ if (processChildLLMRunInputs) {
203
+ try {
204
+ inputs = processChildLLMRunInputs(inputs);
205
+ }
206
+ catch (e) {
207
+ console.error("Error in processChildLLMRunInputs, using raw inputs:", e);
208
+ }
209
+ }
210
+ }
211
+ const stepRunTree = state.rootRunTree.createChild({
212
+ name: event.provider,
213
+ run_type: "llm",
214
+ inputs,
215
+ extra: { metadata: { step_number: stepNumber } },
216
+ });
217
+ state.stepRunTrees.set(stepNumber, stepRunTree);
218
+ await stepRunTree.postRun();
219
+ };
220
+ const onLanguageModelCallStart = async (event) => {
221
+ const state = invocationsByCallId.get(event.callId);
222
+ if (!state)
223
+ return;
224
+ const stepRunTree = getOpenStepOrRoot(state);
225
+ if (stepRunTree.run_type !== "llm")
226
+ return;
227
+ const prevParams = (0, types_js_1.isRecord)(stepRunTree.extra?.invocation_params)
228
+ ? stepRunTree.extra.invocation_params
229
+ : {};
230
+ const nextParams = { ...event };
231
+ // Remove properties that are already in the step run tree
232
+ delete nextParams.messages;
233
+ delete nextParams.provider;
234
+ delete nextParams.modelId;
235
+ // Remove telemetry options (except functionId)
236
+ delete nextParams.recordInputs;
237
+ delete nextParams.recordOutputs;
238
+ delete nextParams.includeToolsContext;
239
+ // Massage tools for LangSmith to render schema nicely
240
+ nextParams.tools = nextParams.tools?.map((tool) => {
241
+ const newTool = { ...tool };
242
+ if ("inputSchema" in newTool) {
243
+ newTool.input_schema = newTool.inputSchema;
244
+ delete newTool.inputSchema;
245
+ }
246
+ return newTool;
247
+ });
248
+ stepRunTree.extra = {
249
+ ...stepRunTree.extra,
250
+ invocation_params: { ...prevParams, ...nextParams },
251
+ };
252
+ };
253
+ const onToolExecutionStart = async (event) => {
254
+ const state = invocationsByCallId.get(event.callId);
255
+ if (!state)
256
+ return;
257
+ const parentRunTree = getOpenStepOrRoot(state);
258
+ let inputs = {};
259
+ if (event.recordInputs !== false) {
260
+ if ((0, types_js_1.isRecord)(event.toolCall.input)) {
261
+ inputs = { ...event.toolCall.input };
262
+ }
263
+ else if (typeof event.toolCall.input !== "undefined") {
264
+ inputs = { input: event.toolCall.input };
265
+ }
266
+ if ("toolContext" in event && event.toolContext != null) {
267
+ inputs.toolContext = event.toolContext;
268
+ }
269
+ }
270
+ else {
271
+ inputs = {};
272
+ }
273
+ const toolRunTree = parentRunTree.createChild({
274
+ name: event.toolCall.toolName,
275
+ run_type: "tool",
276
+ inputs,
277
+ extra: {
278
+ metadata: {
279
+ tool_call_id: event.toolCall.toolCallId,
280
+ ai_sdk_call_id: event.callId,
281
+ },
282
+ },
283
+ });
284
+ await toolRunTree.postRun();
285
+ state.toolRunTrees.set(event.toolCall.toolCallId, toolRunTree);
286
+ };
287
+ const onToolExecutionEnd = async (event) => {
288
+ const state = invocationsByCallId.get(event.callId);
289
+ if (!state)
290
+ return;
291
+ const toolRunTree = state.toolRunTrees.get(event.toolCall.toolCallId);
292
+ if (!toolRunTree)
293
+ return;
294
+ state.toolRunTrees.delete(event.toolCall.toolCallId);
295
+ let outputs;
296
+ let error;
297
+ if (event.recordOutputs !== false) {
298
+ if (event.toolOutput.type === "tool-result") {
299
+ outputs = { output: event.toolOutput.output };
300
+ }
301
+ else if (event.toolOutput.type === "tool-error") {
302
+ const err = event.toolOutput.error;
303
+ error = err instanceof Error ? err.message : String(err);
304
+ }
305
+ }
306
+ else {
307
+ outputs = {};
308
+ }
309
+ await toolRunTree.end(outputs, error, Math.floor(toolRunTree.start_time + event.toolExecutionMs));
310
+ await toolRunTree.patchRun({ excludeInputs: true });
311
+ };
312
+ const onStepFinish = async (event) => {
313
+ const state = invocationsByCallId.get(event.callId);
314
+ if (!state)
315
+ return;
316
+ const stepNumber = event.stepNumber ?? 0;
317
+ const stepRunTree = state.stepRunTrees.get(stepNumber);
318
+ if (!stepRunTree)
319
+ return;
320
+ let outputs = {};
321
+ if (event.recordOutputs !== false) {
322
+ outputs = _formatStepOutput(event, traceRawHttp);
323
+ if (processChildLLMRunOutputs) {
324
+ try {
325
+ outputs = processChildLLMRunOutputs(outputs);
326
+ }
327
+ catch (e) {
328
+ console.error("Error in processChildLLMRunOutputs, using raw outputs:", e);
329
+ }
330
+ }
331
+ }
332
+ // Set usage metadata
333
+ // @ts-expect-error SharedV4ProviderMetadata is not assignable to SharedV2ProviderMetadata
334
+ (0, utils_js_2.setUsageMetadataOnRunTree)(event, stepRunTree);
335
+ await stepRunTree.end(outputs);
336
+ await stepRunTree.patchRun({ excludeInputs: true });
337
+ state.stepRunTrees.delete(stepNumber);
338
+ };
339
+ const onEnd = async (event) => {
340
+ if (!("callId" in event) || typeof event.callId !== "string")
341
+ return;
342
+ const state = invocationsByCallId.get(event.callId);
343
+ if (!state)
344
+ return;
345
+ const { rootRunTree } = state;
346
+ await finalizeOpenToolRuns(state, { note: "closed on finish" });
347
+ // Ensure any remaining step runs are closed
348
+ const remainingSteps = Array.from(state.stepRunTrees.entries());
349
+ for (let i = 0; i < remainingSteps.length; i++) {
350
+ const [stepNumber, stepRt] = remainingSteps[i];
351
+ if (stepRt.end_time == null) {
352
+ await stepRt.end({ note: "closed on finish" });
353
+ await stepRt.patchRun({ excludeInputs: true });
354
+ }
355
+ state.stepRunTrees.delete(stepNumber);
356
+ }
357
+ let outputs = {};
358
+ if (event.recordOutputs !== false) {
359
+ // Final result output
360
+ if ("text" in event && event.text != null) {
361
+ outputs.content = event.text;
362
+ }
363
+ else if ("content" in event && event.content != null) {
364
+ outputs.content = event.content;
365
+ }
366
+ if (outputs.content != null) {
367
+ outputs.role = "assistant";
368
+ }
369
+ if ("object" in event && event.object != null) {
370
+ outputs.object = event.object;
371
+ }
372
+ if ("toolCalls" in event &&
373
+ Array.isArray(event.toolCalls) &&
374
+ event.toolCalls.length > 0) {
375
+ outputs.tool_calls = _formatToolCalls(event.toolCalls);
376
+ }
377
+ if ("finishReason" in event && event.finishReason != null) {
378
+ outputs.finish_reason = event.finishReason;
379
+ }
380
+ if (traceResponseMetadata &&
381
+ "steps" in event &&
382
+ Array.isArray(event.steps)) {
383
+ outputs.steps = event.steps.map((step, idx) => ({
384
+ step_number: idx,
385
+ ..._formatStepOutput(step, traceRawHttp),
386
+ }));
387
+ }
388
+ if (processOutputs) {
389
+ try {
390
+ outputs = processOutputs(outputs);
391
+ }
392
+ catch (e) {
393
+ console.error("Error in processOutputs, using raw outputs:", e);
394
+ }
395
+ }
396
+ }
397
+ // Set aggregated usage on root
398
+ if ("totalUsage" in event && event.totalUsage != null) {
399
+ (0, utils_js_2.setUsageMetadataOnRunTree)(
400
+ // @ts-expect-error SharedV4ProviderMetadata is not assignable to SharedV2ProviderMetadata
401
+ { usage: event.totalUsage, providerMetadata: event.providerMetadata }, rootRunTree);
402
+ }
403
+ else if ("usage" in event && event.usage != null) {
404
+ // @ts-expect-error SharedV4ProviderMetadata is not assignable to SharedV2ProviderMetadata
405
+ (0, utils_js_2.setUsageMetadataOnRunTree)(event, rootRunTree);
406
+ }
407
+ await rootRunTree.end(outputs);
408
+ await rootRunTree.patchRun({ excludeInputs: true });
409
+ invocationsByCallId.delete(event.callId);
410
+ };
411
+ const onError = async (payload) => {
412
+ const callId = typeof payload === "object" &&
413
+ payload !== null &&
414
+ "callId" in payload &&
415
+ typeof payload.callId === "string"
416
+ ? payload.callId
417
+ : undefined;
418
+ const error = typeof payload === "object" && payload !== null && "error" in payload
419
+ ? payload.error
420
+ : payload;
421
+ if (callId === undefined)
422
+ return;
423
+ const state = invocationsByCallId.get(callId);
424
+ if (!state)
425
+ return;
426
+ const { rootRunTree } = state;
427
+ const errorMsg = error instanceof Error ? error.message : String(error);
428
+ await finalizeOpenToolRuns(state, { error: errorMsg });
429
+ // Close any open step runs with error
430
+ const errorSteps = Array.from(state.stepRunTrees.entries());
431
+ for (let i = 0; i < errorSteps.length; i++) {
432
+ const [stepNumber, stepRt] = errorSteps[i];
433
+ if (stepRt.end_time == null) {
434
+ await stepRt.end(undefined, errorMsg);
435
+ await stepRt.patchRun({ excludeInputs: true });
436
+ }
437
+ state.stepRunTrees.delete(stepNumber);
438
+ }
439
+ await rootRunTree.end(undefined, errorMsg);
440
+ await rootRunTree.patchRun({ excludeInputs: true });
441
+ invocationsByCallId.delete(callId);
442
+ };
443
+ const executeTool = async (params) => {
444
+ const state = invocationsByCallId.get(params.callId);
445
+ const toolRunTree = state?.toolRunTrees.get(params.toolCallId);
446
+ if (toolRunTree != null) {
447
+ return (0, traceable_js_1.withRunTree)(toolRunTree, () => params.execute());
448
+ }
449
+ return params.execute();
450
+ };
451
+ return {
452
+ onStart,
453
+ onStepStart,
454
+ onLanguageModelCallStart,
455
+ onToolExecutionStart,
456
+ onToolExecutionEnd,
457
+ onStepFinish,
458
+ onEnd,
459
+ onError,
460
+ executeTool,
461
+ };
462
+ }