autotel-devtools 2.0.6 → 2.1.0

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.
@@ -0,0 +1,586 @@
1
+ 'use strict';
2
+
3
+ // src/widget/genai/detect.ts
4
+ var GENAI_MARKERS = [
5
+ "gen_ai.system",
6
+ "gen_ai.provider.name",
7
+ "gen_ai.operation.name",
8
+ "ai.model.provider"
9
+ ];
10
+ function isGenAiSpan(span) {
11
+ const attrs = span.attributes ?? {};
12
+ for (const key of GENAI_MARKERS) {
13
+ if (attrs[key] != null) return true;
14
+ }
15
+ return false;
16
+ }
17
+
18
+ // src/widget/genai/prices.ts
19
+ var TABLE = {
20
+ "openai/gpt-4o": { inputPerMTok: 2.5, outputPerMTok: 10 },
21
+ "openai/gpt-4o-mini": { inputPerMTok: 0.15, outputPerMTok: 0.6 },
22
+ "openai/gpt-4-turbo": { inputPerMTok: 10, outputPerMTok: 30 },
23
+ "openai/gpt-3.5-turbo": { inputPerMTok: 0.5, outputPerMTok: 1.5 },
24
+ "anthropic/claude-opus-4": { inputPerMTok: 15, outputPerMTok: 75 },
25
+ "anthropic/claude-sonnet-4": { inputPerMTok: 3, outputPerMTok: 15 },
26
+ "anthropic/claude-3-5-sonnet": { inputPerMTok: 3, outputPerMTok: 15 },
27
+ "anthropic/claude-3-5-haiku": { inputPerMTok: 0.8, outputPerMTok: 4 },
28
+ "anthropic/claude-3-opus": { inputPerMTok: 15, outputPerMTok: 75 },
29
+ "anthropic/claude-3-haiku": { inputPerMTok: 0.25, outputPerMTok: 1.25 },
30
+ "google/gemini-2.5-flash": { inputPerMTok: 0.3, outputPerMTok: 2.5 },
31
+ "google/gemini-2.0-flash": { inputPerMTok: 0.1, outputPerMTok: 0.4 },
32
+ "google/gemini-1.5-pro": { inputPerMTok: 1.25, outputPerMTok: 5 },
33
+ "google/gemini-1.5-flash": { inputPerMTok: 0.075, outputPerMTok: 0.3 },
34
+ "mistral/mistral-large": { inputPerMTok: 2, outputPerMTok: 6 },
35
+ "mistral/mistral-small": { inputPerMTok: 0.2, outputPerMTok: 0.6 },
36
+ "groq/llama-3.1-70b": { inputPerMTok: 0.59, outputPerMTok: 0.79 },
37
+ "deepseek/deepseek-chat": { inputPerMTok: 0.27, outputPerMTok: 1.1 }
38
+ };
39
+ function normalizeProvider(provider) {
40
+ const p = provider.toLowerCase();
41
+ if (p === "az.ai.openai" || p === "azure_openai") return "openai";
42
+ if (p === "vertex_ai" || p === "gcp.vertex_ai" || p === "gcp.gemini") return "google";
43
+ return p;
44
+ }
45
+ var SORTED_KEYS = Object.keys(TABLE).sort((a, b) => {
46
+ const am = a.split("/")[1] ?? "";
47
+ const bm = b.split("/")[1] ?? "";
48
+ return bm.length - am.length;
49
+ });
50
+ function lookupPrice(provider, model) {
51
+ const normalizedProvider = normalizeProvider(provider);
52
+ const normalizedModel = model.toLowerCase();
53
+ for (const key of SORTED_KEYS) {
54
+ const [tableProvider, tableModel] = key.split("/");
55
+ if (tableProvider === normalizedProvider && normalizedModel.startsWith(tableModel)) {
56
+ return TABLE[key];
57
+ }
58
+ }
59
+ return void 0;
60
+ }
61
+ function priceCall(inputs) {
62
+ const entry = lookupPrice(inputs.provider, inputs.model);
63
+ if (!entry) {
64
+ return { currency: "USD", input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0, source: "unknown" };
65
+ }
66
+ const cacheReadRate = entry.cacheReadPerMTok ?? entry.inputPerMTok * 0.1;
67
+ const cacheWriteRate = entry.cacheWritePerMTok ?? entry.inputPerMTok * 1.25;
68
+ const cacheRead = (inputs.cacheReadInputTokens ?? 0) / 1e6 * cacheReadRate;
69
+ const cacheWrite = (inputs.cacheCreationInputTokens ?? 0) / 1e6 * cacheWriteRate;
70
+ const billableInputTokens = Math.max(
71
+ 0,
72
+ (inputs.inputTokens ?? 0) - (inputs.cacheReadInputTokens ?? 0) - (inputs.cacheCreationInputTokens ?? 0)
73
+ );
74
+ const input = billableInputTokens / 1e6 * entry.inputPerMTok;
75
+ const output = (inputs.outputTokens ?? 0) / 1e6 * entry.outputPerMTok;
76
+ const total = input + output + cacheRead + cacheWrite;
77
+ return { currency: "USD", input, output, cacheRead, cacheWrite, total, source: "table" };
78
+ }
79
+
80
+ // src/widget/genai/normalize.ts
81
+ function str(v) {
82
+ return typeof v === "string" ? v : void 0;
83
+ }
84
+ function num(v) {
85
+ if (typeof v === "number") return v;
86
+ if (typeof v === "string" && v !== "") {
87
+ const n = Number(v);
88
+ return Number.isFinite(n) ? n : void 0;
89
+ }
90
+ return void 0;
91
+ }
92
+ function strArray(v) {
93
+ if (Array.isArray(v) && v.every((x) => typeof x === "string")) return v;
94
+ if (typeof v === "string") return [v];
95
+ return void 0;
96
+ }
97
+ function bool(v) {
98
+ if (typeof v === "boolean") return v;
99
+ if (v === "true") return true;
100
+ if (v === "false") return false;
101
+ return void 0;
102
+ }
103
+ function parseJson(v) {
104
+ if (v == null) return void 0;
105
+ if (typeof v === "object") return v;
106
+ if (typeof v !== "string") return void 0;
107
+ try {
108
+ return JSON.parse(v);
109
+ } catch {
110
+ return void 0;
111
+ }
112
+ }
113
+ function normalizeMessageParts(raw) {
114
+ if (Array.isArray(raw.parts)) {
115
+ return raw.parts.map((p) => {
116
+ const type = p.type ?? "text";
117
+ if (type === "text") return { kind: "text", text: String(p.content ?? "") };
118
+ if (type === "image") return { kind: "image", mediaType: "image/*", dataRef: String(p.content ?? "") };
119
+ if (type === "audio") return { kind: "audio", mediaType: "audio/*", dataRef: String(p.content ?? "") };
120
+ return { kind: "json", value: p.content };
121
+ });
122
+ }
123
+ if (Array.isArray(raw.content)) {
124
+ return raw.content.map((p) => {
125
+ if (p.type === "text" || p.text !== void 0) {
126
+ return { kind: "text", text: String(p.text ?? "") };
127
+ }
128
+ if (p.type === "image") {
129
+ return { kind: "image", mediaType: p.mimeType ?? "image/*", dataRef: String(p.image ?? p.data ?? "") };
130
+ }
131
+ if (p.type === "audio") {
132
+ return { kind: "audio", mediaType: p.mimeType ?? "audio/*", dataRef: String(p.data ?? "") };
133
+ }
134
+ return { kind: "json", value: p };
135
+ });
136
+ }
137
+ if (typeof raw.content === "string") return [{ kind: "text", text: raw.content }];
138
+ if (raw.content != null) return [{ kind: "json", value: raw.content }];
139
+ return [];
140
+ }
141
+ function normalizeMessage(raw) {
142
+ const role = raw.role ?? "user";
143
+ if (Array.isArray(raw.content)) {
144
+ const content = raw.content;
145
+ const hasToolPart = content.some(
146
+ (p) => p.type === "tool-call" || p.type === "tool-result"
147
+ );
148
+ if (hasToolPart) {
149
+ const toolCalls = [];
150
+ const parts = [];
151
+ const toolResults = [];
152
+ for (const part of content) {
153
+ if (part.type === "tool-call") {
154
+ toolCalls.push({
155
+ id: part.toolCallId,
156
+ name: part.toolName ?? "",
157
+ arguments: parseJson(part.input) ?? part.input ?? {}
158
+ });
159
+ } else if (part.type === "tool-result") {
160
+ const out = part.output;
161
+ const value = out && typeof out === "object" && "value" in out ? out.value : out;
162
+ toolResults.push({ id: part.toolCallId, value });
163
+ } else if (part.type === "text" || part.text !== void 0) {
164
+ parts.push({ kind: "text", text: String(part.text ?? "") });
165
+ }
166
+ }
167
+ if (toolResults.length > 1) {
168
+ const msg3 = { role, parts: [] };
169
+ msg3._toolResults = toolResults;
170
+ return msg3;
171
+ }
172
+ const msg2 = { role, parts };
173
+ if (toolCalls.length > 0) msg2.toolCalls = toolCalls;
174
+ if (toolResults.length === 1) {
175
+ msg2.toolCallId = toolResults[0].id;
176
+ msg2.parts = [{ kind: "json", value: toolResults[0].value }];
177
+ }
178
+ if (raw.finish_reason) msg2.finishReason = raw.finish_reason;
179
+ return msg2;
180
+ }
181
+ }
182
+ const msg = { role, parts: normalizeMessageParts(raw) };
183
+ if (Array.isArray(raw.tool_calls) && raw.tool_calls.length > 0) {
184
+ msg.toolCalls = raw.tool_calls.map((tc) => ({
185
+ id: tc.id,
186
+ name: tc.function?.name ?? "",
187
+ arguments: parseJson(tc.function?.arguments) ?? tc.function?.arguments ?? {},
188
+ type: tc.type
189
+ }));
190
+ }
191
+ if (raw.tool_call_id) msg.toolCallId = raw.tool_call_id;
192
+ if (raw.finish_reason) msg.finishReason = raw.finish_reason;
193
+ return msg;
194
+ }
195
+ function readMessagesAttribute(attrs, key) {
196
+ const value = parseJson(attrs[key]);
197
+ if (!Array.isArray(value)) return void 0;
198
+ return value.map(normalizeMessage);
199
+ }
200
+ function messagesFromEvents(events) {
201
+ if (!events || events.length === 0) return [];
202
+ const out = [];
203
+ for (const ev of events) {
204
+ const attrs = ev.attributes ?? {};
205
+ if (ev.name === "gen_ai.choice") {
206
+ const message = parseJson(attrs.message) ?? attrs.message;
207
+ const finishReason = str(attrs.finish_reason);
208
+ if (message) {
209
+ const normalized = normalizeMessage(message);
210
+ if (finishReason) normalized.finishReason = finishReason;
211
+ out.push(normalized);
212
+ }
213
+ continue;
214
+ }
215
+ const m = ev.name.match(/^gen_ai\.(system|user|assistant|tool)\.message$/);
216
+ if (!m) continue;
217
+ const role = m[1];
218
+ const content = attrs.content;
219
+ const parsed = typeof content === "string" ? parseJson(content) ?? content : content;
220
+ out.push(normalizeMessage({
221
+ role,
222
+ content: parsed,
223
+ tool_calls: parseJson(attrs.tool_calls),
224
+ tool_call_id: str(attrs.id) ?? str(attrs.tool_call_id)
225
+ }));
226
+ }
227
+ return out;
228
+ }
229
+ function readToolDefinitions(attrs) {
230
+ const raw = parseJson(
231
+ attrs["gen_ai.tool.definitions"] ?? attrs["gen_ai.orchestrator.agent.definitions"]
232
+ );
233
+ if (!Array.isArray(raw)) return void 0;
234
+ return raw.filter((d) => typeof d.name === "string").map((d) => ({
235
+ name: d.name,
236
+ description: d.description,
237
+ type: d.type,
238
+ schema: d.schema ?? d.parameters
239
+ }));
240
+ }
241
+ function readUsage(attrs) {
242
+ const inputTokens = num(attrs["gen_ai.usage.input_tokens"]) ?? num(attrs["gen_ai.usage.prompt_tokens"]) ?? num(attrs["llm.usage.prompt_tokens"]) ?? num(attrs["ai.usage.inputTokens"]);
243
+ const outputTokens = num(attrs["gen_ai.usage.output_tokens"]) ?? num(attrs["gen_ai.usage.completion_tokens"]) ?? num(attrs["llm.usage.completion_tokens"]) ?? num(attrs["ai.usage.outputTokens"]);
244
+ return {
245
+ inputTokens,
246
+ outputTokens,
247
+ reasoningOutputTokens: num(attrs["gen_ai.usage.reasoning.output_tokens"]),
248
+ cacheReadInputTokens: num(attrs["gen_ai.usage.cache_read.input_tokens"]),
249
+ cacheCreationInputTokens: num(attrs["gen_ai.usage.cache_creation.input_tokens"])
250
+ };
251
+ }
252
+ function normalizeProviderName(raw) {
253
+ const p = raw.toLowerCase();
254
+ if (p === "az.ai.openai" || p === "azure_openai") return "openai";
255
+ if (p === "gcp.vertex_ai" || p === "vertex_ai" || p === "gcp.gemini" || p === "google-gla" || // Logfire / Pydantic AI naming for Google GenAI library
256
+ p === "google_genai" || p === "gemini") {
257
+ return "google";
258
+ }
259
+ return p;
260
+ }
261
+ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
262
+ "gen_ai.system",
263
+ "gen_ai.provider.name",
264
+ "gen_ai.operation.name",
265
+ "gen_ai.request.model",
266
+ "gen_ai.response.model",
267
+ "gen_ai.response.id",
268
+ "gen_ai.response.finish_reasons",
269
+ "gen_ai.request.temperature",
270
+ "gen_ai.request.top_p",
271
+ "gen_ai.request.top_k",
272
+ "gen_ai.request.max_tokens",
273
+ "gen_ai.request.stop_sequences",
274
+ "gen_ai.request.seed",
275
+ "gen_ai.request.frequency_penalty",
276
+ "gen_ai.request.presence_penalty",
277
+ "gen_ai.request.choice.count",
278
+ "gen_ai.usage.input_tokens",
279
+ "gen_ai.usage.output_tokens",
280
+ "gen_ai.usage.prompt_tokens",
281
+ "gen_ai.usage.completion_tokens",
282
+ "gen_ai.usage.reasoning.output_tokens",
283
+ "gen_ai.usage.cache_read.input_tokens",
284
+ "gen_ai.usage.cache_creation.input_tokens",
285
+ "gen_ai.input.messages",
286
+ "gen_ai.output.messages",
287
+ "gen_ai.input.messages.ref",
288
+ "gen_ai.output.messages.ref",
289
+ "gen_ai.system_instructions",
290
+ "gen_ai.tool.definitions",
291
+ "gen_ai.orchestrator.agent.definitions",
292
+ "gen_ai.agent.id",
293
+ "gen_ai.agent.name",
294
+ "gen_ai.agent.description",
295
+ "gen_ai.handoff.from_agent",
296
+ "gen_ai.handoff.to_agent",
297
+ "gen_ai.guardrail.name",
298
+ "gen_ai.guardrail.triggered",
299
+ "gen_ai.conversation.id",
300
+ "gen_ai.evaluation.name",
301
+ "gen_ai.evaluation.score.value",
302
+ "gen_ai.evaluation.score.label",
303
+ "gen_ai.evaluation.explanation",
304
+ "gen_ai.audio.input.format",
305
+ "gen_ai.audio.output.format",
306
+ "gen_ai.speech.voice",
307
+ "gen_ai.speech.input_text",
308
+ "gen_ai.transcription.text",
309
+ "gen_ai.embeddings.dimension.count",
310
+ "gen_ai.openai.request.service_tier",
311
+ "gen_ai.openai.response.service_tier",
312
+ "gen_ai.openai.response.system_fingerprint",
313
+ "gen_ai.openai.request.response_format",
314
+ "openai.request.service_tier",
315
+ "openai.response.service_tier",
316
+ "openai.response.system_fingerprint",
317
+ "openai.request.response_format"
318
+ ]);
319
+ function toGenAiSpan(span) {
320
+ const attrs = span.attributes ?? {};
321
+ const rawProvider = str(attrs["gen_ai.provider.name"]) ?? str(attrs["gen_ai.system"]) ?? // Vercel AI SDK wrapper spans expose only `ai.model.provider`.
322
+ str(attrs["ai.model.provider"]) ?? "unknown";
323
+ const provider = normalizeProviderName(rawProvider);
324
+ const operation = str(attrs["gen_ai.operation.name"]) ?? "chat";
325
+ const requestModel = str(attrs["gen_ai.request.model"]) ?? str(attrs["llm.request.model"]) ?? // Pydantic AI's `agent run` parent span carries `model_name` but not
326
+ // `gen_ai.request.model`. Same pattern for any host-language convention
327
+ // that mirrors gen_ai.* loosely.
328
+ str(attrs["model_name"]) ?? str(attrs["ai.model.id"]) ?? "unknown";
329
+ const responseModel = str(attrs["gen_ai.response.model"]) ?? str(attrs["llm.response.model"]) ?? str(attrs["ai.response.model"]);
330
+ const inputMessages = readMessagesAttribute(attrs, "gen_ai.input.messages") ?? [];
331
+ const outputMessages = readMessagesAttribute(attrs, "gen_ai.output.messages") ?? [];
332
+ let systemInstructions = [];
333
+ const rawSystem = parseJson(attrs["gen_ai.system_instructions"]);
334
+ if (Array.isArray(rawSystem) && rawSystem.length > 0) {
335
+ const first = rawSystem[0];
336
+ if (first && typeof first === "object" && "role" in first) {
337
+ systemInstructions = rawSystem.map((m) => normalizeMessage(m));
338
+ } else {
339
+ const parts = rawSystem.map(
340
+ (p) => p.type === "text" || p.content !== void 0 ? { kind: "text", text: String(p.content ?? "") } : { kind: "json", value: p }
341
+ );
342
+ systemInstructions = [{ role: "system", parts }];
343
+ }
344
+ }
345
+ let messages = inputMessages.length || outputMessages.length || systemInstructions.length ? [...systemInstructions, ...inputMessages, ...outputMessages] : messagesFromEvents(span.events);
346
+ const inputRef = str(attrs["gen_ai.input.messages.ref"]);
347
+ const outputRef = str(attrs["gen_ai.output.messages.ref"]);
348
+ const hasInputContent = messages.some((m) => m.role === "system" || m.role === "user");
349
+ const hasOutputContent = messages.some((m) => m.role === "assistant");
350
+ if (inputRef && !hasInputContent) {
351
+ messages.push({ role: "user", parts: [{ kind: "ref", ref: inputRef, direction: "input" }] });
352
+ }
353
+ if (outputRef && !hasOutputContent) {
354
+ messages.push({ role: "assistant", parts: [{ kind: "ref", ref: outputRef, direction: "output" }] });
355
+ }
356
+ if (messages.length === 0) {
357
+ const aiPrompt = parseJson(attrs["ai.prompt.messages"]);
358
+ if (Array.isArray(aiPrompt)) {
359
+ messages.push(...aiPrompt.map(normalizeMessage));
360
+ } else {
361
+ const aiPromptBlob = parseJson(attrs["ai.prompt"]);
362
+ if (aiPromptBlob) {
363
+ if (Array.isArray(aiPromptBlob.messages)) {
364
+ messages.push(...aiPromptBlob.messages.map(normalizeMessage));
365
+ } else {
366
+ if (aiPromptBlob.system) {
367
+ messages.push({ role: "system", parts: [{ kind: "text", text: aiPromptBlob.system }] });
368
+ }
369
+ if (aiPromptBlob.prompt) {
370
+ messages.push({ role: "user", parts: [{ kind: "text", text: aiPromptBlob.prompt }] });
371
+ }
372
+ }
373
+ }
374
+ }
375
+ const aiResponseToolCalls = parseJson(
376
+ attrs["ai.response.toolCalls"]
377
+ );
378
+ const assistantToolCalls = Array.isArray(aiResponseToolCalls) ? aiResponseToolCalls.filter((c) => typeof c.toolName === "string").map((c) => ({
379
+ id: c.toolCallId,
380
+ name: c.toolName,
381
+ arguments: parseJson(c.input ?? c.args) ?? c.input ?? c.args ?? {}
382
+ })) : void 0;
383
+ const aiResponseText = str(attrs["ai.response.text"]);
384
+ if (aiResponseText || assistantToolCalls && assistantToolCalls.length > 0) {
385
+ const finishReason = strArray(attrs["gen_ai.response.finish_reasons"])?.[0] ?? str(attrs["ai.response.finishReason"]);
386
+ const msg = normalizeMessage({ role: "assistant", content: aiResponseText ?? "" });
387
+ if (finishReason) msg.finishReason = finishReason;
388
+ if (assistantToolCalls && assistantToolCalls.length > 0) msg.toolCalls = assistantToolCalls;
389
+ messages.push(msg);
390
+ }
391
+ }
392
+ if (messages.length === 0) {
393
+ const pydanticMessages = parseJson(attrs["pydantic_ai.all_messages"]);
394
+ if (Array.isArray(pydanticMessages)) {
395
+ messages.push(...pydanticMessages.map(normalizeMessage));
396
+ }
397
+ }
398
+ if (messages.length === 0) {
399
+ const legacy = [];
400
+ for (let i = 0; i < 32; i++) {
401
+ const role = str(attrs[`gen_ai.prompt.${i}.role`]) ?? str(attrs[`llm.prompts.${i}.role`]);
402
+ const content = attrs[`gen_ai.prompt.${i}.content`] ?? attrs[`llm.prompts.${i}.content`];
403
+ if (!role) break;
404
+ legacy.push(normalizeMessage({ role, content }));
405
+ }
406
+ for (let i = 0; i < 32; i++) {
407
+ const role = str(attrs[`gen_ai.completion.${i}.role`]) ?? str(attrs[`llm.completions.${i}.role`]);
408
+ const content = attrs[`gen_ai.completion.${i}.content`] ?? attrs[`llm.completions.${i}.content`];
409
+ const finishReason = str(attrs[`gen_ai.completion.${i}.finish_reason`]);
410
+ if (!role) break;
411
+ const msg = normalizeMessage({ role, content });
412
+ if (finishReason) msg.finishReason = finishReason;
413
+ legacy.push(msg);
414
+ }
415
+ if (legacy.length > 0) messages = legacy;
416
+ }
417
+ messages = messages.flatMap((m) => {
418
+ const bundled = m._toolResults;
419
+ if (!bundled) return [m];
420
+ return bundled.map((r) => ({
421
+ role: "tool",
422
+ parts: [{ kind: "json", value: r.value }],
423
+ toolCallId: r.id
424
+ }));
425
+ });
426
+ const toolCalls = [];
427
+ for (const m of messages) if (m.toolCalls) toolCalls.push(...m.toolCalls);
428
+ if (toolCalls.length > 0) {
429
+ const callsById = /* @__PURE__ */ new Map();
430
+ for (const tc of toolCalls) if (tc.id) callsById.set(tc.id, tc);
431
+ for (const m of messages) {
432
+ if (m.role !== "tool" || !m.toolCallId) continue;
433
+ const target = callsById.get(m.toolCallId);
434
+ if (!target || target.result !== void 0) continue;
435
+ const textPart = m.parts.find((p) => p.kind === "text");
436
+ const jsonPart = m.parts.find((p) => p.kind === "json");
437
+ if (textPart && textPart.kind === "text") {
438
+ target.result = parseJson(textPart.text) ?? textPart.text;
439
+ } else if (jsonPart && jsonPart.kind === "json") {
440
+ target.result = jsonPart.value;
441
+ }
442
+ }
443
+ }
444
+ const usage = readUsage(attrs);
445
+ const cost = priceCall({
446
+ provider,
447
+ model: responseModel ?? requestModel,
448
+ inputTokens: usage.inputTokens,
449
+ outputTokens: usage.outputTokens,
450
+ cacheReadInputTokens: usage.cacheReadInputTokens,
451
+ cacheCreationInputTokens: usage.cacheCreationInputTokens
452
+ });
453
+ const finishReasons = strArray(attrs["gen_ai.response.finish_reasons"]);
454
+ const agentName = str(attrs["gen_ai.agent.name"]);
455
+ const agentId = str(attrs["gen_ai.agent.id"]);
456
+ const agentDescription = str(attrs["gen_ai.agent.description"]);
457
+ const handoffFrom = str(attrs["gen_ai.handoff.from_agent"]);
458
+ const handoffTo = str(attrs["gen_ai.handoff.to_agent"]);
459
+ const guardrailName = str(attrs["gen_ai.guardrail.name"]);
460
+ const guardrailTriggered = bool(attrs["gen_ai.guardrail.triggered"]);
461
+ const evalName = str(attrs["gen_ai.evaluation.name"]);
462
+ const evalScoreValue = num(attrs["gen_ai.evaluation.score.value"]);
463
+ const evalScoreLabel = str(attrs["gen_ai.evaluation.score.label"]);
464
+ const evalExplanation = str(attrs["gen_ai.evaluation.explanation"]);
465
+ const audioIn = str(attrs["gen_ai.audio.input.format"]);
466
+ const audioOut = str(attrs["gen_ai.audio.output.format"]);
467
+ const speechVoice = str(attrs["gen_ai.speech.voice"]);
468
+ const speechInputText = str(attrs["gen_ai.speech.input_text"]);
469
+ const transcriptionText = str(attrs["gen_ai.transcription.text"]);
470
+ const embeddingDims = num(attrs["gen_ai.embeddings.dimension.count"]);
471
+ const raw = {};
472
+ for (const [k, v] of Object.entries(attrs)) {
473
+ if (KNOWN_TOP_LEVEL_KEYS.has(k)) continue;
474
+ if (k.startsWith("gen_ai.prompt.") || k.startsWith("gen_ai.completion.")) continue;
475
+ if (k.startsWith("llm.prompts.") || k.startsWith("llm.completions.")) continue;
476
+ raw[k] = v;
477
+ }
478
+ return {
479
+ traceId: span.traceId,
480
+ spanId: span.spanId,
481
+ parentSpanId: span.parentSpanId,
482
+ name: span.name,
483
+ startNs: span.startTime,
484
+ endNs: span.endTime,
485
+ status: span.status?.code === "OK" ? "ok" : span.status?.code === "ERROR" ? "error" : "unset",
486
+ errorMessage: span.status?.message,
487
+ provider,
488
+ operation,
489
+ requestModel,
490
+ responseModel,
491
+ params: {
492
+ temperature: num(attrs["gen_ai.request.temperature"]),
493
+ topP: num(attrs["gen_ai.request.top_p"]),
494
+ topK: num(attrs["gen_ai.request.top_k"]),
495
+ maxTokens: num(attrs["gen_ai.request.max_tokens"]),
496
+ stopSequences: strArray(attrs["gen_ai.request.stop_sequences"]),
497
+ seed: num(attrs["gen_ai.request.seed"]),
498
+ frequencyPenalty: num(attrs["gen_ai.request.frequency_penalty"]),
499
+ presencePenalty: num(attrs["gen_ai.request.presence_penalty"]),
500
+ choiceCount: num(attrs["gen_ai.request.choice.count"])
501
+ },
502
+ messages,
503
+ toolDefinitions: readToolDefinitions(attrs),
504
+ toolCalls,
505
+ usage,
506
+ cost,
507
+ finishReasons,
508
+ responseId: str(attrs["gen_ai.response.id"]),
509
+ agent: agentId || agentName || agentDescription ? { id: agentId, name: agentName, description: agentDescription } : void 0,
510
+ handoff: handoffFrom || handoffTo ? { fromAgent: handoffFrom, toAgent: handoffTo } : void 0,
511
+ guardrail: guardrailName || guardrailTriggered !== void 0 ? { name: guardrailName, triggered: guardrailTriggered } : void 0,
512
+ conversationId: str(attrs["gen_ai.conversation.id"]),
513
+ evaluation: evalName || evalScoreValue !== void 0 || evalScoreLabel || evalExplanation ? {
514
+ name: evalName,
515
+ scoreValue: evalScoreValue,
516
+ scoreLabel: evalScoreLabel,
517
+ explanation: evalExplanation
518
+ } : void 0,
519
+ modality: audioIn || audioOut || speechVoice || speechInputText || transcriptionText || embeddingDims ? {
520
+ audioInputFormat: audioIn,
521
+ audioOutputFormat: audioOut,
522
+ speechVoice,
523
+ speechInputText,
524
+ transcriptionText,
525
+ embeddingDimensions: embeddingDims
526
+ } : void 0,
527
+ extras: {
528
+ openaiServiceTier: str(attrs["gen_ai.openai.request.service_tier"]) || str(attrs["gen_ai.openai.response.service_tier"]) || str(attrs["openai.request.service_tier"]) || str(attrs["openai.response.service_tier"]) ? {
529
+ request: str(attrs["gen_ai.openai.request.service_tier"]) ?? str(attrs["openai.request.service_tier"]),
530
+ response: str(attrs["gen_ai.openai.response.service_tier"]) ?? str(attrs["openai.response.service_tier"])
531
+ } : void 0,
532
+ openaiSystemFingerprint: str(attrs["gen_ai.openai.response.system_fingerprint"]) ?? str(attrs["openai.response.system_fingerprint"]),
533
+ openaiResponseFormat: str(attrs["gen_ai.openai.request.response_format"]) ?? str(attrs["openai.request.response_format"]),
534
+ raw
535
+ }
536
+ };
537
+ }
538
+
539
+ // src/widget/genai/stitch.ts
540
+ function buildToolResultIndex(spans) {
541
+ const index = /* @__PURE__ */ new Map();
542
+ for (const span of spans) {
543
+ const attrs = span.attributes ?? {};
544
+ const id = attrs["ai.toolCall.id"];
545
+ if (typeof id !== "string") continue;
546
+ const raw = attrs["ai.toolCall.result"];
547
+ if (raw == null) continue;
548
+ if (typeof raw === "string") {
549
+ try {
550
+ index.set(id, JSON.parse(raw));
551
+ } catch {
552
+ index.set(id, raw);
553
+ }
554
+ } else {
555
+ index.set(id, raw);
556
+ }
557
+ }
558
+ return index;
559
+ }
560
+ function hydrateToolResults(genAiSpan, index) {
561
+ if (index.size === 0) return;
562
+ for (const call of genAiSpan.toolCalls) {
563
+ if (call.result !== void 0) continue;
564
+ if (!call.id) continue;
565
+ const result = index.get(call.id);
566
+ if (result !== void 0) call.result = result;
567
+ }
568
+ for (const msg of genAiSpan.messages) {
569
+ if (!msg.toolCalls) continue;
570
+ for (const call of msg.toolCalls) {
571
+ if (call.result !== void 0) continue;
572
+ if (!call.id) continue;
573
+ const result = index.get(call.id);
574
+ if (result !== void 0) call.result = result;
575
+ }
576
+ }
577
+ }
578
+
579
+ exports.buildToolResultIndex = buildToolResultIndex;
580
+ exports.hydrateToolResults = hydrateToolResults;
581
+ exports.isGenAiSpan = isGenAiSpan;
582
+ exports.lookupPrice = lookupPrice;
583
+ exports.priceCall = priceCall;
584
+ exports.toGenAiSpan = toGenAiSpan;
585
+ //# sourceMappingURL=index.cjs.map
586
+ //# sourceMappingURL=index.cjs.map