@arizeai/openinference-instrumentation-beeai 1.5.21 → 1.5.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arizeai/openinference-instrumentation-beeai",
3
- "version": "1.5.21",
3
+ "version": "1.5.23",
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-semantic-conventions": "2.7.0",
37
- "@arizeai/openinference-core": "2.5.1"
36
+ "@arizeai/openinference-core": "2.5.3",
37
+ "@arizeai/openinference-semantic-conventions": "2.8.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@opentelemetry/exporter-trace-otlp-proto": "^0.50.0",
@@ -16,16 +16,13 @@
16
16
 
17
17
  import { diag } from "@opentelemetry/api";
18
18
  import { BaseAgent } from "beeai-framework/agents/base";
19
- import type { ReActAgentCallbacks } from "beeai-framework/agents/react/types";
20
- import type { ChatModelEvents } from "beeai-framework/backend/chat";
21
19
  import { ChatModel } from "beeai-framework/backend/chat";
22
- import type { Message, MessageContentPart } from "beeai-framework/backend/message";
23
- import type { EventMeta, InferCallbackValue } from "beeai-framework/emitter/types";
20
+ import type { EventMeta } from "beeai-framework/emitter/types";
24
21
  import { getProp } from "beeai-framework/internals/helpers/object";
25
22
  import { Serializable } from "beeai-framework/internals/serializable";
26
- import type { ToolEvents } from "beeai-framework/tools/base";
27
23
  import { Tool } from "beeai-framework/tools/base";
28
24
 
25
+ import { isObjectWithStringKeys } from "@arizeai/openinference-core";
29
26
  import {
30
27
  LLMAttributePostfixes,
31
28
  MessageAttributePostfixes,
@@ -57,98 +54,109 @@ import {
57
54
  updateEventName,
58
55
  } from "../config";
59
56
 
60
- function parserLLMInputMessages(messages: readonly Message<MessageContentPart, string>[]) {
61
- return messages.reduce(
62
- (acc, item, key) => ({
63
- ...acc,
64
- [`${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.input_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.role}`]:
65
- item.role,
66
- [`${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.input_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.content}`]:
67
- item.content
68
- .filter((c) => c.type === "text")
69
- .map((c) => c.text)
70
- .join(""),
71
- }),
72
- {},
73
- );
57
+ function getMessageParts(message: unknown): { role?: string; text: string } {
58
+ if (!isObjectWithStringKeys(message)) return { text: "" };
59
+ const role = typeof message.role === "string" ? message.role : undefined;
60
+ const content = Array.isArray(message.content) ? message.content : [];
61
+ const text = content
62
+ .filter(isObjectWithStringKeys)
63
+ .filter((part) => part.type === "text" && typeof part.text === "string")
64
+ .map((part) => String(part.text))
65
+ .join("");
66
+ return { role, text };
74
67
  }
75
68
 
76
- function parseLLMOutputMessages(messages: readonly Message<MessageContentPart, string>[]) {
77
- return messages.reduce(
78
- (acc, item, key) => ({
79
- ...acc,
80
- [`${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.output_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.role}`]:
81
- item.role,
82
- [`${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.output_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.content}`]:
83
- item.content
84
- .filter((c) => c.type === "text")
85
- .map((c) => c.text)
86
- .join(""),
87
- }),
88
- {},
89
- );
69
+ function parserLLMInputMessages(messages: readonly unknown[]) {
70
+ return messages.reduce((acc: Record<string, string>, item, key) => {
71
+ const { role, text } = getMessageParts(item);
72
+ if (role != null) {
73
+ acc[
74
+ `${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.input_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.role}`
75
+ ] = role;
76
+ }
77
+ acc[
78
+ `${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.input_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.content}`
79
+ ] = text;
80
+ return acc;
81
+ }, {});
82
+ }
83
+
84
+ function parseLLMOutputMessages(messages: readonly unknown[]) {
85
+ return messages.reduce((acc: Record<string, string>, item, key) => {
86
+ const { role, text } = getMessageParts(item);
87
+ if (role != null) {
88
+ acc[
89
+ `${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.output_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.role}`
90
+ ] = role;
91
+ }
92
+ acc[
93
+ `${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.output_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.content}`
94
+ ] = text;
95
+ return acc;
96
+ }, {});
90
97
  }
91
98
 
99
+ const matchesEvent = (name: string, events: readonly string[]): boolean => events.includes(name);
100
+
92
101
  export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unknown>) {
93
102
  try {
94
103
  // agent events
95
104
  if (
96
- [startEventName, successEventName, errorEventName, retryEventName].includes(
97
- meta.name as keyof ReActAgentCallbacks,
98
- ) &&
105
+ matchesEvent(meta.name, [startEventName, successEventName, errorEventName, retryEventName]) &&
99
106
  meta.creator instanceof BaseAgent
100
107
  ) {
101
- const { meta, tools, memory, error, data } = dataObject as InferCallbackValue<
102
- | ReActAgentCallbacks["start"]
103
- | ReActAgentCallbacks["error"]
104
- | ReActAgentCallbacks["success"]
105
- | ReActAgentCallbacks["retry"]
106
- >;
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;
107
115
  return {
108
116
  [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.AGENT,
109
- iteration: meta?.iteration,
117
+ ...(typeof agentMeta?.iteration === "number" && { iteration: agentMeta.iteration }),
110
118
  ...(tools?.length > 0 && {
111
119
  [SemanticConventions.LLM_TOOLS]: tools.map((tool) => ({
112
- [SemanticConventions.TOOL_NAME]: tool.name,
113
- [SemanticConventions.TOOL_DESCRIPTION]: tool.description,
120
+ [SemanticConventions.TOOL_NAME]: typeof tool.name === "string" ? tool.name : undefined,
121
+ [SemanticConventions.TOOL_DESCRIPTION]:
122
+ typeof tool.description === "string" ? tool.description : undefined,
114
123
  "tool.options": tool.options,
115
124
  })),
116
125
  }),
117
- ...(memory?.messages.length > 0 && {
126
+ ...(messages.length > 0 && {
118
127
  [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
119
- [SemanticConventions.INPUT_VALUE]: JSON.stringify(memory.messages),
128
+ [SemanticConventions.INPUT_VALUE]: JSON.stringify(messages),
120
129
  }),
121
130
  ...(error && {
122
131
  "exception.message": error.message,
123
132
  "exception.stacktrace": error.stack,
124
133
  "exception.type": error.name,
125
134
  }),
126
- ...(data && {
127
- [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
128
- [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(data),
129
- }),
135
+ ...(data != null
136
+ ? {
137
+ [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
138
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(data),
139
+ }
140
+ : {}),
130
141
  };
131
142
  }
132
143
 
133
144
  // update events
134
- if (
135
- [updateEventName, partialUpdateEventName].includes(meta.name as keyof ReActAgentCallbacks)
136
- ) {
137
- const { data } = dataObject as InferCallbackValue<
138
- ReActAgentCallbacks["partialUpdate"] | ReActAgentCallbacks["update"]
139
- >;
145
+ if (matchesEvent(meta.name, [updateEventName, partialUpdateEventName])) {
146
+ const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
147
+ const data = isObjectWithStringKeys(event.data) ? event.data : {};
140
148
 
141
- const output = data?.final_answer || data?.tool_output;
149
+ const output = data.final_answer || data.tool_output;
142
150
  return {
143
151
  [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.AGENT,
144
- thought: data.thought,
145
- ...(data?.tool_name && {
152
+ ...(typeof data.thought === "string" && { thought: data.thought }),
153
+ ...(typeof data.tool_name === "string" && {
146
154
  [SemanticConventions.TOOL_NAME]: data.tool_name,
147
155
  }),
148
- ...(data?.tool_input && {
149
- [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.tool_input),
150
- }),
151
- ...(output && {
156
+ ...(data.tool_input != null
157
+ ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.tool_input) }
158
+ : {}),
159
+ ...(typeof output === "string" && {
152
160
  [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
153
161
  [SemanticConventions.OUTPUT_VALUE]: output,
154
162
  }),
@@ -156,41 +164,34 @@ export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unk
156
164
  }
157
165
 
158
166
  // tool events (from agent)
159
- if (
160
- [toolErrorEventName, toolStartEventName, toolSuccessEventName].includes(
161
- meta.name as keyof ReActAgentCallbacks,
162
- )
163
- ) {
164
- const { data } = dataObject as InferCallbackValue<
165
- | ReActAgentCallbacks["toolError"]
166
- | ReActAgentCallbacks["toolStart"]
167
- | ReActAgentCallbacks["toolSuccess"]
168
- >;
169
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
- const output: any = data?.result && data.result.createSnapshot();
167
+ 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;
171
174
 
172
175
  return {
173
176
  [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
174
- thought: data?.iteration?.thought,
177
+ ...(typeof iteration?.thought === "string" && { thought: iteration.thought }),
175
178
  ...(data?.input
176
179
  ? {
177
180
  [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.input),
178
181
  }
179
182
  : {}),
180
- ...(data?.tool.description && {
181
- [SemanticConventions.TOOL_DESCRIPTION]: data.tool.description,
182
- }),
183
- ...(data?.tool.name && {
184
- [SemanticConventions.TOOL_NAME]: data.tool.name,
183
+ ...(typeof tool?.description === "string" && {
184
+ [SemanticConventions.TOOL_DESCRIPTION]: tool.description,
185
185
  }),
186
- ...(data?.error && {
187
- "exception.message": data.error.message,
188
- "exception.stacktrace": data.error.stack,
189
- "exception.type": data.error.name,
186
+ ...(typeof tool?.name === "string" && {
187
+ [SemanticConventions.TOOL_NAME]: tool.name,
190
188
  }),
191
- ...(output && {
192
- [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output),
189
+ ...(error && {
190
+ "exception.message": error.message,
191
+ "exception.stacktrace": error.stack,
192
+ "exception.type": error.name,
193
193
  }),
194
+ ...(output != null ? { [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output) } : {}),
194
195
  };
195
196
  }
196
197
  // tool events native
@@ -201,7 +202,7 @@ export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unk
201
202
  finishToolEventName,
202
203
  errorToolEventName,
203
204
  retryToolEventName,
204
- ].includes(meta.name as keyof ToolEvents) &&
205
+ ].some((eventName) => eventName === meta.name) &&
205
206
  meta.creator instanceof Tool
206
207
  ) {
207
208
  if (!dataObject) {
@@ -209,16 +210,14 @@ export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unk
209
210
  [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
210
211
  };
211
212
  }
212
- const { input, output, error } = dataObject as InferCallbackValue<
213
- ToolEvents["start"] | ToolEvents["success"] | ToolEvents["retry"] | ToolEvents["error"]
214
- >;
213
+ const event = isObjectWithStringKeys(dataObject) ? dataObject : {};
214
+ const { input, output } = event;
215
+ const error = event.error instanceof Error ? event.error : undefined;
215
216
 
216
217
  return {
217
218
  [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
218
- ...(input ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(input) } : {}),
219
- ...(output && {
220
- [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output),
221
- }),
219
+ ...(input != null ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(input) } : {}),
220
+ ...(output != null ? { [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output) } : {}),
222
221
  ...(error && {
223
222
  "exception.message": error.message,
224
223
  "exception.stacktrace": error.stack,
@@ -229,58 +228,67 @@ export function getSerializedObjectSafe(dataObject: unknown, meta: EventMeta<unk
229
228
 
230
229
  // llm events
231
230
  if (
232
- [successLLMEventName, startLLMEventName, errorLLMEventName, newTokenLLMEventName].includes(
233
- meta.name as keyof ChatModelEvents,
234
- ) &&
231
+ matchesEvent(meta.name, [
232
+ successLLMEventName,
233
+ startLLMEventName,
234
+ errorLLMEventName,
235
+ newTokenLLMEventName,
236
+ ]) &&
235
237
  meta.creator instanceof ChatModel
236
238
  ) {
237
- const { value, input, error } = dataObject as InferCallbackValue<
238
- | ChatModelEvents["success"]
239
- | ChatModelEvents["start"]
240
- | ChatModelEvents["error"]
241
- | ChatModelEvents["newToken"]
242
- >;
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;
243
246
 
244
- const creator = meta.creator.createSnapshot();
247
+ const creatorSnapshot = meta.creator.createSnapshot();
248
+ const creator: Record<string, unknown> = isObjectWithStringKeys(creatorSnapshot)
249
+ ? creatorSnapshot
250
+ : {};
245
251
  return {
246
252
  [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
247
- ...(value?.usage?.completionTokens && {
248
- [SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]: value?.usage?.completionTokens,
253
+ ...(typeof usage?.completionTokens === "number" && {
254
+ [SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]: usage.completionTokens,
249
255
  }),
250
- ...(value?.usage?.promptTokens && {
251
- [SemanticConventions.LLM_TOKEN_COUNT_PROMPT]: value?.usage?.promptTokens,
256
+ ...(typeof usage?.promptTokens === "number" && {
257
+ [SemanticConventions.LLM_TOKEN_COUNT_PROMPT]: usage.promptTokens,
252
258
  }),
253
- ...(value?.usage?.totalTokens && {
254
- [SemanticConventions.LLM_TOKEN_COUNT_TOTAL]: value?.usage?.totalTokens,
259
+ ...(typeof usage?.totalTokens === "number" && {
260
+ [SemanticConventions.LLM_TOKEN_COUNT_TOTAL]: usage.totalTokens,
255
261
  }),
256
- ...(input?.messages.length > 0 && {
262
+ ...(inputMessages.length > 0 && {
257
263
  [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
258
- [SemanticConventions.INPUT_VALUE]: JSON.stringify(input.messages),
259
- ...parserLLMInputMessages(input.messages),
264
+ [SemanticConventions.INPUT_VALUE]: JSON.stringify(inputMessages),
265
+ ...parserLLMInputMessages(inputMessages),
260
266
  }),
261
- ...(value?.messages.length > 0 && {
267
+ ...(outputMessages.length > 0 && {
262
268
  [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
263
- [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(value.messages),
264
- ...parseLLMOutputMessages(value.messages),
269
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(outputMessages),
270
+ ...parseLLMOutputMessages(outputMessages),
265
271
  }),
266
272
  ...(error && {
267
273
  "exception.message": error.message,
268
274
  "exception.stacktrace": error.stack,
269
275
  "exception.type": error.name,
270
276
  }),
271
- ...("providerId" in creator && {
277
+ ...(typeof creator.providerId === "string" && {
272
278
  [SemanticConventions.LLM_PROVIDER]: creator.providerId,
273
279
  [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.provider}`]:
274
280
  creator.providerId,
275
281
  }),
276
- ...("modelId" in creator && {
282
+ ...(typeof creator.modelId === "string" && {
277
283
  [SemanticConventions.LLM_MODEL_NAME]: creator.modelId,
278
284
  [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.model_name}`]:
279
285
  creator.modelId,
280
286
  }),
281
- ...(creator?.parameters && {
282
- [SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(creator.parameters),
283
- }),
287
+ ...(creator.parameters != null
288
+ ? {
289
+ [SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(creator.parameters),
290
+ }
291
+ : {}),
284
292
  };
285
293
  }
286
294
  if (meta.name === finishLLMEventName && meta.creator instanceof ChatModel) {
package/src/middleware.ts CHANGED
@@ -28,6 +28,7 @@ import { FrameworkError } from "beeai-framework/errors";
28
28
  import { Version } from "beeai-framework/version";
29
29
  import { findLast, isEmpty } from "remeda";
30
30
 
31
+ import { isObjectWithStringKeys } from "@arizeai/openinference-core";
31
32
  import type { OITracer } from "@arizeai/openinference-core";
32
33
  import type { OpenInferenceSpanKind } from "@arizeai/openinference-semantic-conventions";
33
34
  import { SemanticConventions } from "@arizeai/openinference-semantic-conventions";
@@ -78,7 +79,10 @@ export function createTelemetryMiddleware(tracer: OITracer, mainSpanKind: OpenIn
78
79
 
79
80
  let prompt: string | undefined | null = null;
80
81
  if (instance instanceof BaseAgent) {
81
- prompt = (runParams as Parameters<ReActAgent["run"]>)[0].prompt;
82
+ const firstParam = Array.isArray(runParams) ? runParams[0] : undefined;
83
+ if (isObjectWithStringKeys(firstParam) && typeof firstParam.prompt === "string") {
84
+ prompt = firstParam.prompt;
85
+ }
82
86
  }
83
87
 
84
88
  const spansMap = new Map<string, FrameworkSpan>();
@@ -227,7 +231,10 @@ export function createTelemetryMiddleware(tracer: OITracer, mainSpanKind: OpenIn
227
231
  const serializedData = getSerializedObjectSafe(data, meta);
228
232
 
229
233
  // skip partialUpdate events with no data
230
- if (meta.name === partialUpdateEventName && isEmpty(serializedData)) {
234
+ if (
235
+ meta.name === partialUpdateEventName &&
236
+ (serializedData == null || isEmpty(serializedData))
237
+ ) {
231
238
  return;
232
239
  }
233
240