@fallom/trace 0.1.10 → 0.1.11

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.d.mts CHANGED
@@ -1,3 +1,6 @@
1
+ import { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';
2
+ import { ExportResult } from '@opentelemetry/core';
3
+
1
4
  /**
2
5
  * Fallom tracing module.
3
6
  *
@@ -222,6 +225,31 @@ declare function wrapAISDK<T extends {
222
225
  generateObject: T["generateObject"];
223
226
  streamObject: T["streamObject"];
224
227
  };
228
+ /**
229
+ * Wrap a Mastra agent to automatically trace all generate() calls.
230
+ *
231
+ * @param agent - The Mastra Agent instance
232
+ * @returns The same agent with tracing enabled
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * import { trace } from "@fallom/trace";
237
+ * import { Agent } from "@mastra/core";
238
+ *
239
+ * await trace.init({ apiKey: "your-key" });
240
+ *
241
+ * const agent = new Agent({ ... });
242
+ * const tracedAgent = trace.wrapMastraAgent(agent);
243
+ *
244
+ * trace.setSession("my-app", "session-123", "user-456");
245
+ * const result = await tracedAgent.generate([{ role: "user", content: "Hello" }]);
246
+ * // ^ Automatically traced!
247
+ * ```
248
+ */
249
+ declare function wrapMastraAgent<T extends {
250
+ generate: (...args: any[]) => Promise<any>;
251
+ name?: string;
252
+ }>(agent: T): T;
225
253
 
226
254
  declare const trace_clearSession: typeof clearSession;
227
255
  declare const trace_getSession: typeof getSession;
@@ -232,9 +260,10 @@ declare const trace_span: typeof span;
232
260
  declare const trace_wrapAISDK: typeof wrapAISDK;
233
261
  declare const trace_wrapAnthropic: typeof wrapAnthropic;
234
262
  declare const trace_wrapGoogleAI: typeof wrapGoogleAI;
263
+ declare const trace_wrapMastraAgent: typeof wrapMastraAgent;
235
264
  declare const trace_wrapOpenAI: typeof wrapOpenAI;
236
265
  declare namespace trace {
237
- export { trace_clearSession as clearSession, trace_getSession as getSession, init$3 as init, trace_runWithSession as runWithSession, trace_setSession as setSession, trace_shutdown as shutdown, trace_span as span, trace_wrapAISDK as wrapAISDK, trace_wrapAnthropic as wrapAnthropic, trace_wrapGoogleAI as wrapGoogleAI, trace_wrapOpenAI as wrapOpenAI };
266
+ export { trace_clearSession as clearSession, trace_getSession as getSession, init$3 as init, trace_runWithSession as runWithSession, trace_setSession as setSession, trace_shutdown as shutdown, trace_span as span, trace_wrapAISDK as wrapAISDK, trace_wrapAnthropic as wrapAnthropic, trace_wrapGoogleAI as wrapGoogleAI, trace_wrapMastraAgent as wrapMastraAgent, trace_wrapOpenAI as wrapOpenAI };
238
267
  }
239
268
 
240
269
  /**
@@ -444,6 +473,112 @@ interface InitOptions {
444
473
  */
445
474
  declare function init(options?: InitOptions): Promise<void>;
446
475
 
476
+ /**
477
+ * Fallom Exporter for Mastra
478
+ *
479
+ * Custom OpenTelemetry exporter that sends traces from Mastra agents to Fallom.
480
+ * Reads session context from the shared trace module (set via trace.setSession()).
481
+ *
482
+ * Usage with Mastra:
483
+ * ```typescript
484
+ * import { trace, FallomExporter } from "@fallom/trace";
485
+ * import { Mastra } from "@mastra/core/mastra";
486
+ *
487
+ * // Initialize trace module
488
+ * await trace.init({ apiKey: process.env.FALLOM_API_KEY });
489
+ *
490
+ * // Create Mastra with Fallom exporter
491
+ * const mastra = new Mastra({
492
+ * agents: { myAgent },
493
+ * telemetry: {
494
+ * serviceName: "my-agent",
495
+ * enabled: true,
496
+ * export: {
497
+ * type: "custom",
498
+ * exporter: new FallomExporter(),
499
+ * },
500
+ * },
501
+ * });
502
+ *
503
+ * // In your request handler:
504
+ * trace.setSession("my-app", "session-123", "user-456");
505
+ * const result = await mastra.getAgent("myAgent").generate("Hello!");
506
+ * ```
507
+ */
508
+
509
+ interface FallomExporterOptions {
510
+ /** Fallom API key. Defaults to FALLOM_API_KEY env var. */
511
+ apiKey?: string;
512
+ /** Base URL for traces endpoint (defaults to https://traces.fallom.com) */
513
+ baseUrl?: string;
514
+ /** Enable debug logging */
515
+ debug?: boolean;
516
+ }
517
+ /**
518
+ * Set prompt tracking info.
519
+ * Call this after prompts.get() to track which prompt was used.
520
+ */
521
+ declare function setMastraPrompt(promptKey: string, version?: number): void;
522
+ /**
523
+ * Set A/B test prompt tracking info.
524
+ * Call this after prompts.getAB() to track which variant was used.
525
+ */
526
+ declare function setMastraPromptAB(abTestKey: string, variantIndex: number): void;
527
+ /**
528
+ * Clear prompt tracking info.
529
+ */
530
+ declare function clearMastraPrompt(): void;
531
+ /**
532
+ * OpenTelemetry SpanExporter that sends traces to Fallom.
533
+ *
534
+ * Reads session context from trace.setSession() automatically.
535
+ * Compatible with Mastra's custom exporter interface.
536
+ */
537
+ declare class FallomExporter implements SpanExporter {
538
+ private apiKey;
539
+ private baseUrl;
540
+ private debug;
541
+ private pendingExports;
542
+ constructor(options?: FallomExporterOptions);
543
+ private log;
544
+ /**
545
+ * Export spans to Fallom.
546
+ */
547
+ export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
548
+ /**
549
+ * Shutdown the exporter, waiting for pending exports.
550
+ */
551
+ shutdown(): Promise<void>;
552
+ /**
553
+ * Force flush pending exports.
554
+ */
555
+ forceFlush(): Promise<void>;
556
+ /**
557
+ * Send spans to Fallom's OTLP endpoint.
558
+ */
559
+ private sendSpans;
560
+ /**
561
+ * Convert OpenTelemetry spans to OTLP JSON format.
562
+ */
563
+ private spansToOtlpJson;
564
+ /**
565
+ * Convert a single span to OTLP format.
566
+ */
567
+ private spanToOtlp;
568
+ /**
569
+ * Convert attributes to OTLP format.
570
+ */
571
+ private attributesToOtlp;
572
+ /**
573
+ * Convert a value to OTLP AnyValue format.
574
+ */
575
+ private valueToOtlp;
576
+ /**
577
+ * Convert HrTime to nanoseconds string.
578
+ */
579
+ private hrTimeToNanos;
580
+ }
581
+
447
582
  /**
448
583
  * Fallom - Model A/B testing, prompt management, and tracing for LLM applications.
449
584
  *
@@ -488,4 +623,4 @@ declare const _default: {
488
623
  prompts: typeof prompts;
489
624
  };
490
625
 
491
- export { type InitOptions, type PromptResult, _default as default, init, models, prompts, trace };
626
+ export { FallomExporter, type FallomExporterOptions, type InitOptions, type PromptResult, clearMastraPrompt, _default as default, init, models, prompts, setMastraPrompt, setMastraPromptAB, trace };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';
2
+ import { ExportResult } from '@opentelemetry/core';
3
+
1
4
  /**
2
5
  * Fallom tracing module.
3
6
  *
@@ -222,6 +225,31 @@ declare function wrapAISDK<T extends {
222
225
  generateObject: T["generateObject"];
223
226
  streamObject: T["streamObject"];
224
227
  };
228
+ /**
229
+ * Wrap a Mastra agent to automatically trace all generate() calls.
230
+ *
231
+ * @param agent - The Mastra Agent instance
232
+ * @returns The same agent with tracing enabled
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * import { trace } from "@fallom/trace";
237
+ * import { Agent } from "@mastra/core";
238
+ *
239
+ * await trace.init({ apiKey: "your-key" });
240
+ *
241
+ * const agent = new Agent({ ... });
242
+ * const tracedAgent = trace.wrapMastraAgent(agent);
243
+ *
244
+ * trace.setSession("my-app", "session-123", "user-456");
245
+ * const result = await tracedAgent.generate([{ role: "user", content: "Hello" }]);
246
+ * // ^ Automatically traced!
247
+ * ```
248
+ */
249
+ declare function wrapMastraAgent<T extends {
250
+ generate: (...args: any[]) => Promise<any>;
251
+ name?: string;
252
+ }>(agent: T): T;
225
253
 
226
254
  declare const trace_clearSession: typeof clearSession;
227
255
  declare const trace_getSession: typeof getSession;
@@ -232,9 +260,10 @@ declare const trace_span: typeof span;
232
260
  declare const trace_wrapAISDK: typeof wrapAISDK;
233
261
  declare const trace_wrapAnthropic: typeof wrapAnthropic;
234
262
  declare const trace_wrapGoogleAI: typeof wrapGoogleAI;
263
+ declare const trace_wrapMastraAgent: typeof wrapMastraAgent;
235
264
  declare const trace_wrapOpenAI: typeof wrapOpenAI;
236
265
  declare namespace trace {
237
- export { trace_clearSession as clearSession, trace_getSession as getSession, init$3 as init, trace_runWithSession as runWithSession, trace_setSession as setSession, trace_shutdown as shutdown, trace_span as span, trace_wrapAISDK as wrapAISDK, trace_wrapAnthropic as wrapAnthropic, trace_wrapGoogleAI as wrapGoogleAI, trace_wrapOpenAI as wrapOpenAI };
266
+ export { trace_clearSession as clearSession, trace_getSession as getSession, init$3 as init, trace_runWithSession as runWithSession, trace_setSession as setSession, trace_shutdown as shutdown, trace_span as span, trace_wrapAISDK as wrapAISDK, trace_wrapAnthropic as wrapAnthropic, trace_wrapGoogleAI as wrapGoogleAI, trace_wrapMastraAgent as wrapMastraAgent, trace_wrapOpenAI as wrapOpenAI };
238
267
  }
239
268
 
240
269
  /**
@@ -444,6 +473,112 @@ interface InitOptions {
444
473
  */
445
474
  declare function init(options?: InitOptions): Promise<void>;
446
475
 
476
+ /**
477
+ * Fallom Exporter for Mastra
478
+ *
479
+ * Custom OpenTelemetry exporter that sends traces from Mastra agents to Fallom.
480
+ * Reads session context from the shared trace module (set via trace.setSession()).
481
+ *
482
+ * Usage with Mastra:
483
+ * ```typescript
484
+ * import { trace, FallomExporter } from "@fallom/trace";
485
+ * import { Mastra } from "@mastra/core/mastra";
486
+ *
487
+ * // Initialize trace module
488
+ * await trace.init({ apiKey: process.env.FALLOM_API_KEY });
489
+ *
490
+ * // Create Mastra with Fallom exporter
491
+ * const mastra = new Mastra({
492
+ * agents: { myAgent },
493
+ * telemetry: {
494
+ * serviceName: "my-agent",
495
+ * enabled: true,
496
+ * export: {
497
+ * type: "custom",
498
+ * exporter: new FallomExporter(),
499
+ * },
500
+ * },
501
+ * });
502
+ *
503
+ * // In your request handler:
504
+ * trace.setSession("my-app", "session-123", "user-456");
505
+ * const result = await mastra.getAgent("myAgent").generate("Hello!");
506
+ * ```
507
+ */
508
+
509
+ interface FallomExporterOptions {
510
+ /** Fallom API key. Defaults to FALLOM_API_KEY env var. */
511
+ apiKey?: string;
512
+ /** Base URL for traces endpoint (defaults to https://traces.fallom.com) */
513
+ baseUrl?: string;
514
+ /** Enable debug logging */
515
+ debug?: boolean;
516
+ }
517
+ /**
518
+ * Set prompt tracking info.
519
+ * Call this after prompts.get() to track which prompt was used.
520
+ */
521
+ declare function setMastraPrompt(promptKey: string, version?: number): void;
522
+ /**
523
+ * Set A/B test prompt tracking info.
524
+ * Call this after prompts.getAB() to track which variant was used.
525
+ */
526
+ declare function setMastraPromptAB(abTestKey: string, variantIndex: number): void;
527
+ /**
528
+ * Clear prompt tracking info.
529
+ */
530
+ declare function clearMastraPrompt(): void;
531
+ /**
532
+ * OpenTelemetry SpanExporter that sends traces to Fallom.
533
+ *
534
+ * Reads session context from trace.setSession() automatically.
535
+ * Compatible with Mastra's custom exporter interface.
536
+ */
537
+ declare class FallomExporter implements SpanExporter {
538
+ private apiKey;
539
+ private baseUrl;
540
+ private debug;
541
+ private pendingExports;
542
+ constructor(options?: FallomExporterOptions);
543
+ private log;
544
+ /**
545
+ * Export spans to Fallom.
546
+ */
547
+ export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
548
+ /**
549
+ * Shutdown the exporter, waiting for pending exports.
550
+ */
551
+ shutdown(): Promise<void>;
552
+ /**
553
+ * Force flush pending exports.
554
+ */
555
+ forceFlush(): Promise<void>;
556
+ /**
557
+ * Send spans to Fallom's OTLP endpoint.
558
+ */
559
+ private sendSpans;
560
+ /**
561
+ * Convert OpenTelemetry spans to OTLP JSON format.
562
+ */
563
+ private spansToOtlpJson;
564
+ /**
565
+ * Convert a single span to OTLP format.
566
+ */
567
+ private spanToOtlp;
568
+ /**
569
+ * Convert attributes to OTLP format.
570
+ */
571
+ private attributesToOtlp;
572
+ /**
573
+ * Convert a value to OTLP AnyValue format.
574
+ */
575
+ private valueToOtlp;
576
+ /**
577
+ * Convert HrTime to nanoseconds string.
578
+ */
579
+ private hrTimeToNanos;
580
+ }
581
+
447
582
  /**
448
583
  * Fallom - Model A/B testing, prompt management, and tracing for LLM applications.
449
584
  *
@@ -488,4 +623,4 @@ declare const _default: {
488
623
  prompts: typeof prompts;
489
624
  };
490
625
 
491
- export { type InitOptions, type PromptResult, _default as default, init, models, prompts, trace };
626
+ export { FallomExporter, type FallomExporterOptions, type InitOptions, type PromptResult, clearMastraPrompt, _default as default, init, models, prompts, setMastraPrompt, setMastraPromptAB, trace };
package/dist/index.js CHANGED
@@ -269,10 +269,14 @@ var init_prompts = __esm({
269
269
  // src/index.ts
270
270
  var index_exports = {};
271
271
  __export(index_exports, {
272
+ FallomExporter: () => FallomExporter,
273
+ clearMastraPrompt: () => clearMastraPrompt,
272
274
  default: () => index_default,
273
275
  init: () => init4,
274
276
  models: () => models_exports,
275
277
  prompts: () => prompts_exports,
278
+ setMastraPrompt: () => setMastraPrompt,
279
+ setMastraPromptAB: () => setMastraPromptAB,
276
280
  trace: () => trace_exports
277
281
  });
278
282
  module.exports = __toCommonJS(index_exports);
@@ -290,6 +294,7 @@ __export(trace_exports, {
290
294
  wrapAISDK: () => wrapAISDK,
291
295
  wrapAnthropic: () => wrapAnthropic,
292
296
  wrapGoogleAI: () => wrapGoogleAI,
297
+ wrapMastraAgent: () => wrapMastraAgent,
293
298
  wrapOpenAI: () => wrapOpenAI
294
299
  });
295
300
  var import_async_hooks = require("async_hooks");
@@ -299,7 +304,7 @@ var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otl
299
304
  // node_modules/@opentelemetry/resources/build/esm/Resource.js
300
305
  var import_api = require("@opentelemetry/api");
301
306
 
302
- // node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
307
+ // node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
303
308
  var SemanticResourceAttributes = {
304
309
  /**
305
310
  * Name of the cloud provider.
@@ -679,35 +684,9 @@ var SemanticResourceAttributes = {
679
684
  */
680
685
  WEBENGINE_DESCRIPTION: "webengine.description"
681
686
  };
682
- var TelemetrySdkLanguageValues = {
683
- /** cpp. */
684
- CPP: "cpp",
685
- /** dotnet. */
686
- DOTNET: "dotnet",
687
- /** erlang. */
688
- ERLANG: "erlang",
689
- /** go. */
690
- GO: "go",
691
- /** java. */
692
- JAVA: "java",
693
- /** nodejs. */
694
- NODEJS: "nodejs",
695
- /** php. */
696
- PHP: "php",
697
- /** python. */
698
- PYTHON: "python",
699
- /** ruby. */
700
- RUBY: "ruby",
701
- /** webjs. */
702
- WEBJS: "webjs"
703
- };
704
-
705
- // node_modules/@opentelemetry/core/build/esm/version.js
706
- var VERSION = "1.19.0";
707
687
 
708
- // node_modules/@opentelemetry/core/build/esm/platform/node/sdk-info.js
709
- var _a;
710
- var SDK_INFO = (_a = {}, _a[SemanticResourceAttributes.TELEMETRY_SDK_NAME] = "opentelemetry", _a[SemanticResourceAttributes.PROCESS_RUNTIME_NAME] = "node", _a[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE] = TelemetrySdkLanguageValues.NODEJS, _a[SemanticResourceAttributes.TELEMETRY_SDK_VERSION] = VERSION, _a);
688
+ // node_modules/@opentelemetry/resources/build/esm/Resource.js
689
+ var import_core = require("@opentelemetry/core");
711
690
 
712
691
  // node_modules/@opentelemetry/resources/build/esm/platform/node/default-service-name.js
713
692
  function defaultServiceName() {
@@ -844,10 +823,10 @@ var Resource = (
844
823
  (function() {
845
824
  function Resource2(attributes, asyncAttributesPromise) {
846
825
  var _this = this;
847
- var _a2;
826
+ var _a;
848
827
  this._attributes = attributes;
849
828
  this.asyncAttributesPending = asyncAttributesPromise != null;
850
- this._syncAttributes = (_a2 = this._attributes) !== null && _a2 !== void 0 ? _a2 : {};
829
+ this._syncAttributes = (_a = this._attributes) !== null && _a !== void 0 ? _a : {};
851
830
  this._asyncAttributesPromise = asyncAttributesPromise === null || asyncAttributesPromise === void 0 ? void 0 : asyncAttributesPromise.then(function(asyncAttributes) {
852
831
  _this._attributes = Object.assign({}, _this._attributes, asyncAttributes);
853
832
  _this.asyncAttributesPending = false;
@@ -862,30 +841,30 @@ var Resource = (
862
841
  return Resource2.EMPTY;
863
842
  };
864
843
  Resource2.default = function() {
865
- var _a2;
866
- return new Resource2((_a2 = {}, _a2[SemanticResourceAttributes.SERVICE_NAME] = defaultServiceName(), _a2[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE], _a2[SemanticResourceAttributes.TELEMETRY_SDK_NAME] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_NAME], _a2[SemanticResourceAttributes.TELEMETRY_SDK_VERSION] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_VERSION], _a2));
844
+ var _a;
845
+ return new Resource2((_a = {}, _a[SemanticResourceAttributes.SERVICE_NAME] = defaultServiceName(), _a[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE] = import_core.SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE], _a[SemanticResourceAttributes.TELEMETRY_SDK_NAME] = import_core.SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_NAME], _a[SemanticResourceAttributes.TELEMETRY_SDK_VERSION] = import_core.SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_VERSION], _a));
867
846
  };
868
847
  Object.defineProperty(Resource2.prototype, "attributes", {
869
848
  get: function() {
870
- var _a2;
849
+ var _a;
871
850
  if (this.asyncAttributesPending) {
872
851
  import_api.diag.error("Accessing resource attributes before async attributes settled");
873
852
  }
874
- return (_a2 = this._attributes) !== null && _a2 !== void 0 ? _a2 : {};
853
+ return (_a = this._attributes) !== null && _a !== void 0 ? _a : {};
875
854
  },
876
855
  enumerable: false,
877
856
  configurable: true
878
857
  });
879
858
  Resource2.prototype.waitForAsyncAttributes = function() {
880
859
  return __awaiter(this, void 0, void 0, function() {
881
- return __generator(this, function(_a2) {
882
- switch (_a2.label) {
860
+ return __generator(this, function(_a) {
861
+ switch (_a.label) {
883
862
  case 0:
884
863
  if (!this.asyncAttributesPending) return [3, 2];
885
864
  return [4, this._asyncAttributesPromise];
886
865
  case 1:
887
- _a2.sent();
888
- _a2.label = 2;
866
+ _a.sent();
867
+ _a.label = 2;
889
868
  case 2:
890
869
  return [
891
870
  2
@@ -897,19 +876,19 @@ var Resource = (
897
876
  };
898
877
  Resource2.prototype.merge = function(other) {
899
878
  var _this = this;
900
- var _a2;
879
+ var _a;
901
880
  if (!other)
902
881
  return this;
903
- var mergedSyncAttributes = __assign(__assign({}, this._syncAttributes), (_a2 = other._syncAttributes) !== null && _a2 !== void 0 ? _a2 : other.attributes);
882
+ var mergedSyncAttributes = __assign(__assign({}, this._syncAttributes), (_a = other._syncAttributes) !== null && _a !== void 0 ? _a : other.attributes);
904
883
  if (!this._asyncAttributesPromise && !other._asyncAttributesPromise) {
905
884
  return new Resource2(mergedSyncAttributes);
906
885
  }
907
886
  var mergedAttributesPromise = Promise.all([
908
887
  this._asyncAttributesPromise,
909
888
  other._asyncAttributesPromise
910
- ]).then(function(_a3) {
889
+ ]).then(function(_a2) {
911
890
  var _b;
912
- var _c = __read(_a3, 2), thisAsyncAttributes = _c[0], otherAsyncAttributes = _c[1];
891
+ var _c = __read(_a2, 2), thisAsyncAttributes = _c[0], otherAsyncAttributes = _c[1];
913
892
  return __assign(__assign(__assign(__assign({}, _this._syncAttributes), thisAsyncAttributes), (_b = other._syncAttributes) !== null && _b !== void 0 ? _b : other.attributes), otherAsyncAttributes);
914
893
  });
915
894
  return new Resource2(mergedSyncAttributes, mergedAttributesPromise);
@@ -1893,6 +1872,127 @@ function createStreamObjectWrapper(aiModule) {
1893
1872
  return result;
1894
1873
  };
1895
1874
  }
1875
+ function wrapMastraAgent(agent) {
1876
+ const originalGenerate = agent.generate.bind(agent);
1877
+ const agentName = agent.name || "MastraAgent";
1878
+ agent.generate = async function(...args) {
1879
+ const ctx = sessionStorage.getStore() || fallbackSession;
1880
+ if (!ctx || !initialized2) {
1881
+ return originalGenerate(...args);
1882
+ }
1883
+ let promptCtx = null;
1884
+ try {
1885
+ const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
1886
+ promptCtx = getPromptContext2();
1887
+ } catch {
1888
+ }
1889
+ const traceId = generateHexId(32);
1890
+ const spanId = generateHexId(16);
1891
+ const startTime = Date.now();
1892
+ const messages = args[0] || [];
1893
+ try {
1894
+ const result = await originalGenerate(...args);
1895
+ const endTime = Date.now();
1896
+ const model = result?.model?.modelId || "unknown";
1897
+ const toolCalls = [];
1898
+ if (result?.steps?.length) {
1899
+ for (const step of result.steps) {
1900
+ if (step.toolCalls?.length) {
1901
+ for (let i = 0; i < step.toolCalls.length; i++) {
1902
+ const tc = step.toolCalls[i];
1903
+ const tr = step.toolResults?.[i];
1904
+ toolCalls.push({
1905
+ name: tc.toolName,
1906
+ arguments: tc.args,
1907
+ result: tr?.result
1908
+ });
1909
+ }
1910
+ }
1911
+ }
1912
+ }
1913
+ const attributes = {
1914
+ "gen_ai.system": "Mastra",
1915
+ "gen_ai.request.model": model,
1916
+ "gen_ai.response.model": model,
1917
+ "fallom.source": "mastra-agent",
1918
+ "llm.request.type": "chat"
1919
+ };
1920
+ if (Array.isArray(messages)) {
1921
+ messages.forEach((msg, i) => {
1922
+ attributes[`gen_ai.prompt.${i}.role`] = msg.role || "user";
1923
+ attributes[`gen_ai.prompt.${i}.content`] = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1924
+ });
1925
+ }
1926
+ if (result?.text) {
1927
+ attributes["gen_ai.completion.0.role"] = "assistant";
1928
+ attributes["gen_ai.completion.0.content"] = result.text;
1929
+ attributes["gen_ai.completion.0.finish_reason"] = "stop";
1930
+ }
1931
+ if (toolCalls.length > 0) {
1932
+ attributes["fallom.tool_calls"] = JSON.stringify(toolCalls);
1933
+ toolCalls.forEach((tc, i) => {
1934
+ attributes[`gen_ai.completion.0.tool_calls.${i}.name`] = tc.name;
1935
+ attributes[`gen_ai.completion.0.tool_calls.${i}.type`] = "function";
1936
+ attributes[`gen_ai.completion.0.tool_calls.${i}.arguments`] = JSON.stringify(tc.arguments);
1937
+ });
1938
+ }
1939
+ if (result?.usage) {
1940
+ attributes["gen_ai.usage.prompt_tokens"] = result.usage.promptTokens;
1941
+ attributes["gen_ai.usage.completion_tokens"] = result.usage.completionTokens;
1942
+ attributes["llm.usage.total_tokens"] = result.usage.totalTokens;
1943
+ }
1944
+ const traceData = {
1945
+ config_key: ctx.configKey,
1946
+ session_id: ctx.sessionId,
1947
+ customer_id: ctx.customerId,
1948
+ trace_id: traceId,
1949
+ span_id: spanId,
1950
+ name: `mastra.${agentName}.generate`,
1951
+ kind: "client",
1952
+ model,
1953
+ start_time: new Date(startTime).toISOString(),
1954
+ end_time: new Date(endTime).toISOString(),
1955
+ duration_ms: endTime - startTime,
1956
+ status: "OK",
1957
+ prompt_tokens: result?.usage?.promptTokens,
1958
+ completion_tokens: result?.usage?.completionTokens,
1959
+ total_tokens: result?.usage?.totalTokens,
1960
+ attributes,
1961
+ prompt_key: promptCtx?.promptKey,
1962
+ prompt_version: promptCtx?.promptVersion,
1963
+ prompt_ab_test_key: promptCtx?.abTestKey,
1964
+ prompt_variant_index: promptCtx?.variantIndex
1965
+ };
1966
+ sendTrace(traceData).catch(() => {
1967
+ });
1968
+ return result;
1969
+ } catch (error) {
1970
+ const endTime = Date.now();
1971
+ const traceData = {
1972
+ config_key: ctx.configKey,
1973
+ session_id: ctx.sessionId,
1974
+ customer_id: ctx.customerId,
1975
+ trace_id: traceId,
1976
+ span_id: spanId,
1977
+ name: `mastra.${agentName}.generate`,
1978
+ kind: "client",
1979
+ start_time: new Date(startTime).toISOString(),
1980
+ end_time: new Date(endTime).toISOString(),
1981
+ duration_ms: endTime - startTime,
1982
+ status: "ERROR",
1983
+ error_message: error instanceof Error ? error.message : String(error),
1984
+ prompt_key: promptCtx?.promptKey,
1985
+ prompt_version: promptCtx?.promptVersion,
1986
+ prompt_ab_test_key: promptCtx?.abTestKey,
1987
+ prompt_variant_index: promptCtx?.variantIndex
1988
+ };
1989
+ sendTrace(traceData).catch(() => {
1990
+ });
1991
+ throw error;
1992
+ }
1993
+ };
1994
+ return agent;
1995
+ }
1896
1996
 
1897
1997
  // src/models.ts
1898
1998
  var models_exports = {};
@@ -2162,6 +2262,241 @@ async function init4(options = {}) {
2162
2262
  });
2163
2263
  }
2164
2264
 
2265
+ // src/mastra.ts
2266
+ var import_core2 = require("@opentelemetry/core");
2267
+ var promptContext2 = {};
2268
+ function setMastraPrompt(promptKey, version) {
2269
+ promptContext2 = {
2270
+ promptKey,
2271
+ promptVersion: version,
2272
+ promptAbTestKey: void 0,
2273
+ promptVariantIndex: void 0
2274
+ };
2275
+ }
2276
+ function setMastraPromptAB(abTestKey, variantIndex) {
2277
+ promptContext2 = {
2278
+ promptKey: void 0,
2279
+ promptVersion: void 0,
2280
+ promptAbTestKey: abTestKey,
2281
+ promptVariantIndex: variantIndex
2282
+ };
2283
+ }
2284
+ function clearMastraPrompt() {
2285
+ promptContext2 = {};
2286
+ }
2287
+ var FallomExporter = class {
2288
+ constructor(options = {}) {
2289
+ this.pendingExports = [];
2290
+ this.apiKey = options.apiKey ?? process.env.FALLOM_API_KEY ?? "";
2291
+ this.baseUrl = options.baseUrl ?? "https://traces.fallom.com";
2292
+ this.debug = options.debug ?? false;
2293
+ console.log("[FallomExporter] Constructor called, debug:", this.debug);
2294
+ console.log("[FallomExporter] API key present:", !!this.apiKey);
2295
+ console.log("[FallomExporter] Base URL:", this.baseUrl);
2296
+ if (!this.apiKey) {
2297
+ console.warn(
2298
+ "[FallomExporter] No API key provided. Set FALLOM_API_KEY env var or pass apiKey option."
2299
+ );
2300
+ }
2301
+ }
2302
+ log(...args) {
2303
+ if (this.debug) {
2304
+ console.log("[FallomExporter]", ...args);
2305
+ }
2306
+ }
2307
+ /**
2308
+ * Export spans to Fallom.
2309
+ */
2310
+ export(spans, resultCallback) {
2311
+ if (spans.length === 0) {
2312
+ resultCallback({ code: import_core2.ExportResultCode.SUCCESS });
2313
+ return;
2314
+ }
2315
+ this.log(`Exporting ${spans.length} spans...`);
2316
+ if (this.debug) {
2317
+ for (const span2 of spans) {
2318
+ this.log(` - ${span2.name}`, {
2319
+ attributes: Object.fromEntries(
2320
+ Object.entries(span2.attributes).filter(
2321
+ ([k]) => k.startsWith("gen_ai") || k.startsWith("llm")
2322
+ )
2323
+ )
2324
+ });
2325
+ }
2326
+ }
2327
+ const exportPromise = this.sendSpans(spans).then(() => {
2328
+ this.log("Export successful");
2329
+ resultCallback({ code: import_core2.ExportResultCode.SUCCESS });
2330
+ }).catch((error) => {
2331
+ console.error("[FallomExporter] Export failed:", error);
2332
+ resultCallback({
2333
+ code: import_core2.ExportResultCode.FAILED,
2334
+ error: error instanceof Error ? error : new Error(String(error))
2335
+ });
2336
+ });
2337
+ this.pendingExports.push(exportPromise);
2338
+ }
2339
+ /**
2340
+ * Shutdown the exporter, waiting for pending exports.
2341
+ */
2342
+ async shutdown() {
2343
+ await Promise.all(this.pendingExports);
2344
+ this.pendingExports = [];
2345
+ }
2346
+ /**
2347
+ * Force flush pending exports.
2348
+ */
2349
+ async forceFlush() {
2350
+ await Promise.all(this.pendingExports);
2351
+ }
2352
+ /**
2353
+ * Send spans to Fallom's OTLP endpoint.
2354
+ */
2355
+ async sendSpans(spans) {
2356
+ const session = getSession();
2357
+ const resourceSpans = this.spansToOtlpJson(spans);
2358
+ const headers = {
2359
+ "Content-Type": "application/json",
2360
+ Authorization: `Bearer ${this.apiKey}`
2361
+ };
2362
+ if (session?.configKey) {
2363
+ headers["X-Fallom-Config-Key"] = session.configKey;
2364
+ }
2365
+ if (session?.sessionId) {
2366
+ headers["X-Fallom-Session-Id"] = session.sessionId;
2367
+ }
2368
+ if (session?.customerId) {
2369
+ headers["X-Fallom-Customer-Id"] = session.customerId;
2370
+ }
2371
+ if (promptContext2.promptKey) {
2372
+ headers["X-Fallom-Prompt-Key"] = promptContext2.promptKey;
2373
+ }
2374
+ if (promptContext2.promptVersion !== void 0) {
2375
+ headers["X-Fallom-Prompt-Version"] = String(promptContext2.promptVersion);
2376
+ }
2377
+ if (promptContext2.promptAbTestKey) {
2378
+ headers["X-Fallom-Prompt-AB-Test"] = promptContext2.promptAbTestKey;
2379
+ }
2380
+ if (promptContext2.promptVariantIndex !== void 0) {
2381
+ headers["X-Fallom-Prompt-Variant"] = String(
2382
+ promptContext2.promptVariantIndex
2383
+ );
2384
+ }
2385
+ const endpoint = `${this.baseUrl}/v1/traces`;
2386
+ this.log("Sending to", endpoint);
2387
+ this.log("Headers:", {
2388
+ ...headers,
2389
+ Authorization: "Bearer ***"
2390
+ });
2391
+ const response = await fetch(endpoint, {
2392
+ method: "POST",
2393
+ headers,
2394
+ body: JSON.stringify({ resourceSpans })
2395
+ });
2396
+ if (!response.ok) {
2397
+ const text = await response.text();
2398
+ throw new Error(`Failed to export: ${response.status} ${text}`);
2399
+ }
2400
+ }
2401
+ /**
2402
+ * Convert OpenTelemetry spans to OTLP JSON format.
2403
+ */
2404
+ spansToOtlpJson(spans) {
2405
+ const resourceMap = /* @__PURE__ */ new Map();
2406
+ for (const span2 of spans) {
2407
+ const resourceKey = JSON.stringify(span2.resource.attributes);
2408
+ if (!resourceMap.has(resourceKey)) {
2409
+ resourceMap.set(resourceKey, []);
2410
+ }
2411
+ resourceMap.get(resourceKey).push(span2);
2412
+ }
2413
+ const resourceSpans = [];
2414
+ for (const [_resourceKey, resourceSpanList] of resourceMap) {
2415
+ const firstSpan = resourceSpanList[0];
2416
+ resourceSpans.push({
2417
+ resource: {
2418
+ attributes: this.attributesToOtlp(firstSpan.resource.attributes)
2419
+ },
2420
+ scopeSpans: [
2421
+ {
2422
+ scope: {
2423
+ name: firstSpan.instrumentationLibrary.name,
2424
+ version: firstSpan.instrumentationLibrary.version
2425
+ },
2426
+ spans: resourceSpanList.map((span2) => this.spanToOtlp(span2))
2427
+ }
2428
+ ]
2429
+ });
2430
+ }
2431
+ return resourceSpans;
2432
+ }
2433
+ /**
2434
+ * Convert a single span to OTLP format.
2435
+ */
2436
+ spanToOtlp(span2) {
2437
+ return {
2438
+ traceId: span2.spanContext().traceId,
2439
+ spanId: span2.spanContext().spanId,
2440
+ parentSpanId: span2.parentSpanId,
2441
+ name: span2.name,
2442
+ kind: span2.kind,
2443
+ startTimeUnixNano: this.hrTimeToNanos(span2.startTime),
2444
+ endTimeUnixNano: this.hrTimeToNanos(span2.endTime),
2445
+ attributes: this.attributesToOtlp(span2.attributes),
2446
+ status: {
2447
+ code: span2.status.code,
2448
+ message: span2.status.message
2449
+ },
2450
+ events: span2.events.map((event) => ({
2451
+ timeUnixNano: this.hrTimeToNanos(event.time),
2452
+ name: event.name,
2453
+ attributes: this.attributesToOtlp(event.attributes || {})
2454
+ }))
2455
+ };
2456
+ }
2457
+ /**
2458
+ * Convert attributes to OTLP format.
2459
+ */
2460
+ attributesToOtlp(attrs) {
2461
+ return Object.entries(attrs).map(([key, value]) => ({
2462
+ key,
2463
+ value: this.valueToOtlp(value)
2464
+ }));
2465
+ }
2466
+ /**
2467
+ * Convert a value to OTLP AnyValue format.
2468
+ */
2469
+ valueToOtlp(value) {
2470
+ if (typeof value === "string") {
2471
+ return { stringValue: value };
2472
+ }
2473
+ if (typeof value === "number") {
2474
+ if (Number.isInteger(value)) {
2475
+ return { intValue: value };
2476
+ }
2477
+ return { doubleValue: value };
2478
+ }
2479
+ if (typeof value === "boolean") {
2480
+ return { boolValue: value };
2481
+ }
2482
+ if (Array.isArray(value)) {
2483
+ return {
2484
+ arrayValue: {
2485
+ values: value.map((v) => this.valueToOtlp(v))
2486
+ }
2487
+ };
2488
+ }
2489
+ return { stringValue: String(value) };
2490
+ }
2491
+ /**
2492
+ * Convert HrTime to nanoseconds string.
2493
+ */
2494
+ hrTimeToNanos(hrTime) {
2495
+ const [seconds, nanos] = hrTime;
2496
+ return String(BigInt(seconds) * BigInt(1e9) + BigInt(nanos));
2497
+ }
2498
+ };
2499
+
2165
2500
  // src/index.ts
2166
2501
  init_prompts();
2167
2502
  var index_default = {
@@ -2172,8 +2507,12 @@ var index_default = {
2172
2507
  };
2173
2508
  // Annotate the CommonJS export names for ESM import in node:
2174
2509
  0 && (module.exports = {
2510
+ FallomExporter,
2511
+ clearMastraPrompt,
2175
2512
  init,
2176
2513
  models,
2177
2514
  prompts,
2515
+ setMastraPrompt,
2516
+ setMastraPromptAB,
2178
2517
  trace
2179
2518
  });
package/dist/index.mjs CHANGED
@@ -17,6 +17,7 @@ __export(trace_exports, {
17
17
  wrapAISDK: () => wrapAISDK,
18
18
  wrapAnthropic: () => wrapAnthropic,
19
19
  wrapGoogleAI: () => wrapGoogleAI,
20
+ wrapMastraAgent: () => wrapMastraAgent,
20
21
  wrapOpenAI: () => wrapOpenAI
21
22
  });
22
23
  import { AsyncLocalStorage } from "async_hooks";
@@ -26,7 +27,7 @@ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
26
27
  // node_modules/@opentelemetry/resources/build/esm/Resource.js
27
28
  import { diag } from "@opentelemetry/api";
28
29
 
29
- // node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
30
+ // node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
30
31
  var SemanticResourceAttributes = {
31
32
  /**
32
33
  * Name of the cloud provider.
@@ -406,35 +407,9 @@ var SemanticResourceAttributes = {
406
407
  */
407
408
  WEBENGINE_DESCRIPTION: "webengine.description"
408
409
  };
409
- var TelemetrySdkLanguageValues = {
410
- /** cpp. */
411
- CPP: "cpp",
412
- /** dotnet. */
413
- DOTNET: "dotnet",
414
- /** erlang. */
415
- ERLANG: "erlang",
416
- /** go. */
417
- GO: "go",
418
- /** java. */
419
- JAVA: "java",
420
- /** nodejs. */
421
- NODEJS: "nodejs",
422
- /** php. */
423
- PHP: "php",
424
- /** python. */
425
- PYTHON: "python",
426
- /** ruby. */
427
- RUBY: "ruby",
428
- /** webjs. */
429
- WEBJS: "webjs"
430
- };
431
-
432
- // node_modules/@opentelemetry/core/build/esm/version.js
433
- var VERSION = "1.19.0";
434
410
 
435
- // node_modules/@opentelemetry/core/build/esm/platform/node/sdk-info.js
436
- var _a;
437
- var SDK_INFO = (_a = {}, _a[SemanticResourceAttributes.TELEMETRY_SDK_NAME] = "opentelemetry", _a[SemanticResourceAttributes.PROCESS_RUNTIME_NAME] = "node", _a[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE] = TelemetrySdkLanguageValues.NODEJS, _a[SemanticResourceAttributes.TELEMETRY_SDK_VERSION] = VERSION, _a);
411
+ // node_modules/@opentelemetry/resources/build/esm/Resource.js
412
+ import { SDK_INFO } from "@opentelemetry/core";
438
413
 
439
414
  // node_modules/@opentelemetry/resources/build/esm/platform/node/default-service-name.js
440
415
  function defaultServiceName() {
@@ -571,10 +546,10 @@ var Resource = (
571
546
  (function() {
572
547
  function Resource2(attributes, asyncAttributesPromise) {
573
548
  var _this = this;
574
- var _a2;
549
+ var _a;
575
550
  this._attributes = attributes;
576
551
  this.asyncAttributesPending = asyncAttributesPromise != null;
577
- this._syncAttributes = (_a2 = this._attributes) !== null && _a2 !== void 0 ? _a2 : {};
552
+ this._syncAttributes = (_a = this._attributes) !== null && _a !== void 0 ? _a : {};
578
553
  this._asyncAttributesPromise = asyncAttributesPromise === null || asyncAttributesPromise === void 0 ? void 0 : asyncAttributesPromise.then(function(asyncAttributes) {
579
554
  _this._attributes = Object.assign({}, _this._attributes, asyncAttributes);
580
555
  _this.asyncAttributesPending = false;
@@ -589,30 +564,30 @@ var Resource = (
589
564
  return Resource2.EMPTY;
590
565
  };
591
566
  Resource2.default = function() {
592
- var _a2;
593
- return new Resource2((_a2 = {}, _a2[SemanticResourceAttributes.SERVICE_NAME] = defaultServiceName(), _a2[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE], _a2[SemanticResourceAttributes.TELEMETRY_SDK_NAME] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_NAME], _a2[SemanticResourceAttributes.TELEMETRY_SDK_VERSION] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_VERSION], _a2));
567
+ var _a;
568
+ return new Resource2((_a = {}, _a[SemanticResourceAttributes.SERVICE_NAME] = defaultServiceName(), _a[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE], _a[SemanticResourceAttributes.TELEMETRY_SDK_NAME] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_NAME], _a[SemanticResourceAttributes.TELEMETRY_SDK_VERSION] = SDK_INFO[SemanticResourceAttributes.TELEMETRY_SDK_VERSION], _a));
594
569
  };
595
570
  Object.defineProperty(Resource2.prototype, "attributes", {
596
571
  get: function() {
597
- var _a2;
572
+ var _a;
598
573
  if (this.asyncAttributesPending) {
599
574
  diag.error("Accessing resource attributes before async attributes settled");
600
575
  }
601
- return (_a2 = this._attributes) !== null && _a2 !== void 0 ? _a2 : {};
576
+ return (_a = this._attributes) !== null && _a !== void 0 ? _a : {};
602
577
  },
603
578
  enumerable: false,
604
579
  configurable: true
605
580
  });
606
581
  Resource2.prototype.waitForAsyncAttributes = function() {
607
582
  return __awaiter(this, void 0, void 0, function() {
608
- return __generator(this, function(_a2) {
609
- switch (_a2.label) {
583
+ return __generator(this, function(_a) {
584
+ switch (_a.label) {
610
585
  case 0:
611
586
  if (!this.asyncAttributesPending) return [3, 2];
612
587
  return [4, this._asyncAttributesPromise];
613
588
  case 1:
614
- _a2.sent();
615
- _a2.label = 2;
589
+ _a.sent();
590
+ _a.label = 2;
616
591
  case 2:
617
592
  return [
618
593
  2
@@ -624,19 +599,19 @@ var Resource = (
624
599
  };
625
600
  Resource2.prototype.merge = function(other) {
626
601
  var _this = this;
627
- var _a2;
602
+ var _a;
628
603
  if (!other)
629
604
  return this;
630
- var mergedSyncAttributes = __assign(__assign({}, this._syncAttributes), (_a2 = other._syncAttributes) !== null && _a2 !== void 0 ? _a2 : other.attributes);
605
+ var mergedSyncAttributes = __assign(__assign({}, this._syncAttributes), (_a = other._syncAttributes) !== null && _a !== void 0 ? _a : other.attributes);
631
606
  if (!this._asyncAttributesPromise && !other._asyncAttributesPromise) {
632
607
  return new Resource2(mergedSyncAttributes);
633
608
  }
634
609
  var mergedAttributesPromise = Promise.all([
635
610
  this._asyncAttributesPromise,
636
611
  other._asyncAttributesPromise
637
- ]).then(function(_a3) {
612
+ ]).then(function(_a2) {
638
613
  var _b;
639
- var _c = __read(_a3, 2), thisAsyncAttributes = _c[0], otherAsyncAttributes = _c[1];
614
+ var _c = __read(_a2, 2), thisAsyncAttributes = _c[0], otherAsyncAttributes = _c[1];
640
615
  return __assign(__assign(__assign(__assign({}, _this._syncAttributes), thisAsyncAttributes), (_b = other._syncAttributes) !== null && _b !== void 0 ? _b : other.attributes), otherAsyncAttributes);
641
616
  });
642
617
  return new Resource2(mergedSyncAttributes, mergedAttributesPromise);
@@ -1620,6 +1595,127 @@ function createStreamObjectWrapper(aiModule) {
1620
1595
  return result;
1621
1596
  };
1622
1597
  }
1598
+ function wrapMastraAgent(agent) {
1599
+ const originalGenerate = agent.generate.bind(agent);
1600
+ const agentName = agent.name || "MastraAgent";
1601
+ agent.generate = async function(...args) {
1602
+ const ctx = sessionStorage.getStore() || fallbackSession;
1603
+ if (!ctx || !initialized) {
1604
+ return originalGenerate(...args);
1605
+ }
1606
+ let promptCtx = null;
1607
+ try {
1608
+ const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
1609
+ promptCtx = getPromptContext();
1610
+ } catch {
1611
+ }
1612
+ const traceId = generateHexId(32);
1613
+ const spanId = generateHexId(16);
1614
+ const startTime = Date.now();
1615
+ const messages = args[0] || [];
1616
+ try {
1617
+ const result = await originalGenerate(...args);
1618
+ const endTime = Date.now();
1619
+ const model = result?.model?.modelId || "unknown";
1620
+ const toolCalls = [];
1621
+ if (result?.steps?.length) {
1622
+ for (const step of result.steps) {
1623
+ if (step.toolCalls?.length) {
1624
+ for (let i = 0; i < step.toolCalls.length; i++) {
1625
+ const tc = step.toolCalls[i];
1626
+ const tr = step.toolResults?.[i];
1627
+ toolCalls.push({
1628
+ name: tc.toolName,
1629
+ arguments: tc.args,
1630
+ result: tr?.result
1631
+ });
1632
+ }
1633
+ }
1634
+ }
1635
+ }
1636
+ const attributes = {
1637
+ "gen_ai.system": "Mastra",
1638
+ "gen_ai.request.model": model,
1639
+ "gen_ai.response.model": model,
1640
+ "fallom.source": "mastra-agent",
1641
+ "llm.request.type": "chat"
1642
+ };
1643
+ if (Array.isArray(messages)) {
1644
+ messages.forEach((msg, i) => {
1645
+ attributes[`gen_ai.prompt.${i}.role`] = msg.role || "user";
1646
+ attributes[`gen_ai.prompt.${i}.content`] = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1647
+ });
1648
+ }
1649
+ if (result?.text) {
1650
+ attributes["gen_ai.completion.0.role"] = "assistant";
1651
+ attributes["gen_ai.completion.0.content"] = result.text;
1652
+ attributes["gen_ai.completion.0.finish_reason"] = "stop";
1653
+ }
1654
+ if (toolCalls.length > 0) {
1655
+ attributes["fallom.tool_calls"] = JSON.stringify(toolCalls);
1656
+ toolCalls.forEach((tc, i) => {
1657
+ attributes[`gen_ai.completion.0.tool_calls.${i}.name`] = tc.name;
1658
+ attributes[`gen_ai.completion.0.tool_calls.${i}.type`] = "function";
1659
+ attributes[`gen_ai.completion.0.tool_calls.${i}.arguments`] = JSON.stringify(tc.arguments);
1660
+ });
1661
+ }
1662
+ if (result?.usage) {
1663
+ attributes["gen_ai.usage.prompt_tokens"] = result.usage.promptTokens;
1664
+ attributes["gen_ai.usage.completion_tokens"] = result.usage.completionTokens;
1665
+ attributes["llm.usage.total_tokens"] = result.usage.totalTokens;
1666
+ }
1667
+ const traceData = {
1668
+ config_key: ctx.configKey,
1669
+ session_id: ctx.sessionId,
1670
+ customer_id: ctx.customerId,
1671
+ trace_id: traceId,
1672
+ span_id: spanId,
1673
+ name: `mastra.${agentName}.generate`,
1674
+ kind: "client",
1675
+ model,
1676
+ start_time: new Date(startTime).toISOString(),
1677
+ end_time: new Date(endTime).toISOString(),
1678
+ duration_ms: endTime - startTime,
1679
+ status: "OK",
1680
+ prompt_tokens: result?.usage?.promptTokens,
1681
+ completion_tokens: result?.usage?.completionTokens,
1682
+ total_tokens: result?.usage?.totalTokens,
1683
+ attributes,
1684
+ prompt_key: promptCtx?.promptKey,
1685
+ prompt_version: promptCtx?.promptVersion,
1686
+ prompt_ab_test_key: promptCtx?.abTestKey,
1687
+ prompt_variant_index: promptCtx?.variantIndex
1688
+ };
1689
+ sendTrace(traceData).catch(() => {
1690
+ });
1691
+ return result;
1692
+ } catch (error) {
1693
+ const endTime = Date.now();
1694
+ const traceData = {
1695
+ config_key: ctx.configKey,
1696
+ session_id: ctx.sessionId,
1697
+ customer_id: ctx.customerId,
1698
+ trace_id: traceId,
1699
+ span_id: spanId,
1700
+ name: `mastra.${agentName}.generate`,
1701
+ kind: "client",
1702
+ start_time: new Date(startTime).toISOString(),
1703
+ end_time: new Date(endTime).toISOString(),
1704
+ duration_ms: endTime - startTime,
1705
+ status: "ERROR",
1706
+ error_message: error instanceof Error ? error.message : String(error),
1707
+ prompt_key: promptCtx?.promptKey,
1708
+ prompt_version: promptCtx?.promptVersion,
1709
+ prompt_ab_test_key: promptCtx?.abTestKey,
1710
+ prompt_variant_index: promptCtx?.variantIndex
1711
+ };
1712
+ sendTrace(traceData).catch(() => {
1713
+ });
1714
+ throw error;
1715
+ }
1716
+ };
1717
+ return agent;
1718
+ }
1623
1719
 
1624
1720
  // src/models.ts
1625
1721
  var models_exports = {};
@@ -1885,6 +1981,241 @@ async function init4(options = {}) {
1885
1981
  });
1886
1982
  }
1887
1983
 
1984
+ // src/mastra.ts
1985
+ import { ExportResultCode } from "@opentelemetry/core";
1986
+ var promptContext = {};
1987
+ function setMastraPrompt(promptKey, version) {
1988
+ promptContext = {
1989
+ promptKey,
1990
+ promptVersion: version,
1991
+ promptAbTestKey: void 0,
1992
+ promptVariantIndex: void 0
1993
+ };
1994
+ }
1995
+ function setMastraPromptAB(abTestKey, variantIndex) {
1996
+ promptContext = {
1997
+ promptKey: void 0,
1998
+ promptVersion: void 0,
1999
+ promptAbTestKey: abTestKey,
2000
+ promptVariantIndex: variantIndex
2001
+ };
2002
+ }
2003
+ function clearMastraPrompt() {
2004
+ promptContext = {};
2005
+ }
2006
+ var FallomExporter = class {
2007
+ constructor(options = {}) {
2008
+ this.pendingExports = [];
2009
+ this.apiKey = options.apiKey ?? process.env.FALLOM_API_KEY ?? "";
2010
+ this.baseUrl = options.baseUrl ?? "https://traces.fallom.com";
2011
+ this.debug = options.debug ?? false;
2012
+ console.log("[FallomExporter] Constructor called, debug:", this.debug);
2013
+ console.log("[FallomExporter] API key present:", !!this.apiKey);
2014
+ console.log("[FallomExporter] Base URL:", this.baseUrl);
2015
+ if (!this.apiKey) {
2016
+ console.warn(
2017
+ "[FallomExporter] No API key provided. Set FALLOM_API_KEY env var or pass apiKey option."
2018
+ );
2019
+ }
2020
+ }
2021
+ log(...args) {
2022
+ if (this.debug) {
2023
+ console.log("[FallomExporter]", ...args);
2024
+ }
2025
+ }
2026
+ /**
2027
+ * Export spans to Fallom.
2028
+ */
2029
+ export(spans, resultCallback) {
2030
+ if (spans.length === 0) {
2031
+ resultCallback({ code: ExportResultCode.SUCCESS });
2032
+ return;
2033
+ }
2034
+ this.log(`Exporting ${spans.length} spans...`);
2035
+ if (this.debug) {
2036
+ for (const span2 of spans) {
2037
+ this.log(` - ${span2.name}`, {
2038
+ attributes: Object.fromEntries(
2039
+ Object.entries(span2.attributes).filter(
2040
+ ([k]) => k.startsWith("gen_ai") || k.startsWith("llm")
2041
+ )
2042
+ )
2043
+ });
2044
+ }
2045
+ }
2046
+ const exportPromise = this.sendSpans(spans).then(() => {
2047
+ this.log("Export successful");
2048
+ resultCallback({ code: ExportResultCode.SUCCESS });
2049
+ }).catch((error) => {
2050
+ console.error("[FallomExporter] Export failed:", error);
2051
+ resultCallback({
2052
+ code: ExportResultCode.FAILED,
2053
+ error: error instanceof Error ? error : new Error(String(error))
2054
+ });
2055
+ });
2056
+ this.pendingExports.push(exportPromise);
2057
+ }
2058
+ /**
2059
+ * Shutdown the exporter, waiting for pending exports.
2060
+ */
2061
+ async shutdown() {
2062
+ await Promise.all(this.pendingExports);
2063
+ this.pendingExports = [];
2064
+ }
2065
+ /**
2066
+ * Force flush pending exports.
2067
+ */
2068
+ async forceFlush() {
2069
+ await Promise.all(this.pendingExports);
2070
+ }
2071
+ /**
2072
+ * Send spans to Fallom's OTLP endpoint.
2073
+ */
2074
+ async sendSpans(spans) {
2075
+ const session = getSession();
2076
+ const resourceSpans = this.spansToOtlpJson(spans);
2077
+ const headers = {
2078
+ "Content-Type": "application/json",
2079
+ Authorization: `Bearer ${this.apiKey}`
2080
+ };
2081
+ if (session?.configKey) {
2082
+ headers["X-Fallom-Config-Key"] = session.configKey;
2083
+ }
2084
+ if (session?.sessionId) {
2085
+ headers["X-Fallom-Session-Id"] = session.sessionId;
2086
+ }
2087
+ if (session?.customerId) {
2088
+ headers["X-Fallom-Customer-Id"] = session.customerId;
2089
+ }
2090
+ if (promptContext.promptKey) {
2091
+ headers["X-Fallom-Prompt-Key"] = promptContext.promptKey;
2092
+ }
2093
+ if (promptContext.promptVersion !== void 0) {
2094
+ headers["X-Fallom-Prompt-Version"] = String(promptContext.promptVersion);
2095
+ }
2096
+ if (promptContext.promptAbTestKey) {
2097
+ headers["X-Fallom-Prompt-AB-Test"] = promptContext.promptAbTestKey;
2098
+ }
2099
+ if (promptContext.promptVariantIndex !== void 0) {
2100
+ headers["X-Fallom-Prompt-Variant"] = String(
2101
+ promptContext.promptVariantIndex
2102
+ );
2103
+ }
2104
+ const endpoint = `${this.baseUrl}/v1/traces`;
2105
+ this.log("Sending to", endpoint);
2106
+ this.log("Headers:", {
2107
+ ...headers,
2108
+ Authorization: "Bearer ***"
2109
+ });
2110
+ const response = await fetch(endpoint, {
2111
+ method: "POST",
2112
+ headers,
2113
+ body: JSON.stringify({ resourceSpans })
2114
+ });
2115
+ if (!response.ok) {
2116
+ const text = await response.text();
2117
+ throw new Error(`Failed to export: ${response.status} ${text}`);
2118
+ }
2119
+ }
2120
+ /**
2121
+ * Convert OpenTelemetry spans to OTLP JSON format.
2122
+ */
2123
+ spansToOtlpJson(spans) {
2124
+ const resourceMap = /* @__PURE__ */ new Map();
2125
+ for (const span2 of spans) {
2126
+ const resourceKey = JSON.stringify(span2.resource.attributes);
2127
+ if (!resourceMap.has(resourceKey)) {
2128
+ resourceMap.set(resourceKey, []);
2129
+ }
2130
+ resourceMap.get(resourceKey).push(span2);
2131
+ }
2132
+ const resourceSpans = [];
2133
+ for (const [_resourceKey, resourceSpanList] of resourceMap) {
2134
+ const firstSpan = resourceSpanList[0];
2135
+ resourceSpans.push({
2136
+ resource: {
2137
+ attributes: this.attributesToOtlp(firstSpan.resource.attributes)
2138
+ },
2139
+ scopeSpans: [
2140
+ {
2141
+ scope: {
2142
+ name: firstSpan.instrumentationLibrary.name,
2143
+ version: firstSpan.instrumentationLibrary.version
2144
+ },
2145
+ spans: resourceSpanList.map((span2) => this.spanToOtlp(span2))
2146
+ }
2147
+ ]
2148
+ });
2149
+ }
2150
+ return resourceSpans;
2151
+ }
2152
+ /**
2153
+ * Convert a single span to OTLP format.
2154
+ */
2155
+ spanToOtlp(span2) {
2156
+ return {
2157
+ traceId: span2.spanContext().traceId,
2158
+ spanId: span2.spanContext().spanId,
2159
+ parentSpanId: span2.parentSpanId,
2160
+ name: span2.name,
2161
+ kind: span2.kind,
2162
+ startTimeUnixNano: this.hrTimeToNanos(span2.startTime),
2163
+ endTimeUnixNano: this.hrTimeToNanos(span2.endTime),
2164
+ attributes: this.attributesToOtlp(span2.attributes),
2165
+ status: {
2166
+ code: span2.status.code,
2167
+ message: span2.status.message
2168
+ },
2169
+ events: span2.events.map((event) => ({
2170
+ timeUnixNano: this.hrTimeToNanos(event.time),
2171
+ name: event.name,
2172
+ attributes: this.attributesToOtlp(event.attributes || {})
2173
+ }))
2174
+ };
2175
+ }
2176
+ /**
2177
+ * Convert attributes to OTLP format.
2178
+ */
2179
+ attributesToOtlp(attrs) {
2180
+ return Object.entries(attrs).map(([key, value]) => ({
2181
+ key,
2182
+ value: this.valueToOtlp(value)
2183
+ }));
2184
+ }
2185
+ /**
2186
+ * Convert a value to OTLP AnyValue format.
2187
+ */
2188
+ valueToOtlp(value) {
2189
+ if (typeof value === "string") {
2190
+ return { stringValue: value };
2191
+ }
2192
+ if (typeof value === "number") {
2193
+ if (Number.isInteger(value)) {
2194
+ return { intValue: value };
2195
+ }
2196
+ return { doubleValue: value };
2197
+ }
2198
+ if (typeof value === "boolean") {
2199
+ return { boolValue: value };
2200
+ }
2201
+ if (Array.isArray(value)) {
2202
+ return {
2203
+ arrayValue: {
2204
+ values: value.map((v) => this.valueToOtlp(v))
2205
+ }
2206
+ };
2207
+ }
2208
+ return { stringValue: String(value) };
2209
+ }
2210
+ /**
2211
+ * Convert HrTime to nanoseconds string.
2212
+ */
2213
+ hrTimeToNanos(hrTime) {
2214
+ const [seconds, nanos] = hrTime;
2215
+ return String(BigInt(seconds) * BigInt(1e9) + BigInt(nanos));
2216
+ }
2217
+ };
2218
+
1888
2219
  // src/index.ts
1889
2220
  var index_default = {
1890
2221
  init: init4,
@@ -1893,9 +2224,13 @@ var index_default = {
1893
2224
  prompts: prompts_exports
1894
2225
  };
1895
2226
  export {
2227
+ FallomExporter,
2228
+ clearMastraPrompt,
1896
2229
  index_default as default,
1897
2230
  init4 as init,
1898
2231
  models_exports as models,
1899
2232
  prompts_exports as prompts,
2233
+ setMastraPrompt,
2234
+ setMastraPromptAB,
1900
2235
  trace_exports as trace
1901
2236
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fallom/trace",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Model A/B testing and tracing for LLM applications. Zero latency, production-ready.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -44,7 +44,9 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@opentelemetry/api": "^1.7.0",
47
+ "@opentelemetry/core": "^1.19.0",
47
48
  "@opentelemetry/sdk-node": "^0.46.0",
49
+ "@opentelemetry/sdk-trace-base": "^1.19.0",
48
50
  "@opentelemetry/sdk-trace-node": "^1.19.0",
49
51
  "@opentelemetry/exporter-trace-otlp-http": "^0.46.0",
50
52
  "@traceloop/instrumentation-openai": "^0.11.0",