@mastra/arthur 0.4.5 → 0.4.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 +16 -0
- package/dist/index.cjs +224 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +221 -229
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @mastra/arthur
|
|
2
2
|
|
|
3
|
+
## 0.4.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
|
+
|
|
11
|
+
## 0.4.6-alpha.0
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`ce93a3c`](https://github.com/mastra-ai/mastra/commit/ce93a3c114ea1cbfbd576f3db41d7c26c9844f5b), [`5718a22`](https://github.com/mastra-ai/mastra/commit/5718a229281dcfd36bcd1f42a242e3717e510a33), [`5807d3a`](https://github.com/mastra-ai/mastra/commit/5807d3ae1d259b8b7d6df7e5bf2b485c694af9c8), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`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), [`c093146`](https://github.com/mastra-ai/mastra/commit/c0931466404d3c521308ea119cb165bb7e695155)]:
|
|
16
|
+
- @mastra/core@1.54.0-alpha.1
|
|
17
|
+
- @mastra/otel-exporter@1.3.6-alpha.0
|
|
18
|
+
|
|
3
19
|
## 0.4.5
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1,244 +1,235 @@
|
|
|
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 _mastra_otel_exporter = require("@mastra/otel-exporter");
|
|
3
|
+
let _arizeai_openinference_genai = require("@arizeai/openinference-genai");
|
|
4
|
+
let _arizeai_openinference_semantic_conventions = require("@arizeai/openinference-semantic-conventions");
|
|
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
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/tracing.ts
|
|
142
|
+
const LOG_PREFIX = "[ArthurExporter]";
|
|
143
|
+
/**
|
|
144
|
+
* Exports Mastra traces to Arthur AI using OpenInference semantic conventions.
|
|
145
|
+
*
|
|
146
|
+
* Supports zero-config setup via environment variables (ARTHUR_API_KEY, ARTHUR_BASE_URL,
|
|
147
|
+
* ARTHUR_TASK_ID) or explicit configuration. Automatically disables itself with a warning
|
|
148
|
+
* when required credentials are missing.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* const mastra = new Mastra({
|
|
153
|
+
* observability: new Observability({
|
|
154
|
+
* configs: {
|
|
155
|
+
* arthur: {
|
|
156
|
+
* serviceName: 'my-service',
|
|
157
|
+
* exporters: [new ArthurExporter()],
|
|
158
|
+
* },
|
|
159
|
+
* },
|
|
160
|
+
* }),
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
var ArthurExporter = class extends _mastra_otel_exporter.OtelExporter {
|
|
165
|
+
name = "arthur";
|
|
166
|
+
taskId;
|
|
167
|
+
/**
|
|
168
|
+
* @param config - Arthur exporter configuration. All fields are optional when
|
|
169
|
+
* the corresponding environment variables are set.
|
|
170
|
+
*/
|
|
171
|
+
constructor(config = {}) {
|
|
172
|
+
const apiKey = config.apiKey ?? process.env.ARTHUR_API_KEY;
|
|
173
|
+
const endpoint = config.endpoint ?? process.env.ARTHUR_BASE_URL;
|
|
174
|
+
const taskId = config.taskId ?? process.env.ARTHUR_TASK_ID;
|
|
175
|
+
const headers = { ...config.headers };
|
|
176
|
+
let disabledReason;
|
|
177
|
+
if (!apiKey) disabledReason = `${LOG_PREFIX} API key is required. Set ARTHUR_API_KEY environment variable or pass apiKey in config.`;
|
|
178
|
+
else headers["Authorization"] = `Bearer ${apiKey}`;
|
|
179
|
+
if (!disabledReason && !endpoint) disabledReason = `${LOG_PREFIX} Endpoint is required. Set ARTHUR_BASE_URL environment variable or pass endpoint in config.`;
|
|
180
|
+
const tracesEndpoint = endpoint ? `${stripTrailingSlashes(endpoint)}/api/v1/traces` : "http://disabled";
|
|
181
|
+
if (disabledReason) {
|
|
182
|
+
super({
|
|
183
|
+
...config,
|
|
184
|
+
provider: { custom: {
|
|
185
|
+
endpoint: "http://disabled",
|
|
186
|
+
headers: {},
|
|
187
|
+
protocol: "http/protobuf"
|
|
188
|
+
} }
|
|
189
|
+
});
|
|
190
|
+
this.setDisabled(disabledReason);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
super({
|
|
194
|
+
exporter: new OpenInferenceOTLPTraceExporter({
|
|
195
|
+
url: tracesEndpoint,
|
|
196
|
+
headers
|
|
197
|
+
}),
|
|
198
|
+
...config,
|
|
199
|
+
resourceAttributes: {
|
|
200
|
+
...taskId ? { "arthur.task.id": taskId } : {},
|
|
201
|
+
...config.resourceAttributes
|
|
202
|
+
},
|
|
203
|
+
provider: { custom: {
|
|
204
|
+
endpoint: tracesEndpoint,
|
|
205
|
+
headers,
|
|
206
|
+
protocol: "http/protobuf"
|
|
207
|
+
} }
|
|
208
|
+
});
|
|
209
|
+
this.taskId = taskId;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Called after construction with the observability config.
|
|
213
|
+
* Validates that traces can be routed to a task via either taskId or serviceName.
|
|
214
|
+
*/
|
|
215
|
+
init(options) {
|
|
216
|
+
super.init(options);
|
|
217
|
+
const serviceName = options.config?.serviceName;
|
|
218
|
+
if (this.taskId && serviceName) this.logger.warn(`${LOG_PREFIX} Both taskId and serviceName are set. Arthur Engine will use serviceName to route traces, ignoring the provided taskId.`);
|
|
219
|
+
else if (!this.taskId && !serviceName) this.logger.warn(`${LOG_PREFIX} Neither taskId nor serviceName is set. Set ARTHUR_TASK_ID environment variable, pass taskId in config, or set serviceName in the observability config so Arthur Engine can route traces to a task.`);
|
|
220
|
+
}
|
|
233
221
|
};
|
|
222
|
+
/**
|
|
223
|
+
* Remove trailing '/' characters procedurally. Avoids the polynomial
|
|
224
|
+
* backtracking that a greedy regex like `/\/+$/` can exhibit when the
|
|
225
|
+
* input is attacker-controlled.
|
|
226
|
+
*/
|
|
234
227
|
function stripTrailingSlashes(s) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
return end === s.length ? s : s.slice(0, end);
|
|
228
|
+
let end = s.length;
|
|
229
|
+
while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
|
|
230
|
+
return end === s.length ? s : s.slice(0, end);
|
|
240
231
|
}
|
|
241
|
-
|
|
232
|
+
//#endregion
|
|
242
233
|
exports.ArthurExporter = ArthurExporter;
|
|
243
|
-
|
|
234
|
+
|
|
244
235
|
//# 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"],"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)\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 type { InitExporterOptions } from '@mastra/core/observability';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArthurExporter]';\n\nexport type ArthurExporterConfig = Omit<OtelExporterConfig, 'provider' | 'exporter'> & {\n /**\n * Arthur API key for authentication.\n * Falls back to ARTHUR_API_KEY environment variable.\n */\n apiKey?: string;\n /**\n * Arthur platform endpoint (e.g. https://app.arthur.ai).\n * Falls back to ARTHUR_BASE_URL environment variable.\n */\n endpoint?: string;\n /**\n * Arthur task ID to associate traces with.\n * Falls back to ARTHUR_TASK_ID environment variable.\n * At least one of taskId or serviceName (from the observability config) should be provided.\n */\n taskId?: string;\n /**\n * Optional headers to be added to each OTLP request.\n * Note: the Authorization header is managed internally when apiKey is provided.\n */\n headers?: Record<string, string>;\n};\n\n/**\n * Exports Mastra traces to Arthur AI using OpenInference semantic conventions.\n *\n * Supports zero-config setup via environment variables (ARTHUR_API_KEY, ARTHUR_BASE_URL,\n * ARTHUR_TASK_ID) or explicit configuration. Automatically disables itself with a warning\n * when required credentials are missing.\n *\n * @example\n * ```ts\n * const mastra = new Mastra({\n * observability: new Observability({\n * configs: {\n * arthur: {\n * serviceName: 'my-service',\n * exporters: [new ArthurExporter()],\n * },\n * },\n * }),\n * });\n * ```\n */\nexport class ArthurExporter extends OtelExporter {\n name = 'arthur';\n private taskId?: string;\n\n /**\n * @param config - Arthur exporter configuration. All fields are optional when\n * the corresponding environment variables are set.\n */\n constructor(config: ArthurExporterConfig = {}) {\n const apiKey = config.apiKey ?? process.env.ARTHUR_API_KEY;\n const endpoint = config.endpoint ?? process.env.ARTHUR_BASE_URL;\n const taskId = config.taskId ?? process.env.ARTHUR_TASK_ID;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n let disabledReason: string | undefined;\n\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required. ` + `Set ARTHUR_API_KEY environment variable or pass apiKey in config.`;\n } else {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required. ` + `Set ARTHUR_BASE_URL environment variable or pass endpoint in config.`;\n }\n\n // Ensure the endpoint ends with /api/v1/traces\n const tracesEndpoint = endpoint ? `${stripTrailingSlashes(endpoint)}/api/v1/traces` : 'http://disabled';\n\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: tracesEndpoint,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(taskId ? { 'arthur.task.id': taskId } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: tracesEndpoint,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n\n this.taskId = taskId;\n }\n\n /**\n * Called after construction with the observability config.\n * Validates that traces can be routed to a task via either taskId or serviceName.\n */\n init(options: InitExporterOptions) {\n super.init(options);\n\n const serviceName = options.config?.serviceName;\n\n if (this.taskId && serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Both taskId and serviceName are set. Arthur Engine will use serviceName to route traces, ` +\n `ignoring the provided taskId.`,\n );\n } else if (!this.taskId && !serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Neither taskId nor serviceName is set. Set ARTHUR_TASK_ID environment variable, ` +\n `pass taskId in config, or set serviceName in the observability config so Arthur Engine can route traces to a task.`,\n );\n }\n }\n}\n\n/**\n * Remove trailing '/' characters procedurally. Avoids the polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\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"],"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)\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 type { InitExporterOptions } from '@mastra/core/observability';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArthurExporter]';\n\nexport type ArthurExporterConfig = Omit<OtelExporterConfig, 'provider' | 'exporter'> & {\n /**\n * Arthur API key for authentication.\n * Falls back to ARTHUR_API_KEY environment variable.\n */\n apiKey?: string;\n /**\n * Arthur platform endpoint (e.g. https://app.arthur.ai).\n * Falls back to ARTHUR_BASE_URL environment variable.\n */\n endpoint?: string;\n /**\n * Arthur task ID to associate traces with.\n * Falls back to ARTHUR_TASK_ID environment variable.\n * At least one of taskId or serviceName (from the observability config) should be provided.\n */\n taskId?: string;\n /**\n * Optional headers to be added to each OTLP request.\n * Note: the Authorization header is managed internally when apiKey is provided.\n */\n headers?: Record<string, string>;\n};\n\n/**\n * Exports Mastra traces to Arthur AI using OpenInference semantic conventions.\n *\n * Supports zero-config setup via environment variables (ARTHUR_API_KEY, ARTHUR_BASE_URL,\n * ARTHUR_TASK_ID) or explicit configuration. Automatically disables itself with a warning\n * when required credentials are missing.\n *\n * @example\n * ```ts\n * const mastra = new Mastra({\n * observability: new Observability({\n * configs: {\n * arthur: {\n * serviceName: 'my-service',\n * exporters: [new ArthurExporter()],\n * },\n * },\n * }),\n * });\n * ```\n */\nexport class ArthurExporter extends OtelExporter {\n name = 'arthur';\n private taskId?: string;\n\n /**\n * @param config - Arthur exporter configuration. All fields are optional when\n * the corresponding environment variables are set.\n */\n constructor(config: ArthurExporterConfig = {}) {\n const apiKey = config.apiKey ?? process.env.ARTHUR_API_KEY;\n const endpoint = config.endpoint ?? process.env.ARTHUR_BASE_URL;\n const taskId = config.taskId ?? process.env.ARTHUR_TASK_ID;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n let disabledReason: string | undefined;\n\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required. ` + `Set ARTHUR_API_KEY environment variable or pass apiKey in config.`;\n } else {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required. ` + `Set ARTHUR_BASE_URL environment variable or pass endpoint in config.`;\n }\n\n // Ensure the endpoint ends with /api/v1/traces\n const tracesEndpoint = endpoint ? `${stripTrailingSlashes(endpoint)}/api/v1/traces` : 'http://disabled';\n\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: tracesEndpoint,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(taskId ? { 'arthur.task.id': taskId } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: tracesEndpoint,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n\n this.taskId = taskId;\n }\n\n /**\n * Called after construction with the observability config.\n * Validates that traces can be routed to a task via either taskId or serviceName.\n */\n init(options: InitExporterOptions) {\n super.init(options);\n\n const serviceName = options.config?.serviceName;\n\n if (this.taskId && serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Both taskId and serviceName are set. Arthur Engine will use serviceName to route traces, ` +\n `ignoring the provided taskId.`,\n );\n } else if (!this.taskId && !serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Neither taskId nor serviceName is set. Set ARTHUR_TASK_ID environment variable, ` +\n `pass taskId in config, or set serviceName in the observability config so Arthur Engine can route traces to a task.`,\n );\n }\n }\n}\n\n/**\n * Remove trailing '/' characters procedurally. Avoids the polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,242 +1,234 @@
|
|
|
1
|
-
import { OtelExporter } from
|
|
2
|
-
import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from
|
|
3
|
-
import {
|
|
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 { OtelExporter } from "@mastra/otel-exporter";
|
|
2
|
+
import { convertGenAISpanAttributesToOpenInferenceSpanAttributes } from "@arizeai/openinference-genai";
|
|
3
|
+
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, SESSION_ID, SemanticConventions, TAG_TAGS, USER_ID } from "@arizeai/openinference-semantic-conventions";
|
|
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
|
-
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/tracing.ts
|
|
141
|
+
const LOG_PREFIX = "[ArthurExporter]";
|
|
142
|
+
/**
|
|
143
|
+
* Exports Mastra traces to Arthur AI using OpenInference semantic conventions.
|
|
144
|
+
*
|
|
145
|
+
* Supports zero-config setup via environment variables (ARTHUR_API_KEY, ARTHUR_BASE_URL,
|
|
146
|
+
* ARTHUR_TASK_ID) or explicit configuration. Automatically disables itself with a warning
|
|
147
|
+
* when required credentials are missing.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* const mastra = new Mastra({
|
|
152
|
+
* observability: new Observability({
|
|
153
|
+
* configs: {
|
|
154
|
+
* arthur: {
|
|
155
|
+
* serviceName: 'my-service',
|
|
156
|
+
* exporters: [new ArthurExporter()],
|
|
157
|
+
* },
|
|
158
|
+
* },
|
|
159
|
+
* }),
|
|
160
|
+
* });
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
156
163
|
var ArthurExporter = class extends OtelExporter {
|
|
157
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Called after construction with the observability config.
|
|
216
|
-
* Validates that traces can be routed to a task via either taskId or serviceName.
|
|
217
|
-
*/
|
|
218
|
-
init(options) {
|
|
219
|
-
super.init(options);
|
|
220
|
-
const serviceName = options.config?.serviceName;
|
|
221
|
-
if (this.taskId && serviceName) {
|
|
222
|
-
this.logger.warn(
|
|
223
|
-
`${LOG_PREFIX} Both taskId and serviceName are set. Arthur Engine will use serviceName to route traces, ignoring the provided taskId.`
|
|
224
|
-
);
|
|
225
|
-
} else if (!this.taskId && !serviceName) {
|
|
226
|
-
this.logger.warn(
|
|
227
|
-
`${LOG_PREFIX} Neither taskId nor serviceName is set. Set ARTHUR_TASK_ID environment variable, pass taskId in config, or set serviceName in the observability config so Arthur Engine can route traces to a task.`
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
164
|
+
name = "arthur";
|
|
165
|
+
taskId;
|
|
166
|
+
/**
|
|
167
|
+
* @param config - Arthur exporter configuration. All fields are optional when
|
|
168
|
+
* the corresponding environment variables are set.
|
|
169
|
+
*/
|
|
170
|
+
constructor(config = {}) {
|
|
171
|
+
const apiKey = config.apiKey ?? process.env.ARTHUR_API_KEY;
|
|
172
|
+
const endpoint = config.endpoint ?? process.env.ARTHUR_BASE_URL;
|
|
173
|
+
const taskId = config.taskId ?? process.env.ARTHUR_TASK_ID;
|
|
174
|
+
const headers = { ...config.headers };
|
|
175
|
+
let disabledReason;
|
|
176
|
+
if (!apiKey) disabledReason = `${LOG_PREFIX} API key is required. Set ARTHUR_API_KEY environment variable or pass apiKey in config.`;
|
|
177
|
+
else headers["Authorization"] = `Bearer ${apiKey}`;
|
|
178
|
+
if (!disabledReason && !endpoint) disabledReason = `${LOG_PREFIX} Endpoint is required. Set ARTHUR_BASE_URL environment variable or pass endpoint in config.`;
|
|
179
|
+
const tracesEndpoint = endpoint ? `${stripTrailingSlashes(endpoint)}/api/v1/traces` : "http://disabled";
|
|
180
|
+
if (disabledReason) {
|
|
181
|
+
super({
|
|
182
|
+
...config,
|
|
183
|
+
provider: { custom: {
|
|
184
|
+
endpoint: "http://disabled",
|
|
185
|
+
headers: {},
|
|
186
|
+
protocol: "http/protobuf"
|
|
187
|
+
} }
|
|
188
|
+
});
|
|
189
|
+
this.setDisabled(disabledReason);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
super({
|
|
193
|
+
exporter: new OpenInferenceOTLPTraceExporter({
|
|
194
|
+
url: tracesEndpoint,
|
|
195
|
+
headers
|
|
196
|
+
}),
|
|
197
|
+
...config,
|
|
198
|
+
resourceAttributes: {
|
|
199
|
+
...taskId ? { "arthur.task.id": taskId } : {},
|
|
200
|
+
...config.resourceAttributes
|
|
201
|
+
},
|
|
202
|
+
provider: { custom: {
|
|
203
|
+
endpoint: tracesEndpoint,
|
|
204
|
+
headers,
|
|
205
|
+
protocol: "http/protobuf"
|
|
206
|
+
} }
|
|
207
|
+
});
|
|
208
|
+
this.taskId = taskId;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Called after construction with the observability config.
|
|
212
|
+
* Validates that traces can be routed to a task via either taskId or serviceName.
|
|
213
|
+
*/
|
|
214
|
+
init(options) {
|
|
215
|
+
super.init(options);
|
|
216
|
+
const serviceName = options.config?.serviceName;
|
|
217
|
+
if (this.taskId && serviceName) this.logger.warn(`${LOG_PREFIX} Both taskId and serviceName are set. Arthur Engine will use serviceName to route traces, ignoring the provided taskId.`);
|
|
218
|
+
else if (!this.taskId && !serviceName) this.logger.warn(`${LOG_PREFIX} Neither taskId nor serviceName is set. Set ARTHUR_TASK_ID environment variable, pass taskId in config, or set serviceName in the observability config so Arthur Engine can route traces to a task.`);
|
|
219
|
+
}
|
|
231
220
|
};
|
|
221
|
+
/**
|
|
222
|
+
* Remove trailing '/' characters procedurally. Avoids the polynomial
|
|
223
|
+
* backtracking that a greedy regex like `/\/+$/` can exhibit when the
|
|
224
|
+
* input is attacker-controlled.
|
|
225
|
+
*/
|
|
232
226
|
function stripTrailingSlashes(s) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
return end === s.length ? s : s.slice(0, end);
|
|
227
|
+
let end = s.length;
|
|
228
|
+
while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
|
|
229
|
+
return end === s.length ? s : s.slice(0, end);
|
|
238
230
|
}
|
|
239
|
-
|
|
231
|
+
//#endregion
|
|
240
232
|
export { ArthurExporter };
|
|
241
|
-
|
|
233
|
+
|
|
242
234
|
//# 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)\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 type { InitExporterOptions } from '@mastra/core/observability';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArthurExporter]';\n\nexport type ArthurExporterConfig = Omit<OtelExporterConfig, 'provider' | 'exporter'> & {\n /**\n * Arthur API key for authentication.\n * Falls back to ARTHUR_API_KEY environment variable.\n */\n apiKey?: string;\n /**\n * Arthur platform endpoint (e.g. https://app.arthur.ai).\n * Falls back to ARTHUR_BASE_URL environment variable.\n */\n endpoint?: string;\n /**\n * Arthur task ID to associate traces with.\n * Falls back to ARTHUR_TASK_ID environment variable.\n * At least one of taskId or serviceName (from the observability config) should be provided.\n */\n taskId?: string;\n /**\n * Optional headers to be added to each OTLP request.\n * Note: the Authorization header is managed internally when apiKey is provided.\n */\n headers?: Record<string, string>;\n};\n\n/**\n * Exports Mastra traces to Arthur AI using OpenInference semantic conventions.\n *\n * Supports zero-config setup via environment variables (ARTHUR_API_KEY, ARTHUR_BASE_URL,\n * ARTHUR_TASK_ID) or explicit configuration. Automatically disables itself with a warning\n * when required credentials are missing.\n *\n * @example\n * ```ts\n * const mastra = new Mastra({\n * observability: new Observability({\n * configs: {\n * arthur: {\n * serviceName: 'my-service',\n * exporters: [new ArthurExporter()],\n * },\n * },\n * }),\n * });\n * ```\n */\nexport class ArthurExporter extends OtelExporter {\n name = 'arthur';\n private taskId?: string;\n\n /**\n * @param config - Arthur exporter configuration. All fields are optional when\n * the corresponding environment variables are set.\n */\n constructor(config: ArthurExporterConfig = {}) {\n const apiKey = config.apiKey ?? process.env.ARTHUR_API_KEY;\n const endpoint = config.endpoint ?? process.env.ARTHUR_BASE_URL;\n const taskId = config.taskId ?? process.env.ARTHUR_TASK_ID;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n let disabledReason: string | undefined;\n\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required. ` + `Set ARTHUR_API_KEY environment variable or pass apiKey in config.`;\n } else {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required. ` + `Set ARTHUR_BASE_URL environment variable or pass endpoint in config.`;\n }\n\n // Ensure the endpoint ends with /api/v1/traces\n const tracesEndpoint = endpoint ? `${stripTrailingSlashes(endpoint)}/api/v1/traces` : 'http://disabled';\n\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: tracesEndpoint,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(taskId ? { 'arthur.task.id': taskId } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: tracesEndpoint,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n\n this.taskId = taskId;\n }\n\n /**\n * Called after construction with the observability config.\n * Validates that traces can be routed to a task via either taskId or serviceName.\n */\n init(options: InitExporterOptions) {\n super.init(options);\n\n const serviceName = options.config?.serviceName;\n\n if (this.taskId && serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Both taskId and serviceName are set. Arthur Engine will use serviceName to route traces, ` +\n `ignoring the provided taskId.`,\n );\n } else if (!this.taskId && !serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Neither taskId nor serviceName is set. Set ARTHUR_TASK_ID environment variable, ` +\n `pass taskId in config, or set serviceName in the observability config so Arthur Engine can route traces to a task.`,\n );\n }\n }\n}\n\n/**\n * Remove trailing '/' characters procedurally. Avoids the polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\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)\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 type { InitExporterOptions } from '@mastra/core/observability';\nimport { OtelExporter } from '@mastra/otel-exporter';\nimport type { OtelExporterConfig } from '@mastra/otel-exporter';\n\nimport { OpenInferenceOTLPTraceExporter } from './openInferenceOTLPExporter.js';\n\nconst LOG_PREFIX = '[ArthurExporter]';\n\nexport type ArthurExporterConfig = Omit<OtelExporterConfig, 'provider' | 'exporter'> & {\n /**\n * Arthur API key for authentication.\n * Falls back to ARTHUR_API_KEY environment variable.\n */\n apiKey?: string;\n /**\n * Arthur platform endpoint (e.g. https://app.arthur.ai).\n * Falls back to ARTHUR_BASE_URL environment variable.\n */\n endpoint?: string;\n /**\n * Arthur task ID to associate traces with.\n * Falls back to ARTHUR_TASK_ID environment variable.\n * At least one of taskId or serviceName (from the observability config) should be provided.\n */\n taskId?: string;\n /**\n * Optional headers to be added to each OTLP request.\n * Note: the Authorization header is managed internally when apiKey is provided.\n */\n headers?: Record<string, string>;\n};\n\n/**\n * Exports Mastra traces to Arthur AI using OpenInference semantic conventions.\n *\n * Supports zero-config setup via environment variables (ARTHUR_API_KEY, ARTHUR_BASE_URL,\n * ARTHUR_TASK_ID) or explicit configuration. Automatically disables itself with a warning\n * when required credentials are missing.\n *\n * @example\n * ```ts\n * const mastra = new Mastra({\n * observability: new Observability({\n * configs: {\n * arthur: {\n * serviceName: 'my-service',\n * exporters: [new ArthurExporter()],\n * },\n * },\n * }),\n * });\n * ```\n */\nexport class ArthurExporter extends OtelExporter {\n name = 'arthur';\n private taskId?: string;\n\n /**\n * @param config - Arthur exporter configuration. All fields are optional when\n * the corresponding environment variables are set.\n */\n constructor(config: ArthurExporterConfig = {}) {\n const apiKey = config.apiKey ?? process.env.ARTHUR_API_KEY;\n const endpoint = config.endpoint ?? process.env.ARTHUR_BASE_URL;\n const taskId = config.taskId ?? process.env.ARTHUR_TASK_ID;\n\n const headers: Record<string, string> = {\n ...config.headers,\n };\n\n let disabledReason: string | undefined;\n\n if (!apiKey) {\n disabledReason =\n `${LOG_PREFIX} API key is required. ` + `Set ARTHUR_API_KEY environment variable or pass apiKey in config.`;\n } else {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (!disabledReason && !endpoint) {\n disabledReason =\n `${LOG_PREFIX} Endpoint is required. ` + `Set ARTHUR_BASE_URL environment variable or pass endpoint in config.`;\n }\n\n // Ensure the endpoint ends with /api/v1/traces\n const tracesEndpoint = endpoint ? `${stripTrailingSlashes(endpoint)}/api/v1/traces` : 'http://disabled';\n\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: tracesEndpoint,\n headers,\n }),\n ...config,\n resourceAttributes: {\n ...(taskId ? { 'arthur.task.id': taskId } : {}),\n ...config.resourceAttributes,\n },\n provider: {\n custom: {\n endpoint: tracesEndpoint,\n headers,\n protocol: 'http/protobuf',\n },\n } satisfies OtelExporterConfig['provider'],\n } satisfies OtelExporterConfig);\n\n this.taskId = taskId;\n }\n\n /**\n * Called after construction with the observability config.\n * Validates that traces can be routed to a task via either taskId or serviceName.\n */\n init(options: InitExporterOptions) {\n super.init(options);\n\n const serviceName = options.config?.serviceName;\n\n if (this.taskId && serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Both taskId and serviceName are set. Arthur Engine will use serviceName to route traces, ` +\n `ignoring the provided taskId.`,\n );\n } else if (!this.taskId && !serviceName) {\n this.logger.warn(\n `${LOG_PREFIX} Neither taskId nor serviceName is set. Set ARTHUR_TASK_ID environment variable, ` +\n `pass taskId in config, or set serviceName in the observability config so Arthur Engine can route traces to a task.`,\n );\n }\n }\n}\n\n/**\n * Remove trailing '/' characters procedurally. Avoids the polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\n}\n"],"mappings":";;;;;;AAmCA,MAAM,gCAAgC;AACtC,MAAM,uCAAuC;AAC7C,MAAM,2CAA2C;AACjD,MAAM,kCAAkC;AACxC,MAAM,mCAAmC;AAEzC,MAAM,wBAAwB;AAC9B,MAAM,yBAAyB;AAC/B,MAAM,0BAA0B;AAChC,MAAM,2BAA2B;AACjC,MAAM,4BAA4B;AAClC,MAAM,mBAAmB;;;;;;AAOzB,MAAM,oBAA2D;CAE/D,kBAAkB,sBAAsB;CACxC,YAAY,sBAAsB;CAClC,aAAa,sBAAsB;CAEnC,WAAW,sBAAsB;CACjC,eAAe,sBAAsB;CAErC,WAAW,sBAAsB;AACnC;;;;;;;;AASA,SAAS,mCAAmC,YAAsD;CAChG,MAAM,SAA8B,CAAC;CAErC,MAAM,cAAc,WAAW;CAC/B,MAAM,eAAe,WAAW;CAGhC,IAAI,gBAAgB,KAAA,GAClB,OAAO,0BAA0B;CAEnC,IAAI,iBAAiB,KAAA,GACnB,OAAO,8BAA8B;CAIvC,IAAI,gBAAgB,KAAA,KAAa,iBAAiB,KAAA,GAChD,OAAO,yBAAyB,cAAc;CAIhD,MAAM,uBAAuB,WAAW;CACxC,IAAI,yBAAyB,KAAA,GAC3B,OAAO,6CAA6C;CAGtD,MAAM,2BAA2B,WAAW;CAC5C,IAAI,6BAA6B,KAAA,GAC/B,OAAO,8CAA8C;CAIvD,MAAM,kBAAkB,WAAW;CACnC,IAAI,oBAAoB,KAAA,GACtB,OAAO,gDAAgD;CAIzD,MAAM,mBAAmB,WAAW;CACpC,IAAI,qBAAqB,KAAA,GACvB,OAAO,wCAAwC;CAGjD,MAAM,oBAAoB,WAAW;CACrC,IAAI,sBAAsB,KAAA,GACxB,OAAO,4CAA4C;CAGrD,OAAO;AACT;;;;;;;;AASA,SAAS,sBAAsB,YAG7B;CACA,OAAO,OAAO,QAAQ,UAAU,CAAC,CAAC,QAC/B,KAAK,CAAC,KAAK,WAAW;EACrB,IAAI,IAAI,WAAW,qBAAqB,GACtC,IAAI,IAAI,WAAW,sBAAsB,GAAG;GAC1C,MAAM,cAAc,IAAI,MAAM,EAA6B;GAC3D,IAAI,eAAe,eAAe;EACpC,OACE,IAAI,YAAY,OAAO;EAG3B,OAAO;CACT,GACA;EACE,gBAAgB,CAAC;EACjB,aAAa,CAAC;CAChB,CACF;AACF;AAEA,IAAa,iCAAb,cAAoD,kBAAkB;CACpE,OAAO,OAAuB,gBAAgD;EAC5E,MAAM,iBAAiB,MAAM,KAAI,SAAQ;GACvC,MAAM,aAAa,EAAE,GAAI,KAAK,cAAc,CAAC,EAAG;GAChD,MAAM,cAAc;GAEpB,MAAM,EAAE,gBAAgB,gBAAgB,sBAAsB,UAAU;GACxE,MAAM,sBAAsB,wDAAwD,UAAU;GAG9F,IAAI,qBAAqB;IACvB,MAAM,WAAW,eAAe;IAChC,IAAI,UAAU;KACZ,OAAO,eAAe;KACtB,oBAAoB,cAAc;IACpC;IAGA,IAAI,YAAY,gBAAgB;KAC9B,oBAAoB,YAAY,YAAY;KAC5C,OAAO,YAAY;IACrB;IAEA,MAAM,SAAS,eAAe;IAC9B,IAAI,QAAQ;KACV,OAAO,eAAe;KACtB,oBAAoB,WAAW;IACjC;IAGA,IAAI,OAAO,KAAK,cAAc,CAAC,CAAC,SAAS,GACvC,IAAI;KACF,oBAAoB,YAAY,KAAK,UAAU,cAAc;IAC/D,QAAQ,CAER;IAGF,MAAM,gBACJ,WAAW,+BACX,WAAW,oCACX,YAAY;IACd,IAAI,eAAe;KACjB,oBAAoB,mBAAmB;KACvC,oBAAoB,eAAe;IACrC;IACA,MAAM,iBACJ,WAAW,gCACX,WAAW,iCACX,YAAY,6BACZ,YAAY;IACd,IAAI,gBAAgB;KAClB,oBAAoB,oBAAoB;KACxC,oBAAoB,gBAAgB;IACtC;IAKA,IAAI,CAAC,oBAAoB,cAClB;UAAA,MAAM,OAAO,OAAO,KAAK,WAAW,GACvC,IAAI,IAAI,SAAS,QAAQ,GAAG;MAC1B,oBAAoB,mBAAmB;MACvC,oBAAoB,eAAe,YAAY;MAC/C;KACF;;IAGJ,IAAI,CAAC,oBAAoB,eAClB;UAAA,MAAM,OAAO,OAAO,KAAK,WAAW,GACvC,IAAI,IAAI,SAAS,SAAS,GAAG;MAC3B,oBAAoB,oBAAoB;MACxC,oBAAoB,gBAAgB,YAAY;MAChD;KACF;;IAKJ,MAAM,eAAe,mCAAmC,UAAU;IAClE,OAAO,OAAO,qBAAqB,YAAY;IAE/C,YAAY,aAAa;KAAE,GAAG;KAAqB,GAAG;IAAY;IAGlE,MAAM,WAAW,YAAY;IAC7B,IAAI,OAAO,aAAa,UACtB,YAAY,WAAW,oBAAoB,2BACzC,kBAAkB,aAAa,sBAAsB;GAE3D;GAEA,OAAO;EACT,CAAC;EAED,MAAM,OAAO,gBAAgB,cAAc;CAC7C;AACF;;;ACnPA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;AA+CnB,IAAa,iBAAb,cAAoC,aAAa;CAC/C,OAAO;CACP;;;;;CAMA,YAAY,SAA+B,CAAC,GAAG;EAC7C,MAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;EAC5C,MAAM,WAAW,OAAO,YAAY,QAAQ,IAAI;EAChD,MAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;EAE5C,MAAM,UAAkC,EACtC,GAAG,OAAO,QACZ;EAEA,IAAI;EAEJ,IAAI,CAAC,QACH,iBACE,GAAG,WAAW;OAEhB,QAAQ,mBAAmB,UAAU;EAGvC,IAAI,CAAC,kBAAkB,CAAC,UACtB,iBACE,GAAG,WAAW;EAIlB,MAAM,iBAAiB,WAAW,GAAG,qBAAqB,QAAQ,EAAE,kBAAkB;EAEtF,IAAI,gBAAgB;GAClB,MAAM;IACJ,GAAG;IACH,UAAU,EACR,QAAQ;KACN,UAAU;KACV,SAAS,CAAC;KACV,UAAU;IACZ,EACF;GACF,CAAC;GACD,KAAK,YAAY,cAAc;GAC/B;EACF;EAEA,MAAM;GACJ,UAAU,IAAI,+BAA+B;IAC3C,KAAK;IACL;GACF,CAAC;GACD,GAAG;GACH,oBAAoB;IAClB,GAAI,SAAS,EAAE,kBAAkB,OAAO,IAAI,CAAC;IAC7C,GAAG,OAAO;GACZ;GACA,UAAU,EACR,QAAQ;IACN,UAAU;IACV;IACA,UAAU;GACZ,EACF;EACF,CAA8B;EAE9B,KAAK,SAAS;CAChB;;;;;CAMA,KAAK,SAA8B;EACjC,MAAM,KAAK,OAAO;EAElB,MAAM,cAAc,QAAQ,QAAQ;EAEpC,IAAI,KAAK,UAAU,aACjB,KAAK,OAAO,KACV,GAAG,WAAW,wHAEhB;OACK,IAAI,CAAC,KAAK,UAAU,CAAC,aAC1B,KAAK,OAAO,KACV,GAAG,WAAW,oMAEhB;CAEJ;AACF;;;;;;AAOA,SAAS,qBAAqB,GAAmB;CAC/C,IAAI,MAAM,EAAE;CACZ,OAAO,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC,MAAM,IAC1C;CAEF,OAAO,QAAQ,EAAE,SAAS,IAAI,EAAE,MAAM,GAAG,GAAG;AAC9C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/arthur",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Arthur AI observability provider for Mastra - exports traces using OpenInference semantic conventions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
|
31
31
|
"@opentelemetry/sdk-trace-base": "^2.7.1",
|
|
32
32
|
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
33
|
-
"@mastra/otel-exporter": "1.3.
|
|
33
|
+
"@mastra/otel-exporter": "1.3.6"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "22.20.1",
|
|
37
37
|
"@vitest/coverage-v8": "4.1.10",
|
|
38
38
|
"@vitest/ui": "4.1.10",
|
|
39
39
|
"eslint": "^10.7.0",
|
|
40
|
-
"
|
|
40
|
+
"tsdown": "0.22.9",
|
|
41
41
|
"typescript": "^6.0.3",
|
|
42
42
|
"vitest": "4.1.10",
|
|
43
|
-
"@internal/lint": "0.0.
|
|
44
|
-
"@internal/types-builder": "0.0.
|
|
45
|
-
"@mastra/core": "1.
|
|
43
|
+
"@internal/lint": "0.0.118",
|
|
44
|
+
"@internal/types-builder": "0.0.93",
|
|
45
|
+
"@mastra/core": "1.54.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@mastra/core": ">=1.16.0-0 <2.0.0-0"
|
|
@@ -60,10 +60,11 @@
|
|
|
60
60
|
"node": ">=22.13.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
|
-
"build": "
|
|
63
|
+
"build": "tsdown --silent --config tsdown.config.ts",
|
|
64
64
|
"build:watch": "pnpm build --watch",
|
|
65
65
|
"test": "vitest run",
|
|
66
66
|
"test:watch": "vitest watch",
|
|
67
|
-
"lint": "eslint ."
|
|
67
|
+
"lint": "oxlint . && eslint .",
|
|
68
|
+
"lint:fix": "oxlint --fix . && eslint --fix ."
|
|
68
69
|
}
|
|
69
70
|
}
|