@ai-sdk/devtools 1.0.24 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,767 +1,745 @@
1
- // src/db.ts
2
- import path from "path";
3
- import fs from "fs";
4
- var DEVTOOLS_DB_DIR = ".devtools";
5
- var DEVTOOLS_DB_FILE = "generations.json";
6
- var MAX_DB_BYTES = 100 * 1024 * 1024;
7
- var DB_DIR = path.join(process.cwd(), DEVTOOLS_DB_DIR);
8
- var DB_PATH = path.join(DB_DIR, DEVTOOLS_DB_FILE);
9
- var DEVTOOLS_PORT = process.env.AI_SDK_DEVTOOLS_PORT ? parseInt(process.env.AI_SDK_DEVTOOLS_PORT) : 4983;
10
- var notifyServer = (event) => {
11
- notifyServerAsync(event);
1
+ import path from "node:path";
2
+ import fs from "node:fs";
3
+ //#region src/db.ts
4
+ const DEVTOOLS_DB_DIR = ".devtools";
5
+ const DEVTOOLS_DB_FILE = "generations.json";
6
+ const DB_DIR = path.join(process.cwd(), DEVTOOLS_DB_DIR);
7
+ const DB_PATH = path.join(DB_DIR, DEVTOOLS_DB_FILE);
8
+ const DEVTOOLS_PORT = process.env.AI_SDK_DEVTOOLS_PORT ? parseInt(process.env.AI_SDK_DEVTOOLS_PORT) : 4983;
9
+ /**
10
+ * Notify the devtools server that data has changed.
11
+ * Fire-and-forget: doesn't block, ignores errors if server isn't running.
12
+ */
13
+ const notifyServer = (event) => {
14
+ notifyServerAsync(event);
12
15
  };
13
- var notifyServerAsync = async (event) => {
14
- try {
15
- await fetch(`http://localhost:${DEVTOOLS_PORT}/api/notify`, {
16
- method: "POST",
17
- headers: { "Content-Type": "application/json" },
18
- body: JSON.stringify({ event, timestamp: Date.now(), dbPath: DB_PATH })
19
- });
20
- } catch {
21
- }
16
+ /**
17
+ * Notify the devtools server and wait for the request to complete.
18
+ * Used during process cleanup to ensure notifications are sent before exit.
19
+ */
20
+ const notifyServerAsync = async (event) => {
21
+ try {
22
+ await fetch(`http://localhost:${DEVTOOLS_PORT}/api/notify`, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/json" },
25
+ body: JSON.stringify({
26
+ event,
27
+ timestamp: Date.now(),
28
+ dbPath: DB_PATH
29
+ })
30
+ });
31
+ } catch {}
22
32
  };
23
- var ensureGitignore = () => {
24
- const gitignorePath = path.join(process.cwd(), ".gitignore");
25
- if (!fs.existsSync(gitignorePath)) {
26
- return;
27
- }
28
- const content = fs.readFileSync(gitignorePath, "utf-8");
29
- const lines = content.split("\n");
30
- const alreadyIgnored = lines.some(
31
- (line) => line.trim() === ".devtools" || line.trim() === ".devtools/"
32
- );
33
- if (!alreadyIgnored) {
34
- const newContent = content.endsWith("\n") ? `${content}.devtools
35
- ` : `${content}
36
- .devtools
37
- `;
38
- fs.writeFileSync(gitignorePath, newContent);
39
- }
33
+ /**
34
+ * Ensure .devtools is in .gitignore.
35
+ * Only writes if .gitignore exists and doesn't already contain .devtools.
36
+ */
37
+ const ensureGitignore = () => {
38
+ const gitignorePath = path.join(process.cwd(), ".gitignore");
39
+ if (!fs.existsSync(gitignorePath)) return;
40
+ const content = fs.readFileSync(gitignorePath, "utf-8");
41
+ if (!content.split("\n").some((line) => line.trim() === ".devtools" || line.trim() === ".devtools/")) {
42
+ const newContent = content.endsWith("\n") ? `${content}.devtools\n` : `${content}\n.devtools\n`;
43
+ fs.writeFileSync(gitignorePath, newContent);
44
+ }
40
45
  };
41
- var readDb = () => {
42
- try {
43
- if (fs.existsSync(DB_PATH)) {
44
- const content = fs.readFileSync(DB_PATH, "utf-8");
45
- return JSON.parse(content);
46
- }
47
- } catch {
48
- }
49
- return { runs: [], steps: [] };
46
+ const readDb = () => {
47
+ try {
48
+ if (fs.existsSync(DB_PATH)) {
49
+ const content = fs.readFileSync(DB_PATH, "utf-8");
50
+ return JSON.parse(content);
51
+ }
52
+ } catch {}
53
+ return {
54
+ runs: [],
55
+ steps: []
56
+ };
50
57
  };
51
- var writeDb = (db) => {
52
- const isFirstRun = !fs.existsSync(DB_DIR);
53
- if (isFirstRun) {
54
- fs.mkdirSync(DB_DIR, { recursive: true });
55
- ensureGitignore();
56
- }
57
- fs.writeFileSync(DB_PATH, JSON.stringify(db, null, 2));
58
+ const writeDb = (db) => {
59
+ if (!fs.existsSync(DB_DIR)) {
60
+ fs.mkdirSync(DB_DIR, { recursive: true });
61
+ ensureGitignore();
62
+ }
63
+ fs.writeFileSync(DB_PATH, JSON.stringify(db, null, 2));
58
64
  };
59
- var dbCache = null;
60
- var getDb = () => {
61
- if (!dbCache) {
62
- dbCache = readDb();
63
- }
64
- return dbCache;
65
+ let dbCache = null;
66
+ const getDb = () => {
67
+ if (!dbCache) dbCache = readDb();
68
+ return dbCache;
65
69
  };
66
- var saveDb = (db) => {
67
- dbCache = db;
68
- writeDb(db);
70
+ const saveDb = (db) => {
71
+ dbCache = db;
72
+ writeDb(db);
69
73
  };
70
- var createRun = async (id, parent, functionId) => {
71
- const db = getDb();
72
- const started_at = (/* @__PURE__ */ new Date()).toISOString();
73
- const existing = db.runs.find((r) => r.id === id);
74
- if (existing) {
75
- return existing;
76
- }
77
- const run = {
78
- id,
79
- started_at,
80
- parent_run_id: parent?.runId ?? null,
81
- parent_step_id: parent?.stepId ?? null,
82
- function_id: functionId ?? null
83
- };
84
- db.runs.push(run);
85
- saveDb(db);
86
- notifyServer("run");
87
- return run;
74
+ const createRun = async (id, parent, functionId) => {
75
+ const db = getDb();
76
+ const started_at = (/* @__PURE__ */ new Date()).toISOString();
77
+ const existing = db.runs.find((r) => r.id === id);
78
+ if (existing) return existing;
79
+ const run = {
80
+ id,
81
+ started_at,
82
+ parent_run_id: parent?.runId ?? null,
83
+ parent_step_id: parent?.stepId ?? null,
84
+ function_id: functionId ?? null
85
+ };
86
+ db.runs.push(run);
87
+ saveDb(db);
88
+ notifyServer("run");
89
+ return run;
88
90
  };
89
- var createStep = async (step) => {
90
- const db = getDb();
91
- const newStep = {
92
- ...step,
93
- duration_ms: null,
94
- output: null,
95
- usage: null,
96
- error: null,
97
- raw_request: null,
98
- raw_response: null,
99
- raw_chunks: null
100
- };
101
- db.steps.push(newStep);
102
- saveDb(db);
103
- notifyServer("step");
91
+ const createStep = async (step) => {
92
+ const db = getDb();
93
+ const newStep = {
94
+ ...step,
95
+ duration_ms: null,
96
+ output: null,
97
+ usage: null,
98
+ error: null,
99
+ raw_request: null,
100
+ raw_response: null,
101
+ raw_chunks: null
102
+ };
103
+ db.steps.push(newStep);
104
+ saveDb(db);
105
+ notifyServer("step");
104
106
  };
105
- var updateStepResult = async (stepId, result) => {
106
- const db = getDb();
107
- const step = db.steps.find((s) => s.id === stepId);
108
- if (step) {
109
- step.duration_ms = result.duration_ms;
110
- step.output = result.output;
111
- step.usage = result.usage;
112
- step.error = result.error;
113
- step.raw_request = result.raw_request ?? null;
114
- step.raw_response = result.raw_response ?? null;
115
- step.raw_chunks = result.raw_chunks ?? null;
116
- saveDb(db);
117
- notifyServer("step-update");
118
- }
107
+ const updateStepResult = async (stepId, result) => {
108
+ const db = getDb();
109
+ const step = db.steps.find((s) => s.id === stepId);
110
+ if (step) {
111
+ step.duration_ms = result.duration_ms;
112
+ step.output = result.output;
113
+ step.usage = result.usage;
114
+ step.error = result.error;
115
+ step.raw_request = result.raw_request ?? null;
116
+ step.raw_response = result.raw_response ?? null;
117
+ step.raw_chunks = result.raw_chunks ?? null;
118
+ saveDb(db);
119
+ notifyServer("step-update");
120
+ }
119
121
  };
120
- var getStepsForRun = async (runId) => {
121
- const db = getDb();
122
- return db.steps.filter((s) => s.run_id === runId).sort((a, b) => a.step_number - b.step_number);
122
+ const getStepsForRun = async (runId) => {
123
+ return getDb().steps.filter((s) => s.run_id === runId).sort((a, b) => a.step_number - b.step_number);
123
124
  };
124
-
125
- // src/serialize.ts
125
+ //#endregion
126
+ //#region src/serialize.ts
126
127
  function normalizeBinaryData(value) {
127
- if (value instanceof ArrayBuffer) {
128
- return Buffer.from(value).toString("base64");
129
- }
130
- if (ArrayBuffer.isView(value)) {
131
- return Buffer.from(
132
- value.buffer,
133
- value.byteOffset,
134
- value.byteLength
135
- ).toString("base64");
136
- }
137
- return value;
128
+ if (value instanceof ArrayBuffer) return Buffer.from(value).toString("base64");
129
+ if (ArrayBuffer.isView(value)) return Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString("base64");
130
+ return value;
138
131
  }
139
132
  function isRecord(value) {
140
- return value != null && typeof value === "object" && !Array.isArray(value);
133
+ return value != null && typeof value === "object" && !Array.isArray(value);
141
134
  }
142
135
  function collectMediaBinaryValues(value) {
143
- const mediaBinaryValues = /* @__PURE__ */ new WeakSet();
144
- const visited = /* @__PURE__ */ new WeakSet();
145
- const markBinary = (candidate) => {
146
- if (candidate instanceof ArrayBuffer || ArrayBuffer.isView(candidate)) {
147
- mediaBinaryValues.add(candidate);
148
- }
149
- };
150
- const markGeneratedFile = (candidate) => {
151
- if (!isRecord(candidate)) return;
152
- if (Object.hasOwn(candidate, "uint8Array")) {
153
- markBinary(candidate.uint8Array);
154
- }
155
- markBinary(candidate.uint8ArrayData);
156
- };
157
- const markDataContent = (candidate) => {
158
- markBinary(candidate);
159
- if (!isRecord(candidate)) return;
160
- if (candidate.type === "data") {
161
- markBinary(candidate.data);
162
- }
163
- };
164
- const visit = (candidate) => {
165
- if (candidate == null || typeof candidate !== "object" || candidate instanceof ArrayBuffer || ArrayBuffer.isView(candidate) || visited.has(candidate)) {
166
- return;
167
- }
168
- visited.add(candidate);
169
- if (isRecord(candidate) && typeof candidate.type === "string") {
170
- switch (candidate.type) {
171
- case "file":
172
- case "reasoning-file":
173
- markDataContent(candidate.data);
174
- markGeneratedFile(candidate);
175
- markGeneratedFile(candidate.file);
176
- break;
177
- case "image":
178
- markDataContent(candidate.image);
179
- break;
180
- case "media":
181
- case "file-data":
182
- case "image-data":
183
- markBinary(candidate.data);
184
- break;
185
- }
186
- }
187
- for (const child of Array.isArray(candidate) ? candidate : Object.values(candidate)) {
188
- visit(child);
189
- }
190
- };
191
- visit(value);
192
- return mediaBinaryValues;
136
+ const mediaBinaryValues = /* @__PURE__ */ new WeakSet();
137
+ const visited = /* @__PURE__ */ new WeakSet();
138
+ const markBinary = (candidate) => {
139
+ if (candidate instanceof ArrayBuffer || ArrayBuffer.isView(candidate)) mediaBinaryValues.add(candidate);
140
+ };
141
+ const markGeneratedFile = (candidate) => {
142
+ if (!isRecord(candidate)) return;
143
+ if (Object.hasOwn(candidate, "uint8Array")) markBinary(candidate.uint8Array);
144
+ markBinary(candidate.uint8ArrayData);
145
+ };
146
+ const markDataContent = (candidate) => {
147
+ markBinary(candidate);
148
+ if (!isRecord(candidate)) return;
149
+ if (candidate.type === "data") markBinary(candidate.data);
150
+ };
151
+ const visit = (candidate) => {
152
+ if (candidate == null || typeof candidate !== "object" || candidate instanceof ArrayBuffer || ArrayBuffer.isView(candidate) || visited.has(candidate)) return;
153
+ visited.add(candidate);
154
+ if (isRecord(candidate) && typeof candidate.type === "string") switch (candidate.type) {
155
+ case "file":
156
+ case "reasoning-file":
157
+ markDataContent(candidate.data);
158
+ markGeneratedFile(candidate);
159
+ markGeneratedFile(candidate.file);
160
+ break;
161
+ case "image":
162
+ markDataContent(candidate.image);
163
+ break;
164
+ case "media":
165
+ case "file-data":
166
+ case "image-data": markBinary(candidate.data);
167
+ }
168
+ for (const child of Array.isArray(candidate) ? candidate : Object.values(candidate)) visit(child);
169
+ };
170
+ visit(value);
171
+ return mediaBinaryValues;
193
172
  }
173
+ /**
174
+ * Serializes captured DevTools values while preserving binary values in
175
+ * recognized media-bearing fields as base64. Unrelated binary values retain
176
+ * their normal JSON.stringify representation.
177
+ */
194
178
  function serializeForDevTools(value) {
195
- const mediaBinaryValues = collectMediaBinaryValues(value);
196
- return JSON.stringify(value, function(key, serializedValue) {
197
- const originalValue = this[key];
198
- return originalValue != null && typeof originalValue === "object" && mediaBinaryValues.has(originalValue) ? normalizeBinaryData(originalValue) : serializedValue;
199
- });
179
+ const mediaBinaryValues = collectMediaBinaryValues(value);
180
+ return JSON.stringify(value, function(key, serializedValue) {
181
+ const originalValue = this[key];
182
+ return originalValue != null && typeof originalValue === "object" && mediaBinaryValues.has(originalValue) ? normalizeBinaryData(originalValue) : serializedValue;
183
+ });
200
184
  }
201
-
202
- // src/middleware.ts
203
- var generateId = () => crypto.randomUUID();
204
- var activeSteps = /* @__PURE__ */ new Map();
205
- var signalHandlersRegistered = false;
206
- var registerSignalHandlers = () => {
207
- if (signalHandlersRegistered) return;
208
- signalHandlersRegistered = true;
209
- const cleanup = async () => {
210
- if (activeSteps.size === 0) return;
211
- const promises = Array.from(activeSteps.entries()).map(
212
- async ([stepId, data]) => {
213
- const durationMs = Date.now() - data.startTime;
214
- await updateStepResult(stepId, {
215
- duration_ms: durationMs,
216
- output: serializeForDevTools(data.collectedOutput),
217
- usage: null,
218
- error: "Request aborted",
219
- raw_request: data.request && typeof data.request === "object" && "body" in data.request ? JSON.stringify(data.request.body) : null,
220
- raw_response: JSON.stringify(data.fullStreamChunks),
221
- raw_chunks: JSON.stringify(data.rawChunks)
222
- });
223
- }
224
- );
225
- await Promise.all(promises);
226
- await notifyServerAsync("step-update");
227
- };
228
- process.on("SIGINT", () => {
229
- cleanup().then(() => process.exit(130));
230
- });
231
- process.on("SIGTERM", () => {
232
- cleanup().then(() => process.exit(143));
233
- });
185
+ //#endregion
186
+ //#region src/middleware.ts
187
+ const generateId = () => crypto.randomUUID();
188
+ const activeSteps$1 = /* @__PURE__ */ new Map();
189
+ let signalHandlersRegistered$1 = false;
190
+ const registerSignalHandlers$1 = () => {
191
+ if (signalHandlersRegistered$1) return;
192
+ signalHandlersRegistered$1 = true;
193
+ const cleanup = async () => {
194
+ if (activeSteps$1.size === 0) return;
195
+ const promises = Array.from(activeSteps$1.entries()).map(async ([stepId, data]) => {
196
+ const durationMs = Date.now() - data.startTime;
197
+ await updateStepResult(stepId, {
198
+ duration_ms: durationMs,
199
+ output: serializeForDevTools(data.collectedOutput),
200
+ usage: null,
201
+ error: "Request aborted",
202
+ raw_request: data.request && typeof data.request === "object" && "body" in data.request ? JSON.stringify(data.request.body) : null,
203
+ raw_response: JSON.stringify(data.fullStreamChunks),
204
+ raw_chunks: JSON.stringify(data.rawChunks)
205
+ });
206
+ });
207
+ await Promise.all(promises);
208
+ await notifyServerAsync("step-update");
209
+ };
210
+ process.on("SIGINT", () => {
211
+ cleanup().then(() => process.exit(130));
212
+ });
213
+ process.on("SIGTERM", () => {
214
+ cleanup().then(() => process.exit(143));
215
+ });
234
216
  };
235
- var generateRunId = () => {
236
- const now = /* @__PURE__ */ new Date();
237
- const timestamp = now.toISOString().replace(/[-:T.Z]/g, "").slice(0, 17);
238
- const uniqueId = crypto.randomUUID().slice(0, 8);
239
- return `${timestamp}-${uniqueId}`;
217
+ /**
218
+ * Generate a unique run ID with timestamp prefix for sorting.
219
+ */
220
+ const generateRunId$1 = () => {
221
+ return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:T.Z]/g, "").slice(0, 17)}-${crypto.randomUUID().slice(0, 8)}`;
240
222
  };
241
- var devToolsMiddleware = () => {
242
- if (process.env.NODE_ENV === "production") {
243
- throw new Error(
244
- "@ai-sdk/devtools should not be used in production. Remove devToolsMiddleware from your model configuration for production builds."
245
- );
246
- }
247
- registerSignalHandlers();
248
- const runId = generateRunId();
249
- let runCreated = false;
250
- let stepCounter = 0;
251
- const ensureRunCreated = async () => {
252
- if (!runCreated) {
253
- await createRun(runId);
254
- runCreated = true;
255
- }
256
- };
257
- const getNextStepNumber = () => {
258
- stepCounter++;
259
- return stepCounter;
260
- };
261
- return {
262
- specificationVersion: "v4",
263
- wrapGenerate: async ({ doGenerate, params, model }) => {
264
- const startTime = Date.now();
265
- const stepId = generateId();
266
- const stepNumber = getNextStepNumber();
267
- await ensureRunCreated();
268
- await createStep({
269
- id: stepId,
270
- run_id: runId,
271
- step_number: stepNumber,
272
- type: "generate",
273
- model_id: model.modelId,
274
- // @ts-expect-error broken type
275
- provider: model.config?.provider,
276
- started_at: (/* @__PURE__ */ new Date()).toISOString(),
277
- input: serializeForDevTools({
278
- prompt: params.prompt,
279
- tools: params.tools,
280
- toolChoice: params.toolChoice,
281
- maxOutputTokens: params.maxOutputTokens,
282
- temperature: params.temperature,
283
- topP: params.topP,
284
- topK: params.topK,
285
- presencePenalty: params.presencePenalty,
286
- frequencyPenalty: params.frequencyPenalty,
287
- seed: params.seed,
288
- responseFormat: params.responseFormat
289
- }),
290
- provider_options: params.providerOptions ? JSON.stringify(params.providerOptions) : null
291
- });
292
- try {
293
- const result = await doGenerate();
294
- const durationMs = Date.now() - startTime;
295
- await updateStepResult(stepId, {
296
- duration_ms: durationMs,
297
- output: serializeForDevTools({
298
- content: result.content,
299
- finishReason: result.finishReason,
300
- response: result.response
301
- }),
302
- usage: result.usage ? JSON.stringify(result.usage) : null,
303
- error: null,
304
- raw_request: result.request?.body ? JSON.stringify(result.request.body) : null,
305
- raw_response: result.response?.body ? JSON.stringify(result.response.body) : null
306
- });
307
- return result;
308
- } catch (error) {
309
- const durationMs = Date.now() - startTime;
310
- await updateStepResult(stepId, {
311
- duration_ms: durationMs,
312
- output: null,
313
- usage: null,
314
- error: error instanceof Error ? error.message : String(error),
315
- raw_request: null,
316
- raw_response: null
317
- });
318
- throw error;
319
- }
320
- },
321
- wrapStream: async ({ doStream, params, model }) => {
322
- const startTime = Date.now();
323
- const stepId = generateId();
324
- const stepNumber = getNextStepNumber();
325
- await ensureRunCreated();
326
- const userRequestedRawChunks = params.includeRawChunks === true;
327
- params.includeRawChunks = true;
328
- await createStep({
329
- id: stepId,
330
- run_id: runId,
331
- step_number: stepNumber,
332
- type: "stream",
333
- model_id: model.modelId,
334
- // @ts-expect-error broken type
335
- provider: model.config?.provider,
336
- started_at: (/* @__PURE__ */ new Date()).toISOString(),
337
- input: serializeForDevTools({
338
- prompt: params.prompt,
339
- tools: params.tools,
340
- toolChoice: params.toolChoice,
341
- maxOutputTokens: params.maxOutputTokens,
342
- temperature: params.temperature,
343
- topP: params.topP,
344
- topK: params.topK,
345
- presencePenalty: params.presencePenalty,
346
- frequencyPenalty: params.frequencyPenalty,
347
- seed: params.seed,
348
- responseFormat: params.responseFormat
349
- }),
350
- provider_options: params.providerOptions ? JSON.stringify(params.providerOptions) : null
351
- });
352
- try {
353
- const { stream, request, response, ...rest } = await doStream();
354
- const collectedOutput = {
355
- textParts: [],
356
- reasoningParts: [],
357
- toolCalls: []
358
- };
359
- const currentText = /* @__PURE__ */ new Map();
360
- const currentReasoning = /* @__PURE__ */ new Map();
361
- const fullStreamChunks = [];
362
- const rawChunks = [];
363
- activeSteps.set(stepId, {
364
- startTime,
365
- collectedOutput,
366
- request,
367
- fullStreamChunks,
368
- rawChunks
369
- });
370
- const transformStream = new TransformStream({
371
- transform(chunk, controller) {
372
- if (chunk.type === "raw") {
373
- rawChunks.push(chunk.rawValue);
374
- if (userRequestedRawChunks) {
375
- controller.enqueue(chunk);
376
- }
377
- return;
378
- }
379
- fullStreamChunks.push(chunk);
380
- switch (chunk.type) {
381
- case "text-start":
382
- currentText.set(chunk.id, "");
383
- break;
384
- case "text-delta":
385
- currentText.set(
386
- chunk.id,
387
- (currentText.get(chunk.id) ?? "") + chunk.delta
388
- );
389
- break;
390
- case "text-end":
391
- collectedOutput.textParts.push({
392
- id: chunk.id,
393
- text: currentText.get(chunk.id) ?? ""
394
- });
395
- break;
396
- case "reasoning-start":
397
- currentReasoning.set(chunk.id, "");
398
- break;
399
- case "reasoning-delta":
400
- currentReasoning.set(
401
- chunk.id,
402
- (currentReasoning.get(chunk.id) ?? "") + chunk.delta
403
- );
404
- break;
405
- case "reasoning-end":
406
- collectedOutput.reasoningParts.push({
407
- id: chunk.id,
408
- text: currentReasoning.get(chunk.id) ?? ""
409
- });
410
- break;
411
- case "tool-call":
412
- collectedOutput.toolCalls.push(chunk);
413
- break;
414
- case "file":
415
- case "reasoning-file":
416
- case "tool-result":
417
- (collectedOutput.content ??= []).push(chunk);
418
- break;
419
- case "finish":
420
- collectedOutput.finishReason = chunk.finishReason;
421
- collectedOutput.usage = chunk.usage;
422
- break;
423
- }
424
- controller.enqueue(chunk);
425
- },
426
- async flush() {
427
- activeSteps.delete(stepId);
428
- const durationMs = Date.now() - startTime;
429
- await updateStepResult(stepId, {
430
- duration_ms: durationMs,
431
- output: serializeForDevTools(collectedOutput),
432
- usage: collectedOutput.usage ? JSON.stringify(collectedOutput.usage) : null,
433
- error: null,
434
- raw_request: request?.body ? JSON.stringify(request.body) : null,
435
- raw_response: JSON.stringify(fullStreamChunks),
436
- raw_chunks: JSON.stringify(rawChunks)
437
- });
438
- },
439
- // @ts-expect-error - cancel is valid per WHATWG Streams spec but missing from TS types
440
- async cancel() {
441
- activeSteps.delete(stepId);
442
- const durationMs = Date.now() - startTime;
443
- await updateStepResult(stepId, {
444
- duration_ms: durationMs,
445
- output: serializeForDevTools(collectedOutput),
446
- usage: collectedOutput.usage ? JSON.stringify(collectedOutput.usage) : null,
447
- error: "Request aborted",
448
- raw_request: request?.body ? JSON.stringify(request.body) : null,
449
- raw_response: JSON.stringify(fullStreamChunks),
450
- raw_chunks: JSON.stringify(rawChunks)
451
- });
452
- }
453
- });
454
- return {
455
- stream: stream.pipeThrough(transformStream),
456
- request,
457
- response,
458
- ...rest
459
- };
460
- } catch (error) {
461
- activeSteps.delete(stepId);
462
- const durationMs = Date.now() - startTime;
463
- await updateStepResult(stepId, {
464
- duration_ms: durationMs,
465
- output: null,
466
- usage: null,
467
- error: error instanceof Error ? error.message : String(error),
468
- raw_request: null,
469
- raw_response: null,
470
- raw_chunks: null
471
- });
472
- throw error;
473
- }
474
- }
475
- };
223
+ /**
224
+ * Factory function that creates a devtools middleware instance.
225
+ * Each call generates a unique run ID, so all steps within a single
226
+ * streamText/generateText call share the same run.
227
+ *
228
+ * Usage:
229
+ * ```ts
230
+ * const result = streamText({
231
+ * model: wrapLanguageModel({
232
+ * middleware: devToolsMiddleware(),
233
+ * model: yourModel,
234
+ * }),
235
+ * prompt: "...",
236
+ * });
237
+ * ```
238
+ */
239
+ const devToolsMiddleware = () => {
240
+ if (process.env.NODE_ENV === "production") throw new Error("@ai-sdk/devtools should not be used in production. Remove devToolsMiddleware from your model configuration for production builds.");
241
+ registerSignalHandlers$1();
242
+ const runId = generateRunId$1();
243
+ let runCreated = false;
244
+ let stepCounter = 0;
245
+ const ensureRunCreated = async () => {
246
+ if (!runCreated) {
247
+ await createRun(runId);
248
+ runCreated = true;
249
+ }
250
+ };
251
+ const getNextStepNumber = () => {
252
+ stepCounter++;
253
+ return stepCounter;
254
+ };
255
+ return {
256
+ specificationVersion: "v4",
257
+ wrapGenerate: async ({ doGenerate, params, model }) => {
258
+ const startTime = Date.now();
259
+ const stepId = generateId();
260
+ const stepNumber = getNextStepNumber();
261
+ await ensureRunCreated();
262
+ await createStep({
263
+ id: stepId,
264
+ run_id: runId,
265
+ step_number: stepNumber,
266
+ type: "generate",
267
+ model_id: model.modelId,
268
+ provider: model.config?.provider,
269
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
270
+ input: serializeForDevTools({
271
+ prompt: params.prompt,
272
+ tools: params.tools,
273
+ toolChoice: params.toolChoice,
274
+ maxOutputTokens: params.maxOutputTokens,
275
+ temperature: params.temperature,
276
+ topP: params.topP,
277
+ topK: params.topK,
278
+ presencePenalty: params.presencePenalty,
279
+ frequencyPenalty: params.frequencyPenalty,
280
+ seed: params.seed,
281
+ responseFormat: params.responseFormat
282
+ }),
283
+ provider_options: params.providerOptions ? JSON.stringify(params.providerOptions) : null
284
+ });
285
+ try {
286
+ const result = await doGenerate();
287
+ const durationMs = Date.now() - startTime;
288
+ await updateStepResult(stepId, {
289
+ duration_ms: durationMs,
290
+ output: serializeForDevTools({
291
+ content: result.content,
292
+ finishReason: result.finishReason,
293
+ response: result.response
294
+ }),
295
+ usage: result.usage ? JSON.stringify(result.usage) : null,
296
+ error: null,
297
+ raw_request: result.request?.body ? JSON.stringify(result.request.body) : null,
298
+ raw_response: result.response?.body ? JSON.stringify(result.response.body) : null
299
+ });
300
+ return result;
301
+ } catch (error) {
302
+ const durationMs = Date.now() - startTime;
303
+ await updateStepResult(stepId, {
304
+ duration_ms: durationMs,
305
+ output: null,
306
+ usage: null,
307
+ error: error instanceof Error ? error.message : String(error),
308
+ raw_request: null,
309
+ raw_response: null
310
+ });
311
+ throw error;
312
+ }
313
+ },
314
+ wrapStream: async ({ doStream, params, model }) => {
315
+ const startTime = Date.now();
316
+ const stepId = generateId();
317
+ const stepNumber = getNextStepNumber();
318
+ await ensureRunCreated();
319
+ const userRequestedRawChunks = params.includeRawChunks === true;
320
+ params.includeRawChunks = true;
321
+ await createStep({
322
+ id: stepId,
323
+ run_id: runId,
324
+ step_number: stepNumber,
325
+ type: "stream",
326
+ model_id: model.modelId,
327
+ provider: model.config?.provider,
328
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
329
+ input: serializeForDevTools({
330
+ prompt: params.prompt,
331
+ tools: params.tools,
332
+ toolChoice: params.toolChoice,
333
+ maxOutputTokens: params.maxOutputTokens,
334
+ temperature: params.temperature,
335
+ topP: params.topP,
336
+ topK: params.topK,
337
+ presencePenalty: params.presencePenalty,
338
+ frequencyPenalty: params.frequencyPenalty,
339
+ seed: params.seed,
340
+ responseFormat: params.responseFormat
341
+ }),
342
+ provider_options: params.providerOptions ? JSON.stringify(params.providerOptions) : null
343
+ });
344
+ try {
345
+ const { stream, request, response, ...rest } = await doStream();
346
+ const collectedOutput = {
347
+ textParts: [],
348
+ reasoningParts: [],
349
+ toolCalls: []
350
+ };
351
+ const currentText = /* @__PURE__ */ new Map();
352
+ const currentReasoning = /* @__PURE__ */ new Map();
353
+ const fullStreamChunks = [];
354
+ const rawChunks = [];
355
+ activeSteps$1.set(stepId, {
356
+ startTime,
357
+ collectedOutput,
358
+ request,
359
+ fullStreamChunks,
360
+ rawChunks
361
+ });
362
+ const transformStream = new TransformStream({
363
+ transform(chunk, controller) {
364
+ if (chunk.type === "raw") {
365
+ rawChunks.push(chunk.rawValue);
366
+ if (userRequestedRawChunks) controller.enqueue(chunk);
367
+ return;
368
+ }
369
+ fullStreamChunks.push(chunk);
370
+ switch (chunk.type) {
371
+ case "text-start":
372
+ currentText.set(chunk.id, "");
373
+ break;
374
+ case "text-delta":
375
+ currentText.set(chunk.id, (currentText.get(chunk.id) ?? "") + chunk.delta);
376
+ break;
377
+ case "text-end":
378
+ collectedOutput.textParts.push({
379
+ id: chunk.id,
380
+ text: currentText.get(chunk.id) ?? ""
381
+ });
382
+ break;
383
+ case "reasoning-start":
384
+ currentReasoning.set(chunk.id, "");
385
+ break;
386
+ case "reasoning-delta":
387
+ currentReasoning.set(chunk.id, (currentReasoning.get(chunk.id) ?? "") + chunk.delta);
388
+ break;
389
+ case "reasoning-end":
390
+ collectedOutput.reasoningParts.push({
391
+ id: chunk.id,
392
+ text: currentReasoning.get(chunk.id) ?? ""
393
+ });
394
+ break;
395
+ case "tool-call":
396
+ collectedOutput.toolCalls.push(chunk);
397
+ break;
398
+ case "file":
399
+ case "reasoning-file":
400
+ case "tool-result":
401
+ (collectedOutput.content ??= []).push(chunk);
402
+ break;
403
+ case "finish":
404
+ collectedOutput.finishReason = chunk.finishReason;
405
+ collectedOutput.usage = chunk.usage;
406
+ }
407
+ controller.enqueue(chunk);
408
+ },
409
+ async flush() {
410
+ activeSteps$1.delete(stepId);
411
+ const durationMs = Date.now() - startTime;
412
+ await updateStepResult(stepId, {
413
+ duration_ms: durationMs,
414
+ output: serializeForDevTools(collectedOutput),
415
+ usage: collectedOutput.usage ? JSON.stringify(collectedOutput.usage) : null,
416
+ error: null,
417
+ raw_request: request?.body ? JSON.stringify(request.body) : null,
418
+ raw_response: JSON.stringify(fullStreamChunks),
419
+ raw_chunks: JSON.stringify(rawChunks)
420
+ });
421
+ },
422
+ async cancel() {
423
+ activeSteps$1.delete(stepId);
424
+ const durationMs = Date.now() - startTime;
425
+ await updateStepResult(stepId, {
426
+ duration_ms: durationMs,
427
+ output: serializeForDevTools(collectedOutput),
428
+ usage: collectedOutput.usage ? JSON.stringify(collectedOutput.usage) : null,
429
+ error: "Request aborted",
430
+ raw_request: request?.body ? JSON.stringify(request.body) : null,
431
+ raw_response: JSON.stringify(fullStreamChunks),
432
+ raw_chunks: JSON.stringify(rawChunks)
433
+ });
434
+ }
435
+ });
436
+ return {
437
+ stream: stream.pipeThrough(transformStream),
438
+ request,
439
+ response,
440
+ ...rest
441
+ };
442
+ } catch (error) {
443
+ activeSteps$1.delete(stepId);
444
+ const durationMs = Date.now() - startTime;
445
+ await updateStepResult(stepId, {
446
+ duration_ms: durationMs,
447
+ output: null,
448
+ usage: null,
449
+ error: error instanceof Error ? error.message : String(error),
450
+ raw_request: null,
451
+ raw_response: null,
452
+ raw_chunks: null
453
+ });
454
+ throw error;
455
+ }
456
+ }
457
+ };
476
458
  };
477
-
478
- // src/integration.ts
479
- var activeSteps2 = /* @__PURE__ */ new Map();
480
- var signalHandlersRegistered2 = false;
481
- var registerSignalHandlers2 = () => {
482
- if (signalHandlersRegistered2) return;
483
- signalHandlersRegistered2 = true;
484
- const cleanup = async () => {
485
- if (activeSteps2.size === 0) return;
486
- const promises = Array.from(activeSteps2.entries()).map(
487
- async ([stepId, data]) => {
488
- const durationMs = Date.now() - data.startTime;
489
- await updateStepResult(stepId, {
490
- duration_ms: durationMs,
491
- output: null,
492
- usage: null,
493
- error: "Request aborted",
494
- raw_request: null,
495
- raw_response: null,
496
- raw_chunks: null
497
- });
498
- }
499
- );
500
- await Promise.all(promises);
501
- await notifyServerAsync("step-update");
502
- };
503
- process.on("SIGINT", () => {
504
- cleanup().then(() => process.exit(130));
505
- });
506
- process.on("SIGTERM", () => {
507
- cleanup().then(() => process.exit(143));
508
- });
459
+ //#endregion
460
+ //#region src/integration.ts
461
+ const activeSteps = /* @__PURE__ */ new Map();
462
+ let signalHandlersRegistered = false;
463
+ const registerSignalHandlers = () => {
464
+ if (signalHandlersRegistered) return;
465
+ signalHandlersRegistered = true;
466
+ const cleanup = async () => {
467
+ if (activeSteps.size === 0) return;
468
+ const promises = Array.from(activeSteps.entries()).map(async ([stepId, data]) => {
469
+ const durationMs = Date.now() - data.startTime;
470
+ await updateStepResult(stepId, {
471
+ duration_ms: durationMs,
472
+ output: null,
473
+ usage: null,
474
+ error: "Request aborted",
475
+ raw_request: null,
476
+ raw_response: null,
477
+ raw_chunks: null
478
+ });
479
+ });
480
+ await Promise.all(promises);
481
+ await notifyServerAsync("step-update");
482
+ };
483
+ process.on("SIGINT", () => {
484
+ cleanup().then(() => process.exit(130));
485
+ });
486
+ process.on("SIGTERM", () => {
487
+ cleanup().then(() => process.exit(143));
488
+ });
509
489
  };
510
- var generateRunId2 = () => {
511
- const now = /* @__PURE__ */ new Date();
512
- const timestamp = now.toISOString().replace(/[-:T.Z]/g, "").slice(0, 17);
513
- const uniqueId = crypto.randomUUID().slice(0, 8);
514
- return `${timestamp}-${uniqueId}`;
490
+ const generateRunId = () => {
491
+ return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:T.Z]/g, "").slice(0, 17)}-${crypto.randomUUID().slice(0, 8)}`;
515
492
  };
516
493
  function getOperationType(operationId) {
517
- if (operationId === "ai.streamText" || operationId === "ai.streamObject") {
518
- return "stream";
519
- }
520
- return "generate";
494
+ if (operationId === "ai.streamText" || operationId === "ai.streamObject") return "stream";
495
+ return "generate";
521
496
  }
497
+ /**
498
+ * Creates a devtools telemetry integration that logs all AI SDK operations
499
+ * to the devtools viewer.
500
+ *
501
+ * Usage:
502
+ * ```ts
503
+ * import { registerTelemetry } from 'ai';
504
+ * import { DevToolsTelemetry } from '@ai-sdk/devtools';
505
+ *
506
+ * registerTelemetry(DevToolsTelemetry());
507
+ * ```
508
+ *
509
+ * Telemetry is enabled by default — no need to set `telemetry`
510
+ * unless you want to configure `functionId`, `recordInputs`, or `recordOutputs`.
511
+ */
522
512
  function DevToolsTelemetry(options = {}) {
523
- if (process.env.NODE_ENV === "production") {
524
- throw new Error(
525
- "@ai-sdk/devtools should not be used in production. Remove DevToolsTelemetry from your telemetry configuration for production builds."
526
- );
527
- }
528
- registerSignalHandlers2();
529
- const callStates = /* @__PURE__ */ new Map();
530
- const toolContextMap = /* @__PURE__ */ new Map();
531
- let currentToolCallId = null;
532
- function resolveParentInfo() {
533
- if (!currentToolCallId) return void 0;
534
- const toolCallContext = toolContextMap.get(currentToolCallId);
535
- if (!toolCallContext) return void 0;
536
- const parentState = callStates.get(toolCallContext.parentCallId);
537
- if (!parentState) return void 0;
538
- let latestStepId;
539
- let latestStepNumber = -1;
540
- for (const [stepNumber, stepState] of parentState.stepStates) {
541
- if (stepNumber > latestStepNumber) {
542
- latestStepNumber = stepNumber;
543
- latestStepId = stepState.stepId;
544
- }
545
- }
546
- if (!latestStepId) return void 0;
547
- return { runId: parentState.runId, stepId: latestStepId };
548
- }
549
- function getOrCreateCallState(callId, operationId, event) {
550
- let state = callStates.get(callId);
551
- if (state) return state;
552
- state = {
553
- runId: generateRunId2(),
554
- operationType: getOperationType(operationId),
555
- functionId: event.functionId,
556
- settings: {
557
- maxOutputTokens: event.maxOutputTokens,
558
- temperature: event.temperature,
559
- topP: event.topP,
560
- topK: event.topK,
561
- presencePenalty: event.presencePenalty,
562
- frequencyPenalty: event.frequencyPenalty,
563
- seed: event.seed
564
- },
565
- stepStates: /* @__PURE__ */ new Map(),
566
- stepNumberOffset: 0
567
- };
568
- callStates.set(callId, state);
569
- return state;
570
- }
571
- const integration = {
572
- onStart: async (event) => {
573
- const operationId = event.operationId;
574
- if (operationId === "ai.embed" || operationId === "ai.embedMany" || operationId === "ai.rerank") {
575
- return;
576
- }
577
- const startEvent = event;
578
- const parentInfo = resolveParentInfo();
579
- const state = getOrCreateCallState(
580
- startEvent.callId,
581
- operationId,
582
- startEvent
583
- );
584
- if (options.runId != null) {
585
- state.runId = options.runId;
586
- state.stepNumberOffset = (await getStepsForRun(options.runId)).length;
587
- }
588
- await createRun(state.runId, parentInfo, state.functionId);
589
- },
590
- onStepStart: async (event) => {
591
- const stepStartEvent = event;
592
- const state = callStates.get(stepStartEvent.callId);
593
- if (!state) return;
594
- const stepId = crypto.randomUUID();
595
- const startTime = Date.now();
596
- const stepState = {
597
- stepId,
598
- startTime
599
- };
600
- const stepNumber = stepStartEvent.steps.length;
601
- state.stepStates.set(stepNumber, stepState);
602
- activeSteps2.set(stepId, stepState);
603
- const prompt = stepStartEvent.promptMessages ?? stepStartEvent.messages;
604
- await createStep({
605
- id: stepId,
606
- run_id: state.runId,
607
- step_number: state.stepNumberOffset + stepNumber + 1,
608
- type: state.operationType,
609
- model_id: stepStartEvent.modelId,
610
- provider: stepStartEvent.provider ?? null,
611
- started_at: (/* @__PURE__ */ new Date()).toISOString(),
612
- input: serializeForDevTools({
613
- prompt,
614
- tools: stepStartEvent.tools ? Object.entries(stepStartEvent.tools).map(([name, tool]) => ({
615
- name,
616
- description: tool.description,
617
- parameters: tool.parameters
618
- })) : void 0,
619
- toolChoice: stepStartEvent.toolChoice,
620
- maxOutputTokens: state.settings.maxOutputTokens,
621
- temperature: state.settings.temperature,
622
- topP: state.settings.topP,
623
- topK: state.settings.topK,
624
- presencePenalty: state.settings.presencePenalty,
625
- frequencyPenalty: state.settings.frequencyPenalty,
626
- seed: state.settings.seed
627
- }),
628
- provider_options: stepStartEvent.providerOptions ? JSON.stringify(stepStartEvent.providerOptions) : null
629
- });
630
- },
631
- onObjectStepStart: async (event) => {
632
- const stepStartEvent = event;
633
- const state = callStates.get(stepStartEvent.callId);
634
- if (!state) return;
635
- const stepId = crypto.randomUUID();
636
- const startTime = Date.now();
637
- const stepState = {
638
- stepId,
639
- startTime
640
- };
641
- state.stepStates.set(stepStartEvent.stepNumber, stepState);
642
- activeSteps2.set(stepId, stepState);
643
- await createStep({
644
- id: stepId,
645
- run_id: state.runId,
646
- step_number: state.stepNumberOffset + stepStartEvent.stepNumber + 1,
647
- type: state.operationType,
648
- model_id: stepStartEvent.modelId,
649
- provider: stepStartEvent.provider ?? null,
650
- started_at: (/* @__PURE__ */ new Date()).toISOString(),
651
- input: serializeForDevTools({
652
- prompt: stepStartEvent.promptMessages,
653
- maxOutputTokens: state.settings.maxOutputTokens,
654
- temperature: state.settings.temperature,
655
- topP: state.settings.topP,
656
- topK: state.settings.topK,
657
- presencePenalty: state.settings.presencePenalty,
658
- frequencyPenalty: state.settings.frequencyPenalty,
659
- seed: state.settings.seed
660
- }),
661
- provider_options: stepStartEvent.providerOptions ? JSON.stringify(stepStartEvent.providerOptions) : null
662
- });
663
- },
664
- onStepEnd: async (event) => {
665
- const stepResult = event;
666
- const state = callStates.get(stepResult.callId);
667
- if (!state) return;
668
- const stepState = state.stepStates.get(stepResult.stepNumber);
669
- if (!stepState) return;
670
- activeSteps2.delete(stepState.stepId);
671
- const durationMs = Date.now() - stepState.startTime;
672
- const output = {
673
- content: stepResult.content,
674
- finishReason: stepResult.finishReason,
675
- response: {
676
- id: stepResult.response.id,
677
- modelId: stepResult.response.modelId,
678
- timestamp: stepResult.response.timestamp,
679
- messages: stepResult.response.messages
680
- }
681
- };
682
- await updateStepResult(stepState.stepId, {
683
- duration_ms: durationMs,
684
- output: serializeForDevTools(output),
685
- usage: stepResult.usage ? JSON.stringify(stepResult.usage) : null,
686
- error: null,
687
- raw_request: stepResult.request?.body ? JSON.stringify(stepResult.request.body) : null,
688
- raw_response: stepResult.response?.body ? JSON.stringify(stepResult.response.body) : null,
689
- raw_chunks: null
690
- });
691
- state.stepStates.delete(stepResult.stepNumber);
692
- },
693
- onObjectStepEnd: async (event) => {
694
- const stepResult = event;
695
- const state = callStates.get(stepResult.callId);
696
- if (!state) return;
697
- const stepState = state.stepStates.get(stepResult.stepNumber);
698
- if (!stepState) return;
699
- activeSteps2.delete(stepState.stepId);
700
- const durationMs = Date.now() - stepState.startTime;
701
- const output = {
702
- finishReason: stepResult.finishReason,
703
- objectText: stepResult.objectText,
704
- response: {
705
- id: stepResult.response.id,
706
- modelId: stepResult.response.modelId,
707
- timestamp: stepResult.response.timestamp
708
- }
709
- };
710
- await updateStepResult(stepState.stepId, {
711
- duration_ms: durationMs,
712
- output: JSON.stringify(output),
713
- usage: stepResult.usage ? JSON.stringify(stepResult.usage) : null,
714
- error: null,
715
- raw_request: stepResult.request?.body ? JSON.stringify(stepResult.request.body) : null,
716
- raw_response: stepResult.response?.body ? JSON.stringify(stepResult.response.body) : null
717
- });
718
- state.stepStates.delete(stepResult.stepNumber);
719
- },
720
- onEnd: async (event) => {
721
- const endEvent = event;
722
- callStates.delete(endEvent.callId);
723
- },
724
- onError: async (error) => {
725
- const errorObj = error;
726
- const callId = errorObj?.callId;
727
- if (!callId) return;
728
- const state = callStates.get(callId);
729
- if (!state) return;
730
- const cause = errorObj?.error ?? error;
731
- const errorMessage = cause instanceof Error ? cause.message : String(cause);
732
- for (const [, stepState] of state.stepStates) {
733
- activeSteps2.delete(stepState.stepId);
734
- const durationMs = Date.now() - stepState.startTime;
735
- await updateStepResult(stepState.stepId, {
736
- duration_ms: durationMs,
737
- output: null,
738
- usage: null,
739
- error: errorMessage,
740
- raw_request: null,
741
- raw_response: null,
742
- raw_chunks: null
743
- });
744
- }
745
- callStates.delete(callId);
746
- },
747
- executeTool: async ({ callId, toolCallId, execute }) => {
748
- toolContextMap.set(toolCallId, {
749
- parentCallId: callId,
750
- parentToolCallId: toolCallId
751
- });
752
- const previousToolCallId = currentToolCallId;
753
- currentToolCallId = toolCallId;
754
- try {
755
- return await execute();
756
- } finally {
757
- currentToolCallId = previousToolCallId;
758
- toolContextMap.delete(toolCallId);
759
- }
760
- }
761
- };
762
- return integration;
513
+ if (process.env.NODE_ENV === "production") throw new Error("@ai-sdk/devtools should not be used in production. Remove DevToolsTelemetry from your telemetry configuration for production builds.");
514
+ registerSignalHandlers();
515
+ const callStates = /* @__PURE__ */ new Map();
516
+ const toolContextMap = /* @__PURE__ */ new Map();
517
+ let currentToolCallId = null;
518
+ function resolveParentInfo() {
519
+ if (!currentToolCallId) return void 0;
520
+ const toolCallContext = toolContextMap.get(currentToolCallId);
521
+ if (!toolCallContext) return void 0;
522
+ const parentState = callStates.get(toolCallContext.parentCallId);
523
+ if (!parentState) return void 0;
524
+ let latestStepId;
525
+ let latestStepNumber = -1;
526
+ for (const [stepNumber, stepState] of parentState.stepStates) if (stepNumber > latestStepNumber) {
527
+ latestStepNumber = stepNumber;
528
+ latestStepId = stepState.stepId;
529
+ }
530
+ if (!latestStepId) return void 0;
531
+ return {
532
+ runId: parentState.runId,
533
+ stepId: latestStepId
534
+ };
535
+ }
536
+ function getOrCreateCallState(callId, operationId, event) {
537
+ let state = callStates.get(callId);
538
+ if (state) return state;
539
+ state = {
540
+ runId: generateRunId(),
541
+ operationType: getOperationType(operationId),
542
+ functionId: event.functionId,
543
+ settings: {
544
+ maxOutputTokens: event.maxOutputTokens,
545
+ temperature: event.temperature,
546
+ topP: event.topP,
547
+ topK: event.topK,
548
+ presencePenalty: event.presencePenalty,
549
+ frequencyPenalty: event.frequencyPenalty,
550
+ seed: event.seed
551
+ },
552
+ stepStates: /* @__PURE__ */ new Map(),
553
+ stepNumberOffset: 0
554
+ };
555
+ callStates.set(callId, state);
556
+ return state;
557
+ }
558
+ return {
559
+ onStart: async (event) => {
560
+ const operationId = event.operationId;
561
+ if (operationId === "ai.embed" || operationId === "ai.embedMany" || operationId === "ai.rerank") return;
562
+ const startEvent = event;
563
+ const parentInfo = resolveParentInfo();
564
+ const state = getOrCreateCallState(startEvent.callId, operationId, startEvent);
565
+ if (options.runId != null) {
566
+ state.runId = options.runId;
567
+ state.stepNumberOffset = (await getStepsForRun(options.runId)).length;
568
+ }
569
+ await createRun(state.runId, parentInfo, state.functionId);
570
+ },
571
+ onStepStart: async (event) => {
572
+ const stepStartEvent = event;
573
+ const state = callStates.get(stepStartEvent.callId);
574
+ if (!state) return;
575
+ const stepId = crypto.randomUUID();
576
+ const stepState = {
577
+ stepId,
578
+ startTime: Date.now()
579
+ };
580
+ const stepNumber = stepStartEvent.steps.length;
581
+ state.stepStates.set(stepNumber, stepState);
582
+ activeSteps.set(stepId, stepState);
583
+ const prompt = stepStartEvent.promptMessages ?? stepStartEvent.messages;
584
+ await createStep({
585
+ id: stepId,
586
+ run_id: state.runId,
587
+ step_number: state.stepNumberOffset + stepNumber + 1,
588
+ type: state.operationType,
589
+ model_id: stepStartEvent.modelId,
590
+ provider: stepStartEvent.provider ?? null,
591
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
592
+ input: serializeForDevTools({
593
+ prompt,
594
+ tools: stepStartEvent.tools ? Object.entries(stepStartEvent.tools).map(([name, tool]) => ({
595
+ name,
596
+ description: tool.description,
597
+ parameters: tool.parameters
598
+ })) : void 0,
599
+ toolChoice: stepStartEvent.toolChoice,
600
+ maxOutputTokens: state.settings.maxOutputTokens,
601
+ temperature: state.settings.temperature,
602
+ topP: state.settings.topP,
603
+ topK: state.settings.topK,
604
+ presencePenalty: state.settings.presencePenalty,
605
+ frequencyPenalty: state.settings.frequencyPenalty,
606
+ seed: state.settings.seed
607
+ }),
608
+ provider_options: stepStartEvent.providerOptions ? JSON.stringify(stepStartEvent.providerOptions) : null
609
+ });
610
+ },
611
+ onObjectStepStart: async (event) => {
612
+ const stepStartEvent = event;
613
+ const state = callStates.get(stepStartEvent.callId);
614
+ if (!state) return;
615
+ const stepId = crypto.randomUUID();
616
+ const stepState = {
617
+ stepId,
618
+ startTime: Date.now()
619
+ };
620
+ state.stepStates.set(stepStartEvent.stepNumber, stepState);
621
+ activeSteps.set(stepId, stepState);
622
+ await createStep({
623
+ id: stepId,
624
+ run_id: state.runId,
625
+ step_number: state.stepNumberOffset + stepStartEvent.stepNumber + 1,
626
+ type: state.operationType,
627
+ model_id: stepStartEvent.modelId,
628
+ provider: stepStartEvent.provider ?? null,
629
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
630
+ input: serializeForDevTools({
631
+ prompt: stepStartEvent.promptMessages,
632
+ maxOutputTokens: state.settings.maxOutputTokens,
633
+ temperature: state.settings.temperature,
634
+ topP: state.settings.topP,
635
+ topK: state.settings.topK,
636
+ presencePenalty: state.settings.presencePenalty,
637
+ frequencyPenalty: state.settings.frequencyPenalty,
638
+ seed: state.settings.seed
639
+ }),
640
+ provider_options: stepStartEvent.providerOptions ? JSON.stringify(stepStartEvent.providerOptions) : null
641
+ });
642
+ },
643
+ onStepEnd: async (event) => {
644
+ const stepResult = event;
645
+ const state = callStates.get(stepResult.callId);
646
+ if (!state) return;
647
+ const stepState = state.stepStates.get(stepResult.stepNumber);
648
+ if (!stepState) return;
649
+ activeSteps.delete(stepState.stepId);
650
+ const durationMs = Date.now() - stepState.startTime;
651
+ const output = {
652
+ content: stepResult.content,
653
+ finishReason: stepResult.finishReason,
654
+ response: {
655
+ id: stepResult.response.id,
656
+ modelId: stepResult.response.modelId,
657
+ timestamp: stepResult.response.timestamp,
658
+ messages: stepResult.response.messages
659
+ }
660
+ };
661
+ await updateStepResult(stepState.stepId, {
662
+ duration_ms: durationMs,
663
+ output: serializeForDevTools(output),
664
+ usage: stepResult.usage ? JSON.stringify(stepResult.usage) : null,
665
+ error: null,
666
+ raw_request: stepResult.request?.body ? JSON.stringify(stepResult.request.body) : null,
667
+ raw_response: stepResult.response?.body ? JSON.stringify(stepResult.response.body) : null,
668
+ raw_chunks: null
669
+ });
670
+ state.stepStates.delete(stepResult.stepNumber);
671
+ },
672
+ onObjectStepEnd: async (event) => {
673
+ const stepResult = event;
674
+ const state = callStates.get(stepResult.callId);
675
+ if (!state) return;
676
+ const stepState = state.stepStates.get(stepResult.stepNumber);
677
+ if (!stepState) return;
678
+ activeSteps.delete(stepState.stepId);
679
+ const durationMs = Date.now() - stepState.startTime;
680
+ const output = {
681
+ finishReason: stepResult.finishReason,
682
+ objectText: stepResult.objectText,
683
+ response: {
684
+ id: stepResult.response.id,
685
+ modelId: stepResult.response.modelId,
686
+ timestamp: stepResult.response.timestamp
687
+ }
688
+ };
689
+ await updateStepResult(stepState.stepId, {
690
+ duration_ms: durationMs,
691
+ output: JSON.stringify(output),
692
+ usage: stepResult.usage ? JSON.stringify(stepResult.usage) : null,
693
+ error: null,
694
+ raw_request: stepResult.request?.body ? JSON.stringify(stepResult.request.body) : null,
695
+ raw_response: stepResult.response?.body ? JSON.stringify(stepResult.response.body) : null
696
+ });
697
+ state.stepStates.delete(stepResult.stepNumber);
698
+ },
699
+ onEnd: async (event) => {
700
+ const endEvent = event;
701
+ callStates.delete(endEvent.callId);
702
+ },
703
+ onError: async (error) => {
704
+ const errorObj = error;
705
+ const callId = errorObj?.callId;
706
+ if (!callId) return;
707
+ const state = callStates.get(callId);
708
+ if (!state) return;
709
+ const cause = errorObj?.error ?? error;
710
+ const errorMessage = cause instanceof Error ? cause.message : String(cause);
711
+ for (const [, stepState] of state.stepStates) {
712
+ activeSteps.delete(stepState.stepId);
713
+ const durationMs = Date.now() - stepState.startTime;
714
+ await updateStepResult(stepState.stepId, {
715
+ duration_ms: durationMs,
716
+ output: null,
717
+ usage: null,
718
+ error: errorMessage,
719
+ raw_request: null,
720
+ raw_response: null,
721
+ raw_chunks: null
722
+ });
723
+ }
724
+ callStates.delete(callId);
725
+ },
726
+ executeTool: async ({ callId, toolCallId, execute }) => {
727
+ toolContextMap.set(toolCallId, {
728
+ parentCallId: callId,
729
+ parentToolCallId: toolCallId
730
+ });
731
+ const previousToolCallId = currentToolCallId;
732
+ currentToolCallId = toolCallId;
733
+ try {
734
+ return await execute();
735
+ } finally {
736
+ currentToolCallId = previousToolCallId;
737
+ toolContextMap.delete(toolCallId);
738
+ }
739
+ }
740
+ };
763
741
  }
764
- export {
765
- DevToolsTelemetry,
766
- devToolsMiddleware
767
- };
742
+ //#endregion
743
+ export { DevToolsTelemetry, devToolsMiddleware };
744
+
745
+ //# sourceMappingURL=index.js.map