@mastra/arize 1.3.6-alpha.0 → 1.3.6
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/CHANGELOG.md +8 -0
- package/dist/index.cjs +185 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +182 -208
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @mastra/arize
|
|
2
2
|
|
|
3
|
+
## 1.3.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`ce93a3c`](https://github.com/mastra-ai/mastra/commit/ce93a3c114ea1cbfbd576f3db41d7c26c9844f5b), [`5718a22`](https://github.com/mastra-ai/mastra/commit/5718a229281dcfd36bcd1f42a242e3717e510a33), [`a211d09`](https://github.com/mastra-ai/mastra/commit/a211d09185dc65a746534914cf38b67f21ee9bac), [`0dca9d0`](https://github.com/mastra-ai/mastra/commit/0dca9d0b1356024a53b72ea6f040db528b126caa), [`6218217`](https://github.com/mastra-ai/mastra/commit/62182171b6cfca0b099f1c6a77a2e65e7639ab86), [`5807d3a`](https://github.com/mastra-ai/mastra/commit/5807d3ae1d259b8b7d6df7e5bf2b485c694af9c8), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`05db566`](https://github.com/mastra-ai/mastra/commit/05db566fcbdcbf33d0bffca0c72ec30129e2e3ca), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`5718a22`](https://github.com/mastra-ai/mastra/commit/5718a229281dcfd36bcd1f42a242e3717e510a33), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`d1b7e3a`](https://github.com/mastra-ai/mastra/commit/d1b7e3a978a309a5653eeaa490d2d6c7c53bd093), [`29c584a`](https://github.com/mastra-ai/mastra/commit/29c584a13a88831e5ed1fdeb0ff8e82eae180433), [`c093146`](https://github.com/mastra-ai/mastra/commit/c0931466404d3c521308ea119cb165bb7e695155), [`8124754`](https://github.com/mastra-ai/mastra/commit/8124754ae89fbc69f8136d1df4a91904d0f84c4e), [`d12b2e4`](https://github.com/mastra-ai/mastra/commit/d12b2e4023fd9e3d3e93a9169f5088bcee2a849c)]:
|
|
8
|
+
- @mastra/core@1.54.0
|
|
9
|
+
- @mastra/otel-exporter@1.3.6
|
|
10
|
+
|
|
3
11
|
## 1.3.6-alpha.0
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1,222 +1,195 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _arizeai_openinference_semantic_conventions = require("@arizeai/openinference-semantic-conventions");
|
|
3
|
+
let _mastra_otel_exporter = require("@mastra/otel-exporter");
|
|
4
|
+
let _arizeai_openinference_genai = require("@arizeai/openinference-genai");
|
|
5
|
+
let _opentelemetry_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
|
|
6
|
+
let _opentelemetry_semantic_conventions_incubating = require("@opentelemetry/semantic-conventions/incubating");
|
|
7
|
+
//#region src/openInferenceOTLPExporter.ts
|
|
8
|
+
const GEN_AI_USAGE_REASONING_TOKENS = "gen_ai.usage.reasoning_tokens";
|
|
9
|
+
const GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
10
|
+
const GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = "gen_ai.usage.cache_creation.input_tokens";
|
|
11
|
+
const GEN_AI_USAGE_AUDIO_INPUT_TOKENS = "gen_ai.usage.audio_input_tokens";
|
|
12
|
+
const GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS = "gen_ai.usage.audio_output_tokens";
|
|
13
|
+
const MASTRA_GENERAL_PREFIX = "mastra.";
|
|
14
|
+
const MASTRA_METADATA_PREFIX = "mastra.metadata.";
|
|
15
|
+
const MASTRA_MODEL_STEP_INPUT = "mastra.model_step.input";
|
|
16
|
+
const MASTRA_MODEL_STEP_OUTPUT = "mastra.model_step.output";
|
|
17
|
+
const MASTRA_MODEL_CHUNK_OUTPUT = "mastra.model_chunk.output";
|
|
18
|
+
const MASTRA_SPAN_TYPE = "mastra.span.type";
|
|
19
|
+
/**
|
|
20
|
+
* Maps Mastra span types to OpenInference span kinds for proper trace categorization.
|
|
21
|
+
*
|
|
22
|
+
* Only non-CHAIN types are mapped here - all other span types default to CHAIN.
|
|
23
|
+
*/
|
|
24
|
+
const SPAN_TYPE_TO_KIND = {
|
|
25
|
+
model_generation: _arizeai_openinference_semantic_conventions.OpenInferenceSpanKind.LLM,
|
|
26
|
+
model_step: _arizeai_openinference_semantic_conventions.OpenInferenceSpanKind.LLM,
|
|
27
|
+
model_chunk: _arizeai_openinference_semantic_conventions.OpenInferenceSpanKind.LLM,
|
|
28
|
+
tool_call: _arizeai_openinference_semantic_conventions.OpenInferenceSpanKind.TOOL,
|
|
29
|
+
mcp_tool_call: _arizeai_openinference_semantic_conventions.OpenInferenceSpanKind.TOOL,
|
|
30
|
+
agent_run: _arizeai_openinference_semantic_conventions.OpenInferenceSpanKind.AGENT
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Converts GenAI usage metrics to OpenInference LLM token count attributes.
|
|
34
|
+
* Maps from OTEL GenAI semantic conventions to OpenInference semantic conventions.
|
|
35
|
+
*
|
|
36
|
+
* @param attributes - The span attributes containing GenAI usage metrics
|
|
37
|
+
* @returns OpenInference token count attributes
|
|
38
|
+
*/
|
|
32
39
|
function convertUsageMetricsToOpenInference(attributes) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (cacheCreationInputTokens !== void 0) {
|
|
51
|
-
result[openinferenceSemanticConventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;
|
|
52
|
-
}
|
|
53
|
-
const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];
|
|
54
|
-
if (reasoningTokens !== void 0) {
|
|
55
|
-
result[openinferenceSemanticConventions.LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;
|
|
56
|
-
}
|
|
57
|
-
const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];
|
|
58
|
-
if (audioInputTokens !== void 0) {
|
|
59
|
-
result[openinferenceSemanticConventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;
|
|
60
|
-
}
|
|
61
|
-
const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];
|
|
62
|
-
if (audioOutputTokens !== void 0) {
|
|
63
|
-
result[openinferenceSemanticConventions.LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;
|
|
64
|
-
}
|
|
65
|
-
return result;
|
|
40
|
+
const result = {};
|
|
41
|
+
const inputTokens = attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS];
|
|
42
|
+
const outputTokens = attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS];
|
|
43
|
+
if (inputTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_PROMPT] = inputTokens;
|
|
44
|
+
if (outputTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_COMPLETION] = outputTokens;
|
|
45
|
+
if (inputTokens !== void 0 && outputTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_TOTAL] = inputTokens + outputTokens;
|
|
46
|
+
const cacheReadInputTokens = attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS];
|
|
47
|
+
if (cacheReadInputTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ] = cacheReadInputTokens;
|
|
48
|
+
const cacheCreationInputTokens = attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS];
|
|
49
|
+
if (cacheCreationInputTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;
|
|
50
|
+
const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];
|
|
51
|
+
if (reasoningTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;
|
|
52
|
+
const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];
|
|
53
|
+
if (audioInputTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;
|
|
54
|
+
const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];
|
|
55
|
+
if (audioOutputTokens !== void 0) result[_arizeai_openinference_semantic_conventions.LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;
|
|
56
|
+
return result;
|
|
66
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Splits Mastra span attributes into two groups:
|
|
60
|
+
* - `metadata`: keys starting with "mastra.metadata." (prefix removed)
|
|
61
|
+
* - `other`: all remaining keys starting with "mastra."
|
|
62
|
+
*
|
|
63
|
+
* Any attributes not starting with "mastra." are ignored entirely.
|
|
64
|
+
*/
|
|
67
65
|
function splitMastraAttributes(attributes) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return acc;
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
mastraMetadata: {},
|
|
82
|
-
mastraOther: {}
|
|
83
|
-
}
|
|
84
|
-
);
|
|
66
|
+
return Object.entries(attributes).reduce((acc, [key, value]) => {
|
|
67
|
+
if (key.startsWith(MASTRA_GENERAL_PREFIX)) if (key.startsWith(MASTRA_METADATA_PREFIX)) {
|
|
68
|
+
const strippedKey = key.slice(16);
|
|
69
|
+
acc.mastraMetadata[strippedKey] = value;
|
|
70
|
+
} else acc.mastraOther[key] = value;
|
|
71
|
+
return acc;
|
|
72
|
+
}, {
|
|
73
|
+
mastraMetadata: {},
|
|
74
|
+
mastraOther: {}
|
|
75
|
+
});
|
|
85
76
|
}
|
|
86
|
-
var OpenInferenceOTLPTraceExporter = class extends
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return mutableSpan;
|
|
151
|
-
});
|
|
152
|
-
super.export(processedSpans, resultCallback);
|
|
153
|
-
}
|
|
77
|
+
var OpenInferenceOTLPTraceExporter = class extends _opentelemetry_exporter_trace_otlp_proto.OTLPTraceExporter {
|
|
78
|
+
export(spans, resultCallback) {
|
|
79
|
+
const processedSpans = spans.map((span) => {
|
|
80
|
+
const attributes = { ...span.attributes ?? {} };
|
|
81
|
+
const mutableSpan = span;
|
|
82
|
+
const { mastraMetadata, mastraOther } = splitMastraAttributes(attributes);
|
|
83
|
+
const processedAttributes = (0, _arizeai_openinference_genai.convertGenAISpanAttributesToOpenInferenceSpanAttributes)(attributes);
|
|
84
|
+
if (processedAttributes) {
|
|
85
|
+
const threadId = mastraMetadata["threadId"];
|
|
86
|
+
if (threadId) {
|
|
87
|
+
delete mastraMetadata["threadId"];
|
|
88
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.SESSION_ID] = threadId;
|
|
89
|
+
}
|
|
90
|
+
if (mastraOther["mastra.tags"]) {
|
|
91
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.TAG_TAGS] = mastraOther["mastra.tags"];
|
|
92
|
+
delete mastraOther["mastra.tags"];
|
|
93
|
+
}
|
|
94
|
+
const userId = mastraMetadata["userId"];
|
|
95
|
+
if (userId) {
|
|
96
|
+
delete mastraMetadata["userId"];
|
|
97
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.USER_ID] = userId;
|
|
98
|
+
}
|
|
99
|
+
if (Object.keys(mastraMetadata).length > 0) try {
|
|
100
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.METADATA] = JSON.stringify(mastraMetadata);
|
|
101
|
+
} catch {}
|
|
102
|
+
const inputMessages = attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_INPUT_MESSAGES] ?? attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_TOOL_CALL_ARGUMENTS] ?? mastraOther[MASTRA_MODEL_STEP_INPUT];
|
|
103
|
+
if (inputMessages) {
|
|
104
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.INPUT_MIME_TYPE] = "application/json";
|
|
105
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.INPUT_VALUE] = inputMessages;
|
|
106
|
+
}
|
|
107
|
+
const outputMessages = attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_OUTPUT_MESSAGES] ?? attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_TOOL_CALL_RESULT] ?? mastraOther[MASTRA_MODEL_STEP_OUTPUT] ?? mastraOther[MASTRA_MODEL_CHUNK_OUTPUT];
|
|
108
|
+
if (outputMessages) {
|
|
109
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.OUTPUT_MIME_TYPE] = "application/json";
|
|
110
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.OUTPUT_VALUE] = outputMessages;
|
|
111
|
+
}
|
|
112
|
+
if (!processedAttributes[_arizeai_openinference_semantic_conventions.INPUT_VALUE]) {
|
|
113
|
+
for (const key of Object.keys(mastraOther)) if (key.endsWith(".input")) {
|
|
114
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.INPUT_MIME_TYPE] = "application/json";
|
|
115
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.INPUT_VALUE] = mastraOther[key];
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!processedAttributes[_arizeai_openinference_semantic_conventions.OUTPUT_VALUE]) {
|
|
120
|
+
for (const key of Object.keys(mastraOther)) if (key.endsWith(".output")) {
|
|
121
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.OUTPUT_MIME_TYPE] = "application/json";
|
|
122
|
+
processedAttributes[_arizeai_openinference_semantic_conventions.OUTPUT_VALUE] = mastraOther[key];
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const usageMetrics = convertUsageMetricsToOpenInference(attributes);
|
|
127
|
+
Object.assign(processedAttributes, usageMetrics);
|
|
128
|
+
mutableSpan.attributes = {
|
|
129
|
+
...processedAttributes,
|
|
130
|
+
...mastraOther
|
|
131
|
+
};
|
|
132
|
+
const spanType = mastraOther[MASTRA_SPAN_TYPE];
|
|
133
|
+
if (typeof spanType === "string") mutableSpan.attributes[_arizeai_openinference_semantic_conventions.SemanticConventions.OPENINFERENCE_SPAN_KIND] = SPAN_TYPE_TO_KIND[spanType] ?? _arizeai_openinference_semantic_conventions.OpenInferenceSpanKind.CHAIN;
|
|
134
|
+
}
|
|
135
|
+
return mutableSpan;
|
|
136
|
+
});
|
|
137
|
+
super.export(processedSpans, resultCallback);
|
|
138
|
+
}
|
|
154
139
|
};
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
var ArizeExporter = class extends
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
...projectName ? { [openinferenceSemanticConventions.SEMRESATTRS_PROJECT_NAME]: projectName } : {},
|
|
206
|
-
...config.resourceAttributes
|
|
207
|
-
},
|
|
208
|
-
provider: {
|
|
209
|
-
custom: {
|
|
210
|
-
endpoint,
|
|
211
|
-
headers,
|
|
212
|
-
protocol: "http/protobuf"
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
}
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/tracing.ts
|
|
142
|
+
const LOG_PREFIX = "[ArizeExporter]";
|
|
143
|
+
const ARIZE_AX_ENDPOINT = "https://otlp.arize.com/v1/traces";
|
|
144
|
+
var ArizeExporter = class extends _mastra_otel_exporter.OtelExporter {
|
|
145
|
+
name = "arize";
|
|
146
|
+
constructor(config = {}) {
|
|
147
|
+
const spaceId = config.spaceId ?? process.env.ARIZE_SPACE_ID;
|
|
148
|
+
const apiKey = config.apiKey ?? process.env.ARIZE_API_KEY ?? process.env.PHOENIX_API_KEY;
|
|
149
|
+
const projectName = config.projectName ?? process.env.ARIZE_PROJECT_NAME ?? process.env.PHOENIX_PROJECT_NAME;
|
|
150
|
+
let endpoint = config.endpoint ?? process.env.PHOENIX_COLLECTOR_ENDPOINT ?? process.env.PHOENIX_ENDPOINT;
|
|
151
|
+
const headers = { ...config.headers };
|
|
152
|
+
let disabledReason;
|
|
153
|
+
if (spaceId) if (!apiKey) disabledReason = `${LOG_PREFIX} API key is required for Arize AX. Set ARIZE_API_KEY environment variable or pass apiKey in config.`;
|
|
154
|
+
else {
|
|
155
|
+
headers["space_id"] = spaceId;
|
|
156
|
+
headers["api_key"] = apiKey;
|
|
157
|
+
endpoint = endpoint || "https://otlp.arize.com/v1/traces";
|
|
158
|
+
}
|
|
159
|
+
else if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
|
160
|
+
if (!disabledReason && !endpoint) disabledReason = `${LOG_PREFIX} Endpoint is required in configuration. Set PHOENIX_COLLECTOR_ENDPOINT environment variable, or ARIZE_SPACE_ID for Arize AX, or pass endpoint in config.`;
|
|
161
|
+
if (disabledReason) {
|
|
162
|
+
super({
|
|
163
|
+
...config,
|
|
164
|
+
provider: { custom: {
|
|
165
|
+
endpoint: "http://disabled",
|
|
166
|
+
headers: {},
|
|
167
|
+
protocol: "http/protobuf"
|
|
168
|
+
} }
|
|
169
|
+
});
|
|
170
|
+
this.setDisabled(disabledReason);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
super({
|
|
174
|
+
exporter: new OpenInferenceOTLPTraceExporter({
|
|
175
|
+
url: endpoint,
|
|
176
|
+
headers
|
|
177
|
+
}),
|
|
178
|
+
...config,
|
|
179
|
+
resourceAttributes: {
|
|
180
|
+
...projectName ? { [_arizeai_openinference_semantic_conventions.SEMRESATTRS_PROJECT_NAME]: projectName } : {},
|
|
181
|
+
...config.resourceAttributes
|
|
182
|
+
},
|
|
183
|
+
provider: { custom: {
|
|
184
|
+
endpoint,
|
|
185
|
+
headers,
|
|
186
|
+
protocol: "http/protobuf"
|
|
187
|
+
} }
|
|
188
|
+
});
|
|
189
|
+
}
|
|
217
190
|
};
|
|
218
|
-
|
|
191
|
+
//#endregion
|
|
219
192
|
exports.ARIZE_AX_ENDPOINT = ARIZE_AX_ENDPOINT;
|
|
220
193
|
exports.ArizeExporter = ArizeExporter;
|
|
221
|
-
|
|
194
|
+
|
|
222
195
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/openInferenceOTLPExporter.ts","../src/tracing.ts"],"names":["OpenInferenceSpanKind","ATTR_GEN_AI_USAGE_INPUT_TOKENS","ATTR_GEN_AI_USAGE_OUTPUT_TOKENS","LLM_TOKEN_COUNT_PROMPT","LLM_TOKEN_COUNT_COMPLETION","LLM_TOKEN_COUNT_TOTAL","LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ","LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE","LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING","LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO","LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO","OTLPTraceExporter","convertGenAISpanAttributesToOpenInferenceSpanAttributes","SESSION_ID","TAG_TAGS","USER_ID","METADATA","ATTR_GEN_AI_INPUT_MESSAGES","ATTR_GEN_AI_TOOL_CALL_ARGUMENTS","INPUT_MIME_TYPE","INPUT_VALUE","ATTR_GEN_AI_OUTPUT_MESSAGES","ATTR_GEN_AI_TOOL_CALL_RESULT","OUTPUT_MIME_TYPE","OUTPUT_VALUE","SemanticConventions","OtelExporter","SEMRESATTRS_PROJECT_NAME"],"mappings":"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","file":"index.cjs","sourcesContent":["import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from '@arizeai/openinference-genai';\nimport type { Mutable } from '@arizeai/openinference-genai/types';\nimport {\n INPUT_MIME_TYPE,\n INPUT_VALUE,\n LLM_TOKEN_COUNT_COMPLETION,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING,\n LLM_TOKEN_COUNT_PROMPT,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE,\n LLM_TOKEN_COUNT_TOTAL,\n METADATA,\n OpenInferenceSpanKind,\n OUTPUT_MIME_TYPE,\n OUTPUT_VALUE,\n SemanticConventions,\n SESSION_ID,\n TAG_TAGS,\n USER_ID,\n} from '@arizeai/openinference-semantic-conventions';\nimport type { ExportResult } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n ATTR_GEN_AI_INPUT_MESSAGES,\n ATTR_GEN_AI_OUTPUT_MESSAGES,\n ATTR_GEN_AI_USAGE_INPUT_TOKENS,\n ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,\n ATTR_GEN_AI_TOOL_CALL_ARGUMENTS,\n ATTR_GEN_AI_TOOL_CALL_RESULT,\n} from '@opentelemetry/semantic-conventions/incubating';\n\n// GenAI usage attribute keys (not all are in @opentelemetry/semantic-conventions yet)\n// @see https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/\nconst GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens';\nconst GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = 'gen_ai.usage.cache_read.input_tokens';\nconst GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = 'gen_ai.usage.cache_creation.input_tokens';\nconst GEN_AI_USAGE_AUDIO_INPUT_TOKENS = 'gen_ai.usage.audio_input_tokens';\nconst GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS = 'gen_ai.usage.audio_output_tokens';\n\nconst MASTRA_GENERAL_PREFIX = 'mastra.';\nconst MASTRA_METADATA_PREFIX = 'mastra.metadata.';\nconst MASTRA_MODEL_STEP_INPUT = 'mastra.model_step.input';\nconst MASTRA_MODEL_STEP_OUTPUT = 'mastra.model_step.output';\nconst MASTRA_MODEL_CHUNK_OUTPUT = 'mastra.model_chunk.output';\nconst MASTRA_SPAN_TYPE = 'mastra.span.type';\n\n/**\n * Maps Mastra span types to OpenInference span kinds for proper trace categorization.\n *\n * Only non-CHAIN types are mapped here - all other span types default to CHAIN.\n */\nconst SPAN_TYPE_TO_KIND: Record<string, OpenInferenceSpanKind> = {\n // Model spans -> LLM\n model_generation: OpenInferenceSpanKind.LLM,\n model_step: OpenInferenceSpanKind.LLM,\n model_chunk: OpenInferenceSpanKind.LLM,\n // Tool spans -> TOOL\n tool_call: OpenInferenceSpanKind.TOOL,\n mcp_tool_call: OpenInferenceSpanKind.TOOL,\n // Agent spans -> AGENT\n agent_run: OpenInferenceSpanKind.AGENT,\n};\n\n/**\n * Converts GenAI usage metrics to OpenInference LLM token count attributes.\n * Maps from OTEL GenAI semantic conventions to OpenInference semantic conventions.\n *\n * @param attributes - The span attributes containing GenAI usage metrics\n * @returns OpenInference token count attributes\n */\nfunction convertUsageMetricsToOpenInference(attributes: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n const inputTokens = attributes[ATTR_GEN_AI_USAGE_INPUT_TOKENS];\n const outputTokens = attributes[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS];\n\n // Core token counts\n if (inputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT] = inputTokens;\n }\n if (outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION] = outputTokens;\n }\n\n // Total tokens (compute if we have both input and output)\n if (inputTokens !== undefined && outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_TOTAL] = inputTokens + outputTokens;\n }\n\n // Cache tokens (prompt details)\n const cacheReadInputTokens = attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS];\n if (cacheReadInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ] = cacheReadInputTokens;\n }\n\n const cacheCreationInputTokens = attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS];\n if (cacheCreationInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;\n }\n\n // Reasoning tokens (completion details)\n const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];\n if (reasoningTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;\n }\n\n // Audio tokens\n const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];\n if (audioInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;\n }\n\n const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];\n if (audioOutputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;\n }\n\n return result;\n}\n\n/**\n * Splits Mastra span attributes into two groups:\n * - `metadata`: keys starting with \"mastra.metadata.\" (prefix removed)\n * - `other`: all remaining keys starting with \"mastra.\"\n *\n * Any attributes not starting with \"mastra.\" are ignored entirely.\n */\nfunction splitMastraAttributes(attributes: Record<string, any>): {\n mastraMetadata: Record<string, any>;\n mastraOther: Record<string, any>;\n} {\n return Object.entries(attributes).reduce(\n (acc, [key, value]) => {\n if (key.startsWith(MASTRA_GENERAL_PREFIX)) {\n if (key.startsWith(MASTRA_METADATA_PREFIX)) {\n const strippedKey = key.slice(MASTRA_METADATA_PREFIX.length);\n acc.mastraMetadata[strippedKey] = value;\n } else {\n acc.mastraOther[key] = value;\n }\n }\n return acc;\n },\n {\n mastraMetadata: {} as Record<string, any>,\n mastraOther: {} as Record<string, any>,\n },\n );\n}\n\nexport class OpenInferenceOTLPTraceExporter extends OTLPTraceExporter {\n export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void) {\n const processedSpans = spans.map(span => {\n const attributes = { ...(span.attributes ?? {}) };\n const mutableSpan = span as Mutable<ReadableSpan>;\n\n const { mastraMetadata, mastraOther } = splitMastraAttributes(attributes);\n const processedAttributes = convertGenAISpanAttributesToOpenInferenceSpanAttributes(attributes);\n\n // only add processed attributes if conversion was successful\n if (processedAttributes) {\n const threadId = mastraMetadata['threadId'];\n if (threadId) {\n delete mastraMetadata['threadId'];\n processedAttributes[SESSION_ID] = threadId;\n }\n\n // Map mastra.tags to OpenInference native tag.tags convention (tags are only on root spans)\n if (mastraOther['mastra.tags']) {\n processedAttributes[TAG_TAGS] = mastraOther['mastra.tags'];\n delete mastraOther['mastra.tags'];\n }\n\n const userId = mastraMetadata['userId'];\n if (userId) {\n delete mastraMetadata['userId'];\n processedAttributes[USER_ID] = userId;\n }\n\n // Gather custom metadata into OpenInference metadata (flat best-effort)\n if (Object.keys(mastraMetadata).length > 0) {\n try {\n processedAttributes[METADATA] = JSON.stringify(mastraMetadata);\n } catch {\n // best-effort only\n }\n }\n\n const inputMessages =\n attributes[ATTR_GEN_AI_INPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_ARGUMENTS] ??\n mastraOther[MASTRA_MODEL_STEP_INPUT];\n if (inputMessages) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = inputMessages;\n }\n const outputMessages =\n attributes[ATTR_GEN_AI_OUTPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_RESULT] ??\n mastraOther[MASTRA_MODEL_STEP_OUTPUT] ??\n mastraOther[MASTRA_MODEL_CHUNK_OUTPUT];\n if (outputMessages) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = outputMessages;\n }\n\n // Map generic Mastra span input/output to OpenInference input/output\n // These are set by Mastra's gen-ai-semantics.ts for non-LLM/tool spans\n // (e.g., mastra.processor_run.input, mastra.workflow_run.input, etc.)\n if (!processedAttributes[INPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.input')) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n if (!processedAttributes[OUTPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.output')) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n\n // Convert GenAI usage metrics to OpenInference token count attributes\n const usageMetrics = convertUsageMetricsToOpenInference(attributes);\n Object.assign(processedAttributes, usageMetrics);\n\n mutableSpan.attributes = { ...processedAttributes, ...mastraOther };\n\n // Set span kind based on mastra.span.type for proper trace categorization\n const spanType = mastraOther[MASTRA_SPAN_TYPE];\n if (typeof spanType === 'string') {\n mutableSpan.attributes[SemanticConventions.OPENINFERENCE_SPAN_KIND] =\n SPAN_TYPE_TO_KIND[spanType] ?? OpenInferenceSpanKind.CHAIN;\n }\n }\n\n return mutableSpan;\n });\n\n super.export(processedSpans, resultCallback);\n }\n}\n","import { SEMRESATTRS_PROJECT_NAME } from '@arizeai/openinference-semantic-conventions';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArizeExporter]';\n\nexport const ARIZE_AX_ENDPOINT = 'https://otlp.arize.com/v1/traces';\n\nexport type ArizeExporterConfig = Omit<OtelExporterConfig, 'provider'> & {\n /**\n * Required if sending traces to Arize AX\n */\n spaceId?: string;\n /**\n * Required if sending traces to Arize AX, or to any other collector that\n * requires an Authorization header\n */\n apiKey?: string;\n /**\n * Collector endpoint destination for trace exports.\n * Required when sending traces to Phoenix, Phoenix Cloud, or other collectors.\n * Optional when sending traces to Arize AX.\n */\n endpoint?: string;\n /**\n * Optional project name to be added as a resource attribute using\n * OpenInference Semantic Conventions\n */\n projectName?: string;\n /**\n * Optional headers to be added to each OTLP request\n */\n headers?: Record<string, string>;\n};\n\nexport class ArizeExporter extends OtelExporter {\n name = 'arize';\n\n constructor(config: ArizeExporterConfig = {}) {\n // Read configuration from config or environment variables\n // Priority: config > ARIZE_* env vars > PHOENIX_* env vars\n const spaceId = config.spaceId ?? process.env.ARIZE_SPACE_ID;\n const apiKey = config.apiKey ?? process.env.ARIZE_API_KEY ?? process.env.PHOENIX_API_KEY;\n const projectName = config.projectName ?? process.env.ARIZE_PROJECT_NAME ?? process.env.PHOENIX_PROJECT_NAME;\n\n // Determine endpoint: config > PHOENIX_COLLECTOR_ENDPOINT > PHOENIX_ENDPOINT > ARIZE_AX_ENDPOINT (if spaceId is set)\n let endpoint: string | undefined =\n config.endpoint ?? process.env.PHOENIX_COLLECTOR_ENDPOINT ?? process.env.PHOENIX_ENDPOINT;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n // Validate credentials based on mode\n let disabledReason: string | undefined;\n\n if (spaceId) {\n // Arize AX mode requires an API key\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required for Arize AX. ` +\n `Set ARIZE_API_KEY environment variable or pass apiKey in config.`;\n } else {\n // arize ax header configuration\n headers['space_id'] = spaceId;\n headers['api_key'] = apiKey;\n endpoint = endpoint || ARIZE_AX_ENDPOINT;\n }\n } else if (apiKey) {\n // standard otel header configuration\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required in configuration. ` +\n `Set PHOENIX_COLLECTOR_ENDPOINT environment variable, or ARIZE_SPACE_ID for Arize AX, or pass endpoint in config.`;\n }\n\n // If disabled, create with minimal config and disable\n if (disabledReason) {\n super({\n ...config,\n provider: {\n custom: {\n endpoint: 'http://disabled',\n headers: {},\n protocol: 'http/protobuf',\n },\n },\n });\n this.setDisabled(disabledReason);\n return;\n }\n\n super({\n exporter: new OpenInferenceOTLPTraceExporter({\n url: endpoint!,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(projectName ? { [SEMRESATTRS_PROJECT_NAME]: projectName } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: endpoint!,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["OpenInferenceSpanKind","ATTR_GEN_AI_USAGE_INPUT_TOKENS","ATTR_GEN_AI_USAGE_OUTPUT_TOKENS","LLM_TOKEN_COUNT_PROMPT","LLM_TOKEN_COUNT_COMPLETION","LLM_TOKEN_COUNT_TOTAL","LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ","LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE","LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING","LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO","LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO","OTLPTraceExporter","SESSION_ID","TAG_TAGS","USER_ID","METADATA","ATTR_GEN_AI_INPUT_MESSAGES","ATTR_GEN_AI_TOOL_CALL_ARGUMENTS","INPUT_MIME_TYPE","INPUT_VALUE","ATTR_GEN_AI_OUTPUT_MESSAGES","ATTR_GEN_AI_TOOL_CALL_RESULT","OUTPUT_MIME_TYPE","OUTPUT_VALUE","SemanticConventions","OtelExporter","SEMRESATTRS_PROJECT_NAME"],"sources":["../src/openInferenceOTLPExporter.ts","../src/tracing.ts"],"sourcesContent":["import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from '@arizeai/openinference-genai';\nimport type { Mutable } from '@arizeai/openinference-genai/types';\nimport {\n INPUT_MIME_TYPE,\n INPUT_VALUE,\n LLM_TOKEN_COUNT_COMPLETION,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING,\n LLM_TOKEN_COUNT_PROMPT,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE,\n LLM_TOKEN_COUNT_TOTAL,\n METADATA,\n OpenInferenceSpanKind,\n OUTPUT_MIME_TYPE,\n OUTPUT_VALUE,\n SemanticConventions,\n SESSION_ID,\n TAG_TAGS,\n USER_ID,\n} from '@arizeai/openinference-semantic-conventions';\nimport type { ExportResult } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n ATTR_GEN_AI_INPUT_MESSAGES,\n ATTR_GEN_AI_OUTPUT_MESSAGES,\n ATTR_GEN_AI_USAGE_INPUT_TOKENS,\n ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,\n ATTR_GEN_AI_TOOL_CALL_ARGUMENTS,\n ATTR_GEN_AI_TOOL_CALL_RESULT,\n} from '@opentelemetry/semantic-conventions/incubating';\n\n// GenAI usage attribute keys (not all are in @opentelemetry/semantic-conventions yet)\n// @see https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/\nconst GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens';\nconst GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = 'gen_ai.usage.cache_read.input_tokens';\nconst GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = 'gen_ai.usage.cache_creation.input_tokens';\nconst GEN_AI_USAGE_AUDIO_INPUT_TOKENS = 'gen_ai.usage.audio_input_tokens';\nconst GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS = 'gen_ai.usage.audio_output_tokens';\n\nconst MASTRA_GENERAL_PREFIX = 'mastra.';\nconst MASTRA_METADATA_PREFIX = 'mastra.metadata.';\nconst MASTRA_MODEL_STEP_INPUT = 'mastra.model_step.input';\nconst MASTRA_MODEL_STEP_OUTPUT = 'mastra.model_step.output';\nconst MASTRA_MODEL_CHUNK_OUTPUT = 'mastra.model_chunk.output';\nconst MASTRA_SPAN_TYPE = 'mastra.span.type';\n\n/**\n * Maps Mastra span types to OpenInference span kinds for proper trace categorization.\n *\n * Only non-CHAIN types are mapped here - all other span types default to CHAIN.\n */\nconst SPAN_TYPE_TO_KIND: Record<string, OpenInferenceSpanKind> = {\n // Model spans -> LLM\n model_generation: OpenInferenceSpanKind.LLM,\n model_step: OpenInferenceSpanKind.LLM,\n model_chunk: OpenInferenceSpanKind.LLM,\n // Tool spans -> TOOL\n tool_call: OpenInferenceSpanKind.TOOL,\n mcp_tool_call: OpenInferenceSpanKind.TOOL,\n // Agent spans -> AGENT\n agent_run: OpenInferenceSpanKind.AGENT,\n};\n\n/**\n * Converts GenAI usage metrics to OpenInference LLM token count attributes.\n * Maps from OTEL GenAI semantic conventions to OpenInference semantic conventions.\n *\n * @param attributes - The span attributes containing GenAI usage metrics\n * @returns OpenInference token count attributes\n */\nfunction convertUsageMetricsToOpenInference(attributes: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n const inputTokens = attributes[ATTR_GEN_AI_USAGE_INPUT_TOKENS];\n const outputTokens = attributes[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS];\n\n // Core token counts\n if (inputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT] = inputTokens;\n }\n if (outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION] = outputTokens;\n }\n\n // Total tokens (compute if we have both input and output)\n if (inputTokens !== undefined && outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_TOTAL] = inputTokens + outputTokens;\n }\n\n // Cache tokens (prompt details)\n const cacheReadInputTokens = attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS];\n if (cacheReadInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ] = cacheReadInputTokens;\n }\n\n const cacheCreationInputTokens = attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS];\n if (cacheCreationInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;\n }\n\n // Reasoning tokens (completion details)\n const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];\n if (reasoningTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;\n }\n\n // Audio tokens\n const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];\n if (audioInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;\n }\n\n const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];\n if (audioOutputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;\n }\n\n return result;\n}\n\n/**\n * Splits Mastra span attributes into two groups:\n * - `metadata`: keys starting with \"mastra.metadata.\" (prefix removed)\n * - `other`: all remaining keys starting with \"mastra.\"\n *\n * Any attributes not starting with \"mastra.\" are ignored entirely.\n */\nfunction splitMastraAttributes(attributes: Record<string, any>): {\n mastraMetadata: Record<string, any>;\n mastraOther: Record<string, any>;\n} {\n return Object.entries(attributes).reduce(\n (acc, [key, value]) => {\n if (key.startsWith(MASTRA_GENERAL_PREFIX)) {\n if (key.startsWith(MASTRA_METADATA_PREFIX)) {\n const strippedKey = key.slice(MASTRA_METADATA_PREFIX.length);\n acc.mastraMetadata[strippedKey] = value;\n } else {\n acc.mastraOther[key] = value;\n }\n }\n return acc;\n },\n {\n mastraMetadata: {} as Record<string, any>,\n mastraOther: {} as Record<string, any>,\n },\n );\n}\n\nexport class OpenInferenceOTLPTraceExporter extends OTLPTraceExporter {\n export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void) {\n const processedSpans = spans.map(span => {\n const attributes = { ...(span.attributes ?? {}) };\n const mutableSpan = span as Mutable<ReadableSpan>;\n\n const { mastraMetadata, mastraOther } = splitMastraAttributes(attributes);\n const processedAttributes = convertGenAISpanAttributesToOpenInferenceSpanAttributes(attributes);\n\n // only add processed attributes if conversion was successful\n if (processedAttributes) {\n const threadId = mastraMetadata['threadId'];\n if (threadId) {\n delete mastraMetadata['threadId'];\n processedAttributes[SESSION_ID] = threadId;\n }\n\n // Map mastra.tags to OpenInference native tag.tags convention (tags are only on root spans)\n if (mastraOther['mastra.tags']) {\n processedAttributes[TAG_TAGS] = mastraOther['mastra.tags'];\n delete mastraOther['mastra.tags'];\n }\n\n const userId = mastraMetadata['userId'];\n if (userId) {\n delete mastraMetadata['userId'];\n processedAttributes[USER_ID] = userId;\n }\n\n // Gather custom metadata into OpenInference metadata (flat best-effort)\n if (Object.keys(mastraMetadata).length > 0) {\n try {\n processedAttributes[METADATA] = JSON.stringify(mastraMetadata);\n } catch {\n // best-effort only\n }\n }\n\n const inputMessages =\n attributes[ATTR_GEN_AI_INPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_ARGUMENTS] ??\n mastraOther[MASTRA_MODEL_STEP_INPUT];\n if (inputMessages) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = inputMessages;\n }\n const outputMessages =\n attributes[ATTR_GEN_AI_OUTPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_RESULT] ??\n mastraOther[MASTRA_MODEL_STEP_OUTPUT] ??\n mastraOther[MASTRA_MODEL_CHUNK_OUTPUT];\n if (outputMessages) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = outputMessages;\n }\n\n // Map generic Mastra span input/output to OpenInference input/output\n // These are set by Mastra's gen-ai-semantics.ts for non-LLM/tool spans\n // (e.g., mastra.processor_run.input, mastra.workflow_run.input, etc.)\n if (!processedAttributes[INPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.input')) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n if (!processedAttributes[OUTPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.output')) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n\n // Convert GenAI usage metrics to OpenInference token count attributes\n const usageMetrics = convertUsageMetricsToOpenInference(attributes);\n Object.assign(processedAttributes, usageMetrics);\n\n mutableSpan.attributes = { ...processedAttributes, ...mastraOther };\n\n // Set span kind based on mastra.span.type for proper trace categorization\n const spanType = mastraOther[MASTRA_SPAN_TYPE];\n if (typeof spanType === 'string') {\n mutableSpan.attributes[SemanticConventions.OPENINFERENCE_SPAN_KIND] =\n SPAN_TYPE_TO_KIND[spanType] ?? OpenInferenceSpanKind.CHAIN;\n }\n }\n\n return mutableSpan;\n });\n\n super.export(processedSpans, resultCallback);\n }\n}\n","import { SEMRESATTRS_PROJECT_NAME } from '@arizeai/openinference-semantic-conventions';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArizeExporter]';\n\nexport const ARIZE_AX_ENDPOINT = 'https://otlp.arize.com/v1/traces';\n\nexport type ArizeExporterConfig = Omit<OtelExporterConfig, 'provider'> & {\n /**\n * Required if sending traces to Arize AX\n */\n spaceId?: string;\n /**\n * Required if sending traces to Arize AX, or to any other collector that\n * requires an Authorization header\n */\n apiKey?: string;\n /**\n * Collector endpoint destination for trace exports.\n * Required when sending traces to Phoenix, Phoenix Cloud, or other collectors.\n * Optional when sending traces to Arize AX.\n */\n endpoint?: string;\n /**\n * Optional project name to be added as a resource attribute using\n * OpenInference Semantic Conventions\n */\n projectName?: string;\n /**\n * Optional headers to be added to each OTLP request\n */\n headers?: Record<string, string>;\n};\n\nexport class ArizeExporter extends OtelExporter {\n name = 'arize';\n\n constructor(config: ArizeExporterConfig = {}) {\n // Read configuration from config or environment variables\n // Priority: config > ARIZE_* env vars > PHOENIX_* env vars\n const spaceId = config.spaceId ?? process.env.ARIZE_SPACE_ID;\n const apiKey = config.apiKey ?? process.env.ARIZE_API_KEY ?? process.env.PHOENIX_API_KEY;\n const projectName = config.projectName ?? process.env.ARIZE_PROJECT_NAME ?? process.env.PHOENIX_PROJECT_NAME;\n\n // Determine endpoint: config > PHOENIX_COLLECTOR_ENDPOINT > PHOENIX_ENDPOINT > ARIZE_AX_ENDPOINT (if spaceId is set)\n let endpoint: string | undefined =\n config.endpoint ?? process.env.PHOENIX_COLLECTOR_ENDPOINT ?? process.env.PHOENIX_ENDPOINT;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n // Validate credentials based on mode\n let disabledReason: string | undefined;\n\n if (spaceId) {\n // Arize AX mode requires an API key\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required for Arize AX. ` +\n `Set ARIZE_API_KEY environment variable or pass apiKey in config.`;\n } else {\n // arize ax header configuration\n headers['space_id'] = spaceId;\n headers['api_key'] = apiKey;\n endpoint = endpoint || ARIZE_AX_ENDPOINT;\n }\n } else if (apiKey) {\n // standard otel header configuration\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required in configuration. ` +\n `Set PHOENIX_COLLECTOR_ENDPOINT environment variable, or ARIZE_SPACE_ID for Arize AX, or pass endpoint in config.`;\n }\n\n // If disabled, create with minimal config and disable\n if (disabledReason) {\n super({\n ...config,\n provider: {\n custom: {\n endpoint: 'http://disabled',\n headers: {},\n protocol: 'http/protobuf',\n },\n },\n });\n this.setDisabled(disabledReason);\n return;\n }\n\n super({\n exporter: new OpenInferenceOTLPTraceExporter({\n url: endpoint!,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(projectName ? { [SEMRESATTRS_PROJECT_NAME]: projectName } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: endpoint!,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n }\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,219 +1,193 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { OtelExporter } from
|
|
3
|
-
import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from
|
|
4
|
-
import { OTLPTraceExporter } from
|
|
5
|
-
import { ATTR_GEN_AI_INPUT_MESSAGES,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
import { INPUT_MIME_TYPE, INPUT_VALUE, LLM_TOKEN_COUNT_COMPLETION, LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO, LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING, LLM_TOKEN_COUNT_PROMPT, LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO, LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ, LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE, LLM_TOKEN_COUNT_TOTAL, METADATA, OUTPUT_MIME_TYPE, OUTPUT_VALUE, OpenInferenceSpanKind, SEMRESATTRS_PROJECT_NAME, SESSION_ID, SemanticConventions, TAG_TAGS, USER_ID } from "@arizeai/openinference-semantic-conventions";
|
|
2
|
+
import { OtelExporter } from "@mastra/otel-exporter";
|
|
3
|
+
import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from "@arizeai/openinference-genai";
|
|
4
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
5
|
+
import { ATTR_GEN_AI_INPUT_MESSAGES, ATTR_GEN_AI_OUTPUT_MESSAGES, ATTR_GEN_AI_TOOL_CALL_ARGUMENTS, ATTR_GEN_AI_TOOL_CALL_RESULT, ATTR_GEN_AI_USAGE_INPUT_TOKENS, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS } from "@opentelemetry/semantic-conventions/incubating";
|
|
6
|
+
//#region src/openInferenceOTLPExporter.ts
|
|
7
|
+
const GEN_AI_USAGE_REASONING_TOKENS = "gen_ai.usage.reasoning_tokens";
|
|
8
|
+
const GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
9
|
+
const GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = "gen_ai.usage.cache_creation.input_tokens";
|
|
10
|
+
const GEN_AI_USAGE_AUDIO_INPUT_TOKENS = "gen_ai.usage.audio_input_tokens";
|
|
11
|
+
const GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS = "gen_ai.usage.audio_output_tokens";
|
|
12
|
+
const MASTRA_GENERAL_PREFIX = "mastra.";
|
|
13
|
+
const MASTRA_METADATA_PREFIX = "mastra.metadata.";
|
|
14
|
+
const MASTRA_MODEL_STEP_INPUT = "mastra.model_step.input";
|
|
15
|
+
const MASTRA_MODEL_STEP_OUTPUT = "mastra.model_step.output";
|
|
16
|
+
const MASTRA_MODEL_CHUNK_OUTPUT = "mastra.model_chunk.output";
|
|
17
|
+
const MASTRA_SPAN_TYPE = "mastra.span.type";
|
|
18
|
+
/**
|
|
19
|
+
* Maps Mastra span types to OpenInference span kinds for proper trace categorization.
|
|
20
|
+
*
|
|
21
|
+
* Only non-CHAIN types are mapped here - all other span types default to CHAIN.
|
|
22
|
+
*/
|
|
23
|
+
const SPAN_TYPE_TO_KIND = {
|
|
24
|
+
model_generation: OpenInferenceSpanKind.LLM,
|
|
25
|
+
model_step: OpenInferenceSpanKind.LLM,
|
|
26
|
+
model_chunk: OpenInferenceSpanKind.LLM,
|
|
27
|
+
tool_call: OpenInferenceSpanKind.TOOL,
|
|
28
|
+
mcp_tool_call: OpenInferenceSpanKind.TOOL,
|
|
29
|
+
agent_run: OpenInferenceSpanKind.AGENT
|
|
29
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Converts GenAI usage metrics to OpenInference LLM token count attributes.
|
|
33
|
+
* Maps from OTEL GenAI semantic conventions to OpenInference semantic conventions.
|
|
34
|
+
*
|
|
35
|
+
* @param attributes - The span attributes containing GenAI usage metrics
|
|
36
|
+
* @returns OpenInference token count attributes
|
|
37
|
+
*/
|
|
30
38
|
function convertUsageMetricsToOpenInference(attributes) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (cacheCreationInputTokens !== void 0) {
|
|
49
|
-
result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;
|
|
50
|
-
}
|
|
51
|
-
const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];
|
|
52
|
-
if (reasoningTokens !== void 0) {
|
|
53
|
-
result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;
|
|
54
|
-
}
|
|
55
|
-
const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];
|
|
56
|
-
if (audioInputTokens !== void 0) {
|
|
57
|
-
result[LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;
|
|
58
|
-
}
|
|
59
|
-
const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];
|
|
60
|
-
if (audioOutputTokens !== void 0) {
|
|
61
|
-
result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;
|
|
62
|
-
}
|
|
63
|
-
return result;
|
|
39
|
+
const result = {};
|
|
40
|
+
const inputTokens = attributes[ATTR_GEN_AI_USAGE_INPUT_TOKENS];
|
|
41
|
+
const outputTokens = attributes[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS];
|
|
42
|
+
if (inputTokens !== void 0) result[LLM_TOKEN_COUNT_PROMPT] = inputTokens;
|
|
43
|
+
if (outputTokens !== void 0) result[LLM_TOKEN_COUNT_COMPLETION] = outputTokens;
|
|
44
|
+
if (inputTokens !== void 0 && outputTokens !== void 0) result[LLM_TOKEN_COUNT_TOTAL] = inputTokens + outputTokens;
|
|
45
|
+
const cacheReadInputTokens = attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS];
|
|
46
|
+
if (cacheReadInputTokens !== void 0) result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ] = cacheReadInputTokens;
|
|
47
|
+
const cacheCreationInputTokens = attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS];
|
|
48
|
+
if (cacheCreationInputTokens !== void 0) result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;
|
|
49
|
+
const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];
|
|
50
|
+
if (reasoningTokens !== void 0) result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;
|
|
51
|
+
const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];
|
|
52
|
+
if (audioInputTokens !== void 0) result[LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;
|
|
53
|
+
const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];
|
|
54
|
+
if (audioOutputTokens !== void 0) result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;
|
|
55
|
+
return result;
|
|
64
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Splits Mastra span attributes into two groups:
|
|
59
|
+
* - `metadata`: keys starting with "mastra.metadata." (prefix removed)
|
|
60
|
+
* - `other`: all remaining keys starting with "mastra."
|
|
61
|
+
*
|
|
62
|
+
* Any attributes not starting with "mastra." are ignored entirely.
|
|
63
|
+
*/
|
|
65
64
|
function splitMastraAttributes(attributes) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return acc;
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
mastraMetadata: {},
|
|
80
|
-
mastraOther: {}
|
|
81
|
-
}
|
|
82
|
-
);
|
|
65
|
+
return Object.entries(attributes).reduce((acc, [key, value]) => {
|
|
66
|
+
if (key.startsWith(MASTRA_GENERAL_PREFIX)) if (key.startsWith(MASTRA_METADATA_PREFIX)) {
|
|
67
|
+
const strippedKey = key.slice(16);
|
|
68
|
+
acc.mastraMetadata[strippedKey] = value;
|
|
69
|
+
} else acc.mastraOther[key] = value;
|
|
70
|
+
return acc;
|
|
71
|
+
}, {
|
|
72
|
+
mastraMetadata: {},
|
|
73
|
+
mastraOther: {}
|
|
74
|
+
});
|
|
83
75
|
}
|
|
84
76
|
var OpenInferenceOTLPTraceExporter = class extends OTLPTraceExporter {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return mutableSpan;
|
|
149
|
-
});
|
|
150
|
-
super.export(processedSpans, resultCallback);
|
|
151
|
-
}
|
|
77
|
+
export(spans, resultCallback) {
|
|
78
|
+
const processedSpans = spans.map((span) => {
|
|
79
|
+
const attributes = { ...span.attributes ?? {} };
|
|
80
|
+
const mutableSpan = span;
|
|
81
|
+
const { mastraMetadata, mastraOther } = splitMastraAttributes(attributes);
|
|
82
|
+
const processedAttributes = convertGenAISpanAttributesToOpenInferenceSpanAttributes(attributes);
|
|
83
|
+
if (processedAttributes) {
|
|
84
|
+
const threadId = mastraMetadata["threadId"];
|
|
85
|
+
if (threadId) {
|
|
86
|
+
delete mastraMetadata["threadId"];
|
|
87
|
+
processedAttributes[SESSION_ID] = threadId;
|
|
88
|
+
}
|
|
89
|
+
if (mastraOther["mastra.tags"]) {
|
|
90
|
+
processedAttributes[TAG_TAGS] = mastraOther["mastra.tags"];
|
|
91
|
+
delete mastraOther["mastra.tags"];
|
|
92
|
+
}
|
|
93
|
+
const userId = mastraMetadata["userId"];
|
|
94
|
+
if (userId) {
|
|
95
|
+
delete mastraMetadata["userId"];
|
|
96
|
+
processedAttributes[USER_ID] = userId;
|
|
97
|
+
}
|
|
98
|
+
if (Object.keys(mastraMetadata).length > 0) try {
|
|
99
|
+
processedAttributes[METADATA] = JSON.stringify(mastraMetadata);
|
|
100
|
+
} catch {}
|
|
101
|
+
const inputMessages = attributes[ATTR_GEN_AI_INPUT_MESSAGES] ?? attributes[ATTR_GEN_AI_TOOL_CALL_ARGUMENTS] ?? mastraOther[MASTRA_MODEL_STEP_INPUT];
|
|
102
|
+
if (inputMessages) {
|
|
103
|
+
processedAttributes[INPUT_MIME_TYPE] = "application/json";
|
|
104
|
+
processedAttributes[INPUT_VALUE] = inputMessages;
|
|
105
|
+
}
|
|
106
|
+
const outputMessages = attributes[ATTR_GEN_AI_OUTPUT_MESSAGES] ?? attributes[ATTR_GEN_AI_TOOL_CALL_RESULT] ?? mastraOther[MASTRA_MODEL_STEP_OUTPUT] ?? mastraOther[MASTRA_MODEL_CHUNK_OUTPUT];
|
|
107
|
+
if (outputMessages) {
|
|
108
|
+
processedAttributes[OUTPUT_MIME_TYPE] = "application/json";
|
|
109
|
+
processedAttributes[OUTPUT_VALUE] = outputMessages;
|
|
110
|
+
}
|
|
111
|
+
if (!processedAttributes[INPUT_VALUE]) {
|
|
112
|
+
for (const key of Object.keys(mastraOther)) if (key.endsWith(".input")) {
|
|
113
|
+
processedAttributes[INPUT_MIME_TYPE] = "application/json";
|
|
114
|
+
processedAttributes[INPUT_VALUE] = mastraOther[key];
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!processedAttributes[OUTPUT_VALUE]) {
|
|
119
|
+
for (const key of Object.keys(mastraOther)) if (key.endsWith(".output")) {
|
|
120
|
+
processedAttributes[OUTPUT_MIME_TYPE] = "application/json";
|
|
121
|
+
processedAttributes[OUTPUT_VALUE] = mastraOther[key];
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const usageMetrics = convertUsageMetricsToOpenInference(attributes);
|
|
126
|
+
Object.assign(processedAttributes, usageMetrics);
|
|
127
|
+
mutableSpan.attributes = {
|
|
128
|
+
...processedAttributes,
|
|
129
|
+
...mastraOther
|
|
130
|
+
};
|
|
131
|
+
const spanType = mastraOther[MASTRA_SPAN_TYPE];
|
|
132
|
+
if (typeof spanType === "string") mutableSpan.attributes[SemanticConventions.OPENINFERENCE_SPAN_KIND] = SPAN_TYPE_TO_KIND[spanType] ?? OpenInferenceSpanKind.CHAIN;
|
|
133
|
+
}
|
|
134
|
+
return mutableSpan;
|
|
135
|
+
});
|
|
136
|
+
super.export(processedSpans, resultCallback);
|
|
137
|
+
}
|
|
152
138
|
};
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/tracing.ts
|
|
141
|
+
const LOG_PREFIX = "[ArizeExporter]";
|
|
142
|
+
const ARIZE_AX_ENDPOINT = "https://otlp.arize.com/v1/traces";
|
|
157
143
|
var ArizeExporter = class extends OtelExporter {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
...projectName ? { [SEMRESATTRS_PROJECT_NAME]: projectName } : {},
|
|
204
|
-
...config.resourceAttributes
|
|
205
|
-
},
|
|
206
|
-
provider: {
|
|
207
|
-
custom: {
|
|
208
|
-
endpoint,
|
|
209
|
-
headers,
|
|
210
|
-
protocol: "http/protobuf"
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
}
|
|
144
|
+
name = "arize";
|
|
145
|
+
constructor(config = {}) {
|
|
146
|
+
const spaceId = config.spaceId ?? process.env.ARIZE_SPACE_ID;
|
|
147
|
+
const apiKey = config.apiKey ?? process.env.ARIZE_API_KEY ?? process.env.PHOENIX_API_KEY;
|
|
148
|
+
const projectName = config.projectName ?? process.env.ARIZE_PROJECT_NAME ?? process.env.PHOENIX_PROJECT_NAME;
|
|
149
|
+
let endpoint = config.endpoint ?? process.env.PHOENIX_COLLECTOR_ENDPOINT ?? process.env.PHOENIX_ENDPOINT;
|
|
150
|
+
const headers = { ...config.headers };
|
|
151
|
+
let disabledReason;
|
|
152
|
+
if (spaceId) if (!apiKey) disabledReason = `${LOG_PREFIX} API key is required for Arize AX. Set ARIZE_API_KEY environment variable or pass apiKey in config.`;
|
|
153
|
+
else {
|
|
154
|
+
headers["space_id"] = spaceId;
|
|
155
|
+
headers["api_key"] = apiKey;
|
|
156
|
+
endpoint = endpoint || "https://otlp.arize.com/v1/traces";
|
|
157
|
+
}
|
|
158
|
+
else if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
|
159
|
+
if (!disabledReason && !endpoint) disabledReason = `${LOG_PREFIX} Endpoint is required in configuration. Set PHOENIX_COLLECTOR_ENDPOINT environment variable, or ARIZE_SPACE_ID for Arize AX, or pass endpoint in config.`;
|
|
160
|
+
if (disabledReason) {
|
|
161
|
+
super({
|
|
162
|
+
...config,
|
|
163
|
+
provider: { custom: {
|
|
164
|
+
endpoint: "http://disabled",
|
|
165
|
+
headers: {},
|
|
166
|
+
protocol: "http/protobuf"
|
|
167
|
+
} }
|
|
168
|
+
});
|
|
169
|
+
this.setDisabled(disabledReason);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
super({
|
|
173
|
+
exporter: new OpenInferenceOTLPTraceExporter({
|
|
174
|
+
url: endpoint,
|
|
175
|
+
headers
|
|
176
|
+
}),
|
|
177
|
+
...config,
|
|
178
|
+
resourceAttributes: {
|
|
179
|
+
...projectName ? { [SEMRESATTRS_PROJECT_NAME]: projectName } : {},
|
|
180
|
+
...config.resourceAttributes
|
|
181
|
+
},
|
|
182
|
+
provider: { custom: {
|
|
183
|
+
endpoint,
|
|
184
|
+
headers,
|
|
185
|
+
protocol: "http/protobuf"
|
|
186
|
+
} }
|
|
187
|
+
});
|
|
188
|
+
}
|
|
215
189
|
};
|
|
216
|
-
|
|
190
|
+
//#endregion
|
|
217
191
|
export { ARIZE_AX_ENDPOINT, ArizeExporter };
|
|
218
|
-
|
|
192
|
+
|
|
219
193
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/openInferenceOTLPExporter.ts","../src/tracing.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from '@arizeai/openinference-genai';\nimport type { Mutable } from '@arizeai/openinference-genai/types';\nimport {\n INPUT_MIME_TYPE,\n INPUT_VALUE,\n LLM_TOKEN_COUNT_COMPLETION,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING,\n LLM_TOKEN_COUNT_PROMPT,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE,\n LLM_TOKEN_COUNT_TOTAL,\n METADATA,\n OpenInferenceSpanKind,\n OUTPUT_MIME_TYPE,\n OUTPUT_VALUE,\n SemanticConventions,\n SESSION_ID,\n TAG_TAGS,\n USER_ID,\n} from '@arizeai/openinference-semantic-conventions';\nimport type { ExportResult } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n ATTR_GEN_AI_INPUT_MESSAGES,\n ATTR_GEN_AI_OUTPUT_MESSAGES,\n ATTR_GEN_AI_USAGE_INPUT_TOKENS,\n ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,\n ATTR_GEN_AI_TOOL_CALL_ARGUMENTS,\n ATTR_GEN_AI_TOOL_CALL_RESULT,\n} from '@opentelemetry/semantic-conventions/incubating';\n\n// GenAI usage attribute keys (not all are in @opentelemetry/semantic-conventions yet)\n// @see https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/\nconst GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens';\nconst GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = 'gen_ai.usage.cache_read.input_tokens';\nconst GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = 'gen_ai.usage.cache_creation.input_tokens';\nconst GEN_AI_USAGE_AUDIO_INPUT_TOKENS = 'gen_ai.usage.audio_input_tokens';\nconst GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS = 'gen_ai.usage.audio_output_tokens';\n\nconst MASTRA_GENERAL_PREFIX = 'mastra.';\nconst MASTRA_METADATA_PREFIX = 'mastra.metadata.';\nconst MASTRA_MODEL_STEP_INPUT = 'mastra.model_step.input';\nconst MASTRA_MODEL_STEP_OUTPUT = 'mastra.model_step.output';\nconst MASTRA_MODEL_CHUNK_OUTPUT = 'mastra.model_chunk.output';\nconst MASTRA_SPAN_TYPE = 'mastra.span.type';\n\n/**\n * Maps Mastra span types to OpenInference span kinds for proper trace categorization.\n *\n * Only non-CHAIN types are mapped here - all other span types default to CHAIN.\n */\nconst SPAN_TYPE_TO_KIND: Record<string, OpenInferenceSpanKind> = {\n // Model spans -> LLM\n model_generation: OpenInferenceSpanKind.LLM,\n model_step: OpenInferenceSpanKind.LLM,\n model_chunk: OpenInferenceSpanKind.LLM,\n // Tool spans -> TOOL\n tool_call: OpenInferenceSpanKind.TOOL,\n mcp_tool_call: OpenInferenceSpanKind.TOOL,\n // Agent spans -> AGENT\n agent_run: OpenInferenceSpanKind.AGENT,\n};\n\n/**\n * Converts GenAI usage metrics to OpenInference LLM token count attributes.\n * Maps from OTEL GenAI semantic conventions to OpenInference semantic conventions.\n *\n * @param attributes - The span attributes containing GenAI usage metrics\n * @returns OpenInference token count attributes\n */\nfunction convertUsageMetricsToOpenInference(attributes: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n const inputTokens = attributes[ATTR_GEN_AI_USAGE_INPUT_TOKENS];\n const outputTokens = attributes[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS];\n\n // Core token counts\n if (inputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT] = inputTokens;\n }\n if (outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION] = outputTokens;\n }\n\n // Total tokens (compute if we have both input and output)\n if (inputTokens !== undefined && outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_TOTAL] = inputTokens + outputTokens;\n }\n\n // Cache tokens (prompt details)\n const cacheReadInputTokens = attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS];\n if (cacheReadInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ] = cacheReadInputTokens;\n }\n\n const cacheCreationInputTokens = attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS];\n if (cacheCreationInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;\n }\n\n // Reasoning tokens (completion details)\n const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];\n if (reasoningTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;\n }\n\n // Audio tokens\n const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];\n if (audioInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;\n }\n\n const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];\n if (audioOutputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;\n }\n\n return result;\n}\n\n/**\n * Splits Mastra span attributes into two groups:\n * - `metadata`: keys starting with \"mastra.metadata.\" (prefix removed)\n * - `other`: all remaining keys starting with \"mastra.\"\n *\n * Any attributes not starting with \"mastra.\" are ignored entirely.\n */\nfunction splitMastraAttributes(attributes: Record<string, any>): {\n mastraMetadata: Record<string, any>;\n mastraOther: Record<string, any>;\n} {\n return Object.entries(attributes).reduce(\n (acc, [key, value]) => {\n if (key.startsWith(MASTRA_GENERAL_PREFIX)) {\n if (key.startsWith(MASTRA_METADATA_PREFIX)) {\n const strippedKey = key.slice(MASTRA_METADATA_PREFIX.length);\n acc.mastraMetadata[strippedKey] = value;\n } else {\n acc.mastraOther[key] = value;\n }\n }\n return acc;\n },\n {\n mastraMetadata: {} as Record<string, any>,\n mastraOther: {} as Record<string, any>,\n },\n );\n}\n\nexport class OpenInferenceOTLPTraceExporter extends OTLPTraceExporter {\n export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void) {\n const processedSpans = spans.map(span => {\n const attributes = { ...(span.attributes ?? {}) };\n const mutableSpan = span as Mutable<ReadableSpan>;\n\n const { mastraMetadata, mastraOther } = splitMastraAttributes(attributes);\n const processedAttributes = convertGenAISpanAttributesToOpenInferenceSpanAttributes(attributes);\n\n // only add processed attributes if conversion was successful\n if (processedAttributes) {\n const threadId = mastraMetadata['threadId'];\n if (threadId) {\n delete mastraMetadata['threadId'];\n processedAttributes[SESSION_ID] = threadId;\n }\n\n // Map mastra.tags to OpenInference native tag.tags convention (tags are only on root spans)\n if (mastraOther['mastra.tags']) {\n processedAttributes[TAG_TAGS] = mastraOther['mastra.tags'];\n delete mastraOther['mastra.tags'];\n }\n\n const userId = mastraMetadata['userId'];\n if (userId) {\n delete mastraMetadata['userId'];\n processedAttributes[USER_ID] = userId;\n }\n\n // Gather custom metadata into OpenInference metadata (flat best-effort)\n if (Object.keys(mastraMetadata).length > 0) {\n try {\n processedAttributes[METADATA] = JSON.stringify(mastraMetadata);\n } catch {\n // best-effort only\n }\n }\n\n const inputMessages =\n attributes[ATTR_GEN_AI_INPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_ARGUMENTS] ??\n mastraOther[MASTRA_MODEL_STEP_INPUT];\n if (inputMessages) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = inputMessages;\n }\n const outputMessages =\n attributes[ATTR_GEN_AI_OUTPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_RESULT] ??\n mastraOther[MASTRA_MODEL_STEP_OUTPUT] ??\n mastraOther[MASTRA_MODEL_CHUNK_OUTPUT];\n if (outputMessages) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = outputMessages;\n }\n\n // Map generic Mastra span input/output to OpenInference input/output\n // These are set by Mastra's gen-ai-semantics.ts for non-LLM/tool spans\n // (e.g., mastra.processor_run.input, mastra.workflow_run.input, etc.)\n if (!processedAttributes[INPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.input')) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n if (!processedAttributes[OUTPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.output')) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n\n // Convert GenAI usage metrics to OpenInference token count attributes\n const usageMetrics = convertUsageMetricsToOpenInference(attributes);\n Object.assign(processedAttributes, usageMetrics);\n\n mutableSpan.attributes = { ...processedAttributes, ...mastraOther };\n\n // Set span kind based on mastra.span.type for proper trace categorization\n const spanType = mastraOther[MASTRA_SPAN_TYPE];\n if (typeof spanType === 'string') {\n mutableSpan.attributes[SemanticConventions.OPENINFERENCE_SPAN_KIND] =\n SPAN_TYPE_TO_KIND[spanType] ?? OpenInferenceSpanKind.CHAIN;\n }\n }\n\n return mutableSpan;\n });\n\n super.export(processedSpans, resultCallback);\n }\n}\n","import { SEMRESATTRS_PROJECT_NAME } from '@arizeai/openinference-semantic-conventions';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArizeExporter]';\n\nexport const ARIZE_AX_ENDPOINT = 'https://otlp.arize.com/v1/traces';\n\nexport type ArizeExporterConfig = Omit<OtelExporterConfig, 'provider'> & {\n /**\n * Required if sending traces to Arize AX\n */\n spaceId?: string;\n /**\n * Required if sending traces to Arize AX, or to any other collector that\n * requires an Authorization header\n */\n apiKey?: string;\n /**\n * Collector endpoint destination for trace exports.\n * Required when sending traces to Phoenix, Phoenix Cloud, or other collectors.\n * Optional when sending traces to Arize AX.\n */\n endpoint?: string;\n /**\n * Optional project name to be added as a resource attribute using\n * OpenInference Semantic Conventions\n */\n projectName?: string;\n /**\n * Optional headers to be added to each OTLP request\n */\n headers?: Record<string, string>;\n};\n\nexport class ArizeExporter extends OtelExporter {\n name = 'arize';\n\n constructor(config: ArizeExporterConfig = {}) {\n // Read configuration from config or environment variables\n // Priority: config > ARIZE_* env vars > PHOENIX_* env vars\n const spaceId = config.spaceId ?? process.env.ARIZE_SPACE_ID;\n const apiKey = config.apiKey ?? process.env.ARIZE_API_KEY ?? process.env.PHOENIX_API_KEY;\n const projectName = config.projectName ?? process.env.ARIZE_PROJECT_NAME ?? process.env.PHOENIX_PROJECT_NAME;\n\n // Determine endpoint: config > PHOENIX_COLLECTOR_ENDPOINT > PHOENIX_ENDPOINT > ARIZE_AX_ENDPOINT (if spaceId is set)\n let endpoint: string | undefined =\n config.endpoint ?? process.env.PHOENIX_COLLECTOR_ENDPOINT ?? process.env.PHOENIX_ENDPOINT;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n // Validate credentials based on mode\n let disabledReason: string | undefined;\n\n if (spaceId) {\n // Arize AX mode requires an API key\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required for Arize AX. ` +\n `Set ARIZE_API_KEY environment variable or pass apiKey in config.`;\n } else {\n // arize ax header configuration\n headers['space_id'] = spaceId;\n headers['api_key'] = apiKey;\n endpoint = endpoint || ARIZE_AX_ENDPOINT;\n }\n } else if (apiKey) {\n // standard otel header configuration\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required in configuration. ` +\n `Set PHOENIX_COLLECTOR_ENDPOINT environment variable, or ARIZE_SPACE_ID for Arize AX, or pass endpoint in config.`;\n }\n\n // If disabled, create with minimal config and disable\n if (disabledReason) {\n super({\n ...config,\n provider: {\n custom: {\n endpoint: 'http://disabled',\n headers: {},\n protocol: 'http/protobuf',\n },\n },\n });\n this.setDisabled(disabledReason);\n return;\n }\n\n super({\n exporter: new OpenInferenceOTLPTraceExporter({\n url: endpoint!,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(projectName ? { [SEMRESATTRS_PROJECT_NAME]: projectName } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: endpoint!,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/openInferenceOTLPExporter.ts","../src/tracing.ts"],"sourcesContent":["import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from '@arizeai/openinference-genai';\nimport type { Mutable } from '@arizeai/openinference-genai/types';\nimport {\n INPUT_MIME_TYPE,\n INPUT_VALUE,\n LLM_TOKEN_COUNT_COMPLETION,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING,\n LLM_TOKEN_COUNT_PROMPT,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ,\n LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE,\n LLM_TOKEN_COUNT_TOTAL,\n METADATA,\n OpenInferenceSpanKind,\n OUTPUT_MIME_TYPE,\n OUTPUT_VALUE,\n SemanticConventions,\n SESSION_ID,\n TAG_TAGS,\n USER_ID,\n} from '@arizeai/openinference-semantic-conventions';\nimport type { ExportResult } from '@opentelemetry/core';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n ATTR_GEN_AI_INPUT_MESSAGES,\n ATTR_GEN_AI_OUTPUT_MESSAGES,\n ATTR_GEN_AI_USAGE_INPUT_TOKENS,\n ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,\n ATTR_GEN_AI_TOOL_CALL_ARGUMENTS,\n ATTR_GEN_AI_TOOL_CALL_RESULT,\n} from '@opentelemetry/semantic-conventions/incubating';\n\n// GenAI usage attribute keys (not all are in @opentelemetry/semantic-conventions yet)\n// @see https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/\nconst GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens';\nconst GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = 'gen_ai.usage.cache_read.input_tokens';\nconst GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = 'gen_ai.usage.cache_creation.input_tokens';\nconst GEN_AI_USAGE_AUDIO_INPUT_TOKENS = 'gen_ai.usage.audio_input_tokens';\nconst GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS = 'gen_ai.usage.audio_output_tokens';\n\nconst MASTRA_GENERAL_PREFIX = 'mastra.';\nconst MASTRA_METADATA_PREFIX = 'mastra.metadata.';\nconst MASTRA_MODEL_STEP_INPUT = 'mastra.model_step.input';\nconst MASTRA_MODEL_STEP_OUTPUT = 'mastra.model_step.output';\nconst MASTRA_MODEL_CHUNK_OUTPUT = 'mastra.model_chunk.output';\nconst MASTRA_SPAN_TYPE = 'mastra.span.type';\n\n/**\n * Maps Mastra span types to OpenInference span kinds for proper trace categorization.\n *\n * Only non-CHAIN types are mapped here - all other span types default to CHAIN.\n */\nconst SPAN_TYPE_TO_KIND: Record<string, OpenInferenceSpanKind> = {\n // Model spans -> LLM\n model_generation: OpenInferenceSpanKind.LLM,\n model_step: OpenInferenceSpanKind.LLM,\n model_chunk: OpenInferenceSpanKind.LLM,\n // Tool spans -> TOOL\n tool_call: OpenInferenceSpanKind.TOOL,\n mcp_tool_call: OpenInferenceSpanKind.TOOL,\n // Agent spans -> AGENT\n agent_run: OpenInferenceSpanKind.AGENT,\n};\n\n/**\n * Converts GenAI usage metrics to OpenInference LLM token count attributes.\n * Maps from OTEL GenAI semantic conventions to OpenInference semantic conventions.\n *\n * @param attributes - The span attributes containing GenAI usage metrics\n * @returns OpenInference token count attributes\n */\nfunction convertUsageMetricsToOpenInference(attributes: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n const inputTokens = attributes[ATTR_GEN_AI_USAGE_INPUT_TOKENS];\n const outputTokens = attributes[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS];\n\n // Core token counts\n if (inputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT] = inputTokens;\n }\n if (outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION] = outputTokens;\n }\n\n // Total tokens (compute if we have both input and output)\n if (inputTokens !== undefined && outputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_TOTAL] = inputTokens + outputTokens;\n }\n\n // Cache tokens (prompt details)\n const cacheReadInputTokens = attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS];\n if (cacheReadInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ] = cacheReadInputTokens;\n }\n\n const cacheCreationInputTokens = attributes[GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS];\n if (cacheCreationInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE] = cacheCreationInputTokens;\n }\n\n // Reasoning tokens (completion details)\n const reasoningTokens = attributes[GEN_AI_USAGE_REASONING_TOKENS];\n if (reasoningTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING] = reasoningTokens;\n }\n\n // Audio tokens\n const audioInputTokens = attributes[GEN_AI_USAGE_AUDIO_INPUT_TOKENS];\n if (audioInputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_PROMPT_DETAILS_AUDIO] = audioInputTokens;\n }\n\n const audioOutputTokens = attributes[GEN_AI_USAGE_AUDIO_OUTPUT_TOKENS];\n if (audioOutputTokens !== undefined) {\n result[LLM_TOKEN_COUNT_COMPLETION_DETAILS_AUDIO] = audioOutputTokens;\n }\n\n return result;\n}\n\n/**\n * Splits Mastra span attributes into two groups:\n * - `metadata`: keys starting with \"mastra.metadata.\" (prefix removed)\n * - `other`: all remaining keys starting with \"mastra.\"\n *\n * Any attributes not starting with \"mastra.\" are ignored entirely.\n */\nfunction splitMastraAttributes(attributes: Record<string, any>): {\n mastraMetadata: Record<string, any>;\n mastraOther: Record<string, any>;\n} {\n return Object.entries(attributes).reduce(\n (acc, [key, value]) => {\n if (key.startsWith(MASTRA_GENERAL_PREFIX)) {\n if (key.startsWith(MASTRA_METADATA_PREFIX)) {\n const strippedKey = key.slice(MASTRA_METADATA_PREFIX.length);\n acc.mastraMetadata[strippedKey] = value;\n } else {\n acc.mastraOther[key] = value;\n }\n }\n return acc;\n },\n {\n mastraMetadata: {} as Record<string, any>,\n mastraOther: {} as Record<string, any>,\n },\n );\n}\n\nexport class OpenInferenceOTLPTraceExporter extends OTLPTraceExporter {\n export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void) {\n const processedSpans = spans.map(span => {\n const attributes = { ...(span.attributes ?? {}) };\n const mutableSpan = span as Mutable<ReadableSpan>;\n\n const { mastraMetadata, mastraOther } = splitMastraAttributes(attributes);\n const processedAttributes = convertGenAISpanAttributesToOpenInferenceSpanAttributes(attributes);\n\n // only add processed attributes if conversion was successful\n if (processedAttributes) {\n const threadId = mastraMetadata['threadId'];\n if (threadId) {\n delete mastraMetadata['threadId'];\n processedAttributes[SESSION_ID] = threadId;\n }\n\n // Map mastra.tags to OpenInference native tag.tags convention (tags are only on root spans)\n if (mastraOther['mastra.tags']) {\n processedAttributes[TAG_TAGS] = mastraOther['mastra.tags'];\n delete mastraOther['mastra.tags'];\n }\n\n const userId = mastraMetadata['userId'];\n if (userId) {\n delete mastraMetadata['userId'];\n processedAttributes[USER_ID] = userId;\n }\n\n // Gather custom metadata into OpenInference metadata (flat best-effort)\n if (Object.keys(mastraMetadata).length > 0) {\n try {\n processedAttributes[METADATA] = JSON.stringify(mastraMetadata);\n } catch {\n // best-effort only\n }\n }\n\n const inputMessages =\n attributes[ATTR_GEN_AI_INPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_ARGUMENTS] ??\n mastraOther[MASTRA_MODEL_STEP_INPUT];\n if (inputMessages) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = inputMessages;\n }\n const outputMessages =\n attributes[ATTR_GEN_AI_OUTPUT_MESSAGES] ??\n attributes[ATTR_GEN_AI_TOOL_CALL_RESULT] ??\n mastraOther[MASTRA_MODEL_STEP_OUTPUT] ??\n mastraOther[MASTRA_MODEL_CHUNK_OUTPUT];\n if (outputMessages) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = outputMessages;\n }\n\n // Map generic Mastra span input/output to OpenInference input/output\n // These are set by Mastra's gen-ai-semantics.ts for non-LLM/tool spans\n // (e.g., mastra.processor_run.input, mastra.workflow_run.input, etc.)\n if (!processedAttributes[INPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.input')) {\n processedAttributes[INPUT_MIME_TYPE] = 'application/json';\n processedAttributes[INPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n if (!processedAttributes[OUTPUT_VALUE]) {\n for (const key of Object.keys(mastraOther)) {\n if (key.endsWith('.output')) {\n processedAttributes[OUTPUT_MIME_TYPE] = 'application/json';\n processedAttributes[OUTPUT_VALUE] = mastraOther[key];\n break;\n }\n }\n }\n\n // Convert GenAI usage metrics to OpenInference token count attributes\n const usageMetrics = convertUsageMetricsToOpenInference(attributes);\n Object.assign(processedAttributes, usageMetrics);\n\n mutableSpan.attributes = { ...processedAttributes, ...mastraOther };\n\n // Set span kind based on mastra.span.type for proper trace categorization\n const spanType = mastraOther[MASTRA_SPAN_TYPE];\n if (typeof spanType === 'string') {\n mutableSpan.attributes[SemanticConventions.OPENINFERENCE_SPAN_KIND] =\n SPAN_TYPE_TO_KIND[spanType] ?? OpenInferenceSpanKind.CHAIN;\n }\n }\n\n return mutableSpan;\n });\n\n super.export(processedSpans, resultCallback);\n }\n}\n","import { SEMRESATTRS_PROJECT_NAME } from '@arizeai/openinference-semantic-conventions';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArizeExporter]';\n\nexport const ARIZE_AX_ENDPOINT = 'https://otlp.arize.com/v1/traces';\n\nexport type ArizeExporterConfig = Omit<OtelExporterConfig, 'provider'> & {\n /**\n * Required if sending traces to Arize AX\n */\n spaceId?: string;\n /**\n * Required if sending traces to Arize AX, or to any other collector that\n * requires an Authorization header\n */\n apiKey?: string;\n /**\n * Collector endpoint destination for trace exports.\n * Required when sending traces to Phoenix, Phoenix Cloud, or other collectors.\n * Optional when sending traces to Arize AX.\n */\n endpoint?: string;\n /**\n * Optional project name to be added as a resource attribute using\n * OpenInference Semantic Conventions\n */\n projectName?: string;\n /**\n * Optional headers to be added to each OTLP request\n */\n headers?: Record<string, string>;\n};\n\nexport class ArizeExporter extends OtelExporter {\n name = 'arize';\n\n constructor(config: ArizeExporterConfig = {}) {\n // Read configuration from config or environment variables\n // Priority: config > ARIZE_* env vars > PHOENIX_* env vars\n const spaceId = config.spaceId ?? process.env.ARIZE_SPACE_ID;\n const apiKey = config.apiKey ?? process.env.ARIZE_API_KEY ?? process.env.PHOENIX_API_KEY;\n const projectName = config.projectName ?? process.env.ARIZE_PROJECT_NAME ?? process.env.PHOENIX_PROJECT_NAME;\n\n // Determine endpoint: config > PHOENIX_COLLECTOR_ENDPOINT > PHOENIX_ENDPOINT > ARIZE_AX_ENDPOINT (if spaceId is set)\n let endpoint: string | undefined =\n config.endpoint ?? process.env.PHOENIX_COLLECTOR_ENDPOINT ?? process.env.PHOENIX_ENDPOINT;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n // Validate credentials based on mode\n let disabledReason: string | undefined;\n\n if (spaceId) {\n // Arize AX mode requires an API key\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required for Arize AX. ` +\n `Set ARIZE_API_KEY environment variable or pass apiKey in config.`;\n } else {\n // arize ax header configuration\n headers['space_id'] = spaceId;\n headers['api_key'] = apiKey;\n endpoint = endpoint || ARIZE_AX_ENDPOINT;\n }\n } else if (apiKey) {\n // standard otel header configuration\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required in configuration. ` +\n `Set PHOENIX_COLLECTOR_ENDPOINT environment variable, or ARIZE_SPACE_ID for Arize AX, or pass endpoint in config.`;\n }\n\n // If disabled, create with minimal config and disable\n if (disabledReason) {\n super({\n ...config,\n provider: {\n custom: {\n endpoint: 'http://disabled',\n headers: {},\n protocol: 'http/protobuf',\n },\n },\n });\n this.setDisabled(disabledReason);\n return;\n }\n\n super({\n exporter: new OpenInferenceOTLPTraceExporter({\n url: endpoint!,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(projectName ? { [SEMRESATTRS_PROJECT_NAME]: projectName } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: endpoint!,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/arize",
|
|
3
|
-
"version": "1.3.6
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"description": "Arize observability provider for Mastra - includes tracing and future observability features",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,19 +31,19 @@
|
|
|
31
31
|
"@opentelemetry/resources": "^2.7.1",
|
|
32
32
|
"@opentelemetry/sdk-trace-base": "^2.7.1",
|
|
33
33
|
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
34
|
-
"@mastra/otel-exporter": "1.3.6
|
|
34
|
+
"@mastra/otel-exporter": "1.3.6"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "22.20.1",
|
|
38
38
|
"@vitest/coverage-v8": "4.1.10",
|
|
39
39
|
"@vitest/ui": "4.1.10",
|
|
40
40
|
"eslint": "^10.7.0",
|
|
41
|
-
"
|
|
41
|
+
"tsdown": "0.22.9",
|
|
42
42
|
"typescript": "^6.0.3",
|
|
43
43
|
"vitest": "4.1.10",
|
|
44
|
-
"@internal/lint": "0.0.
|
|
45
|
-
"@
|
|
46
|
-
"@
|
|
44
|
+
"@internal/lint": "0.0.118",
|
|
45
|
+
"@mastra/core": "1.54.0",
|
|
46
|
+
"@internal/types-builder": "0.0.93"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@mastra/core": ">=1.16.0-0 <2.0.0-0"
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"node": ">=22.13.0"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
|
-
"build": "
|
|
64
|
+
"build": "tsdown --silent --config tsdown.config.ts",
|
|
65
65
|
"build:watch": "pnpm build --watch",
|
|
66
66
|
"test": "vitest run",
|
|
67
67
|
"test:watch": "vitest watch",
|