@arizeai/openinference-instrumentation-beeai 1.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 (158) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +80 -0
  3. package/dist/esm/config.d.ts +39 -0
  4. package/dist/esm/config.d.ts.map +1 -0
  5. package/dist/esm/config.js +39 -0
  6. package/dist/esm/config.js.map +1 -0
  7. package/dist/esm/helpers/buildTraceTree.d.ts +21 -0
  8. package/dist/esm/helpers/buildTraceTree.d.ts.map +1 -0
  9. package/dist/esm/helpers/buildTraceTree.js +71 -0
  10. package/dist/esm/helpers/buildTraceTree.js.map +1 -0
  11. package/dist/esm/helpers/create-span.d.ts +32 -0
  12. package/dist/esm/helpers/create-span.d.ts.map +1 -0
  13. package/dist/esm/helpers/create-span.js +42 -0
  14. package/dist/esm/helpers/create-span.js.map +1 -0
  15. package/dist/esm/helpers/getErrorSafe.d.ts +17 -0
  16. package/dist/esm/helpers/getErrorSafe.d.ts.map +1 -0
  17. package/dist/esm/helpers/getErrorSafe.js +24 -0
  18. package/dist/esm/helpers/getErrorSafe.js.map +1 -0
  19. package/dist/esm/helpers/getSerializedObjectSafe.d.ts +18 -0
  20. package/dist/esm/helpers/getSerializedObjectSafe.d.ts.map +1 -0
  21. package/dist/esm/helpers/getSerializedObjectSafe.js +217 -0
  22. package/dist/esm/helpers/getSerializedObjectSafe.js.map +1 -0
  23. package/dist/esm/helpers/idNameManager.d.ts +31 -0
  24. package/dist/esm/helpers/idNameManager.d.ts.map +1 -0
  25. package/dist/esm/helpers/idNameManager.js +50 -0
  26. package/dist/esm/helpers/idNameManager.js.map +1 -0
  27. package/dist/esm/helpers/traceSerializer.d.ts +19 -0
  28. package/dist/esm/helpers/traceSerializer.d.ts.map +1 -0
  29. package/dist/esm/helpers/traceSerializer.js +35 -0
  30. package/dist/esm/helpers/traceSerializer.js.map +1 -0
  31. package/dist/esm/index.d.ts +2 -0
  32. package/dist/esm/index.d.ts.map +1 -0
  33. package/dist/esm/index.js +2 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/instrumentation.d.ts +34 -0
  36. package/dist/esm/instrumentation.d.ts.map +1 -0
  37. package/dist/esm/instrumentation.js +71 -0
  38. package/dist/esm/instrumentation.js.map +1 -0
  39. package/dist/esm/middleware.d.ts +27 -0
  40. package/dist/esm/middleware.d.ts.map +1 -0
  41. package/dist/esm/middleware.js +247 -0
  42. package/dist/esm/middleware.js.map +1 -0
  43. package/dist/esm/package.json +1 -0
  44. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  45. package/dist/esm/types.d.ts +36 -0
  46. package/dist/esm/types.d.ts.map +1 -0
  47. package/dist/esm/types.js +17 -0
  48. package/dist/esm/types.js.map +1 -0
  49. package/dist/esnext/config.d.ts +39 -0
  50. package/dist/esnext/config.d.ts.map +1 -0
  51. package/dist/esnext/config.js +39 -0
  52. package/dist/esnext/config.js.map +1 -0
  53. package/dist/esnext/helpers/buildTraceTree.d.ts +21 -0
  54. package/dist/esnext/helpers/buildTraceTree.d.ts.map +1 -0
  55. package/dist/esnext/helpers/buildTraceTree.js +57 -0
  56. package/dist/esnext/helpers/buildTraceTree.js.map +1 -0
  57. package/dist/esnext/helpers/create-span.d.ts +32 -0
  58. package/dist/esnext/helpers/create-span.d.ts.map +1 -0
  59. package/dist/esnext/helpers/create-span.js +37 -0
  60. package/dist/esnext/helpers/create-span.js.map +1 -0
  61. package/dist/esnext/helpers/getErrorSafe.d.ts +17 -0
  62. package/dist/esnext/helpers/getErrorSafe.d.ts.map +1 -0
  63. package/dist/esnext/helpers/getErrorSafe.js +24 -0
  64. package/dist/esnext/helpers/getErrorSafe.js.map +1 -0
  65. package/dist/esnext/helpers/getSerializedObjectSafe.d.ts +18 -0
  66. package/dist/esnext/helpers/getSerializedObjectSafe.d.ts.map +1 -0
  67. package/dist/esnext/helpers/getSerializedObjectSafe.js +165 -0
  68. package/dist/esnext/helpers/getSerializedObjectSafe.js.map +1 -0
  69. package/dist/esnext/helpers/idNameManager.d.ts +31 -0
  70. package/dist/esnext/helpers/idNameManager.d.ts.map +1 -0
  71. package/dist/esnext/helpers/idNameManager.js +50 -0
  72. package/dist/esnext/helpers/idNameManager.js.map +1 -0
  73. package/dist/esnext/helpers/traceSerializer.d.ts +19 -0
  74. package/dist/esnext/helpers/traceSerializer.d.ts.map +1 -0
  75. package/dist/esnext/helpers/traceSerializer.js +35 -0
  76. package/dist/esnext/helpers/traceSerializer.js.map +1 -0
  77. package/dist/esnext/index.d.ts +2 -0
  78. package/dist/esnext/index.d.ts.map +1 -0
  79. package/dist/esnext/index.js +2 -0
  80. package/dist/esnext/index.js.map +1 -0
  81. package/dist/esnext/instrumentation.d.ts +34 -0
  82. package/dist/esnext/instrumentation.d.ts.map +1 -0
  83. package/dist/esnext/instrumentation.js +71 -0
  84. package/dist/esnext/instrumentation.js.map +1 -0
  85. package/dist/esnext/middleware.d.ts +27 -0
  86. package/dist/esnext/middleware.d.ts.map +1 -0
  87. package/dist/esnext/middleware.js +241 -0
  88. package/dist/esnext/middleware.js.map +1 -0
  89. package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -0
  90. package/dist/esnext/types.d.ts +36 -0
  91. package/dist/esnext/types.d.ts.map +1 -0
  92. package/dist/esnext/types.js +17 -0
  93. package/dist/esnext/types.js.map +1 -0
  94. package/dist/examples/bee.d.ts +2 -0
  95. package/dist/examples/bee.d.ts.map +1 -0
  96. package/dist/examples/bee.js +35 -0
  97. package/dist/examples/bee.js.map +1 -0
  98. package/dist/examples/instrumentation.d.ts +2 -0
  99. package/dist/examples/instrumentation.d.ts.map +1 -0
  100. package/dist/examples/instrumentation.js +54 -0
  101. package/dist/examples/instrumentation.js.map +1 -0
  102. package/dist/src/config.d.ts +39 -0
  103. package/dist/src/config.d.ts.map +1 -0
  104. package/dist/src/config.js +42 -0
  105. package/dist/src/config.js.map +1 -0
  106. package/dist/src/helpers/buildTraceTree.d.ts +21 -0
  107. package/dist/src/helpers/buildTraceTree.d.ts.map +1 -0
  108. package/dist/src/helpers/buildTraceTree.js +60 -0
  109. package/dist/src/helpers/buildTraceTree.js.map +1 -0
  110. package/dist/src/helpers/create-span.d.ts +32 -0
  111. package/dist/src/helpers/create-span.d.ts.map +1 -0
  112. package/dist/src/helpers/create-span.js +40 -0
  113. package/dist/src/helpers/create-span.js.map +1 -0
  114. package/dist/src/helpers/getErrorSafe.d.ts +17 -0
  115. package/dist/src/helpers/getErrorSafe.d.ts.map +1 -0
  116. package/dist/src/helpers/getErrorSafe.js +27 -0
  117. package/dist/src/helpers/getErrorSafe.js.map +1 -0
  118. package/dist/src/helpers/getSerializedObjectSafe.d.ts +18 -0
  119. package/dist/src/helpers/getSerializedObjectSafe.d.ts.map +1 -0
  120. package/dist/src/helpers/getSerializedObjectSafe.js +168 -0
  121. package/dist/src/helpers/getSerializedObjectSafe.js.map +1 -0
  122. package/dist/src/helpers/idNameManager.d.ts +31 -0
  123. package/dist/src/helpers/idNameManager.d.ts.map +1 -0
  124. package/dist/src/helpers/idNameManager.js +54 -0
  125. package/dist/src/helpers/idNameManager.js.map +1 -0
  126. package/dist/src/helpers/traceSerializer.d.ts +19 -0
  127. package/dist/src/helpers/traceSerializer.d.ts.map +1 -0
  128. package/dist/src/helpers/traceSerializer.js +38 -0
  129. package/dist/src/helpers/traceSerializer.js.map +1 -0
  130. package/dist/src/index.d.ts +2 -0
  131. package/dist/src/index.d.ts.map +1 -0
  132. package/dist/src/index.js +18 -0
  133. package/dist/src/index.js.map +1 -0
  134. package/dist/src/instrumentation.d.ts +34 -0
  135. package/dist/src/instrumentation.d.ts.map +1 -0
  136. package/dist/src/instrumentation.js +76 -0
  137. package/dist/src/instrumentation.js.map +1 -0
  138. package/dist/src/middleware.d.ts +27 -0
  139. package/dist/src/middleware.d.ts.map +1 -0
  140. package/dist/src/middleware.js +254 -0
  141. package/dist/src/middleware.js.map +1 -0
  142. package/dist/src/types.d.ts +36 -0
  143. package/dist/src/types.d.ts.map +1 -0
  144. package/dist/src/types.js +18 -0
  145. package/dist/src/types.js.map +1 -0
  146. package/dist/tsconfig.tsbuildinfo +1 -0
  147. package/package.json +64 -0
  148. package/src/config.ts +48 -0
  149. package/src/helpers/buildTraceTree.ts +118 -0
  150. package/src/helpers/create-span.ts +66 -0
  151. package/src/helpers/getErrorSafe.ts +26 -0
  152. package/src/helpers/getSerializedObjectSafe.ts +325 -0
  153. package/src/helpers/idNameManager.ts +53 -0
  154. package/src/helpers/traceSerializer.ts +46 -0
  155. package/src/index.ts +1 -0
  156. package/src/instrumentation.ts +123 -0
  157. package/src/middleware.ts +318 -0
  158. package/src/types.ts +38 -0
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@arizeai/openinference-instrumentation-beeai",
3
+ "version": "1.0.0",
4
+ "license": "Apache-2.0",
5
+ "author": "gallas.milan@gmail.com",
6
+ "description": "OpenInference Instrumentation for BeeAI framework",
7
+ "private": false,
8
+ "main": "dist/src/index.js",
9
+ "module": "dist/esm/index.js",
10
+ "esnext": "dist/esnext/index.js",
11
+ "types": "dist/src/index.d.ts",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/Arize-ai/openinference.git"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "import": "./dist/esm/index.js",
19
+ "require": "./dist/src/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src"
25
+ ],
26
+ "keywords": [
27
+ "beeai",
28
+ "opentelemetry",
29
+ "tracing"
30
+ ],
31
+ "dependencies": {
32
+ "@opentelemetry/api": "^1.9.0",
33
+ "@opentelemetry/instrumentation": "^0.57.1",
34
+ "remeda": "^2.20.2",
35
+ "@arizeai/openinference-core": "1.0.0",
36
+ "@arizeai/openinference-semantic-conventions": "1.0.0"
37
+ },
38
+ "peerDependencies": {
39
+ "beeai-framework": "^0.1.5"
40
+ },
41
+ "devDependencies": {
42
+ "@types/jest": "^29.5.12",
43
+ "@types/node": "^20.14.11",
44
+ "beeai-framework": "^0.1.5",
45
+ "import-in-the-middle": "^1.13.0",
46
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.50.0",
47
+ "@opentelemetry/resources": "^1.30.1",
48
+ "@opentelemetry/sdk-node": "^0.57.1",
49
+ "@opentelemetry/sdk-trace-node": "^1.30.1",
50
+ "@opentelemetry/semantic-conventions": "^1.28.0",
51
+ "jest": "^29.7.0",
52
+ "ts-node": "^10.9.2"
53
+ },
54
+ "optionalDependencies": {
55
+ "ollama": "^0.5.12"
56
+ },
57
+ "scripts": {
58
+ "prebuild": "rimraf dist",
59
+ "build": "tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json && tsc-alias -p tsconfig.esm.json",
60
+ "postbuild": "echo '{\"type\": \"module\"}' > ./dist/esm/package.json && rimraf dist/test",
61
+ "type:check": "tsc --noEmit",
62
+ "test": "jest"
63
+ }
64
+ }
package/src/config.ts ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright 2025 IBM Corp.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { BeeCallbacks } from "beeai-framework/agents/bee/types";
18
+ import { ChatModelEvents } from "beeai-framework/backend/chat";
19
+ import { ToolEvents } from "beeai-framework/tools/base";
20
+
21
+ export const INSTRUMENTATION_IGNORED_KEYS = process.env
22
+ .BEE_FRAMEWORK_INSTRUMENTATION_IGNORED_KEYS
23
+ ? process.env.BEE_FRAMEWORK_INSTRUMENTATION_IGNORED_KEYS.split(",").filter(
24
+ Boolean,
25
+ )
26
+ : [];
27
+
28
+ export const partialUpdateEventName: keyof BeeCallbacks = "partialUpdate";
29
+ export const updateEventName: keyof BeeCallbacks = "update";
30
+ export const toolErrorEventName: keyof BeeCallbacks = "toolError";
31
+ export const toolStartEventName: keyof BeeCallbacks = "toolStart";
32
+ export const toolSuccessEventName: keyof BeeCallbacks = "toolSuccess";
33
+ export const startEventName: keyof BeeCallbacks = "start";
34
+ export const successEventName: keyof BeeCallbacks = "success";
35
+ export const errorEventName: keyof BeeCallbacks = "error";
36
+ export const retryEventName: keyof BeeCallbacks = "retry";
37
+
38
+ export const newTokenLLMEventName: keyof ChatModelEvents = `newToken`;
39
+ export const successLLMEventName: keyof ChatModelEvents = `success`;
40
+ export const startLLMEventName: keyof ChatModelEvents = `start`;
41
+ export const errorLLMEventName: keyof ChatModelEvents = `error`;
42
+ export const finishLLMEventName: keyof ChatModelEvents = `finish`;
43
+
44
+ export const startToolEventName: keyof ToolEvents = "start";
45
+ export const successToolEventName: keyof ToolEvents = "success";
46
+ export const finishToolEventName: keyof ToolEvents = "finish";
47
+ export const retryToolEventName: keyof ToolEvents = "retry";
48
+ export const errorToolEventName: keyof ToolEvents = "error";
@@ -0,0 +1,118 @@
1
+ import { OITracer } from "@arizeai/openinference-core";
2
+ import { FrameworkSpan, GeneratedResponse } from "../types";
3
+ import { SpanStatusCode, TimeInput } from "@opentelemetry/api";
4
+ import {
5
+ OpenInferenceSpanKind,
6
+ SemanticConventions,
7
+ } from "@arizeai/openinference-semantic-conventions";
8
+
9
+ interface BuiltTraceTreeProps {
10
+ tracer: OITracer;
11
+ data: {
12
+ prompt?: string | null;
13
+ history: GeneratedResponse[] | undefined;
14
+ generatedMessage: GeneratedResponse | undefined;
15
+ spans: FrameworkSpan[];
16
+ traceId: string;
17
+ version: string;
18
+ runErrorSpanKey: string;
19
+ startTime: TimeInput;
20
+ endTime: TimeInput;
21
+ source: string;
22
+ };
23
+ }
24
+
25
+ interface BuildSpansForParentProps {
26
+ tracer: OITracer;
27
+ data: {
28
+ spans: FrameworkSpan[];
29
+ traceId: string;
30
+ parentId: string | undefined;
31
+ };
32
+ }
33
+
34
+ function buildSpansForParent({ tracer, data }: BuildSpansForParentProps) {
35
+ data.spans
36
+ .filter((beeaiSpan) => beeaiSpan.parent_id === data.parentId)
37
+ .forEach((beeaiSpan) => {
38
+ tracer.startActiveSpan(
39
+ beeaiSpan.context.span_id,
40
+ {
41
+ // custom start time
42
+ startTime: beeaiSpan.start_time,
43
+ // set span important attributes
44
+ attributes: {
45
+ target: beeaiSpan.attributes.target,
46
+ name: beeaiSpan.name,
47
+ traceId: data.traceId,
48
+ ...(beeaiSpan.attributes.metadata && {
49
+ metadata: JSON.stringify(beeaiSpan.attributes.metadata),
50
+ }),
51
+ ...(beeaiSpan.attributes.data && { ...beeaiSpan.attributes.data }),
52
+ },
53
+ },
54
+ (activeSpan) => {
55
+ // set status
56
+ activeSpan.setStatus(beeaiSpan.status);
57
+
58
+ // set nested spans
59
+ buildSpansForParent({
60
+ tracer,
61
+ data: {
62
+ spans: data.spans,
63
+ traceId: data.traceId,
64
+ parentId: beeaiSpan.context.span_id,
65
+ },
66
+ });
67
+
68
+ // finish the span
69
+ activeSpan.end(beeaiSpan.end_time);
70
+ },
71
+ );
72
+ });
73
+ }
74
+
75
+ export function buildTraceTree({ tracer, data }: BuiltTraceTreeProps) {
76
+ tracer.startActiveSpan(
77
+ `beeai-framework-main`,
78
+ {
79
+ // custom start time
80
+ startTime: data.startTime,
81
+ // set main span important attributes
82
+ attributes: {
83
+ source: data.source,
84
+ traceId: data.traceId,
85
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]:
86
+ OpenInferenceSpanKind.AGENT,
87
+ ["beeai.version"]: data.version,
88
+ ...(data.prompt && { [SemanticConventions.INPUT_VALUE]: data.prompt }),
89
+ ...(data.generatedMessage !== undefined && {
90
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(
91
+ data.generatedMessage,
92
+ ),
93
+ }),
94
+ ...(data.history && { history: JSON.stringify(data.history) }),
95
+ },
96
+ },
97
+ (activeSpan) => {
98
+ // set status
99
+ const runErrorSpan = data.spans.find(
100
+ (span) => span.attributes.target === data.runErrorSpanKey,
101
+ );
102
+ if (runErrorSpan) {
103
+ activeSpan.setStatus(runErrorSpan.status);
104
+ } else {
105
+ activeSpan.setStatus({ code: SpanStatusCode.OK });
106
+ }
107
+
108
+ // set nested spans
109
+ buildSpansForParent({
110
+ tracer,
111
+ data: { spans: data.spans, traceId: data.traceId, parentId: undefined },
112
+ });
113
+
114
+ // finish the main span with custom end time
115
+ activeSpan.end(data.endTime);
116
+ },
117
+ );
118
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Copyright 2025 IBM Corp.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { SpanStatusCode, TimeInput } from "@opentelemetry/api";
18
+ import { FrameworkSpan } from "../types";
19
+ import { isEmpty } from "remeda";
20
+
21
+ interface CreateSpanProps {
22
+ id: string;
23
+ name: string;
24
+ target: string;
25
+ startedAt: TimeInput;
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ ctx?: any;
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ data?: any;
30
+ error?: string;
31
+ parent?: { id: string };
32
+ }
33
+
34
+ export function createSpan({
35
+ target,
36
+ name,
37
+ data,
38
+ error,
39
+ ctx,
40
+ parent,
41
+ id,
42
+ startedAt,
43
+ }: CreateSpanProps): FrameworkSpan {
44
+ return {
45
+ name: name,
46
+ attributes: {
47
+ target,
48
+ metadata: ctx && !isEmpty(ctx) ? { ...ctx } : undefined,
49
+ ...(error && {
50
+ "exception.message": error,
51
+ "exception.type": "FrameworkError",
52
+ }),
53
+ data: data && !isEmpty(data) ? { ...data } : null,
54
+ },
55
+ context: {
56
+ span_id: id,
57
+ },
58
+ parent_id: parent?.id,
59
+ status: {
60
+ code: error ? SpanStatusCode.ERROR : SpanStatusCode.OK,
61
+ message: error ? error : "",
62
+ },
63
+ start_time: startedAt,
64
+ end_time: performance.now(),
65
+ };
66
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright 2025 IBM Corp.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { FrameworkError } from "beeai-framework/errors";
18
+ import { getProp } from "beeai-framework/internals/helpers/object";
19
+
20
+ export function getErrorSafe(data: unknown): string | undefined {
21
+ const error = getProp(data, ["error"], data);
22
+
23
+ if (error instanceof FrameworkError) {
24
+ return FrameworkError.ensure(error).explain();
25
+ }
26
+ }
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Copyright 2025 IBM Corp.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { Serializable } from "beeai-framework/internals/serializable";
18
+ import { instrumentationLogger } from "beeai-framework/instrumentation/logger";
19
+ import { getProp } from "beeai-framework/internals/helpers/object";
20
+ import { EventMeta, InferCallbackValue } from "beeai-framework/emitter/types";
21
+ import { BeeCallbacks } from "beeai-framework/agents/bee/types";
22
+ import { ChatModel, ChatModelEvents } from "beeai-framework/backend/chat";
23
+ import { BaseAgent } from "beeai-framework/agents/base";
24
+ import { Message, MessageContentPart } from "beeai-framework/backend/message";
25
+ import {
26
+ errorEventName,
27
+ errorLLMEventName,
28
+ errorToolEventName,
29
+ finishLLMEventName,
30
+ finishToolEventName,
31
+ newTokenLLMEventName,
32
+ partialUpdateEventName,
33
+ retryEventName,
34
+ retryToolEventName,
35
+ startEventName,
36
+ startLLMEventName,
37
+ startToolEventName,
38
+ successEventName,
39
+ successLLMEventName,
40
+ successToolEventName,
41
+ toolErrorEventName,
42
+ toolStartEventName,
43
+ toolSuccessEventName,
44
+ updateEventName,
45
+ } from "../config";
46
+ import {
47
+ LLMAttributePostfixes,
48
+ MessageAttributePostfixes,
49
+ MimeType,
50
+ OpenInferenceSpanKind,
51
+ SemanticAttributePrefixes,
52
+ SemanticConventions,
53
+ } from "@arizeai/openinference-semantic-conventions";
54
+ import { Tool, ToolEvents } from "beeai-framework/tools/base";
55
+
56
+ function parserLLMInputMessages(
57
+ messages: readonly Message<MessageContentPart, string>[],
58
+ ) {
59
+ return messages.reduce(
60
+ (acc, item, key) => ({
61
+ ...acc,
62
+ [`${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.input_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.role}`]:
63
+ item.role,
64
+ [`${SemanticAttributePrefixes.llm}.${LLMAttributePostfixes.input_messages}.${key}.${SemanticAttributePrefixes.message}.${MessageAttributePostfixes.content}`]:
65
+ item.content
66
+ .filter((c) => c.type === "text")
67
+ .map((c) => c.text)
68
+ .join(""),
69
+ }),
70
+ {},
71
+ );
72
+ }
73
+
74
+ function parseLLMOutputMessages(
75
+ messages: readonly Message<MessageContentPart, string>[],
76
+ ) {
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
+ );
90
+ }
91
+
92
+ export function getSerializedObjectSafe(
93
+ dataObject: unknown,
94
+ meta: EventMeta<unknown>,
95
+ ) {
96
+ // agent events
97
+ if (
98
+ [startEventName, successEventName, errorEventName, retryEventName].includes(
99
+ meta.name as keyof BeeCallbacks,
100
+ ) &&
101
+ meta.creator instanceof BaseAgent
102
+ ) {
103
+ const { meta, tools, memory, error, data } =
104
+ dataObject as InferCallbackValue<
105
+ | BeeCallbacks["start"]
106
+ | BeeCallbacks["error"]
107
+ | BeeCallbacks["success"]
108
+ | BeeCallbacks["retry"]
109
+ >;
110
+ return {
111
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]:
112
+ OpenInferenceSpanKind.AGENT,
113
+ iteration: meta.iteration,
114
+ ...(tools?.length > 0 && {
115
+ [SemanticConventions.LLM_TOOLS]: tools.map((tool) => ({
116
+ [SemanticConventions.TOOL_NAME]: tool.name,
117
+ [SemanticConventions.TOOL_DESCRIPTION]: tool.description,
118
+ "tool.options": tool.options,
119
+ })),
120
+ }),
121
+ ...(memory.messages.length > 0 && {
122
+ [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
123
+ [SemanticConventions.INPUT_VALUE]: JSON.stringify(memory.messages),
124
+ }),
125
+ ...(error && {
126
+ "exception.message": error.message,
127
+ "exception.stacktrace": error.stack,
128
+ "exception.type": error.name,
129
+ }),
130
+ ...(data && {
131
+ [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
132
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(data),
133
+ }),
134
+ };
135
+ }
136
+
137
+ // update events
138
+ if (
139
+ [updateEventName, partialUpdateEventName].includes(
140
+ meta.name as keyof BeeCallbacks,
141
+ )
142
+ ) {
143
+ const { data } = dataObject as InferCallbackValue<
144
+ BeeCallbacks["partialUpdate"] | BeeCallbacks["update"]
145
+ >;
146
+
147
+ const output = data.final_answer || data.tool_output;
148
+ return {
149
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]:
150
+ OpenInferenceSpanKind.AGENT,
151
+ thought: data.thought,
152
+ ...(data.tool_name && {
153
+ [SemanticConventions.TOOL_NAME]: data.tool_name,
154
+ }),
155
+ ...(data.tool_input && {
156
+ [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.tool_input),
157
+ }),
158
+ ...(output && {
159
+ [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
160
+ [SemanticConventions.OUTPUT_VALUE]: output,
161
+ }),
162
+ };
163
+ }
164
+
165
+ // tool events (from agent)
166
+ if (
167
+ [toolErrorEventName, toolStartEventName, toolSuccessEventName].includes(
168
+ meta.name as keyof BeeCallbacks,
169
+ )
170
+ ) {
171
+ const { data } = dataObject as InferCallbackValue<
172
+ | BeeCallbacks["toolError"]
173
+ | BeeCallbacks["toolStart"]
174
+ | BeeCallbacks["toolSuccess"]
175
+ >;
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
+ const output: any = data.result && data.result.createSnapshot();
178
+
179
+ return {
180
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
181
+ thought: data.iteration.thought,
182
+ ...(data.input
183
+ ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(data.input) }
184
+ : {}),
185
+ ...(data.tool.description && {
186
+ [SemanticConventions.TOOL_DESCRIPTION]: data.tool.description,
187
+ }),
188
+ ...(data.tool.name && {
189
+ [SemanticConventions.TOOL_NAME]: data.tool.name,
190
+ }),
191
+ ...(data.error && {
192
+ "exception.message": data.error.message,
193
+ "exception.stacktrace": data.error.stack,
194
+ "exception.type": data.error.name,
195
+ }),
196
+ ...(output && {
197
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output),
198
+ }),
199
+ };
200
+ }
201
+ // tool events native
202
+ if (
203
+ [
204
+ startToolEventName,
205
+ successToolEventName,
206
+ finishToolEventName,
207
+ errorToolEventName,
208
+ retryToolEventName,
209
+ ].includes(meta.name as keyof ToolEvents) &&
210
+ meta.creator instanceof Tool
211
+ ) {
212
+ if (!dataObject) {
213
+ return {
214
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]:
215
+ OpenInferenceSpanKind.TOOL,
216
+ };
217
+ }
218
+ const { input, output, error } = dataObject as InferCallbackValue<
219
+ | ToolEvents["start"]
220
+ | ToolEvents["success"]
221
+ | ToolEvents["retry"]
222
+ | ToolEvents["error"]
223
+ >;
224
+
225
+ return {
226
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.TOOL,
227
+ ...(input
228
+ ? { [SemanticConventions.TOOL_PARAMETERS]: JSON.stringify(input) }
229
+ : {}),
230
+ ...(output && {
231
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(output),
232
+ }),
233
+ ...(error && {
234
+ "exception.message": error.message,
235
+ "exception.stacktrace": error.stack,
236
+ "exception.type": error.name,
237
+ }),
238
+ };
239
+ }
240
+
241
+ // llm events
242
+ if (
243
+ [
244
+ successLLMEventName,
245
+ startLLMEventName,
246
+ errorLLMEventName,
247
+ newTokenLLMEventName,
248
+ ].includes(meta.name as keyof ChatModelEvents) &&
249
+ meta.creator instanceof ChatModel
250
+ ) {
251
+ const { value, input, error } = dataObject as InferCallbackValue<
252
+ | ChatModelEvents["success"]
253
+ | ChatModelEvents["start"]
254
+ | ChatModelEvents["error"]
255
+ | ChatModelEvents["newToken"]
256
+ >;
257
+
258
+ const creator = meta.creator.createSnapshot();
259
+ return {
260
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
261
+ ...(value?.usage?.completionTokens && {
262
+ [SemanticConventions.LLM_TOKEN_COUNT_COMPLETION]:
263
+ value?.usage?.completionTokens,
264
+ }),
265
+ ...(value?.usage?.promptTokens && {
266
+ [SemanticConventions.LLM_TOKEN_COUNT_PROMPT]:
267
+ value?.usage?.promptTokens,
268
+ }),
269
+ ...(value?.usage?.totalTokens && {
270
+ [SemanticConventions.LLM_TOKEN_COUNT_TOTAL]: value?.usage?.totalTokens,
271
+ }),
272
+ ...(input?.messages.length > 0 && {
273
+ [SemanticConventions.INPUT_MIME_TYPE]: MimeType.JSON,
274
+ [SemanticConventions.INPUT_VALUE]: JSON.stringify(input.messages),
275
+ ...parserLLMInputMessages(input.messages),
276
+ }),
277
+ ...(value?.messages.length > 0 && {
278
+ [SemanticConventions.OUTPUT_MIME_TYPE]: MimeType.JSON,
279
+ [SemanticConventions.OUTPUT_VALUE]: JSON.stringify(value.messages),
280
+ ...parseLLMOutputMessages(value.messages),
281
+ }),
282
+ ...(error && {
283
+ "exception.message": error.message,
284
+ "exception.stacktrace": error.stack,
285
+ "exception.type": error.name,
286
+ }),
287
+ ...("providerId" in creator && {
288
+ [SemanticConventions.LLM_PROVIDER]: creator.providerId,
289
+ [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.provider}`]:
290
+ creator.providerId,
291
+ }),
292
+ ...("modelId" in creator && {
293
+ [SemanticConventions.LLM_MODEL_NAME]: creator.modelId,
294
+ [`${SemanticAttributePrefixes.metadata}.${LLMAttributePostfixes.model_name}`]:
295
+ creator.modelId,
296
+ }),
297
+ ...(creator.parameters && {
298
+ [SemanticConventions.LLM_INVOCATION_PARAMETERS]: JSON.stringify(
299
+ creator.parameters,
300
+ ),
301
+ }),
302
+ };
303
+ }
304
+ if (meta.name === finishLLMEventName && meta.creator instanceof ChatModel) {
305
+ return {
306
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: OpenInferenceSpanKind.LLM,
307
+ };
308
+ }
309
+
310
+ // other events
311
+ const data = getProp(dataObject, ["data"], dataObject);
312
+ if (data instanceof Serializable) {
313
+ try {
314
+ return { data: JSON.stringify(data.createSnapshot()), test: "hallo" };
315
+ } catch (e) {
316
+ instrumentationLogger.warn(
317
+ e,
318
+ "Invalid createSnapshot method in the Serializable class",
319
+ );
320
+ return null;
321
+ }
322
+ }
323
+
324
+ return { data: JSON.stringify(data) };
325
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright 2025 IBM Corp.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ interface GetIdsProps {
18
+ path: string;
19
+ id: string;
20
+ runId: string;
21
+ parentRunId?: string;
22
+ groupId?: string;
23
+ }
24
+
25
+ export class IdNameManager {
26
+ /** Current index for each event */
27
+ #idNamesCounter = new Map<string, number>();
28
+ /** The new span id names and the original framework emitter event ids dictionary */
29
+ #idNameMap = new Map<string, string>();
30
+ /** We need to map the run tree structure with duplicities to the event tree structure */
31
+ #runIdMap = new Map<string, string>();
32
+
33
+ #spanIdGenerator(name: string) {
34
+ const count = this.#idNamesCounter.get(name) || 0;
35
+ this.#idNamesCounter.set(name, count + 1);
36
+ return `${name}-${this.#idNamesCounter.get(name)}`;
37
+ }
38
+
39
+ getIds({ path, id, runId, parentRunId, groupId }: GetIdsProps) {
40
+ this.#runIdMap.set(runId, id);
41
+ const spanId = this.#spanIdGenerator(path);
42
+
43
+ this.#idNameMap.set(id, spanId);
44
+ const parentSpanId = parentRunId
45
+ ? this.#idNameMap.get(this.#runIdMap.get(parentRunId) || "")
46
+ : groupId;
47
+
48
+ return {
49
+ spanId,
50
+ parentSpanId,
51
+ };
52
+ }
53
+ }