@arizeai/openinference-instrumentation-beeai 1.5.22 → 1.5.24

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 (54) hide show
  1. package/dist/esm/helpers/buildTraceTree.d.ts.map +1 -1
  2. package/dist/esm/helpers/buildTraceTree.js +10 -8
  3. package/dist/esm/helpers/buildTraceTree.js.map +1 -1
  4. package/dist/esm/helpers/getErrorSafe.d.ts.map +1 -1
  5. package/dist/esm/helpers/getErrorSafe.js +1 -0
  6. package/dist/esm/helpers/getErrorSafe.js.map +1 -1
  7. package/dist/esm/helpers/getSerializedObjectSafe.d.ts +46 -20
  8. package/dist/esm/helpers/getSerializedObjectSafe.d.ts.map +1 -1
  9. package/dist/esm/helpers/getSerializedObjectSafe.js +175 -154
  10. package/dist/esm/helpers/getSerializedObjectSafe.js.map +1 -1
  11. package/dist/esm/helpers/traceSerializer.js +1 -1
  12. package/dist/esm/helpers/traceSerializer.js.map +1 -1
  13. package/dist/esm/middleware.d.ts.map +1 -1
  14. package/dist/esm/middleware.js +68 -34
  15. package/dist/esm/middleware.js.map +1 -1
  16. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  17. package/dist/esnext/helpers/buildTraceTree.d.ts.map +1 -1
  18. package/dist/esnext/helpers/buildTraceTree.js +10 -9
  19. package/dist/esnext/helpers/buildTraceTree.js.map +1 -1
  20. package/dist/esnext/helpers/getErrorSafe.d.ts.map +1 -1
  21. package/dist/esnext/helpers/getErrorSafe.js +1 -0
  22. package/dist/esnext/helpers/getErrorSafe.js.map +1 -1
  23. package/dist/esnext/helpers/getSerializedObjectSafe.d.ts +46 -20
  24. package/dist/esnext/helpers/getSerializedObjectSafe.d.ts.map +1 -1
  25. package/dist/esnext/helpers/getSerializedObjectSafe.js +128 -109
  26. package/dist/esnext/helpers/getSerializedObjectSafe.js.map +1 -1
  27. package/dist/esnext/helpers/traceSerializer.js +1 -1
  28. package/dist/esnext/helpers/traceSerializer.js.map +1 -1
  29. package/dist/esnext/middleware.d.ts.map +1 -1
  30. package/dist/esnext/middleware.js +70 -35
  31. package/dist/esnext/middleware.js.map +1 -1
  32. package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
  33. package/dist/src/helpers/buildTraceTree.d.ts.map +1 -1
  34. package/dist/src/helpers/buildTraceTree.js +10 -9
  35. package/dist/src/helpers/buildTraceTree.js.map +1 -1
  36. package/dist/src/helpers/getErrorSafe.d.ts.map +1 -1
  37. package/dist/src/helpers/getErrorSafe.js +1 -0
  38. package/dist/src/helpers/getErrorSafe.js.map +1 -1
  39. package/dist/src/helpers/getSerializedObjectSafe.d.ts +46 -20
  40. package/dist/src/helpers/getSerializedObjectSafe.d.ts.map +1 -1
  41. package/dist/src/helpers/getSerializedObjectSafe.js +128 -109
  42. package/dist/src/helpers/getSerializedObjectSafe.js.map +1 -1
  43. package/dist/src/helpers/traceSerializer.js +1 -1
  44. package/dist/src/helpers/traceSerializer.js.map +1 -1
  45. package/dist/src/middleware.d.ts.map +1 -1
  46. package/dist/src/middleware.js +70 -35
  47. package/dist/src/middleware.js.map +1 -1
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/package.json +3 -3
  50. package/src/helpers/buildTraceTree.ts +11 -12
  51. package/src/helpers/getErrorSafe.ts +1 -0
  52. package/src/helpers/getSerializedObjectSafe.ts +197 -163
  53. package/src/helpers/traceSerializer.ts +1 -1
  54. package/src/middleware.ts +95 -37
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arizeai/openinference-instrumentation-beeai",
3
- "version": "1.5.22",
3
+ "version": "1.5.24",
4
4
  "private": false,
5
5
  "description": "OpenInference Instrumentation for BeeAI framework",
6
6
  "keywords": [
@@ -33,8 +33,8 @@
33
33
  "@opentelemetry/instrumentation": "^0.57.1",
34
34
  "remeda": "^2.20.2",
35
35
  "semver": "^7.7.0",
36
- "@arizeai/openinference-core": "2.5.2",
37
- "@arizeai/openinference-semantic-conventions": "2.7.0"
36
+ "@arizeai/openinference-core": "2.5.4",
37
+ "@arizeai/openinference-semantic-conventions": "2.8.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@opentelemetry/exporter-trace-otlp-proto": "^0.50.0",
@@ -111,26 +111,25 @@ function buildLLMMainSpanData(data: BuiltTraceTreeProps["data"]) {
111
111
 
112
112
  if (!startBeeaiSpan && !successBeeeaiSpan) return {};
113
113
 
114
+ const startData = startBeeaiSpan?.attributes.data;
115
+ const successData = successBeeeaiSpan?.attributes.data;
116
+
114
117
  const provider =
115
- startBeeaiSpan?.attributes.data?.[SemanticConventions.LLM_PROVIDER] ||
116
- successBeeeaiSpan?.attributes.data?.[SemanticConventions.LLM_PROVIDER];
118
+ startData?.[SemanticConventions.LLM_PROVIDER] ||
119
+ successData?.[SemanticConventions.LLM_PROVIDER];
117
120
 
118
121
  const modelName =
119
- startBeeaiSpan?.attributes.data?.[SemanticConventions.LLM_MODEL_NAME] ||
120
- successBeeeaiSpan?.attributes.data?.[SemanticConventions.LLM_MODEL_NAME];
122
+ startData?.[SemanticConventions.LLM_MODEL_NAME] ||
123
+ successData?.[SemanticConventions.LLM_MODEL_NAME];
121
124
 
122
125
  return {
123
126
  ...(startBeeaiSpan && {
124
- [SemanticConventions.INPUT_VALUE]:
125
- startBeeaiSpan?.attributes.data?.[SemanticConventions.INPUT_VALUE],
126
- [SemanticConventions.INPUT_MIME_TYPE]:
127
- startBeeaiSpan?.attributes.data?.[SemanticConventions.INPUT_MIME_TYPE],
127
+ [SemanticConventions.INPUT_VALUE]: startData?.[SemanticConventions.INPUT_VALUE],
128
+ [SemanticConventions.INPUT_MIME_TYPE]: startData?.[SemanticConventions.INPUT_MIME_TYPE],
128
129
  }),
129
130
  ...(successBeeeaiSpan && {
130
- [SemanticConventions.OUTPUT_MIME_TYPE]:
131
- successBeeeaiSpan.attributes.data?.[SemanticConventions.OUTPUT_MIME_TYPE],
132
- [SemanticConventions.OUTPUT_VALUE]:
133
- successBeeeaiSpan.attributes.data?.[SemanticConventions.OUTPUT_VALUE],
131
+ [SemanticConventions.OUTPUT_MIME_TYPE]: successData?.[SemanticConventions.OUTPUT_MIME_TYPE],
132
+ [SemanticConventions.OUTPUT_VALUE]: successData?.[SemanticConventions.OUTPUT_VALUE],
134
133
  }),
135
134
  ...(provider && { [SemanticConventions.LLM_PROVIDER]: provider }),
136
135
  ...(modelName && { [SemanticConventions.LLM_MODEL_NAME]: modelName }),
@@ -23,4 +23,5 @@ export function getErrorSafe(data: unknown): string | undefined {
23
23
  if (error instanceof FrameworkError) {
24
24
  return FrameworkError.ensure(error).explain();
25
25
  }
26
+ return undefined;
26
27
  }
@@ -98,6 +98,196 @@ function parseLLMOutputMessages(messages: readonly unknown[]) {
98
98
 
99
99
  const matchesEvent = (name: string, events: readonly string[]): boolean => events.includes(name);
100
100
 
101
+ const NATIVE_TOOL_EVENT_NAMES = [
102
+ startToolEventName,
103
+ successToolEventName,
104
+ finishToolEventName,
105
+ errorToolEventName,
106
+ retryToolEventName,
107
+ ] as const;
108
+
109
+ /** Maps an error onto the OpenTelemetry exception attributes. */
110
+ function getExceptionAttributes(error: Error | undefined) {
111
+ if (!error) {
112
+ return {};
113
+ }
114
+ return {
115
+ "exception.message": error.message,
116
+ "exception.stacktrace": error.stack,
117
+ "exception.type": error.name,
118
+ };
119
+ }
120
+
121
+ /** Extracts the attributes for an agent start/success/error/retry event. */
122
+ function getAgentEventAttributes(dataObject: unknown) {
123
+ const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
124
+ const agentMeta = isObjectWithStringKeys(event.meta) ? event.meta : undefined;
125
+ const tools = Array.isArray(event.tools) ? event.tools.filter(isObjectWithStringKeys) : [];
126
+ const memory = isObjectWithStringKeys(event.memory) ? event.memory : undefined;
127
+ const messages = memory && Array.isArray(memory.messages) ? memory.messages : [];
128
+ const error = event.error instanceof Error ? event.error : undefined;
129
+ const data = event.data;
130
+ return {
131
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.AGENT,
132
+ ...(typeof agentMeta?.iteration === "number" && { iteration: agentMeta.iteration }),
133
+ ...(tools?.length > 0 && {
134
+ [SemanticConventions.LLM_TOOLS]: tools.map((tool) => ({
135
+ [SemanticConventions.TOOL_NAME]: typeof tool.name === "string" ? tool.name : undefined,
136
+ [SemanticConventions.TOOL_DESCRIPTION]:
137
+ typeof tool.description === "string" ? tool.description : undefined,
138
+ "tool.options": tool.options,
139
+ })),
140
+ }),
141
+ ...(messages.length > 0 && {
142
+ [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
143
+ [SemanticConventions.INPUT_VALUE]: JSON.stringify(messages),
144
+ }),
145
+ ...getExceptionAttributes(error),
146
+ ...(data != null
147
+ ? {
148
+ [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
149
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(data),
150
+ }
151
+ : {}),
152
+ };
153
+ }
154
+
155
+ /** Extracts the attributes for an agent update/partial update event. */
156
+ function getUpdateEventAttributes(dataObject: unknown) {
157
+ const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
158
+ const data = isObjectWithStringKeys(event.data) ? event.data : {};
159
+
160
+ const output = data.final_answer || data.tool_output;
161
+ return {
162
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.AGENT,
163
+ ...(typeof data.thought === "string" && { thought: data.thought }),
164
+ ...(typeof data.tool_name === "string" && {
165
+ [SemanticConventions.TOOL_NAME]: data.tool_name,
166
+ }),
167
+ ...(data.tool_input != null
168
+ ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.tool_input) }
169
+ : {}),
170
+ ...(typeof output === "string" && {
171
+ [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
172
+ [SemanticConventions.OUTPUT_VALUE]: output,
173
+ }),
174
+ };
175
+ }
176
+
177
+ /** Extracts the attributes for a tool event emitted by an agent. */
178
+ function getAgentToolEventAttributes(dataObject: unknown) {
179
+ const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
180
+ const data = isObjectWithStringKeys(event.data) ? event.data : {};
181
+ const iteration = isObjectWithStringKeys(data.iteration) ? data.iteration : undefined;
182
+ const tool = isObjectWithStringKeys(data.tool) ? data.tool : undefined;
183
+ const error = data.error instanceof Error ? data.error : undefined;
184
+ const output = data.result instanceof Serializable ? data.result.createSnapshot() : undefined;
185
+
186
+ return {
187
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
188
+ ...(typeof iteration?.thought === "string" && { thought: iteration.thought }),
189
+ ...(data?.input
190
+ ? {
191
+ [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.input),
192
+ }
193
+ : {}),
194
+ ...(typeof tool?.description === "string" && {
195
+ [SemanticConventions.TOOL_DESCRIPTION]: tool.description,
196
+ }),
197
+ ...(typeof tool?.name === "string" && {
198
+ [SemanticConventions.TOOL_NAME]: tool.name,
199
+ }),
200
+ ...getExceptionAttributes(error),
201
+ ...(output != null ? { [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output) } : {}),
202
+ };
203
+ }
204
+
205
+ /** Extracts the attributes for a tool event emitted by a Tool itself. */
206
+ function getNativeToolEventAttributes(dataObject: unknown) {
207
+ if (!dataObject) {
208
+ return {
209
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
210
+ };
211
+ }
212
+ const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
213
+ const { input, output } = event;
214
+ const error = event.error instanceof Error ? event.error : undefined;
215
+
216
+ return {
217
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
218
+ ...(input != null ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(input) } : {}),
219
+ ...(output != null ? { [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output) } : {}),
220
+ ...getExceptionAttributes(error),
221
+ };
222
+ }
223
+
224
+ /** Extracts the model/provider attributes from a ChatModel snapshot. */
225
+ function getChatModelCreatorAttributes(creator: ChatModel) {
226
+ const creatorSnapshot = creator.createSnapshot();
227
+ const snapshot: Record<string, unknown> = isObjectWithStringKeys(creatorSnapshot)
228
+ ? creatorSnapshot
229
+ : {};
230
+ return {
231
+ ...(typeof snapshot.providerId === "string" && {
232
+ [SemanticConventions.LLM_PROVIDER]: snapshot.providerId,
233
+ [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.provider}`]:
234
+ snapshot.providerId,
235
+ }),
236
+ ...(typeof snapshot.modelId === "string" && {
237
+ [SemanticConventions.LLM_MODEL_NAME]: snapshot.modelId,
238
+ [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.model_name}`]:
239
+ snapshot.modelId,
240
+ }),
241
+ ...(snapshot.parameters != null
242
+ ? {
243
+ [SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(snapshot.parameters),
244
+ }
245
+ : {}),
246
+ };
247
+ }
248
+
249
+ /** Extracts the attributes for an LLM start/success/error/new-token event. */
250
+ function getLLMEventAttributes({
251
+ dataObject,
252
+ creator,
253
+ }: {
254
+ dataObject: unknown;
255
+ creator: ChatModel;
256
+ }) {
257
+ const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
258
+ const value = isObjectWithStringKeys(event.value) ? event.value : undefined;
259
+ const input = isObjectWithStringKeys(event.input) ? event.input : undefined;
260
+ const usage = value && isObjectWithStringKeys(value.usage) ? value.usage : undefined;
261
+ const inputMessages = input && Array.isArray(input.messages) ? input.messages : [];
262
+ const outputMessages = value && Array.isArray(value.messages) ? value.messages : [];
263
+ const error = event.error instanceof Error ? event.error : undefined;
264
+
265
+ return {
266
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
267
+ ...(typeof usage?.completionTokens === "number" && {
268
+ [SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]: usage.completionTokens,
269
+ }),
270
+ ...(typeof usage?.promptTokens === "number" && {
271
+ [SemanticConventions.LLM_TOKEN_COUNT_PROMPT]: usage.promptTokens,
272
+ }),
273
+ ...(typeof usage?.totalTokens === "number" && {
274
+ [SemanticConventions.LLM_TOKEN_COUNT_TOTAL]: usage.totalTokens,
275
+ }),
276
+ ...(inputMessages.length > 0 && {
277
+ [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
278
+ [SemanticConventions.INPUT_VALUE]: JSON.stringify(inputMessages),
279
+ ...parserLLMInputMessages(inputMessages),
280
+ }),
281
+ ...(outputMessages.length > 0 && {
282
+ [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
283
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(outputMessages),
284
+ ...parseLLMOutputMessages(outputMessages),
285
+ }),
286
+ ...getExceptionAttributes(error),
287
+ ...getChatModelCreatorAttributes(creator),
288
+ };
289
+ }
290
+
101
291
  export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unknown>) {
102
292
  try {
103
293
  // agent events
@@ -105,125 +295,22 @@ export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unk
105
295
  matchesEvent(meta.name, [startEventName, successEventName, errorEventName, retryEventName]) &&
106
296
  meta.creator instanceof BaseAgent
107
297
  ) {
108
- const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
109
- const agentMeta = isObjectWithStringKeys(event.meta) ? event.meta : undefined;
110
- const tools = Array.isArray(event.tools) ? event.tools.filter(isObjectWithStringKeys) : [];
111
- const memory = isObjectWithStringKeys(event.memory) ? event.memory : undefined;
112
- const messages = memory && Array.isArray(memory.messages) ? memory.messages : [];
113
- const error = event.error instanceof Error ? event.error : undefined;
114
- const data = event.data;
115
- return {
116
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.AGENT,
117
- ...(typeof agentMeta?.iteration === "number" && { iteration: agentMeta.iteration }),
118
- ...(tools?.length > 0 && {
119
- [SemanticConventions.LLM_TOOLS]: tools.map((tool) => ({
120
- [SemanticConventions.TOOL_NAME]: typeof tool.name === "string" ? tool.name : undefined,
121
- [SemanticConventions.TOOL_DESCRIPTION]:
122
- typeof tool.description === "string" ? tool.description : undefined,
123
- "tool.options": tool.options,
124
- })),
125
- }),
126
- ...(messages.length > 0 && {
127
- [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
128
- [SemanticConventions.INPUT_VALUE]: JSON.stringify(messages),
129
- }),
130
- ...(error && {
131
- "exception.message": error.message,
132
- "exception.stacktrace": error.stack,
133
- "exception.type": error.name,
134
- }),
135
- ...(data != null
136
- ? {
137
- [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
138
- [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(data),
139
- }
140
- : {}),
141
- };
298
+ return getAgentEventAttributes(dataObject);
142
299
  }
143
300
 
144
301
  // update events
145
302
  if (matchesEvent(meta.name, [updateEventName, partialUpdateEventName])) {
146
- const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
147
- const data = isObjectWithStringKeys(event.data) ? event.data : {};
148
-
149
- const output = data.final_answer || data.tool_output;
150
- return {
151
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.AGENT,
152
- ...(typeof data.thought === "string" && { thought: data.thought }),
153
- ...(typeof data.tool_name === "string" && {
154
- [SemanticConventions.TOOL_NAME]: data.tool_name,
155
- }),
156
- ...(data.tool_input != null
157
- ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.tool_input) }
158
- : {}),
159
- ...(typeof output === "string" && {
160
- [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
161
- [SemanticConventions.OUTPUT_VALUE]: output,
162
- }),
163
- };
303
+ return getUpdateEventAttributes(dataObject);
164
304
  }
165
305
 
166
306
  // tool events (from agent)
167
307
  if (matchesEvent(meta.name, [toolErrorEventName, toolStartEventName, toolSuccessEventName])) {
168
- const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
169
- const data = isObjectWithStringKeys(event.data) ? event.data : {};
170
- const iteration = isObjectWithStringKeys(data.iteration) ? data.iteration : undefined;
171
- const tool = isObjectWithStringKeys(data.tool) ? data.tool : undefined;
172
- const error = data.error instanceof Error ? data.error : undefined;
173
- const output = data.result instanceof Serializable ? data.result.createSnapshot() : undefined;
174
-
175
- return {
176
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
177
- ...(typeof iteration?.thought === "string" && { thought: iteration.thought }),
178
- ...(data?.input
179
- ? {
180
- [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.input),
181
- }
182
- : {}),
183
- ...(typeof tool?.description === "string" && {
184
- [SemanticConventions.TOOL_DESCRIPTION]: tool.description,
185
- }),
186
- ...(typeof tool?.name === "string" && {
187
- [SemanticConventions.TOOL_NAME]: tool.name,
188
- }),
189
- ...(error && {
190
- "exception.message": error.message,
191
- "exception.stacktrace": error.stack,
192
- "exception.type": error.name,
193
- }),
194
- ...(output != null ? { [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output) } : {}),
195
- };
308
+ return getAgentToolEventAttributes(dataObject);
196
309
  }
197
- // tool events native
198
- if (
199
- [
200
- startToolEventName,
201
- successToolEventName,
202
- finishToolEventName,
203
- errorToolEventName,
204
- retryToolEventName,
205
- ].some((eventName) => eventName === meta.name) &&
206
- meta.creator instanceof Tool
207
- ) {
208
- if (!dataObject) {
209
- return {
210
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
211
- };
212
- }
213
- const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
214
- const { input, output } = event;
215
- const error = event.error instanceof Error ? event.error : undefined;
216
310
 
217
- return {
218
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
219
- ...(input != null ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(input) } : {}),
220
- ...(output != null ? { [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output) } : {}),
221
- ...(error && {
222
- "exception.message": error.message,
223
- "exception.stacktrace": error.stack,
224
- "exception.type": error.name,
225
- }),
226
- };
311
+ // tool events native
312
+ if (matchesEvent(meta.name, NATIVE_TOOL_EVENT_NAMES) && meta.creator instanceof Tool) {
313
+ return getNativeToolEventAttributes(dataObject);
227
314
  }
228
315
 
229
316
  // llm events
@@ -236,60 +323,7 @@ export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unk
236
323
  ]) &&
237
324
  meta.creator instanceof ChatModel
238
325
  ) {
239
- const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
240
- const value = isObjectWithStringKeys(event.value) ? event.value : undefined;
241
- const input = isObjectWithStringKeys(event.input) ? event.input : undefined;
242
- const usage = value && isObjectWithStringKeys(value.usage) ? value.usage : undefined;
243
- const inputMessages = input && Array.isArray(input.messages) ? input.messages : [];
244
- const outputMessages = value && Array.isArray(value.messages) ? value.messages : [];
245
- const error = event.error instanceof Error ? event.error : undefined;
246
-
247
- const creatorSnapshot = meta.creator.createSnapshot();
248
- const creator: Record<string, unknown> = isObjectWithStringKeys(creatorSnapshot)
249
- ? creatorSnapshot
250
- : {};
251
- return {
252
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
253
- ...(typeof usage?.completionTokens === "number" && {
254
- [SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]: usage.completionTokens,
255
- }),
256
- ...(typeof usage?.promptTokens === "number" && {
257
- [SemanticConventions.LLM_TOKEN_COUNT_PROMPT]: usage.promptTokens,
258
- }),
259
- ...(typeof usage?.totalTokens === "number" && {
260
- [SemanticConventions.LLM_TOKEN_COUNT_TOTAL]: usage.totalTokens,
261
- }),
262
- ...(inputMessages.length > 0 && {
263
- [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
264
- [SemanticConventions.INPUT_VALUE]: JSON.stringify(inputMessages),
265
- ...parserLLMInputMessages(inputMessages),
266
- }),
267
- ...(outputMessages.length > 0 && {
268
- [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
269
- [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(outputMessages),
270
- ...parseLLMOutputMessages(outputMessages),
271
- }),
272
- ...(error && {
273
- "exception.message": error.message,
274
- "exception.stacktrace": error.stack,
275
- "exception.type": error.name,
276
- }),
277
- ...(typeof creator.providerId === "string" && {
278
- [SemanticConventions.LLM_PROVIDER]: creator.providerId,
279
- [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.provider}`]:
280
- creator.providerId,
281
- }),
282
- ...(typeof creator.modelId === "string" && {
283
- [SemanticConventions.LLM_MODEL_NAME]: creator.modelId,
284
- [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.model_name}`]:
285
- creator.modelId,
286
- }),
287
- ...(creator.parameters != null
288
- ? {
289
- [SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(creator.parameters),
290
- }
291
- : {}),
292
- };
326
+ return getLLMEventAttributes({ dataObject, creator: meta.creator });
293
327
  }
294
328
  if (meta.name === finishLLMEventName && meta.creator instanceof ChatModel) {
295
329
  return {
@@ -26,7 +26,7 @@ export function traceSerializer({ ignored_keys = [] }: { ignored_keys?: string[]
26
26
  return (key, value) => {
27
27
  // Ignore specific keys, all owned entities and keys starting with underscore
28
28
  if (mergedIgnoreKeys.has(key) || key.startsWith("_")) {
29
- return;
29
+ return undefined;
30
30
  }
31
31
 
32
32
  return value;
package/src/middleware.ts CHANGED
@@ -178,6 +178,90 @@ export function createTelemetryMiddleware(tracer: OITracer, mainSpanKind: OpenIn
178
178
  },
179
179
  );
180
180
 
181
+ /**
182
+ * Creates the artificial "iteration" tree level for a top-level groupId the
183
+ * first time it is seen. Nested groups (like tokens) are skipped because
184
+ * they would introduce unuseful complexity.
185
+ */
186
+ function createGroupSpanIfNeeded({
187
+ groupId,
188
+ parentRunId,
189
+ createdAt,
190
+ }: {
191
+ groupId: string | undefined;
192
+ parentRunId: string | undefined;
193
+ createdAt: Date;
194
+ }) {
195
+ if (!groupId || parentRunId || groupIterations.includes(groupId)) {
196
+ return;
197
+ }
198
+ spansMap.set(
199
+ groupId,
200
+ createSpan({
201
+ id: groupId,
202
+ name: groupId,
203
+ target: "groupId",
204
+ data: {
205
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: "Chain",
206
+ },
207
+ startedAt: convertDateToPerformance(createdAt),
208
+ }),
209
+ );
210
+ groupIterations.push(groupId);
211
+ }
212
+
213
+ /**
214
+ * Drops the previous `partialUpdate` span for this iteration once a newer one
215
+ * arrives, unless it has nested spans, in which case it is only marked for
216
+ * deletion.
217
+ */
218
+ function dropSupersededPartialUpdateSpan({
219
+ eventName,
220
+ lastIteration,
221
+ }: {
222
+ eventName: string;
223
+ lastIteration: string | undefined;
224
+ }) {
225
+ if (lastIteration == null || partialUpdateEventName !== eventName) {
226
+ return;
227
+ }
228
+ const lastIterationEventSpanId = eventsIterationsMap.get(lastIteration)?.get(eventName);
229
+ if (!lastIterationEventSpanId || !spansMap.has(lastIterationEventSpanId)) {
230
+ return;
231
+ }
232
+ const { context: spanContext } = spansMap.get(lastIterationEventSpanId)!;
233
+ if (parentIdsMap.has(spanContext.span_id)) {
234
+ spansToDeleteMap.set(lastIterationEventSpanId, undefined);
235
+ return;
236
+ }
237
+ // delete span
238
+ cleanSpanSources({ spanId: lastIterationEventSpanId });
239
+ spansMap.delete(lastIterationEventSpanId);
240
+ }
241
+
242
+ /**
243
+ * Saves the last event span for each iteration.
244
+ */
245
+ function recordIterationEventSpan({
246
+ eventName,
247
+ lastIteration,
248
+ spanId,
249
+ }: {
250
+ eventName: string;
251
+ lastIteration: string | undefined;
252
+ spanId: string;
253
+ }) {
254
+ if (lastIteration == null) {
255
+ return;
256
+ }
257
+ const iterationEvents = eventsIterationsMap.get(lastIteration);
258
+ if (iterationEvents) {
259
+ iterationEvents.set(eventName, spanId);
260
+ } else {
261
+ eventsIterationsMap.set(lastIteration, new Map<string, string>([[eventName, spanId]]));
262
+ }
263
+ }
264
+
181
265
  /**
182
266
  * This block collects all "not run category" events with their data and prepares spans for the OpenTelemetry.
183
267
  * The huge number of `newToken` events are skipped and only the last one for each parent event is saved because of `generated_token_count` information
@@ -204,21 +288,11 @@ export function createTelemetryMiddleware(tracer: OITracer, mainSpanKind: OpenIn
204
288
  * create groupId span level (id does not exist)
205
289
  * I use only the top-level groups like iterations other nested groups like tokens would introduce unuseful complexity
206
290
  */
207
- if (meta.groupId && !meta.trace.parentRunId && !groupIterations.includes(meta.groupId)) {
208
- spansMap.set(
209
- meta.groupId,
210
- createSpan({
211
- id: meta.groupId,
212
- name: meta.groupId,
213
- target: "groupId",
214
- data: {
215
- [SemanticConventions.OPENINFERENCE_SPAN_KIND]: "Chain",
216
- },
217
- startedAt: convertDateToPerformance(meta.createdAt),
218
- }),
219
- );
220
- groupIterations.push(meta.groupId);
221
- }
291
+ createGroupSpanIfNeeded({
292
+ groupId: meta.groupId,
293
+ parentRunId: meta.trace.parentRunId,
294
+ createdAt: meta.createdAt,
295
+ });
222
296
 
223
297
  const { spanId, parentSpanId } = idNameManager.getIds({
224
298
  path: meta.path,
@@ -252,21 +326,7 @@ export function createTelemetryMiddleware(tracer: OITracer, mainSpanKind: OpenIn
252
326
  const lastIteration = groupIterations[groupIterations.length - 1];
253
327
 
254
328
  // delete the `partialUpdate` event if does not have nested spans
255
- const lastIterationEventSpanId = eventsIterationsMap.get(lastIteration)?.get(meta.name);
256
- if (
257
- lastIterationEventSpanId &&
258
- partialUpdateEventName === meta.name &&
259
- spansMap.has(lastIterationEventSpanId)
260
- ) {
261
- const { context } = spansMap.get(lastIterationEventSpanId)!;
262
- if (parentIdsMap.has(context.span_id)) {
263
- spansToDeleteMap.set(lastIterationEventSpanId, undefined);
264
- } else {
265
- // delete span
266
- cleanSpanSources({ spanId: lastIterationEventSpanId });
267
- spansMap.delete(lastIterationEventSpanId);
268
- }
269
- }
329
+ dropSupersededPartialUpdateSpan({ eventName: meta.name, lastIteration });
270
330
 
271
331
  // create new span
272
332
  spansMap.set(span.context.span_id, span);
@@ -276,13 +336,11 @@ export function createTelemetryMiddleware(tracer: OITracer, mainSpanKind: OpenIn
276
336
  }
277
337
 
278
338
  // save the last event for each iteration
279
- if (groupIterations.length > 0) {
280
- if (eventsIterationsMap.has(lastIteration)) {
281
- eventsIterationsMap.get(lastIteration)!.set(meta.name, span.context.span_id);
282
- } else {
283
- eventsIterationsMap.set(lastIteration, new Map().set(meta.name, span.context.span_id));
284
- }
285
- }
339
+ recordIterationEventSpan({
340
+ eventName: meta.name,
341
+ lastIteration,
342
+ spanId: span.context.span_id,
343
+ });
286
344
  } catch (e) {
287
345
  diag.warn("Instrumentation error", e);
288
346
  }