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