@mastra/posthog 1.0.0-beta.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SpanType } from '@mastra/core/observability';
2
- import { BaseExporter } from '@mastra/observability';
2
+ import { TrackingExporter } from '@mastra/observability';
3
3
  import { PostHog } from 'posthog-node';
4
4
 
5
5
  // src/tracing.ts
@@ -13,26 +13,25 @@ function formatUsageMetrics(usage) {
13
13
  props.$ai_cache_creation_input_tokens = usage.inputDetails.cacheWrite;
14
14
  return props;
15
15
  }
16
- var PosthogExporter = class _PosthogExporter extends BaseExporter {
16
+ var DISTINCT_ID = "distinctId";
17
+ var PosthogExporter = class _PosthogExporter extends TrackingExporter {
17
18
  name = "posthog";
18
- client;
19
- config;
20
- traceMap = /* @__PURE__ */ new Map();
19
+ #client;
21
20
  static SERVERLESS_FLUSH_AT = 10;
22
21
  static SERVERLESS_FLUSH_INTERVAL = 2e3;
23
22
  static DEFAULT_FLUSH_AT = 20;
24
23
  static DEFAULT_FLUSH_INTERVAL = 1e4;
25
- constructor(config) {
26
- super(config);
27
- this.config = config;
28
- if (!config.apiKey) {
29
- this.setDisabled("Missing required API key");
30
- this.client = null;
24
+ constructor(config = {}) {
25
+ const apiKey = config.apiKey ?? process.env.POSTHOG_API_KEY;
26
+ super({ ...config, apiKey });
27
+ if (!apiKey) {
28
+ this.setDisabled("Missing required API key. Set POSTHOG_API_KEY environment variable or pass apiKey in config.");
31
29
  return;
32
30
  }
33
- const clientConfig = this.buildClientConfig(config);
34
- this.client = new PostHog(config.apiKey, clientConfig);
35
- this.logInitialization(config.serverless ?? false, clientConfig);
31
+ const clientConfig = this.buildClientConfig(this.config);
32
+ this.#client = new PostHog(apiKey, clientConfig);
33
+ const message = config.serverless ?? false ? "PostHog exporter initialized in serverless mode" : "PostHog exporter initialized";
34
+ this.logger.debug(message, config);
36
35
  }
37
36
  buildClientConfig(config) {
38
37
  const isServerless = config.serverless ?? false;
@@ -40,7 +39,7 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
40
39
  const flushInterval = config.flushInterval ?? (isServerless ? _PosthogExporter.SERVERLESS_FLUSH_INTERVAL : _PosthogExporter.DEFAULT_FLUSH_INTERVAL);
41
40
  const host = config.host || process.env.POSTHOG_HOST || "https://us.i.posthog.com";
42
41
  if (!config.host && !process.env.POSTHOG_HOST) {
43
- this.logger.warn(
42
+ this.logger.info(
44
43
  'No PostHog host specified, using US default (https://us.i.posthog.com). For EU region, set `host: "https://eu.i.posthog.com"` in config or POSTHOG_HOST env var. For self-hosted, provide your instance URL.'
45
44
  );
46
45
  }
@@ -51,85 +50,59 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
51
50
  privacyMode: config.enablePrivacyMode
52
51
  };
53
52
  }
54
- logInitialization(isServerless, config) {
55
- const message = isServerless ? "PostHog exporter initialized in serverless mode" : "PostHog exporter initialized";
56
- this.logger.debug(message, config);
57
- }
58
- async _exportTracingEvent(event) {
59
- if (!this.client) {
60
- return;
61
- }
62
- try {
63
- if (event.exportedSpan.isEvent) {
64
- if (event.type === "span_started") {
65
- await this.captureEventSpan(event.exportedSpan);
66
- }
67
- return;
68
- }
69
- switch (event.type) {
70
- case "span_started":
71
- await this.handleSpanStarted(event.exportedSpan);
72
- break;
73
- case "span_updated":
74
- break;
75
- case "span_ended":
76
- await this.handleSpanEnded(event.exportedSpan);
77
- break;
78
- }
79
- } catch (error) {
80
- this.logger.error("PostHog exporter error", { error, event });
81
- }
53
+ skipBuildRootTask = true;
54
+ async _buildRoot(_args) {
55
+ throw new Error("Method not implemented.");
82
56
  }
83
- async handleSpanStarted(span) {
84
- let traceData = this.traceMap.get(span.traceId);
85
- if (!traceData) {
86
- traceData = {
87
- spans: /* @__PURE__ */ new Map(),
88
- distinctId: void 0
89
- };
90
- this.traceMap.set(span.traceId, traceData);
91
- }
92
- traceData.spans.set(span.id, {
93
- startTime: this.toDate(span.startTime),
94
- type: span.type,
95
- isRootSpan: span.isRootSpan
57
+ skipCachingEventSpans = true;
58
+ async _buildEvent(args) {
59
+ const { span, traceData } = args;
60
+ const eventName = this.mapToPostHogEvent(span.type);
61
+ const distinctId = this.getDistinctId(span, traceData);
62
+ const properties = this.buildEventProperties(span, 0);
63
+ this.#client?.capture({
64
+ distinctId,
65
+ event: eventName,
66
+ properties,
67
+ timestamp: span.endTime ? new Date(span.endTime) : /* @__PURE__ */ new Date()
96
68
  });
97
- if (!traceData.distinctId) {
69
+ return true;
70
+ }
71
+ async _buildSpan(args) {
72
+ const { span, traceData } = args;
73
+ if (!traceData.hasExtraValue(DISTINCT_ID)) {
98
74
  const userId = span.metadata?.userId;
99
75
  if (userId) {
100
- traceData.distinctId = String(userId);
76
+ traceData.setExtraValue(DISTINCT_ID, String(userId));
101
77
  }
102
78
  }
79
+ return span;
103
80
  }
104
- async handleSpanEnded(span) {
105
- const traceData = this.traceMap.get(span.traceId);
106
- if (!traceData) {
107
- return;
108
- }
109
- const cachedSpan = traceData.spans.get(span.id);
110
- if (!cachedSpan) {
111
- return;
112
- }
113
- const startTime = cachedSpan.startTime.getTime();
81
+ skipSpanUpdateEvents = true;
82
+ _updateSpan(_args) {
83
+ throw new Error("Method not implemented.");
84
+ }
85
+ async _finishSpan(args) {
86
+ const { span, traceData } = args;
87
+ const cachedSpan = traceData.getSpan({ spanId: span.id });
88
+ const mergedSpan = !span.input && cachedSpan?.input ? { ...span, input: cachedSpan.input } : span;
89
+ const eventMessage = this.buildEventMessage({ span: mergedSpan, traceData });
90
+ this.#client?.capture(eventMessage);
91
+ }
92
+ async _abortSpan(args) {
93
+ const { span, reason, traceData } = args;
94
+ span.errorInfo = reason;
95
+ const eventMessage = this.buildEventMessage({ span, traceData });
96
+ this.#client?.capture(eventMessage);
97
+ }
98
+ buildEventMessage(args) {
99
+ const { span, traceData } = args;
114
100
  const endTime = span.endTime ? this.toDate(span.endTime).getTime() : Date.now();
115
- const latency = (endTime - startTime) / 1e3;
116
101
  const distinctId = this.getDistinctId(span, traceData);
117
102
  if (span.isRootSpan) {
118
- this.captureTraceEvent(span, distinctId, endTime);
103
+ return this.buildRootEventMessage({ span, distinctId, endTime });
119
104
  } else {
120
- const eventName = this.mapToPostHogEvent(span.type);
121
- const parentIsRootSpan = this.isParentRootSpan(span, traceData);
122
- const properties = this.buildEventProperties(span, latency, parentIsRootSpan);
123
- this.client.capture({
124
- distinctId,
125
- event: eventName,
126
- properties,
127
- timestamp: new Date(endTime)
128
- });
129
- }
130
- traceData.spans.delete(span.id);
131
- if (traceData.spans.size === 0) {
132
- this.traceMap.delete(span.traceId);
105
+ return this.buildChildEventMessage({ span, distinctId, endTime, traceData });
133
106
  }
134
107
  }
135
108
  /**
@@ -137,7 +110,8 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
137
110
  * This gives us control over trace-level metadata like name and tags,
138
111
  * rather than relying on PostHog's pseudo-trace auto-creation.
139
112
  */
140
- captureTraceEvent(span, distinctId, endTime) {
113
+ buildRootEventMessage(args) {
114
+ const { span, distinctId, endTime } = args;
141
115
  const traceProperties = {
142
116
  $ai_trace_id: span.traceId,
143
117
  $ai_span_name: span.name,
@@ -166,32 +140,26 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
166
140
  }
167
141
  const { userId, sessionId, ...customMetadata } = span.metadata ?? {};
168
142
  Object.assign(traceProperties, customMetadata);
169
- this.client.capture({
143
+ return {
170
144
  distinctId,
171
145
  event: "$ai_trace",
172
146
  properties: traceProperties,
173
147
  timestamp: new Date(endTime)
174
- });
148
+ };
175
149
  }
176
- async captureEventSpan(span) {
150
+ buildChildEventMessage(args) {
151
+ const { span, distinctId, endTime, traceData } = args;
177
152
  const eventName = this.mapToPostHogEvent(span.type);
178
- const traceData = this.traceMap.get(span.traceId);
179
- const distinctId = this.getDistinctId(span, traceData);
180
- const properties = this.buildEventProperties(span, 0);
181
- this.client.capture({
153
+ const startTime = span.startTime.getTime();
154
+ const latency = (endTime - startTime) / 1e3;
155
+ const parentIsRootSpan = this.isParentRootSpan(span, traceData);
156
+ const properties = this.buildEventProperties(span, latency, parentIsRootSpan);
157
+ return {
182
158
  distinctId,
183
159
  event: eventName,
184
160
  properties,
185
- timestamp: span.endTime ? new Date(span.endTime) : /* @__PURE__ */ new Date()
186
- });
187
- }
188
- async shutdown() {
189
- if (this.client) {
190
- await this.client.shutdown();
191
- }
192
- this.traceMap.clear();
193
- await super.shutdown();
194
- this.logger.info("PostHog exporter shutdown complete");
161
+ timestamp: new Date(endTime)
162
+ };
195
163
  }
196
164
  toDate(timestamp) {
197
165
  return timestamp instanceof Date ? timestamp : new Date(timestamp);
@@ -206,8 +174,8 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
206
174
  if (span.metadata?.userId) {
207
175
  return String(span.metadata.userId);
208
176
  }
209
- if (traceData?.distinctId) {
210
- return traceData.distinctId;
177
+ if (traceData?.hasExtraValue(DISTINCT_ID)) {
178
+ return String(traceData.getExtraValue(DISTINCT_ID));
211
179
  }
212
180
  if (this.config.defaultDistinctId) {
213
181
  return this.config.defaultDistinctId;
@@ -223,7 +191,7 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
223
191
  if (!span.parentSpanId) {
224
192
  return false;
225
193
  }
226
- const parentCache = traceData.spans.get(span.parentSpanId);
194
+ const parentCache = traceData.getSpan({ spanId: span.parentSpanId });
227
195
  if (parentCache) {
228
196
  return parentCache.isRootSpan;
229
197
  }
@@ -255,18 +223,18 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
255
223
  return { ...baseProperties, ...this.buildSpanProperties(span) };
256
224
  }
257
225
  }
258
- extractErrorProperties(span) {
259
- if (!span.errorInfo) {
226
+ extractErrorProperties(errorInfo) {
227
+ if (!errorInfo) {
260
228
  return {};
261
229
  }
262
230
  const props = {
263
- error_message: span.errorInfo.message
231
+ error_message: errorInfo.message
264
232
  };
265
- if (span.errorInfo.id) {
266
- props.error_id = span.errorInfo.id;
233
+ if (errorInfo.id) {
234
+ props.error_id = errorInfo.id;
267
235
  }
268
- if (span.errorInfo.category) {
269
- props.error_category = span.errorInfo.category;
236
+ if (errorInfo.category) {
237
+ props.error_category = errorInfo.category;
270
238
  }
271
239
  return props;
272
240
  }
@@ -287,7 +255,7 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
287
255
  if (attrs.parameters.maxOutputTokens !== void 0) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;
288
256
  }
289
257
  if (attrs.streaming !== void 0) props.$ai_stream = attrs.streaming;
290
- return { ...props, ...this.extractErrorProperties(span), ...this.extractCustomMetadata(span) };
258
+ return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span) };
291
259
  }
292
260
  buildSpanProperties(span) {
293
261
  const props = {};
@@ -301,7 +269,7 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
301
269
  if (span.attributes) {
302
270
  Object.assign(props, span.attributes);
303
271
  }
304
- return { ...props, ...this.extractErrorProperties(span), ...this.extractCustomMetadata(span) };
272
+ return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span) };
305
273
  }
306
274
  formatMessages(data, defaultRole = "user") {
307
275
  if (this.isMessageArray(data)) {
@@ -340,6 +308,19 @@ var PosthogExporter = class _PosthogExporter extends BaseExporter {
340
308
  return String(data);
341
309
  }
342
310
  }
311
+ /**
312
+ * Force flush any buffered data to PostHog without shutting down.
313
+ */
314
+ async _flush() {
315
+ if (this.#client) {
316
+ await this.#client.flush();
317
+ }
318
+ }
319
+ async _postShutdown() {
320
+ if (this.#client) {
321
+ await this.#client.shutdown();
322
+ }
323
+ }
343
324
  };
344
325
 
345
326
  export { PosthogExporter, formatUsageMetrics };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tracing.ts"],"names":[],"mappings":";;;;;AAuBO,SAAS,mBAAmB,KAAA,EAAyC;AAC1E,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAC;AAEpB,EAAA,MAAM,QAA6B,EAAC;AAEpC,EAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,KAAA,CAAM,mBAAmB,KAAA,CAAM,WAAA;AACpE,EAAA,IAAI,KAAA,CAAM,YAAA,KAAiB,MAAA,EAAW,KAAA,CAAM,oBAAoB,KAAA,CAAM,YAAA;AAGtE,EAAA,IAAI,MAAM,YAAA,EAAc,SAAA,KAAc,QAAW,KAAA,CAAM,2BAAA,GAA8B,MAAM,YAAA,CAAa,SAAA;AAGxG,EAAA,IAAI,KAAA,CAAM,cAAc,UAAA,KAAe,MAAA;AACrC,IAAA,KAAA,CAAM,+BAAA,GAAkC,MAAM,YAAA,CAAa,UAAA;AAE7D,EAAA,OAAO,KAAA;AACT;AA+CO,IAAM,eAAA,GAAN,MAAM,gBAAA,SAAwB,YAAA,CAAa;AAAA,EAChD,IAAA,GAAO,SAAA;AAAA,EACC,MAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA,uBAAe,GAAA,EAA2B;AAAA,EAElD,OAAwB,mBAAA,GAAsB,EAAA;AAAA,EAC9C,OAAwB,yBAAA,GAA4B,GAAA;AAAA,EACpD,OAAwB,gBAAA,GAAmB,EAAA;AAAA,EAC3C,OAAwB,sBAAA,GAAyB,GAAA;AAAA,EAEjD,YAAY,MAAA,EAA+B;AACzC,IAAA,KAAA,CAAM,MAAM,CAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAEd,IAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,MAAA,IAAA,CAAK,YAAY,0BAA0B,CAAA;AAC3C,MAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AACd,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,iBAAA,CAAkB,MAAM,CAAA;AAClD,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,OAAA,CAAQ,MAAA,CAAO,QAAQ,YAAY,CAAA;AACrD,IAAA,IAAA,CAAK,iBAAA,CAAkB,MAAA,CAAO,UAAA,IAAc,KAAA,EAAO,YAAY,CAAA;AAAA,EACjE;AAAA,EAEQ,kBAAkB,MAAA,EAA+B;AACvD,IAAA,MAAM,YAAA,GAAe,OAAO,UAAA,IAAc,KAAA;AAC1C,IAAA,MAAM,UACJ,MAAA,CAAO,OAAA,KAAY,YAAA,GAAe,gBAAA,CAAgB,sBAAsB,gBAAA,CAAgB,gBAAA,CAAA;AAC1F,IAAA,MAAM,gBACJ,MAAA,CAAO,aAAA,KACN,YAAA,GAAe,gBAAA,CAAgB,4BAA4B,gBAAA,CAAgB,sBAAA,CAAA;AAE9E,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,IAAQ,OAAA,CAAQ,IAAI,YAAA,IAAgB,0BAAA;AAExD,IAAA,IAAI,CAAC,MAAA,CAAO,IAAA,IAAQ,CAAC,OAAA,CAAQ,IAAI,YAAA,EAAc;AAC7C,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV;AAAA,OAGF;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,OAAA;AAAA,MACA,aAAA;AAAA,MACA,aAAa,MAAA,CAAO;AAAA,KACtB;AAAA,EACF;AAAA,EAEQ,iBAAA,CACN,cACA,MAAA,EACM;AACN,IAAA,MAAM,OAAA,GAAU,eAAe,iDAAA,GAAoD,8BAAA;AACnF,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,OAAA,EAAS,MAAM,CAAA;AAAA,EACnC;AAAA,EAEA,MAAgB,oBAAoB,KAAA,EAAoC;AACtE,IAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,IAAI,KAAA,CAAM,aAAa,OAAA,EAAS;AAC9B,QAAA,IAAI,KAAA,CAAM,SAAS,cAAA,EAAgB;AACjC,UAAA,MAAM,IAAA,CAAK,gBAAA,CAAiB,KAAA,CAAM,YAAY,CAAA;AAAA,QAChD;AACA,QAAA;AAAA,MACF;AAEA,MAAA,QAAQ,MAAM,IAAA;AAAM,QAClB,KAAK,cAAA;AACH,UAAA,MAAM,IAAA,CAAK,iBAAA,CAAkB,KAAA,CAAM,YAAY,CAAA;AAC/C,UAAA;AAAA,QACF,KAAK,cAAA;AACH,UAAA;AAAA,QACF,KAAK,YAAA;AACH,UAAA,MAAM,IAAA,CAAK,eAAA,CAAgB,KAAA,CAAM,YAAY,CAAA;AAC7C,UAAA;AAAA;AACJ,IACF,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,OAAO,KAAA,CAAM,wBAAA,EAA0B,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,IAAA,EAAsC;AACpE,IAAA,IAAI,SAAA,GAAY,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,OAAO,CAAA;AAE9C,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,SAAA,GAAY;AAAA,QACV,KAAA,sBAAW,GAAA,EAAI;AAAA,QACf,UAAA,EAAY;AAAA,OACd;AACA,MAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS,SAAS,CAAA;AAAA,IAC3C;AAEA,IAAA,SAAA,CAAU,KAAA,CAAM,GAAA,CAAI,IAAA,CAAK,EAAA,EAAI;AAAA,MAC3B,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAAA,MACrC,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,YAAY,IAAA,CAAK;AAAA,KAClB,CAAA;AAED,IAAA,IAAI,CAAC,UAAU,UAAA,EAAY;AACzB,MAAA,MAAM,MAAA,GAAS,KAAK,QAAA,EAAU,MAAA;AAC9B,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,SAAA,CAAU,UAAA,GAAa,OAAO,MAAM,CAAA;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,gBAAgB,IAAA,EAAsC;AAClE,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,OAAO,CAAA;AAEhD,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,SAAA,CAAU,KAAA,CAAM,GAAA,CAAI,KAAK,EAAE,CAAA;AAC9C,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,SAAA,CAAU,OAAA,EAAQ;AAC/C,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,OAAA,EAAQ,GAAI,IAAA,CAAK,GAAA,EAAI;AAC9E,IAAA,MAAM,OAAA,GAAA,CAAW,UAAU,SAAA,IAAa,GAAA;AAExC,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,aAAA,CAAc,IAAA,EAAM,SAAS,CAAA;AAIrD,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,IAAA,CAAK,iBAAA,CAAkB,IAAA,EAAM,UAAA,EAAY,OAAO,CAAA;AAAA,IAClD,CAAA,MAAO;AACL,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,iBAAA,CAAkB,IAAA,CAAK,IAAI,CAAA;AAIlD,MAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,gBAAA,CAAiB,IAAA,EAAM,SAAS,CAAA;AAC9D,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,oBAAA,CAAqB,IAAA,EAAM,SAAS,gBAAgB,CAAA;AAE5E,MAAA,IAAA,CAAK,OAAO,OAAA,CAAQ;AAAA,QAClB,UAAA;AAAA,QACA,KAAA,EAAO,SAAA;AAAA,QACP,UAAA;AAAA,QACA,SAAA,EAAW,IAAI,IAAA,CAAK,OAAO;AAAA,OAC5B,CAAA;AAAA,IACH;AAEA,IAAA,SAAA,CAAU,KAAA,CAAM,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA;AAC9B,IAAA,IAAI,SAAA,CAAU,KAAA,CAAM,IAAA,KAAS,CAAA,EAAG;AAC9B,MAAA,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAA,CAAkB,IAAA,EAAuB,UAAA,EAAoB,OAAA,EAAuB;AAG1F,IAAA,MAAM,eAAA,GAAuC;AAAA,MAC3C,cAAc,IAAA,CAAK,OAAA;AAAA,MACnB,eAAe,IAAA,CAAK,IAAA;AAAA,MACpB,YAAA,EAAc,CAAC,CAAC,IAAA,CAAK;AAAA,KACvB;AAEA,IAAA,IAAI,IAAA,CAAK,UAAU,SAAA,EAAW;AAC5B,MAAA,eAAA,CAAgB,cAAA,GAAiB,KAAK,QAAA,CAAS,SAAA;AAAA,IACjD;AAEA,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,eAAA,CAAgB,kBAAkB,IAAA,CAAK,KAAA;AAAA,IACzC;AAEA,IAAA,IAAI,KAAK,MAAA,EAAQ;AACf,MAAA,eAAA,CAAgB,mBAAmB,IAAA,CAAK,MAAA;AAAA,IAC1C;AAEA,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,eAAA,CAAgB,SAAA,GAAY;AAAA,QAC1B,OAAA,EAAS,KAAK,SAAA,CAAU,OAAA;AAAA,QACxB,GAAI,KAAK,SAAA,CAAU,EAAA,IAAM,EAAE,EAAA,EAAI,IAAA,CAAK,UAAU,EAAA,EAAG;AAAA,QACjD,GAAI,KAAK,SAAA,CAAU,QAAA,IAAY,EAAE,QAAA,EAAU,IAAA,CAAK,UAAU,QAAA;AAAS,OACrE;AAAA,IACF;AAGA,IAAA,IAAI,IAAA,CAAK,MAAM,MAAA,EAAQ;AACrB,MAAA,KAAA,MAAW,GAAA,IAAO,KAAK,IAAA,EAAM;AAC3B,QAAA,eAAA,CAAgB,GAAG,CAAA,GAAI,IAAA;AAAA,MACzB;AAAA,IACF;AAGA,IAAA,MAAM,EAAE,QAAQ,SAAA,EAAW,GAAG,gBAAe,GAAI,IAAA,CAAK,YAAY,EAAC;AACnE,IAAA,MAAA,CAAO,MAAA,CAAO,iBAAiB,cAAc,CAAA;AAE7C,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ;AAAA,MAClB,UAAA;AAAA,MACA,KAAA,EAAO,WAAA;AAAA,MACP,UAAA,EAAY,eAAA;AAAA,MACZ,SAAA,EAAW,IAAI,IAAA,CAAK,OAAO;AAAA,KAC5B,CAAA;AAAA,EACH;AAAA,EAEA,MAAc,iBAAiB,IAAA,EAAsC;AACnE,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,iBAAA,CAAkB,IAAA,CAAK,IAAI,CAAA;AAClD,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,KAAK,OAAO,CAAA;AAEhD,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,aAAA,CAAc,IAAA,EAAM,SAAS,CAAA;AACrD,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,oBAAA,CAAqB,IAAA,EAAM,CAAC,CAAA;AAEpD,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ;AAAA,MAClB,UAAA;AAAA,MACA,KAAA,EAAO,SAAA;AAAA,MACP,UAAA;AAAA,MACA,SAAA,EAAW,KAAK,OAAA,GAAU,IAAI,KAAK,IAAA,CAAK,OAAO,CAAA,mBAAI,IAAI,IAAA;AAAK,KAC7D,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,QAAA,GAA0B;AAC9B,IAAA,IAAI,KAAK,MAAA,EAAQ;AACf,MAAA,MAAM,IAAA,CAAK,OAAO,QAAA,EAAS;AAAA,IAC7B;AACA,IAAA,IAAA,CAAK,SAAS,KAAA,EAAM;AACpB,IAAA,MAAM,MAAM,QAAA,EAAS;AACrB,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,oCAAoC,CAAA;AAAA,EACvD;AAAA,EAEQ,OAAO,SAAA,EAAgC;AAC7C,IAAA,OAAO,SAAA,YAAqB,IAAA,GAAO,SAAA,GAAY,IAAI,KAAK,SAAS,CAAA;AAAA,EACnE;AAAA,EAEQ,kBAAkB,QAAA,EAA4B;AACpD,IAAA,IAAI,QAAA,IAAY,SAAS,gBAAA,EAAkB;AACzC,MAAA,OAAO,gBAAA;AAAA,IACT;AACA,IAAA,OAAO,UAAA;AAAA,EACT;AAAA,EAEQ,aAAA,CAAc,MAAuB,SAAA,EAAmC;AAC9E,IAAA,IAAI,IAAA,CAAK,UAAU,MAAA,EAAQ;AACzB,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AAAA,IACpC;AAEA,IAAA,IAAI,WAAW,UAAA,EAAY;AACzB,MAAA,OAAO,SAAA,CAAU,UAAA;AAAA,IACnB;AAEA,IAAA,IAAI,IAAA,CAAK,OAAO,iBAAA,EAAmB;AACjC,MAAA,OAAO,KAAK,MAAA,CAAO,iBAAA;AAAA,IACrB;AAEA,IAAA,OAAO,WAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,gBAAA,CAAiB,MAAuB,SAAA,EAAmC;AACjF,IAAA,IAAI,CAAC,KAAK,YAAA,EAAc;AACtB,MAAA,OAAO,KAAA;AAAA,IACT;AAGA,IAAA,MAAM,WAAA,GAAc,SAAA,CAAU,KAAA,CAAM,GAAA,CAAI,KAAK,YAAY,CAAA;AACzD,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,OAAO,WAAA,CAAY,UAAA;AAAA,IACrB;AAGA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEQ,oBAAA,CACN,IAAA,EACA,OAAA,EACA,gBAAA,GAA4B,KAAA,EACP;AACrB,IAAA,MAAM,cAAA,GAAsC;AAAA,MAC1C,cAAc,IAAA,CAAK,OAAA;AAAA,MACnB,WAAA,EAAa,OAAA;AAAA,MACb,YAAA,EAAc,CAAC,CAAC,IAAA,CAAK;AAAA,KACvB;AAEA,IAAA,IAAI,KAAK,YAAA,EAAc;AAGrB,MAAA,cAAA,CAAe,aAAA,GAAgB,gBAAA,GAAmB,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,YAAA;AAAA,IACxE;AAEA,IAAA,IAAI,IAAA,CAAK,UAAU,SAAA,EAAW;AAC5B,MAAA,cAAA,CAAe,cAAA,GAAiB,KAAK,QAAA,CAAS,SAAA;AAAA,IAChD;AAKA,IAAA,IAAI,IAAA,CAAK,UAAA,IAAc,IAAA,CAAK,IAAA,EAAM,MAAA,EAAQ;AACxC,MAAA,KAAA,MAAW,GAAA,IAAO,KAAK,IAAA,EAAM;AAC3B,QAAA,cAAA,CAAe,GAAG,CAAA,GAAI,IAAA;AAAA,MACxB;AAAA,IACF;AAEA,IAAA,IAAI,IAAA,CAAK,IAAA,KAAS,QAAA,CAAS,gBAAA,EAAkB;AAC3C,MAAA,cAAA,CAAe,oBAAoB,IAAA,CAAK,EAAA;AACxC,MAAA,OAAO,EAAE,GAAG,cAAA,EAAgB,GAAG,IAAA,CAAK,yBAAA,CAA0B,IAAI,CAAA,EAAE;AAAA,IACtE,CAAA,MAAO;AACL,MAAA,cAAA,CAAe,cAAc,IAAA,CAAK,EAAA;AAClC,MAAA,cAAA,CAAe,gBAAgB,IAAA,CAAK,IAAA;AACpC,MAAA,OAAO,EAAE,GAAG,cAAA,EAAgB,GAAG,IAAA,CAAK,mBAAA,CAAoB,IAAI,CAAA,EAAE;AAAA,IAChE;AAAA,EACF;AAAA,EAEQ,uBAAuB,IAAA,EAA4C;AACzE,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AACnB,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,MAAM,KAAA,GAAgC;AAAA,MACpC,aAAA,EAAe,KAAK,SAAA,CAAU;AAAA,KAChC;AAEA,IAAA,IAAI,IAAA,CAAK,UAAU,EAAA,EAAI;AACrB,MAAA,KAAA,CAAM,QAAA,GAAW,KAAK,SAAA,CAAU,EAAA;AAAA,IAClC;AAEA,IAAA,IAAI,IAAA,CAAK,UAAU,QAAA,EAAU;AAC3B,MAAA,KAAA,CAAM,cAAA,GAAiB,KAAK,SAAA,CAAU,QAAA;AAAA,IACxC;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEQ,sBAAsB,IAAA,EAA4C;AACxE,IAAA,MAAM,EAAE,QAAQ,SAAA,EAAW,GAAG,gBAAe,GAAI,IAAA,CAAK,YAAY,EAAC;AACnE,IAAA,OAAO,cAAA;AAAA,EACT;AAAA,EAEQ,0BAA0B,IAAA,EAA4C;AAC5E,IAAA,MAAM,QAA6B,EAAC;AACpC,IAAA,MAAM,KAAA,GAAS,IAAA,CAAK,UAAA,IAAc,EAAC;AAEnC,IAAA,KAAA,CAAM,SAAA,GAAY,MAAM,KAAA,IAAS,eAAA;AACjC,IAAA,KAAA,CAAM,YAAA,GAAe,MAAM,QAAA,IAAY,kBAAA;AAEvC,IAAA,IAAI,IAAA,CAAK,OAAO,KAAA,CAAM,SAAA,GAAY,KAAK,cAAA,CAAe,IAAA,CAAK,OAAO,MAAM,CAAA;AACxE,IAAA,IAAI,IAAA,CAAK,QAAQ,KAAA,CAAM,kBAAA,GAAqB,KAAK,cAAA,CAAe,IAAA,CAAK,QAAQ,WAAW,CAAA;AAGxF,IAAA,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,kBAAA,CAAmB,KAAA,CAAM,KAAK,CAAC,CAAA;AAEpD,IAAA,IAAI,MAAM,UAAA,EAAY;AACpB,MAAA,IAAI,MAAM,UAAA,CAAW,WAAA,KAAgB,QAAW,KAAA,CAAM,eAAA,GAAkB,MAAM,UAAA,CAAW,WAAA;AACzF,MAAA,IAAI,MAAM,UAAA,CAAW,eAAA,KAAoB,QAAW,KAAA,CAAM,cAAA,GAAiB,MAAM,UAAA,CAAW,eAAA;AAAA,IAC9F;AACA,IAAA,IAAI,KAAA,CAAM,SAAA,KAAc,MAAA,EAAW,KAAA,CAAM,aAAa,KAAA,CAAM,SAAA;AAE5D,IAAA,OAAO,EAAE,GAAG,KAAA,EAAO,GAAG,IAAA,CAAK,sBAAA,CAAuB,IAAI,CAAA,EAAG,GAAG,IAAA,CAAK,qBAAA,CAAsB,IAAI,CAAA,EAAE;AAAA,EAC/F;AAAA,EAEQ,oBAAoB,IAAA,EAA4C;AACtE,IAAA,MAAM,QAA6B,EAAC;AAEpC,IAAA,IAAI,IAAA,CAAK,KAAA,EAAO,KAAA,CAAM,eAAA,GAAkB,IAAA,CAAK,KAAA;AAC7C,IAAA,IAAI,IAAA,CAAK,MAAA,EAAQ,KAAA,CAAM,gBAAA,GAAmB,IAAA,CAAK,MAAA;AAE/C,IAAA,IAAI,IAAA,CAAK,IAAA,KAAS,QAAA,CAAS,WAAA,EAAa;AACtC,MAAA,MAAM,QAAQ,IAAA,CAAK,UAAA;AACnB,MAAA,IAAI,KAAA,EAAO,SAAA,EAAW,KAAA,CAAM,UAAA,GAAa,KAAA,CAAM,SAAA;AAC/C,MAAA,IAAI,KAAA,EAAO,cAAA,KAAmB,MAAA,EAAW,KAAA,CAAM,wBAAwB,KAAA,CAAM,cAAA;AAAA,IAC/E;AAEA,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,IAAA,CAAK,UAAU,CAAA;AAAA,IACtC;AAEA,IAAA,OAAO,EAAE,GAAG,KAAA,EAAO,GAAG,IAAA,CAAK,sBAAA,CAAuB,IAAI,CAAA,EAAG,GAAG,IAAA,CAAK,qBAAA,CAAsB,IAAI,CAAA,EAAE;AAAA,EAC/F;AAAA,EAEQ,cAAA,CAAe,IAAA,EAAgB,WAAA,GAAoC,MAAA,EAA0B;AACnG,IAAA,IAAI,IAAA,CAAK,cAAA,CAAe,IAAI,CAAA,EAAG;AAC7B,MAAA,OAAO,KAAK,GAAA,CAAI,CAAA,GAAA,KAAO,IAAA,CAAK,gBAAA,CAAiB,GAAG,CAAC,CAAA;AAAA,IACnD;AAEA,IAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC5B,MAAA,OAAO,CAAC,EAAE,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,CAAC,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,GAAG,CAAA;AAAA,IACxE;AAEA,IAAA,OAAO,CAAC,EAAE,IAAA,EAAM,WAAA,EAAa,OAAA,EAAS,CAAC,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,KAAK,aAAA,CAAc,IAAI,CAAA,EAAG,GAAG,CAAA;AAAA,EAC5F;AAAA,EAEQ,eAAe,IAAA,EAAwC;AAC7D,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,IAAK,IAAA,CAAK,WAAW,CAAA,EAAG;AAC7C,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,IAAA,KAAQ,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,IAAQ,MAAA,IAAU,IAAA,IAAQ,SAAA,IAAa,IAAI,CAAA;AAAA,EAC5G;AAAA,EAEQ,iBAAiB,GAAA,EAAoC;AAC3D,IAAA,IAAI,OAAO,GAAA,CAAI,OAAA,KAAY,QAAA,EAAU;AACnC,MAAA,OAAO;AAAA,QACL,MAAM,GAAA,CAAI,IAAA;AAAA,QACV,OAAA,EAAS,CAAC,EAAE,IAAA,EAAM,QAAQ,IAAA,EAAM,GAAA,CAAI,SAAS;AAAA,OAC/C;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,SAAS,GAAA,CAAI;AAAA,KACf;AAAA,EACF;AAAA,EAEQ,cAAc,IAAA,EAAuB;AAC3C,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,UAAU,IAAI,CAAA;AAAA,IAC5B,CAAA,CAAA,MAAQ;AACN,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,EAAM;AAC7C,QAAA,OAAO,CAAA,kBAAA,EAAqB,IAAA,CAAK,WAAA,EAAa,IAAA,IAAQ,QAAQ,CAAA,CAAA,CAAA;AAAA,MAChE;AACA,MAAA,OAAO,OAAO,IAAI,CAAA;AAAA,IACpB;AAAA,EACF;AACF","file":"index.js","sourcesContent":["import type { TracingEvent, AnyExportedSpan, ModelGenerationAttributes, UsageStats } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport type { BaseExporterConfig } from '@mastra/observability';\nimport { BaseExporter } from '@mastra/observability';\nimport { PostHog } from 'posthog-node';\n\n/**\n * Token usage format compatible with PostHog.\n * @see https://posthog.com/docs/llm-analytics/generations#event-properties\n */\nexport interface PostHogUsageMetrics {\n $ai_input_tokens?: number;\n $ai_output_tokens?: number;\n $ai_cache_read_input_tokens?: number;\n $ai_cache_creation_input_tokens?: number;\n}\n\n/**\n * Formats UsageStats to PostHog's expected property format.\n *\n * @param usage - The UsageStats from span attributes\n * @returns PostHog-formatted usage properties\n */\nexport function formatUsageMetrics(usage?: UsageStats): PostHogUsageMetrics {\n if (!usage) return {};\n\n const props: PostHogUsageMetrics = {};\n\n if (usage.inputTokens !== undefined) props.$ai_input_tokens = usage.inputTokens;\n if (usage.outputTokens !== undefined) props.$ai_output_tokens = usage.outputTokens;\n\n // Cache read tokens from inputDetails\n if (usage.inputDetails?.cacheRead !== undefined) props.$ai_cache_read_input_tokens = usage.inputDetails.cacheRead;\n\n // Cache write tokens from inputDetails\n if (usage.inputDetails?.cacheWrite !== undefined)\n props.$ai_cache_creation_input_tokens = usage.inputDetails.cacheWrite;\n\n return props;\n}\n\ninterface PostHogMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: PostHogContent[];\n}\n\ninterface PostHogContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface MastraMessage {\n role: string;\n content: string | MastraContent[];\n}\n\ninterface MastraContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ntype SpanData = string | MastraMessage[] | Record<string, unknown> | unknown;\n\nexport interface PosthogExporterConfig extends BaseExporterConfig {\n apiKey: string;\n host?: string;\n flushAt?: number;\n flushInterval?: number;\n serverless?: boolean;\n defaultDistinctId?: string;\n enablePrivacyMode?: boolean;\n}\n\ntype SpanCache = {\n startTime: Date;\n type: SpanType;\n isRootSpan: boolean;\n};\n\ntype TraceMetadata = {\n spans: Map<string, SpanCache>;\n distinctId?: string;\n};\n\nexport class PosthogExporter extends BaseExporter {\n name = 'posthog';\n private client: PostHog;\n private config: PosthogExporterConfig;\n private traceMap = new Map<string, TraceMetadata>();\n\n private static readonly SERVERLESS_FLUSH_AT = 10;\n private static readonly SERVERLESS_FLUSH_INTERVAL = 2000;\n private static readonly DEFAULT_FLUSH_AT = 20;\n private static readonly DEFAULT_FLUSH_INTERVAL = 10000;\n\n constructor(config: PosthogExporterConfig) {\n super(config);\n this.config = config;\n\n if (!config.apiKey) {\n this.setDisabled('Missing required API key');\n this.client = null as any;\n return;\n }\n\n const clientConfig = this.buildClientConfig(config);\n this.client = new PostHog(config.apiKey, clientConfig);\n this.logInitialization(config.serverless ?? false, clientConfig);\n }\n\n private buildClientConfig(config: PosthogExporterConfig) {\n const isServerless = config.serverless ?? false;\n const flushAt =\n config.flushAt ?? (isServerless ? PosthogExporter.SERVERLESS_FLUSH_AT : PosthogExporter.DEFAULT_FLUSH_AT);\n const flushInterval =\n config.flushInterval ??\n (isServerless ? PosthogExporter.SERVERLESS_FLUSH_INTERVAL : PosthogExporter.DEFAULT_FLUSH_INTERVAL);\n\n const host = config.host || process.env.POSTHOG_HOST || 'https://us.i.posthog.com';\n\n if (!config.host && !process.env.POSTHOG_HOST) {\n this.logger.warn(\n 'No PostHog host specified, using US default (https://us.i.posthog.com). ' +\n 'For EU region, set `host: \"https://eu.i.posthog.com\"` in config or POSTHOG_HOST env var. ' +\n 'For self-hosted, provide your instance URL.',\n );\n }\n\n return {\n host,\n flushAt,\n flushInterval,\n privacyMode: config.enablePrivacyMode,\n };\n }\n\n private logInitialization(\n isServerless: boolean,\n config: { host: string; flushAt: number; flushInterval: number; privacyMode?: boolean },\n ): void {\n const message = isServerless ? 'PostHog exporter initialized in serverless mode' : 'PostHog exporter initialized';\n this.logger.debug(message, config);\n }\n\n protected async _exportTracingEvent(event: TracingEvent): Promise<void> {\n if (!this.client) {\n return;\n }\n\n try {\n if (event.exportedSpan.isEvent) {\n if (event.type === 'span_started') {\n await this.captureEventSpan(event.exportedSpan);\n }\n return;\n }\n\n switch (event.type) {\n case 'span_started':\n await this.handleSpanStarted(event.exportedSpan);\n break;\n case 'span_updated':\n break;\n case 'span_ended':\n await this.handleSpanEnded(event.exportedSpan);\n break;\n }\n } catch (error) {\n this.logger.error('PostHog exporter error', { error, event });\n }\n }\n\n private async handleSpanStarted(span: AnyExportedSpan): Promise<void> {\n let traceData = this.traceMap.get(span.traceId);\n\n if (!traceData) {\n traceData = {\n spans: new Map(),\n distinctId: undefined,\n };\n this.traceMap.set(span.traceId, traceData);\n }\n\n traceData.spans.set(span.id, {\n startTime: this.toDate(span.startTime),\n type: span.type,\n isRootSpan: span.isRootSpan,\n });\n\n if (!traceData.distinctId) {\n const userId = span.metadata?.userId;\n if (userId) {\n traceData.distinctId = String(userId);\n }\n }\n }\n\n private async handleSpanEnded(span: AnyExportedSpan): Promise<void> {\n const traceData = this.traceMap.get(span.traceId);\n\n if (!traceData) {\n return;\n }\n\n const cachedSpan = traceData.spans.get(span.id);\n if (!cachedSpan) {\n return;\n }\n\n const startTime = cachedSpan.startTime.getTime();\n const endTime = span.endTime ? this.toDate(span.endTime).getTime() : Date.now();\n const latency = (endTime - startTime) / 1000;\n\n const distinctId = this.getDistinctId(span, traceData);\n\n // For root spans, only send $ai_trace (not $ai_span) to avoid duplicate entries\n // For non-root spans, send $ai_span or $ai_generation as normal\n if (span.isRootSpan) {\n this.captureTraceEvent(span, distinctId, endTime);\n } else {\n const eventName = this.mapToPostHogEvent(span.type);\n\n // Check if parent is the root span - if so, use traceId as parent_id\n // since we don't create an $ai_span for root spans\n const parentIsRootSpan = this.isParentRootSpan(span, traceData);\n const properties = this.buildEventProperties(span, latency, parentIsRootSpan);\n\n this.client.capture({\n distinctId,\n event: eventName,\n properties,\n timestamp: new Date(endTime),\n });\n }\n\n traceData.spans.delete(span.id);\n if (traceData.spans.size === 0) {\n this.traceMap.delete(span.traceId);\n }\n }\n\n /**\n * Capture an explicit $ai_trace event for root spans.\n * This gives us control over trace-level metadata like name and tags,\n * rather than relying on PostHog's pseudo-trace auto-creation.\n */\n private captureTraceEvent(span: AnyExportedSpan, distinctId: string, endTime: number): void {\n // Note: We don't set $ai_latency on $ai_trace events because PostHog\n // aggregates latency from child events. Setting it here causes double-counting.\n const traceProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_span_name: span.name,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.metadata?.sessionId) {\n traceProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n if (span.input) {\n traceProperties.$ai_input_state = span.input;\n }\n\n if (span.output) {\n traceProperties.$ai_output_state = span.output;\n }\n\n if (span.errorInfo) {\n traceProperties.$ai_error = {\n message: span.errorInfo.message,\n ...(span.errorInfo.id && { id: span.errorInfo.id }),\n ...(span.errorInfo.category && { category: span.errorInfo.category }),\n };\n }\n\n // Add tags as custom properties (PostHog doesn't have native tag support on traces)\n if (span.tags?.length) {\n for (const tag of span.tags) {\n traceProperties[tag] = true;\n }\n }\n\n // Add custom metadata (excluding userId and sessionId which are handled separately)\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n Object.assign(traceProperties, customMetadata);\n\n this.client.capture({\n distinctId,\n event: '$ai_trace',\n properties: traceProperties,\n timestamp: new Date(endTime),\n });\n }\n\n private async captureEventSpan(span: AnyExportedSpan): Promise<void> {\n const eventName = this.mapToPostHogEvent(span.type);\n const traceData = this.traceMap.get(span.traceId);\n\n const distinctId = this.getDistinctId(span, traceData);\n const properties = this.buildEventProperties(span, 0);\n\n this.client.capture({\n distinctId,\n event: eventName,\n properties,\n timestamp: span.endTime ? new Date(span.endTime) : new Date(),\n });\n }\n\n async shutdown(): Promise<void> {\n if (this.client) {\n await this.client.shutdown();\n }\n this.traceMap.clear();\n await super.shutdown();\n this.logger.info('PostHog exporter shutdown complete');\n }\n\n private toDate(timestamp: Date | number): Date {\n return timestamp instanceof Date ? timestamp : new Date(timestamp);\n }\n\n private mapToPostHogEvent(spanType: SpanType): string {\n if (spanType == SpanType.MODEL_GENERATION) {\n return '$ai_generation';\n }\n return '$ai_span';\n }\n\n private getDistinctId(span: AnyExportedSpan, traceData?: TraceMetadata): string {\n if (span.metadata?.userId) {\n return String(span.metadata.userId);\n }\n\n if (traceData?.distinctId) {\n return traceData.distinctId;\n }\n\n if (this.config.defaultDistinctId) {\n return this.config.defaultDistinctId;\n }\n\n return 'anonymous';\n }\n\n /**\n * Check if the parent of this span is the root span.\n * We need this because we don't create $ai_span for root spans,\n * so children of root spans should use $ai_trace_id as their $ai_parent_id.\n */\n private isParentRootSpan(span: AnyExportedSpan, traceData: TraceMetadata): boolean {\n if (!span.parentSpanId) {\n return false;\n }\n\n // Look up the parent span in our cache to check if it's a root span\n const parentCache = traceData.spans.get(span.parentSpanId);\n if (parentCache) {\n return parentCache.isRootSpan;\n }\n\n // Parent not found in cache - shouldn't happen normally, but default to false\n return false;\n }\n\n private buildEventProperties(\n span: AnyExportedSpan,\n latency: number,\n parentIsRootSpan: boolean = false,\n ): Record<string, any> {\n const baseProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_latency: latency,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.parentSpanId) {\n // If parent is the root span, use trace_id as parent_id since we don't\n // create an $ai_span for root spans (only $ai_trace)\n baseProperties.$ai_parent_id = parentIsRootSpan ? span.traceId : span.parentSpanId;\n }\n\n if (span.metadata?.sessionId) {\n baseProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n // Include tags for root spans (tags are only set on root spans by design)\n // PostHog doesn't allow setting tags directly, so we iterate through each tag\n // and set it as a property with value true\n if (span.isRootSpan && span.tags?.length) {\n for (const tag of span.tags) {\n baseProperties[tag] = true;\n }\n }\n\n if (span.type === SpanType.MODEL_GENERATION) {\n baseProperties.$ai_generation_id = span.id;\n return { ...baseProperties, ...this.buildGenerationProperties(span) };\n } else {\n baseProperties.$ai_span_id = span.id;\n baseProperties.$ai_span_name = span.name;\n return { ...baseProperties, ...this.buildSpanProperties(span) };\n }\n }\n\n private extractErrorProperties(span: AnyExportedSpan): Record<string, any> {\n if (!span.errorInfo) {\n return {};\n }\n\n const props: Record<string, string> = {\n error_message: span.errorInfo.message,\n };\n\n if (span.errorInfo.id) {\n props.error_id = span.errorInfo.id;\n }\n\n if (span.errorInfo.category) {\n props.error_category = span.errorInfo.category;\n }\n\n return props;\n }\n\n private extractCustomMetadata(span: AnyExportedSpan): Record<string, any> {\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n return customMetadata;\n }\n\n private buildGenerationProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n const attrs = (span.attributes ?? {}) as ModelGenerationAttributes;\n\n props.$ai_model = attrs.model || 'unknown-model';\n props.$ai_provider = attrs.provider || 'unknown-provider';\n\n if (span.input) props.$ai_input = this.formatMessages(span.input, 'user');\n if (span.output) props.$ai_output_choices = this.formatMessages(span.output, 'assistant');\n\n // Extract usage properties using the shared utility\n Object.assign(props, formatUsageMetrics(attrs.usage));\n\n if (attrs.parameters) {\n if (attrs.parameters.temperature !== undefined) props.$ai_temperature = attrs.parameters.temperature;\n if (attrs.parameters.maxOutputTokens !== undefined) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;\n }\n if (attrs.streaming !== undefined) props.$ai_stream = attrs.streaming;\n\n return { ...props, ...this.extractErrorProperties(span), ...this.extractCustomMetadata(span) };\n }\n\n private buildSpanProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n\n if (span.input) props.$ai_input_state = span.input;\n if (span.output) props.$ai_output_state = span.output;\n\n if (span.type === SpanType.MODEL_CHUNK) {\n const attrs = span.attributes as any;\n if (attrs?.chunkType) props.chunk_type = attrs.chunkType;\n if (attrs?.sequenceNumber !== undefined) props.chunk_sequence_number = attrs.sequenceNumber;\n }\n\n if (span.attributes) {\n Object.assign(props, span.attributes);\n }\n\n return { ...props, ...this.extractErrorProperties(span), ...this.extractCustomMetadata(span) };\n }\n\n private formatMessages(data: SpanData, defaultRole: 'user' | 'assistant' = 'user'): PostHogMessage[] {\n if (this.isMessageArray(data)) {\n return data.map(msg => this.normalizeMessage(msg));\n }\n\n if (typeof data === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text: data }] }];\n }\n\n return [{ role: defaultRole, content: [{ type: 'text', text: this.safeStringify(data) }] }];\n }\n\n private isMessageArray(data: unknown): data is MastraMessage[] {\n if (!Array.isArray(data) || data.length === 0) {\n return false;\n }\n\n return data.every(item => typeof item === 'object' && item !== null && 'role' in item && 'content' in item);\n }\n\n private normalizeMessage(msg: MastraMessage): PostHogMessage {\n if (typeof msg.content === 'string') {\n return {\n role: msg.role as PostHogMessage['role'],\n content: [{ type: 'text', text: msg.content }],\n };\n }\n\n return {\n role: msg.role as PostHogMessage['role'],\n content: msg.content as PostHogContent[],\n };\n }\n\n private safeStringify(data: unknown): string {\n try {\n return JSON.stringify(data);\n } catch {\n if (typeof data === 'object' && data !== null) {\n return `[Non-serializable ${data.constructor?.name || 'Object'}]`;\n }\n return String(data);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/tracing.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { AnyExportedSpan, ModelGenerationAttributes, SpanErrorInfo, UsageStats } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { TrackingExporter } from '@mastra/observability';\nimport { PostHog } from 'posthog-node';\nimport type { EventMessage } from 'posthog-node';\n\n/**\n * Token usage format compatible with PostHog.\n * @see https://posthog.com/docs/llm-analytics/generations#event-properties\n */\nexport interface PostHogUsageMetrics {\n $ai_input_tokens?: number;\n $ai_output_tokens?: number;\n $ai_cache_read_input_tokens?: number;\n $ai_cache_creation_input_tokens?: number;\n}\n\n/**\n * Formats UsageStats to PostHog's expected property format.\n *\n * @param usage - The UsageStats from span attributes\n * @returns PostHog-formatted usage properties\n */\nexport function formatUsageMetrics(usage?: UsageStats): PostHogUsageMetrics {\n if (!usage) return {};\n\n const props: PostHogUsageMetrics = {};\n\n if (usage.inputTokens !== undefined) props.$ai_input_tokens = usage.inputTokens;\n if (usage.outputTokens !== undefined) props.$ai_output_tokens = usage.outputTokens;\n\n // Cache read tokens from inputDetails\n if (usage.inputDetails?.cacheRead !== undefined) props.$ai_cache_read_input_tokens = usage.inputDetails.cacheRead;\n\n // Cache write tokens from inputDetails\n if (usage.inputDetails?.cacheWrite !== undefined)\n props.$ai_cache_creation_input_tokens = usage.inputDetails.cacheWrite;\n\n return props;\n}\n\ninterface PostHogMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: PostHogContent[];\n}\n\ninterface PostHogContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface MastraMessage {\n role: string;\n content: string | MastraContent[];\n}\n\ninterface MastraContent {\n type: string;\n text?: string;\n [key: string]: unknown;\n}\n\ntype SpanData = string | MastraMessage[] | Record<string, unknown> | unknown;\n\nconst DISTINCT_ID = 'distinctId';\n\nexport interface PosthogExporterConfig extends TrackingExporterConfig {\n /** PostHog API key. Defaults to POSTHOG_API_KEY environment variable. */\n apiKey?: string;\n /** PostHog host URL. Defaults to POSTHOG_HOST environment variable or US region. */\n host?: string;\n flushAt?: number;\n flushInterval?: number;\n serverless?: boolean;\n defaultDistinctId?: string;\n enablePrivacyMode?: boolean;\n}\n\ntype PosthogRoot = unknown;\ntype PosthogSpan = AnyExportedSpan;\n// used as a placeholder for event data since we don't need to cache\n// event data for Posthog\ntype PosthogEvent = boolean;\ntype PosthogMetadata = unknown;\ntype PosthogTraceData = TraceData<PosthogRoot, PosthogSpan, PosthogEvent, PosthogMetadata>;\n\nexport class PosthogExporter extends TrackingExporter<\n PosthogRoot,\n PosthogSpan,\n PosthogEvent,\n PosthogMetadata,\n PosthogExporterConfig\n> {\n name = 'posthog';\n #client: PostHog | undefined;\n\n private static readonly SERVERLESS_FLUSH_AT = 10;\n private static readonly SERVERLESS_FLUSH_INTERVAL = 2000;\n private static readonly DEFAULT_FLUSH_AT = 20;\n private static readonly DEFAULT_FLUSH_INTERVAL = 10000;\n\n constructor(config: PosthogExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const apiKey = config.apiKey ?? process.env.POSTHOG_API_KEY;\n\n super({ ...config, apiKey });\n\n if (!apiKey) {\n this.setDisabled('Missing required API key. Set POSTHOG_API_KEY environment variable or pass apiKey in config.');\n return;\n }\n\n const clientConfig = this.buildClientConfig(this.config);\n this.#client = new PostHog(apiKey, clientConfig);\n const message =\n (config.serverless ?? false) ? 'PostHog exporter initialized in serverless mode' : 'PostHog exporter initialized';\n this.logger.debug(message, config);\n }\n\n private buildClientConfig(config: PosthogExporterConfig) {\n const isServerless = config.serverless ?? false;\n const flushAt =\n config.flushAt ?? (isServerless ? PosthogExporter.SERVERLESS_FLUSH_AT : PosthogExporter.DEFAULT_FLUSH_AT);\n const flushInterval =\n config.flushInterval ??\n (isServerless ? PosthogExporter.SERVERLESS_FLUSH_INTERVAL : PosthogExporter.DEFAULT_FLUSH_INTERVAL);\n\n const host = config.host || process.env.POSTHOG_HOST || 'https://us.i.posthog.com';\n\n if (!config.host && !process.env.POSTHOG_HOST) {\n this.logger.info(\n 'No PostHog host specified, using US default (https://us.i.posthog.com). ' +\n 'For EU region, set `host: \"https://eu.i.posthog.com\"` in config or POSTHOG_HOST env var. ' +\n 'For self-hosted, provide your instance URL.',\n );\n }\n\n return {\n host,\n flushAt,\n flushInterval,\n privacyMode: config.enablePrivacyMode,\n };\n }\n\n protected override skipBuildRootTask = true;\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogRoot | undefined> {\n throw new Error('Method not implemented.');\n }\n\n protected override skipCachingEventSpans = true;\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogEvent> {\n const { span, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const distinctId = this.getDistinctId(span, traceData);\n const properties = this.buildEventProperties(span, 0);\n\n this.#client?.capture({\n distinctId,\n event: eventName,\n properties,\n timestamp: span.endTime ? new Date(span.endTime) : new Date(),\n });\n\n return true;\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: PosthogTraceData;\n }): Promise<PosthogSpan | undefined> {\n const { span, traceData } = args;\n if (!traceData.hasExtraValue(DISTINCT_ID)) {\n const userId = span.metadata?.userId;\n if (userId) {\n traceData.setExtraValue(DISTINCT_ID, String(userId));\n }\n }\n\n return span;\n }\n\n protected override skipSpanUpdateEvents = true;\n protected override _updateSpan(_args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n throw new Error('Method not implemented.');\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n // Merge input from cached span (SPAN_STARTED) if not present on end span\n // This handles the case where input is only sent at start\n const cachedSpan = traceData.getSpan({ spanId: span.id });\n const mergedSpan = !span.input && cachedSpan?.input ? { ...span, input: cachedSpan.input } : span;\n\n const eventMessage = this.buildEventMessage({ span: mergedSpan, traceData });\n this.#client?.capture(eventMessage);\n }\n\n protected override async _abortSpan(args: {\n span: PosthogSpan;\n reason: SpanErrorInfo;\n traceData: PosthogTraceData;\n }): Promise<void> {\n const { span, reason, traceData } = args;\n\n // update span with the abort reason\n span.errorInfo = reason;\n\n const eventMessage = this.buildEventMessage({ span, traceData });\n this.#client?.capture(eventMessage);\n }\n\n private buildEventMessage(args: { span: AnyExportedSpan; traceData: PosthogTraceData }): EventMessage {\n const { span, traceData } = args;\n\n const endTime = span.endTime ? this.toDate(span.endTime).getTime() : Date.now();\n\n const distinctId = this.getDistinctId(span, traceData);\n\n if (span.isRootSpan) {\n return this.buildRootEventMessage({ span, distinctId, endTime });\n } else {\n return this.buildChildEventMessage({ span, distinctId, endTime, traceData });\n }\n }\n\n /**\n * Capture an explicit $ai_trace event for root spans.\n * This gives us control over trace-level metadata like name and tags,\n * rather than relying on PostHog's pseudo-trace auto-creation.\n */\n private buildRootEventMessage(args: { span: AnyExportedSpan; distinctId: string; endTime: number }): EventMessage {\n const { span, distinctId, endTime } = args;\n\n // Note: We don't set $ai_latency on $ai_trace events because PostHog\n // aggregates latency from child events. Setting it here causes double-counting.\n const traceProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_span_name: span.name,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.metadata?.sessionId) {\n traceProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n if (span.input) {\n traceProperties.$ai_input_state = span.input;\n }\n\n if (span.output) {\n traceProperties.$ai_output_state = span.output;\n }\n\n if (span.errorInfo) {\n traceProperties.$ai_error = {\n message: span.errorInfo.message,\n ...(span.errorInfo.id && { id: span.errorInfo.id }),\n ...(span.errorInfo.category && { category: span.errorInfo.category }),\n };\n }\n\n // Add tags as custom properties (PostHog doesn't have native tag support on traces)\n if (span.tags?.length) {\n for (const tag of span.tags) {\n traceProperties[tag] = true;\n }\n }\n\n // Add custom metadata (excluding userId and sessionId which are handled separately)\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n Object.assign(traceProperties, customMetadata);\n\n return {\n distinctId,\n event: '$ai_trace',\n properties: traceProperties,\n timestamp: new Date(endTime),\n };\n }\n\n private buildChildEventMessage(args: {\n span: AnyExportedSpan;\n distinctId: string;\n endTime: number;\n traceData: PosthogTraceData;\n }): EventMessage {\n const { span, distinctId, endTime, traceData } = args;\n\n const eventName = this.mapToPostHogEvent(span.type);\n const startTime = span.startTime.getTime();\n const latency = (endTime - startTime) / 1000;\n\n // Check if parent is the root span - if so, use traceId as parent_id\n // since we don't create an $ai_span for root spans\n const parentIsRootSpan = this.isParentRootSpan(span, traceData);\n const properties = this.buildEventProperties(span, latency, parentIsRootSpan);\n\n return {\n distinctId,\n event: eventName,\n properties,\n timestamp: new Date(endTime),\n };\n }\n\n private toDate(timestamp: Date | number): Date {\n return timestamp instanceof Date ? timestamp : new Date(timestamp);\n }\n\n private mapToPostHogEvent(spanType: SpanType): string {\n if (spanType == SpanType.MODEL_GENERATION) {\n return '$ai_generation';\n }\n return '$ai_span';\n }\n\n private getDistinctId(span: AnyExportedSpan, traceData?: PosthogTraceData): string {\n if (span.metadata?.userId) {\n return String(span.metadata.userId);\n }\n\n if (traceData?.hasExtraValue(DISTINCT_ID)) {\n return String(traceData.getExtraValue(DISTINCT_ID));\n }\n\n if (this.config.defaultDistinctId) {\n return this.config.defaultDistinctId;\n }\n\n return 'anonymous';\n }\n\n /**\n * Check if the parent of this span is the root span.\n * We need this because we don't create $ai_span for root spans,\n * so children of root spans should use $ai_trace_id as their $ai_parent_id.\n */\n private isParentRootSpan(span: AnyExportedSpan, traceData: PosthogTraceData): boolean {\n if (!span.parentSpanId) {\n return false;\n }\n\n // Look up the parent span in our cache to check if it's a root span\n const parentCache = traceData.getSpan({ spanId: span.parentSpanId });\n if (parentCache) {\n return parentCache.isRootSpan;\n }\n\n // Parent not found in cache - shouldn't happen normally, but default to false\n return false;\n }\n\n private buildEventProperties(\n span: AnyExportedSpan,\n latency: number,\n parentIsRootSpan: boolean = false,\n ): Record<string, any> {\n const baseProperties: Record<string, any> = {\n $ai_trace_id: span.traceId,\n $ai_latency: latency,\n $ai_is_error: !!span.errorInfo,\n };\n\n if (span.parentSpanId) {\n // If parent is the root span, use trace_id as parent_id since we don't\n // create an $ai_span for root spans (only $ai_trace)\n baseProperties.$ai_parent_id = parentIsRootSpan ? span.traceId : span.parentSpanId;\n }\n\n if (span.metadata?.sessionId) {\n baseProperties.$ai_session_id = span.metadata.sessionId;\n }\n\n // Include tags for root spans (tags are only set on root spans by design)\n // PostHog doesn't allow setting tags directly, so we iterate through each tag\n // and set it as a property with value true\n if (span.isRootSpan && span.tags?.length) {\n for (const tag of span.tags) {\n baseProperties[tag] = true;\n }\n }\n\n if (span.type === SpanType.MODEL_GENERATION) {\n baseProperties.$ai_generation_id = span.id;\n return { ...baseProperties, ...this.buildGenerationProperties(span) };\n } else {\n baseProperties.$ai_span_id = span.id;\n baseProperties.$ai_span_name = span.name;\n return { ...baseProperties, ...this.buildSpanProperties(span) };\n }\n }\n\n private extractErrorProperties(errorInfo?: SpanErrorInfo): Record<string, any> {\n if (!errorInfo) {\n return {};\n }\n\n const props: Record<string, string> = {\n error_message: errorInfo.message,\n };\n\n if (errorInfo.id) {\n props.error_id = errorInfo.id;\n }\n\n if (errorInfo.category) {\n props.error_category = errorInfo.category;\n }\n\n return props;\n }\n\n private extractCustomMetadata(span: AnyExportedSpan): Record<string, any> {\n const { userId, sessionId, ...customMetadata } = span.metadata ?? {};\n return customMetadata;\n }\n\n private buildGenerationProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n const attrs = (span.attributes ?? {}) as ModelGenerationAttributes;\n\n props.$ai_model = attrs.model || 'unknown-model';\n props.$ai_provider = attrs.provider || 'unknown-provider';\n\n if (span.input) props.$ai_input = this.formatMessages(span.input, 'user');\n if (span.output) props.$ai_output_choices = this.formatMessages(span.output, 'assistant');\n\n // Extract usage properties using the shared utility\n Object.assign(props, formatUsageMetrics(attrs.usage));\n\n if (attrs.parameters) {\n if (attrs.parameters.temperature !== undefined) props.$ai_temperature = attrs.parameters.temperature;\n if (attrs.parameters.maxOutputTokens !== undefined) props.$ai_max_tokens = attrs.parameters.maxOutputTokens;\n }\n if (attrs.streaming !== undefined) props.$ai_stream = attrs.streaming;\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span) };\n }\n\n private buildSpanProperties(span: AnyExportedSpan): Record<string, any> {\n const props: Record<string, any> = {};\n\n if (span.input) props.$ai_input_state = span.input;\n if (span.output) props.$ai_output_state = span.output;\n\n if (span.type === SpanType.MODEL_CHUNK) {\n const attrs = span.attributes as any;\n if (attrs?.chunkType) props.chunk_type = attrs.chunkType;\n if (attrs?.sequenceNumber !== undefined) props.chunk_sequence_number = attrs.sequenceNumber;\n }\n\n if (span.attributes) {\n Object.assign(props, span.attributes);\n }\n\n return { ...props, ...this.extractErrorProperties(span.errorInfo), ...this.extractCustomMetadata(span) };\n }\n\n private formatMessages(data: SpanData, defaultRole: 'user' | 'assistant' = 'user'): PostHogMessage[] {\n if (this.isMessageArray(data)) {\n return data.map(msg => this.normalizeMessage(msg));\n }\n\n if (typeof data === 'string') {\n return [{ role: defaultRole, content: [{ type: 'text', text: data }] }];\n }\n\n return [{ role: defaultRole, content: [{ type: 'text', text: this.safeStringify(data) }] }];\n }\n\n private isMessageArray(data: unknown): data is MastraMessage[] {\n if (!Array.isArray(data) || data.length === 0) {\n return false;\n }\n\n return data.every(item => typeof item === 'object' && item !== null && 'role' in item && 'content' in item);\n }\n\n private normalizeMessage(msg: MastraMessage): PostHogMessage {\n if (typeof msg.content === 'string') {\n return {\n role: msg.role as PostHogMessage['role'],\n content: [{ type: 'text', text: msg.content }],\n };\n }\n\n return {\n role: msg.role as PostHogMessage['role'],\n content: msg.content as PostHogContent[],\n };\n }\n\n private safeStringify(data: unknown): string {\n try {\n return JSON.stringify(data);\n } catch {\n if (typeof data === 'object' && data !== null) {\n return `[Non-serializable ${data.constructor?.name || 'Object'}]`;\n }\n return String(data);\n }\n }\n\n /**\n * Force flush any buffered data to PostHog without shutting down.\n */\n protected override async _flush(): Promise<void> {\n if (this.#client) {\n await this.#client.flush();\n }\n }\n\n override async _postShutdown(): Promise<void> {\n if (this.#client) {\n await this.#client.shutdown();\n }\n }\n}\n"]}
package/dist/tracing.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { TracingEvent, UsageStats } from '@mastra/core/observability';
2
- import type { BaseExporterConfig } from '@mastra/observability';
3
- import { BaseExporter } from '@mastra/observability';
1
+ import type { AnyExportedSpan, SpanErrorInfo, UsageStats } from '@mastra/core/observability';
2
+ import type { TraceData, TrackingExporterConfig } from '@mastra/observability';
3
+ import { TrackingExporter } from '@mastra/observability';
4
4
  /**
5
5
  * Token usage format compatible with PostHog.
6
6
  * @see https://posthog.com/docs/llm-analytics/generations#event-properties
@@ -18,8 +18,10 @@ export interface PostHogUsageMetrics {
18
18
  * @returns PostHog-formatted usage properties
19
19
  */
20
20
  export declare function formatUsageMetrics(usage?: UsageStats): PostHogUsageMetrics;
21
- export interface PosthogExporterConfig extends BaseExporterConfig {
22
- apiKey: string;
21
+ export interface PosthogExporterConfig extends TrackingExporterConfig {
22
+ /** PostHog API key. Defaults to POSTHOG_API_KEY environment variable. */
23
+ apiKey?: string;
24
+ /** PostHog host URL. Defaults to POSTHOG_HOST environment variable or US region. */
23
25
  host?: string;
24
26
  flushAt?: number;
25
27
  flushInterval?: number;
@@ -27,29 +29,56 @@ export interface PosthogExporterConfig extends BaseExporterConfig {
27
29
  defaultDistinctId?: string;
28
30
  enablePrivacyMode?: boolean;
29
31
  }
30
- export declare class PosthogExporter extends BaseExporter {
32
+ type PosthogRoot = unknown;
33
+ type PosthogSpan = AnyExportedSpan;
34
+ type PosthogEvent = boolean;
35
+ type PosthogMetadata = unknown;
36
+ type PosthogTraceData = TraceData<PosthogRoot, PosthogSpan, PosthogEvent, PosthogMetadata>;
37
+ export declare class PosthogExporter extends TrackingExporter<PosthogRoot, PosthogSpan, PosthogEvent, PosthogMetadata, PosthogExporterConfig> {
38
+ #private;
31
39
  name: string;
32
- private client;
33
- private config;
34
- private traceMap;
35
40
  private static readonly SERVERLESS_FLUSH_AT;
36
41
  private static readonly SERVERLESS_FLUSH_INTERVAL;
37
42
  private static readonly DEFAULT_FLUSH_AT;
38
43
  private static readonly DEFAULT_FLUSH_INTERVAL;
39
- constructor(config: PosthogExporterConfig);
44
+ constructor(config?: PosthogExporterConfig);
40
45
  private buildClientConfig;
41
- private logInitialization;
42
- protected _exportTracingEvent(event: TracingEvent): Promise<void>;
43
- private handleSpanStarted;
44
- private handleSpanEnded;
46
+ protected skipBuildRootTask: boolean;
47
+ protected _buildRoot(_args: {
48
+ span: AnyExportedSpan;
49
+ traceData: PosthogTraceData;
50
+ }): Promise<PosthogRoot | undefined>;
51
+ protected skipCachingEventSpans: boolean;
52
+ protected _buildEvent(args: {
53
+ span: AnyExportedSpan;
54
+ traceData: PosthogTraceData;
55
+ }): Promise<PosthogEvent>;
56
+ protected _buildSpan(args: {
57
+ span: AnyExportedSpan;
58
+ traceData: PosthogTraceData;
59
+ }): Promise<PosthogSpan | undefined>;
60
+ protected skipSpanUpdateEvents: boolean;
61
+ protected _updateSpan(_args: {
62
+ span: AnyExportedSpan;
63
+ traceData: PosthogTraceData;
64
+ }): Promise<void>;
65
+ protected _finishSpan(args: {
66
+ span: AnyExportedSpan;
67
+ traceData: PosthogTraceData;
68
+ }): Promise<void>;
69
+ protected _abortSpan(args: {
70
+ span: PosthogSpan;
71
+ reason: SpanErrorInfo;
72
+ traceData: PosthogTraceData;
73
+ }): Promise<void>;
74
+ private buildEventMessage;
45
75
  /**
46
76
  * Capture an explicit $ai_trace event for root spans.
47
77
  * This gives us control over trace-level metadata like name and tags,
48
78
  * rather than relying on PostHog's pseudo-trace auto-creation.
49
79
  */
50
- private captureTraceEvent;
51
- private captureEventSpan;
52
- shutdown(): Promise<void>;
80
+ private buildRootEventMessage;
81
+ private buildChildEventMessage;
53
82
  private toDate;
54
83
  private mapToPostHogEvent;
55
84
  private getDistinctId;
@@ -68,5 +97,11 @@ export declare class PosthogExporter extends BaseExporter {
68
97
  private isMessageArray;
69
98
  private normalizeMessage;
70
99
  private safeStringify;
100
+ /**
101
+ * Force flush any buffered data to PostHog without shutting down.
102
+ */
103
+ protected _flush(): Promise<void>;
104
+ _postShutdown(): Promise<void>;
71
105
  }
106
+ export {};
72
107
  //# sourceMappingURL=tracing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA8C,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAEvH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,+BAA+B,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,mBAAmB,CAgB1E;AA0BD,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAaD,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,IAAI,SAAa;IACjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,QAAQ,CAAoC;IAEpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAM;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAQ;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAE3C,MAAM,EAAE,qBAAqB;IAezC,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,iBAAiB;cAQT,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YA4BzD,iBAAiB;YAyBjB,eAAe;IA4C7B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;YAgDX,gBAAgB;IAexB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAS/B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,aAAa;IAgBrB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,oBAAoB;IAwC5B,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,yBAAyB;IAsBjC,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,aAAa;CAUtB"}
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/posthog",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0",
4
4
  "description": "PostHog observability provider for Mastra",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,17 +25,18 @@
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
27
  "posthog-node": "^4.0.1",
28
- "@mastra/observability": "1.0.0-beta.9"
28
+ "@mastra/observability": "1.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^20.19.0",
32
32
  "eslint": "^9.37.0",
33
33
  "tsup": "^8.5.0",
34
- "typescript": "^5.8.3",
34
+ "typescript": "^5.9.3",
35
35
  "vitest": "^3.2.4",
36
- "@internal/lint": "0.0.53",
37
- "@mastra/core": "1.0.0-beta.18",
38
- "@internal/types-builder": "0.0.28"
36
+ "@internal/types-builder": "0.0.29",
37
+ "@mastra/core": "1.0.0",
38
+ "@observability/test-utils": "0.0.1",
39
+ "@internal/lint": "0.0.54"
39
40
  },
40
41
  "peerDependencies": {
41
42
  "@mastra/core": ">=1.0.0-0 <2.0.0-0"