@uselemma/tracing 7.6.0 → 7.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -23
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +16 -2
- package/dist/client.js.map +1 -1
- package/dist/langchain.d.ts +52 -13
- package/dist/langchain.d.ts.map +1 -1
- package/dist/langchain.js +1010 -129
- package/dist/langchain.js.map +1 -1
- package/dist/mastra.d.ts.map +1 -1
- package/dist/mastra.js +14 -1
- package/dist/mastra.js.map +1 -1
- package/dist/openai-agents.d.ts +4 -0
- package/dist/openai-agents.d.ts.map +1 -1
- package/dist/openai-agents.js +235 -26
- package/dist/openai-agents.js.map +1 -1
- package/dist/vercel-ai.d.ts +14 -0
- package/dist/vercel-ai.d.ts.map +1 -1
- package/dist/vercel-ai.js +453 -111
- package/dist/vercel-ai.js.map +1 -1
- package/examples/langchain.ts +21 -4
- package/examples/langgraph.ts +18 -2
- package/examples/openai-agents.ts +11 -2
- package/examples/vercel-ai-v6.ts +28 -13
- package/examples/vercel-ai-v7.ts +28 -13
- package/package.json +1 -1
package/dist/vercel-ai.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.vercelAI = vercelAI;
|
|
4
4
|
const client_1 = require("./client");
|
|
5
5
|
const tool_result_1 = require("./tool-result");
|
|
6
|
+
const CONCURRENT_REUSE_ERROR = "vercelAI() is already tracing a run. Create a new vercelAI() integration per concurrent AI SDK operation.";
|
|
6
7
|
function addMs(startedAt, durationMs) {
|
|
7
8
|
return typeof durationMs === "number"
|
|
8
9
|
? new Date(startedAt.getTime() + durationMs)
|
|
@@ -33,6 +34,18 @@ function stringifyContent(content) {
|
|
|
33
34
|
return text;
|
|
34
35
|
return JSON.stringify(content);
|
|
35
36
|
}
|
|
37
|
+
function structuredAssistantOutput(text, content) {
|
|
38
|
+
if (typeof text === "string")
|
|
39
|
+
return text;
|
|
40
|
+
if (!content)
|
|
41
|
+
return undefined;
|
|
42
|
+
const hasNonText = content.some((part) => part &&
|
|
43
|
+
typeof part === "object" &&
|
|
44
|
+
part.type !== "text");
|
|
45
|
+
if (hasNonText)
|
|
46
|
+
return content;
|
|
47
|
+
return stringifyContent(content);
|
|
48
|
+
}
|
|
36
49
|
function errorToolFields(error, recordOutputs) {
|
|
37
50
|
return recordOutputs
|
|
38
51
|
? { error, status: "ERROR" }
|
|
@@ -70,25 +83,89 @@ function resolveDurationMs(startedAt, endedAt, reportedMs) {
|
|
|
70
83
|
// step cannot outlast a too-short reported model-only duration.
|
|
71
84
|
return Math.max(reportedMs, wallMs);
|
|
72
85
|
}
|
|
73
|
-
function
|
|
74
|
-
if (
|
|
86
|
+
function messageContent(message) {
|
|
87
|
+
if (!message || typeof message !== "object")
|
|
88
|
+
return message;
|
|
89
|
+
const record = message;
|
|
90
|
+
if ("content" in record)
|
|
91
|
+
return record.content;
|
|
92
|
+
return message;
|
|
93
|
+
}
|
|
94
|
+
function lookupString(sources, keys) {
|
|
95
|
+
for (const source of sources) {
|
|
96
|
+
if (!source)
|
|
97
|
+
continue;
|
|
98
|
+
for (const key of keys) {
|
|
99
|
+
const value = source[key];
|
|
100
|
+
if (typeof value === "string" && value)
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
/** Normalize v6 system/prompt/messages into a chat message list when possible. */
|
|
107
|
+
function v6NormalizedMessages(event) {
|
|
108
|
+
if (Array.isArray(event.messages))
|
|
75
109
|
return event.messages;
|
|
76
|
-
|
|
110
|
+
const messages = [];
|
|
111
|
+
if ("system" in event && typeof event.system === "string" && event.system) {
|
|
112
|
+
messages.push({ role: "system", content: event.system });
|
|
113
|
+
}
|
|
114
|
+
if ("prompt" in event && event.prompt !== undefined) {
|
|
115
|
+
if (Array.isArray(event.prompt)) {
|
|
116
|
+
return messages.length > 0
|
|
117
|
+
? [...messages, ...event.prompt]
|
|
118
|
+
: event.prompt;
|
|
119
|
+
}
|
|
120
|
+
if (typeof event.prompt === "string") {
|
|
121
|
+
messages.push({ role: "user", content: event.prompt });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return messages.length > 0 ? messages : undefined;
|
|
125
|
+
}
|
|
126
|
+
function v6Input(event) {
|
|
127
|
+
const normalized = v6NormalizedMessages(event);
|
|
128
|
+
if (normalized)
|
|
129
|
+
return normalized;
|
|
130
|
+
if ("prompt" in event)
|
|
131
|
+
return event.prompt;
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Prefer the current user turn for the Lemma root input.
|
|
136
|
+
* Accepts a bare string, message array, or prompt/system start payload.
|
|
137
|
+
*/
|
|
138
|
+
function rootTraceInput(input) {
|
|
139
|
+
if (typeof input === "string")
|
|
140
|
+
return input;
|
|
141
|
+
if (!Array.isArray(input) || input.length === 0)
|
|
142
|
+
return input;
|
|
143
|
+
for (let i = input.length - 1; i >= 0; i--) {
|
|
144
|
+
const message = input[i];
|
|
145
|
+
if (!message || typeof message !== "object")
|
|
146
|
+
continue;
|
|
147
|
+
const role = message.role;
|
|
148
|
+
if (role === "user")
|
|
149
|
+
return messageContent(message);
|
|
150
|
+
}
|
|
151
|
+
return messageContent(input[input.length - 1]);
|
|
77
152
|
}
|
|
78
|
-
function
|
|
153
|
+
function eventTraceInput(event) {
|
|
79
154
|
if (!event)
|
|
80
155
|
return undefined;
|
|
156
|
+
if ("messages" in event || "prompt" in event || "system" in event) {
|
|
157
|
+
if (!("callId" in event) || "model" in event) {
|
|
158
|
+
return v6Input(event);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
81
161
|
if ("messages" in event && event.messages)
|
|
82
162
|
return event.messages;
|
|
83
163
|
if ("prompt" in event)
|
|
84
164
|
return event.prompt;
|
|
85
165
|
return undefined;
|
|
86
166
|
}
|
|
87
|
-
function
|
|
88
|
-
return
|
|
89
|
-
...options.metadata,
|
|
90
|
-
...(event && "metadata" in event ? (event.metadata ?? {}) : {}),
|
|
91
|
-
};
|
|
167
|
+
function eventMetadata(event) {
|
|
168
|
+
return event && "metadata" in event ? (event.metadata ?? undefined) : undefined;
|
|
92
169
|
}
|
|
93
170
|
function traceName(options, event) {
|
|
94
171
|
if (options.agentName)
|
|
@@ -98,31 +175,61 @@ function traceName(options, event) {
|
|
|
98
175
|
: undefined;
|
|
99
176
|
return functionId || "vercel-ai-agent";
|
|
100
177
|
}
|
|
101
|
-
function v6InputMessages(event) {
|
|
102
|
-
const input = v6Input(event);
|
|
103
|
-
return Array.isArray(input) ? input : undefined;
|
|
104
|
-
}
|
|
105
178
|
function v6Output(event) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
179
|
+
return structuredAssistantOutput(event.text, event.content);
|
|
180
|
+
}
|
|
181
|
+
function endOutput(event) {
|
|
182
|
+
return structuredAssistantOutput(event.text, event.content);
|
|
183
|
+
}
|
|
184
|
+
function errorMessage(error) {
|
|
185
|
+
if (typeof error === "string" && error)
|
|
186
|
+
return error;
|
|
187
|
+
if (error instanceof Error && error.message)
|
|
188
|
+
return error.message;
|
|
189
|
+
try {
|
|
190
|
+
return JSON.stringify(error);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return String(error);
|
|
194
|
+
}
|
|
111
195
|
}
|
|
112
196
|
function vercelAI(options = {}) {
|
|
113
197
|
let lemma = options.lemma;
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
198
|
+
const pending = new Set();
|
|
199
|
+
let phase = "idle";
|
|
200
|
+
let modelCalls = new Map();
|
|
201
|
+
let v7Steps = new Map();
|
|
202
|
+
let v6Steps = new Map();
|
|
203
|
+
let v6Starts = [];
|
|
204
|
+
let generationSpanIdsByCallId = new Map();
|
|
205
|
+
let generationSpanIdsByToolCallId = new Map();
|
|
206
|
+
let generationHandlesById = new Map();
|
|
207
|
+
let toolExecutions = new Map();
|
|
208
|
+
let latestV6GenerationId;
|
|
122
209
|
let managedTrace;
|
|
123
210
|
let recordedV6Step = false;
|
|
124
|
-
let
|
|
125
|
-
let
|
|
211
|
+
let sawV7StepZero = false;
|
|
212
|
+
let runStartedAt;
|
|
213
|
+
let runError;
|
|
214
|
+
let endingTrace;
|
|
215
|
+
function resetRunState() {
|
|
216
|
+
phase = "idle";
|
|
217
|
+
modelCalls = new Map();
|
|
218
|
+
v7Steps = new Map();
|
|
219
|
+
v6Steps = new Map();
|
|
220
|
+
v6Starts = [];
|
|
221
|
+
generationSpanIdsByCallId = new Map();
|
|
222
|
+
generationSpanIdsByToolCallId = new Map();
|
|
223
|
+
generationHandlesById = new Map();
|
|
224
|
+
toolExecutions = new Map();
|
|
225
|
+
latestV6GenerationId = undefined;
|
|
226
|
+
managedTrace = undefined;
|
|
227
|
+
recordedV6Step = false;
|
|
228
|
+
sawV7StepZero = false;
|
|
229
|
+
runStartedAt = undefined;
|
|
230
|
+
runError = undefined;
|
|
231
|
+
endingTrace = undefined;
|
|
232
|
+
}
|
|
126
233
|
function trackGeneration(handle) {
|
|
127
234
|
generationHandlesById.set(handle.id, handle);
|
|
128
235
|
}
|
|
@@ -140,55 +247,188 @@ function vercelAI(options = {}) {
|
|
|
140
247
|
});
|
|
141
248
|
return lemma;
|
|
142
249
|
}
|
|
250
|
+
function resolveThreadId(metadata) {
|
|
251
|
+
const key = options.threadIdKey ?? "threadId";
|
|
252
|
+
return lookupString([metadata, options.metadata], [key]);
|
|
253
|
+
}
|
|
254
|
+
function resolveUserId(metadata) {
|
|
255
|
+
const key = options.userIdKey ?? "userId";
|
|
256
|
+
return lookupString([metadata, options.metadata], [key]);
|
|
257
|
+
}
|
|
258
|
+
function mergedMetadata(metadata) {
|
|
259
|
+
return {
|
|
260
|
+
...options.metadata,
|
|
261
|
+
...(metadata ?? {}),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function beginActivity() {
|
|
265
|
+
if (phase === "idle") {
|
|
266
|
+
phase = "active";
|
|
267
|
+
runStartedAt = new Date();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function beginNewRun() {
|
|
271
|
+
if (phase === "active" || phase === "ending") {
|
|
272
|
+
throw new Error(CONCURRENT_REUSE_ERROR);
|
|
273
|
+
}
|
|
274
|
+
resetRunState();
|
|
275
|
+
phase = "active";
|
|
276
|
+
runStartedAt = new Date();
|
|
277
|
+
}
|
|
278
|
+
function hasExplicitEndableTrace() {
|
|
279
|
+
const trace = options.trace;
|
|
280
|
+
return Boolean(trace && typeof trace.end === "function");
|
|
281
|
+
}
|
|
282
|
+
function applyIdentity(trace, metadata) {
|
|
283
|
+
const threadId = resolveThreadId(metadata);
|
|
284
|
+
const userId = resolveUserId(metadata);
|
|
285
|
+
if (threadId)
|
|
286
|
+
trace.threadId(threadId);
|
|
287
|
+
if (userId)
|
|
288
|
+
trace.userId(userId);
|
|
289
|
+
}
|
|
290
|
+
function rootErrorPayload(message) {
|
|
291
|
+
// Preserve ERROR status while redacting message bodies when outputs are off.
|
|
292
|
+
return options.recordOutputs === false ? "error" : message;
|
|
293
|
+
}
|
|
143
294
|
function resolveTrace(event) {
|
|
144
|
-
|
|
295
|
+
// Trailing AI SDK callbacks can arrive while terminal delivery is in flight.
|
|
296
|
+
// Attach them to the still-open trace instead of throwing.
|
|
297
|
+
if (phase === "ending") {
|
|
298
|
+
const trace = options.trace ?? managedTrace;
|
|
299
|
+
return trace
|
|
300
|
+
? {
|
|
301
|
+
trace,
|
|
302
|
+
source: options.trace ? "explicit" : "managed",
|
|
303
|
+
}
|
|
304
|
+
: null;
|
|
305
|
+
}
|
|
306
|
+
beginActivity();
|
|
307
|
+
const metadata = eventMetadata(event);
|
|
308
|
+
if (options.trace) {
|
|
309
|
+
applyIdentity(options.trace, metadata);
|
|
145
310
|
return { trace: options.trace, source: "explicit" };
|
|
311
|
+
}
|
|
146
312
|
if (!managedTrace) {
|
|
313
|
+
const rawInput = options.recordInputs === false ? undefined : eventTraceInput(event);
|
|
147
314
|
managedTrace = getLemma().trace({
|
|
148
315
|
name: traceName(options, event),
|
|
149
|
-
input: options.recordInputs === false
|
|
150
|
-
|
|
316
|
+
input: options.recordInputs === false
|
|
317
|
+
? undefined
|
|
318
|
+
: rootTraceInput(rawInput),
|
|
319
|
+
metadata: mergedMetadata(metadata),
|
|
320
|
+
threadId: resolveThreadId(metadata),
|
|
321
|
+
userId: resolveUserId(metadata),
|
|
322
|
+
startedAt: runStartedAt,
|
|
151
323
|
});
|
|
152
324
|
}
|
|
325
|
+
else {
|
|
326
|
+
applyIdentity(managedTrace, metadata);
|
|
327
|
+
if (options.recordInputs !== false && event) {
|
|
328
|
+
const rawInput = eventTraceInput(event);
|
|
329
|
+
if (rawInput !== undefined) {
|
|
330
|
+
// Keep root input as the latest known current-turn value.
|
|
331
|
+
managedTrace.input(rootTraceInput(rawInput));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
153
335
|
return { trace: managedTrace, source: "managed" };
|
|
154
336
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
337
|
+
function trackPending(promise) {
|
|
338
|
+
pending.add(promise);
|
|
339
|
+
void promise.finally(() => pending.delete(promise));
|
|
340
|
+
return promise;
|
|
341
|
+
}
|
|
342
|
+
function ensureManagedTrace(event) {
|
|
343
|
+
if (options.trace || managedTrace)
|
|
344
|
+
return;
|
|
345
|
+
beginActivity();
|
|
346
|
+
managedTrace = getLemma().trace({
|
|
347
|
+
name: traceName(options, event),
|
|
348
|
+
metadata: mergedMetadata(eventMetadata(event)),
|
|
349
|
+
threadId: resolveThreadId(eventMetadata(event)),
|
|
350
|
+
userId: resolveUserId(eventMetadata(event)),
|
|
351
|
+
startedAt: runStartedAt,
|
|
352
|
+
});
|
|
161
353
|
}
|
|
162
354
|
async function endOwnedTrace(event) {
|
|
355
|
+
if (phase === "idle")
|
|
356
|
+
return;
|
|
357
|
+
if (phase === "ending")
|
|
358
|
+
return;
|
|
359
|
+
phase = "ending";
|
|
163
360
|
const trace = options.trace;
|
|
164
361
|
let ownedTrace;
|
|
362
|
+
let explicit = false;
|
|
165
363
|
if (trace && typeof trace.end === "function") {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return;
|
|
173
|
-
if (ownedTrace.explicit) {
|
|
174
|
-
if (endedExplicitTrace)
|
|
175
|
-
return;
|
|
176
|
-
endedExplicitTrace = true;
|
|
364
|
+
explicit = true;
|
|
365
|
+
ownedTrace = {
|
|
366
|
+
end: trace.end.bind(trace),
|
|
367
|
+
fail: trace.fail.bind(trace),
|
|
368
|
+
output: trace.output.bind(trace),
|
|
369
|
+
};
|
|
177
370
|
}
|
|
178
371
|
else {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
372
|
+
ensureManagedTrace(event);
|
|
373
|
+
if (managedTrace) {
|
|
374
|
+
ownedTrace = managedTrace;
|
|
375
|
+
}
|
|
182
376
|
}
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
377
|
+
const identityTrace = (options.trace ?? managedTrace);
|
|
378
|
+
if (identityTrace) {
|
|
379
|
+
applyIdentity(identityTrace, eventMetadata(event));
|
|
380
|
+
}
|
|
381
|
+
const eventError = "error" in event && event.error != null
|
|
382
|
+
? errorMessage(event.error)
|
|
383
|
+
: undefined;
|
|
384
|
+
if (eventError && !runError) {
|
|
385
|
+
runError = eventError;
|
|
187
386
|
}
|
|
188
|
-
|
|
387
|
+
const endedAt = new Date();
|
|
388
|
+
const startedAt = runStartedAt ?? endedAt;
|
|
389
|
+
const durationMs = Math.max(0, endedAt.getTime() - startedAt.getTime());
|
|
390
|
+
const successOutput = endOutput(event);
|
|
391
|
+
endingTrace = ownedTrace;
|
|
392
|
+
const endPromise = (async () => {
|
|
393
|
+
try {
|
|
394
|
+
if (!ownedTrace)
|
|
395
|
+
return;
|
|
396
|
+
// Yield so a racing fail()/trailing callback in this turn can settle.
|
|
397
|
+
await Promise.resolve();
|
|
398
|
+
const terminalError = runError;
|
|
399
|
+
// Explicit handles already own startedAt from construction; only pass
|
|
400
|
+
// durationMs for managed traces we started with runStartedAt.
|
|
401
|
+
const timing = explicit
|
|
402
|
+
? { endedAt }
|
|
403
|
+
: { durationMs, endedAt };
|
|
404
|
+
if (terminalError) {
|
|
405
|
+
ownedTrace.fail(rootErrorPayload(terminalError));
|
|
406
|
+
ownedTrace.output(undefined);
|
|
407
|
+
await ownedTrace.end(timing);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (options.recordOutputs === false || successOutput === undefined) {
|
|
411
|
+
await ownedTrace.end(timing);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
await ownedTrace.end({
|
|
415
|
+
output: successOutput,
|
|
416
|
+
...timing,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
finally {
|
|
421
|
+
// Fully reset so the same integration object can be reused sequentially.
|
|
422
|
+
resetRunState();
|
|
423
|
+
}
|
|
424
|
+
})();
|
|
425
|
+
await trackPending(endPromise);
|
|
189
426
|
}
|
|
190
427
|
function recordV6Generation(event, stored) {
|
|
191
|
-
const
|
|
428
|
+
const resolved = resolveTrace(stored?.event);
|
|
429
|
+
if (!resolved)
|
|
430
|
+
return;
|
|
431
|
+
const { trace } = resolved;
|
|
192
432
|
const startedAt = stored?.startedAt ?? new Date();
|
|
193
433
|
const endedAt = new Date();
|
|
194
434
|
const durationMs = resolveDurationMs(startedAt, endedAt, undefined);
|
|
@@ -197,12 +437,17 @@ function vercelAI(options = {}) {
|
|
|
197
437
|
? options.generationName(event)
|
|
198
438
|
: (options.generationName ?? "vercel-ai-generation");
|
|
199
439
|
const output = v6Output(event);
|
|
440
|
+
const generationError = "error" in event && event.error != null
|
|
441
|
+
? errorMessage(event.error)
|
|
442
|
+
: undefined;
|
|
200
443
|
const generation = {
|
|
201
444
|
name,
|
|
202
445
|
input: options.recordInputs === false
|
|
203
446
|
? undefined
|
|
204
447
|
: stored?.event && v6Input(stored.event),
|
|
205
|
-
output: options.recordOutputs === false
|
|
448
|
+
output: options.recordOutputs === false || generationError
|
|
449
|
+
? undefined
|
|
450
|
+
: output,
|
|
206
451
|
metadata: options.metadata,
|
|
207
452
|
model: event.model.modelId,
|
|
208
453
|
startedAt,
|
|
@@ -211,15 +456,19 @@ function vercelAI(options = {}) {
|
|
|
211
456
|
llmProvider: event.model.provider,
|
|
212
457
|
llmInputMessages: options.recordInputs === false || !stored?.event
|
|
213
458
|
? undefined
|
|
214
|
-
:
|
|
215
|
-
llmOutputMessages: options.recordOutputs === false ||
|
|
459
|
+
: v6NormalizedMessages(stored.event),
|
|
460
|
+
llmOutputMessages: options.recordOutputs === false ||
|
|
461
|
+
output === undefined ||
|
|
462
|
+
generationError
|
|
216
463
|
? undefined
|
|
217
464
|
: [{ role: "assistant", content: output }],
|
|
218
465
|
llmTools: stored?.event.tools,
|
|
466
|
+
status: generationError ? "ERROR" : undefined,
|
|
467
|
+
error: options.recordOutputs === false ? undefined : generationError,
|
|
219
468
|
};
|
|
220
469
|
if (stored?.handle) {
|
|
221
470
|
stored.handle.end(generation);
|
|
222
|
-
|
|
471
|
+
latestV6GenerationId = stored.handle.id;
|
|
223
472
|
trackGeneration(stored.handle);
|
|
224
473
|
return;
|
|
225
474
|
}
|
|
@@ -227,10 +476,19 @@ function vercelAI(options = {}) {
|
|
|
227
476
|
id,
|
|
228
477
|
...generation,
|
|
229
478
|
});
|
|
230
|
-
|
|
479
|
+
latestV6GenerationId = id;
|
|
231
480
|
}
|
|
232
481
|
function startV7Generation(event) {
|
|
233
|
-
|
|
482
|
+
if (event.stepNumber === 0) {
|
|
483
|
+
if (sawV7StepZero && phase === "active") {
|
|
484
|
+
throw new Error(CONCURRENT_REUSE_ERROR);
|
|
485
|
+
}
|
|
486
|
+
sawV7StepZero = true;
|
|
487
|
+
}
|
|
488
|
+
const resolved = resolveTrace(event);
|
|
489
|
+
if (!resolved)
|
|
490
|
+
return;
|
|
491
|
+
const { trace } = resolved;
|
|
234
492
|
const name = typeof options.generationName === "function"
|
|
235
493
|
? options.generationName({
|
|
236
494
|
callId: event.callId,
|
|
@@ -256,8 +514,11 @@ function vercelAI(options = {}) {
|
|
|
256
514
|
generationSpanIdsByCallId.set(event.callId, handle.id);
|
|
257
515
|
trackGeneration(handle);
|
|
258
516
|
}
|
|
259
|
-
function startV6Generation(event) {
|
|
260
|
-
const
|
|
517
|
+
function startV6Generation(event, key) {
|
|
518
|
+
const resolved = resolveTrace(event);
|
|
519
|
+
if (!resolved)
|
|
520
|
+
return undefined;
|
|
521
|
+
const { trace } = resolved;
|
|
261
522
|
const startedAt = new Date();
|
|
262
523
|
const name = typeof options.generationName === "function"
|
|
263
524
|
? options.generationName({
|
|
@@ -273,44 +534,68 @@ function vercelAI(options = {}) {
|
|
|
273
534
|
model: event.model.modelId,
|
|
274
535
|
startedAt,
|
|
275
536
|
llmProvider: event.model.provider,
|
|
276
|
-
llmInputMessages: options.recordInputs === false
|
|
537
|
+
llmInputMessages: options.recordInputs === false
|
|
538
|
+
? undefined
|
|
539
|
+
: v6NormalizedMessages(event),
|
|
277
540
|
llmTools: event.tools,
|
|
278
541
|
});
|
|
279
|
-
|
|
542
|
+
latestV6GenerationId = handle.id;
|
|
280
543
|
trackGeneration(handle);
|
|
281
|
-
return { event, startedAt, handle };
|
|
544
|
+
return { event, startedAt, handle, key };
|
|
282
545
|
}
|
|
283
546
|
function endV7Generation(event) {
|
|
284
|
-
const
|
|
285
|
-
v7Steps.
|
|
547
|
+
const key = v7StepKey(event.callId, event.stepNumber);
|
|
548
|
+
const stored = v7Steps.get(key);
|
|
549
|
+
v7Steps.delete(key);
|
|
286
550
|
if (!stored)
|
|
287
551
|
return;
|
|
288
552
|
const endedAt = new Date();
|
|
289
553
|
const durationMs = resolveDurationMs(stored.startedAt, endedAt, event.performance?.stepTimeMs ?? event.performance?.responseTimeMs);
|
|
290
|
-
const output =
|
|
291
|
-
|
|
292
|
-
: event.content
|
|
293
|
-
? stringifyContent(event.content)
|
|
294
|
-
: undefined;
|
|
554
|
+
const output = structuredAssistantOutput(event.text, event.content);
|
|
555
|
+
const generationError = event.error != null ? errorMessage(event.error) : undefined;
|
|
295
556
|
for (const toolCall of event.toolCalls ?? []) {
|
|
296
557
|
if (toolCall.toolCallId) {
|
|
297
558
|
generationSpanIdsByToolCallId.set(toolCall.toolCallId, stored.handle.id);
|
|
298
559
|
}
|
|
299
560
|
}
|
|
300
561
|
stored.handle.end({
|
|
301
|
-
output: options.recordOutputs === false
|
|
562
|
+
output: options.recordOutputs === false || generationError
|
|
563
|
+
? undefined
|
|
564
|
+
: output,
|
|
302
565
|
model: event.model.modelId,
|
|
303
566
|
durationMs,
|
|
304
567
|
endedAt: addMs(stored.startedAt, durationMs),
|
|
305
568
|
llmProvider: event.model.provider,
|
|
306
|
-
llmOutputMessages: options.recordOutputs === false ||
|
|
569
|
+
llmOutputMessages: options.recordOutputs === false ||
|
|
570
|
+
output === undefined ||
|
|
571
|
+
generationError
|
|
307
572
|
? undefined
|
|
308
573
|
: [{ role: "assistant", content: output }],
|
|
574
|
+
status: generationError ? "ERROR" : undefined,
|
|
575
|
+
error: options.recordOutputs === false ? undefined : generationError,
|
|
309
576
|
});
|
|
310
577
|
}
|
|
311
|
-
|
|
578
|
+
function resolveToolParentId(callId, toolCallId) {
|
|
579
|
+
return ((toolCallId && generationSpanIdsByToolCallId.get(toolCallId)) ||
|
|
580
|
+
(callId ? generationSpanIdsByCallId.get(callId) : undefined) ||
|
|
581
|
+
latestV6GenerationId);
|
|
582
|
+
}
|
|
583
|
+
const integration = {
|
|
312
584
|
onLanguageModelCallStart(event) {
|
|
313
|
-
|
|
585
|
+
if (phase === "active" &&
|
|
586
|
+
generationSpanIdsByCallId.size > 0 &&
|
|
587
|
+
!generationSpanIdsByCallId.has(event.callId)) {
|
|
588
|
+
const belongsToActiveStep = [...v7Steps.values()].some((step) => step.event.callId === event.callId);
|
|
589
|
+
// Multi-step v7 registers the next callId on step start before the
|
|
590
|
+
// model callback. Any other new callId is concurrent reuse.
|
|
591
|
+
if (!belongsToActiveStep) {
|
|
592
|
+
throw new Error(CONCURRENT_REUSE_ERROR);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const resolved = resolveTrace(event);
|
|
596
|
+
if (!resolved)
|
|
597
|
+
return;
|
|
598
|
+
const { trace } = resolved;
|
|
314
599
|
if (generationSpanIdsByCallId.has(event.callId)) {
|
|
315
600
|
modelCalls.set(event.callId, { event, startedAt: new Date() });
|
|
316
601
|
return;
|
|
@@ -340,32 +625,32 @@ function vercelAI(options = {}) {
|
|
|
340
625
|
trackGeneration(handle);
|
|
341
626
|
},
|
|
342
627
|
onLanguageModelCallEnd(event) {
|
|
628
|
+
if (phase !== "active" && phase !== "ending")
|
|
629
|
+
return;
|
|
343
630
|
const stored = modelCalls.get(event.callId);
|
|
344
631
|
modelCalls.delete(event.callId);
|
|
345
632
|
if (!stored?.handle)
|
|
346
633
|
return;
|
|
347
634
|
const endedAt = new Date();
|
|
348
635
|
const durationMs = resolveDurationMs(stored.startedAt, endedAt, event.performance.responseTimeMs);
|
|
636
|
+
const output = structuredAssistantOutput(undefined, event.content);
|
|
349
637
|
stored.handle.end({
|
|
350
|
-
output: options.recordOutputs === false
|
|
351
|
-
? undefined
|
|
352
|
-
: stringifyContent(event.content),
|
|
638
|
+
output: options.recordOutputs === false ? undefined : output,
|
|
353
639
|
model: event.modelId,
|
|
354
640
|
durationMs,
|
|
355
641
|
endedAt: addMs(stored.startedAt, durationMs),
|
|
356
642
|
llmProvider: event.provider,
|
|
357
|
-
llmOutputMessages: options.recordOutputs === false
|
|
643
|
+
llmOutputMessages: options.recordOutputs === false || output === undefined
|
|
358
644
|
? undefined
|
|
359
|
-
: [{ role: "assistant", content:
|
|
645
|
+
: [{ role: "assistant", content: output }],
|
|
360
646
|
});
|
|
361
647
|
},
|
|
362
648
|
onToolExecutionStart(event) {
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
: generationSpanIdsByCallId.get("v6-latest"));
|
|
649
|
+
const resolved = resolveTrace();
|
|
650
|
+
if (!resolved)
|
|
651
|
+
return;
|
|
652
|
+
const { trace } = resolved;
|
|
653
|
+
const parentId = resolveToolParentId(event.callId, event.toolCall.toolCallId);
|
|
369
654
|
const name = typeof options.toolName === "function"
|
|
370
655
|
? options.toolName({
|
|
371
656
|
...event,
|
|
@@ -380,6 +665,7 @@ function vercelAI(options = {}) {
|
|
|
380
665
|
input: options.recordInputs === false ? undefined : event.toolCall.input,
|
|
381
666
|
metadata: options.metadata,
|
|
382
667
|
startedAt,
|
|
668
|
+
toolName: event.toolCall.toolName,
|
|
383
669
|
});
|
|
384
670
|
if (event.toolCall.toolCallId) {
|
|
385
671
|
toolExecutions.set(event.toolCall.toolCallId, {
|
|
@@ -390,10 +676,13 @@ function vercelAI(options = {}) {
|
|
|
390
676
|
}
|
|
391
677
|
},
|
|
392
678
|
onToolCallStart(event) {
|
|
393
|
-
|
|
679
|
+
integration.onToolExecutionStart?.(event);
|
|
394
680
|
},
|
|
395
681
|
onToolExecutionEnd(event) {
|
|
396
|
-
const
|
|
682
|
+
const resolved = resolveTrace();
|
|
683
|
+
if (!resolved)
|
|
684
|
+
return;
|
|
685
|
+
const { trace } = resolved;
|
|
397
686
|
const name = typeof options.toolName === "function"
|
|
398
687
|
? options.toolName(event)
|
|
399
688
|
: (options.toolName ?? event.toolCall.toolName);
|
|
@@ -409,6 +698,7 @@ function vercelAI(options = {}) {
|
|
|
409
698
|
storedTool.handle.end({
|
|
410
699
|
durationMs,
|
|
411
700
|
endedAt: addMs(storedTool.startedAt, durationMs),
|
|
701
|
+
toolName: event.toolCall.toolName,
|
|
412
702
|
...toolOutput(event, options.recordOutputs !== false),
|
|
413
703
|
});
|
|
414
704
|
coverParent(storedTool.parentId, addMs(storedTool.startedAt, durationMs));
|
|
@@ -417,14 +707,11 @@ function vercelAI(options = {}) {
|
|
|
417
707
|
const endedAt = new Date();
|
|
418
708
|
const startedAt = subtractMs(endedAt, event.toolExecutionMs);
|
|
419
709
|
const durationMs = resolveDurationMs(startedAt, endedAt, event.toolExecutionMs);
|
|
420
|
-
const parentId = (event.toolCall.toolCallId
|
|
421
|
-
generationSpanIdsByToolCallId.get(event.toolCall.toolCallId)) ||
|
|
422
|
-
(event.callId
|
|
423
|
-
? generationSpanIdsByCallId.get(event.callId)
|
|
424
|
-
: generationSpanIdsByCallId.get("v6-latest"));
|
|
710
|
+
const parentId = resolveToolParentId(event.callId, event.toolCall.toolCallId);
|
|
425
711
|
trace.recordTool({
|
|
426
712
|
name,
|
|
427
713
|
parentId,
|
|
714
|
+
toolName: event.toolCall.toolName,
|
|
428
715
|
input: options.recordInputs === false ? undefined : event.toolCall.input,
|
|
429
716
|
metadata: options.metadata,
|
|
430
717
|
durationMs,
|
|
@@ -435,8 +722,10 @@ function vercelAI(options = {}) {
|
|
|
435
722
|
coverParent(parentId, endedAt);
|
|
436
723
|
},
|
|
437
724
|
onStart(event) {
|
|
725
|
+
beginNewRun();
|
|
438
726
|
recordedV6Step = false;
|
|
439
|
-
v6Starts.
|
|
727
|
+
const key = `start:${v6Starts.length}:${Date.now()}`;
|
|
728
|
+
v6Starts.push({ event, startedAt: new Date(), key });
|
|
440
729
|
resolveTrace(event);
|
|
441
730
|
},
|
|
442
731
|
onStepStart(event) {
|
|
@@ -444,32 +733,60 @@ function vercelAI(options = {}) {
|
|
|
444
733
|
startV7Generation(event);
|
|
445
734
|
return;
|
|
446
735
|
}
|
|
447
|
-
|
|
736
|
+
const key = `step:${event.stepNumber}`;
|
|
737
|
+
if (v6Steps.has(key)) {
|
|
738
|
+
// Duplicate step start for the same step number — ignore.
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const stored = startV6Generation(event, key);
|
|
742
|
+
if (stored)
|
|
743
|
+
v6Steps.set(key, stored);
|
|
448
744
|
},
|
|
449
745
|
onStepEnd(event) {
|
|
746
|
+
if (phase !== "active" && phase !== "ending")
|
|
747
|
+
return;
|
|
450
748
|
endV7Generation(event);
|
|
451
749
|
},
|
|
452
750
|
onStepFinish(event) {
|
|
751
|
+
if (phase !== "active" && phase !== "ending")
|
|
752
|
+
return;
|
|
453
753
|
recordedV6Step = true;
|
|
454
|
-
const
|
|
455
|
-
v6Steps.
|
|
754
|
+
const key = `step:${event.stepNumber}`;
|
|
755
|
+
const stored = v6Steps.get(key);
|
|
756
|
+
v6Steps.delete(key);
|
|
456
757
|
recordV6Generation(event, stored);
|
|
457
758
|
},
|
|
458
759
|
async onFinish(event) {
|
|
459
|
-
if (
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
760
|
+
if (phase === "idle") {
|
|
761
|
+
// Terminal-only path for an explicit trace handle with no prior events.
|
|
762
|
+
if (!hasExplicitEndableTrace())
|
|
763
|
+
return;
|
|
764
|
+
beginActivity();
|
|
765
|
+
}
|
|
766
|
+
if (phase === "active") {
|
|
767
|
+
if (recordedV6Step) {
|
|
768
|
+
v6Starts.shift();
|
|
769
|
+
recordedV6Step = false;
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
recordV6Generation(event, v6Starts.shift());
|
|
773
|
+
}
|
|
464
774
|
}
|
|
465
|
-
recordV6Generation(event, v6Starts.shift());
|
|
466
775
|
await endOwnedTrace(event);
|
|
467
776
|
},
|
|
468
777
|
async onEnd(event) {
|
|
778
|
+
if (phase === "idle") {
|
|
779
|
+
if (!hasExplicitEndableTrace())
|
|
780
|
+
return;
|
|
781
|
+
beginActivity();
|
|
782
|
+
}
|
|
469
783
|
await endOwnedTrace(event);
|
|
470
784
|
},
|
|
471
785
|
onToolCallFinish(event) {
|
|
472
|
-
const
|
|
786
|
+
const resolved = resolveTrace();
|
|
787
|
+
if (!resolved)
|
|
788
|
+
return;
|
|
789
|
+
const { trace } = resolved;
|
|
473
790
|
const name = typeof options.toolName === "function"
|
|
474
791
|
? options.toolName(event)
|
|
475
792
|
: (options.toolName ?? event.toolCall.toolName);
|
|
@@ -486,6 +803,7 @@ function vercelAI(options = {}) {
|
|
|
486
803
|
storedTool.handle.end({
|
|
487
804
|
durationMs,
|
|
488
805
|
endedAt: toolEndedAt,
|
|
806
|
+
toolName: event.toolCall.toolName,
|
|
489
807
|
...v6ToolOutput(event, options.recordOutputs !== false),
|
|
490
808
|
});
|
|
491
809
|
coverParent(storedTool.parentId, toolEndedAt);
|
|
@@ -494,12 +812,11 @@ function vercelAI(options = {}) {
|
|
|
494
812
|
const endedAt = new Date();
|
|
495
813
|
const durationMs = resolveDurationMs(subtractMs(endedAt, event.durationMs), endedAt, event.durationMs);
|
|
496
814
|
const startedAt = subtractMs(endedAt, durationMs);
|
|
497
|
-
const parentId = (event.toolCall.toolCallId
|
|
498
|
-
generationSpanIdsByToolCallId.get(event.toolCall.toolCallId)) ||
|
|
499
|
-
generationSpanIdsByCallId.get("v6-latest");
|
|
815
|
+
const parentId = resolveToolParentId(undefined, event.toolCall.toolCallId);
|
|
500
816
|
trace.recordTool({
|
|
501
817
|
name,
|
|
502
818
|
parentId,
|
|
819
|
+
toolName: event.toolCall.toolName,
|
|
503
820
|
input: options.recordInputs === false ? undefined : event.toolCall.input,
|
|
504
821
|
metadata: options.metadata,
|
|
505
822
|
durationMs,
|
|
@@ -509,6 +826,31 @@ function vercelAI(options = {}) {
|
|
|
509
826
|
});
|
|
510
827
|
coverParent(parentId, endedAt);
|
|
511
828
|
},
|
|
829
|
+
async fail(error) {
|
|
830
|
+
const message = errorMessage(error);
|
|
831
|
+
runError = message;
|
|
832
|
+
if (phase === "ending") {
|
|
833
|
+
// Best-effort: apply before the in-flight terminal send reads error state.
|
|
834
|
+
endingTrace?.fail(rootErrorPayload(message));
|
|
835
|
+
endingTrace?.output(undefined);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (phase === "idle") {
|
|
839
|
+
beginActivity();
|
|
840
|
+
}
|
|
841
|
+
await endOwnedTrace({ error });
|
|
842
|
+
},
|
|
843
|
+
async flush() {
|
|
844
|
+
await Promise.all(Array.from(pending));
|
|
845
|
+
},
|
|
846
|
+
async shutdown() {
|
|
847
|
+
if (phase === "active") {
|
|
848
|
+
await endOwnedTrace({});
|
|
849
|
+
}
|
|
850
|
+
await integration.flush();
|
|
851
|
+
resetRunState();
|
|
852
|
+
},
|
|
512
853
|
};
|
|
854
|
+
return integration;
|
|
513
855
|
}
|
|
514
856
|
//# sourceMappingURL=vercel-ai.js.map
|