@fallom/trace 0.1.11 → 0.2.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.mjs CHANGED
@@ -1,25 +1,19 @@
1
1
  import {
2
2
  __export,
3
3
  init,
4
- prompts_exports
5
- } from "./chunk-6MSTRIK4.mjs";
4
+ models_exports
5
+ } from "./chunk-W6M2RQ3W.mjs";
6
6
 
7
7
  // src/trace.ts
8
8
  var trace_exports = {};
9
9
  __export(trace_exports, {
10
- clearSession: () => clearSession,
11
- getSession: () => getSession,
10
+ FallomSession: () => FallomSession,
12
11
  init: () => init2,
13
- runWithSession: () => runWithSession,
14
- setSession: () => setSession,
15
- shutdown: () => shutdown,
16
- span: () => span,
17
- wrapAISDK: () => wrapAISDK,
18
- wrapAnthropic: () => wrapAnthropic,
19
- wrapGoogleAI: () => wrapGoogleAI,
20
- wrapMastraAgent: () => wrapMastraAgent,
21
- wrapOpenAI: () => wrapOpenAI
12
+ session: () => session,
13
+ shutdown: () => shutdown
22
14
  });
15
+
16
+ // src/trace/core.ts
23
17
  import { AsyncLocalStorage } from "async_hooks";
24
18
  import { NodeSDK } from "@opentelemetry/sdk-node";
25
19
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
@@ -621,9 +615,9 @@ var Resource = (
621
615
  })()
622
616
  );
623
617
 
624
- // src/trace.ts
625
- var sessionStorage = new AsyncLocalStorage();
626
- var fallbackSession = null;
618
+ // src/trace/core.ts
619
+ var traceContextStorage = new AsyncLocalStorage();
620
+ var fallbackTraceContext = null;
627
621
  var apiKey = null;
628
622
  var baseUrl = "https://traces.fallom.com";
629
623
  var initialized = false;
@@ -633,28 +627,27 @@ var sdk = null;
633
627
  function log(...args) {
634
628
  if (debugMode) console.log("[Fallom]", ...args);
635
629
  }
630
+ function getTraceContextStorage() {
631
+ return traceContextStorage;
632
+ }
633
+ function getFallbackTraceContext() {
634
+ return fallbackTraceContext;
635
+ }
636
+ function isInitialized() {
637
+ return initialized;
638
+ }
639
+ function shouldCaptureContent() {
640
+ return captureContent;
641
+ }
642
+ function isDebugMode() {
643
+ return debugMode;
644
+ }
636
645
  var fallomSpanProcessor = {
637
- onStart(span2, _parentContext) {
638
- log("\u{1F4CD} Span started:", span2.name || "unknown");
639
- const ctx = sessionStorage.getStore() || fallbackSession;
640
- if (ctx) {
641
- span2.setAttribute("fallom.config_key", ctx.configKey);
642
- span2.setAttribute("fallom.session_id", ctx.sessionId);
643
- if (ctx.customerId) {
644
- span2.setAttribute("fallom.customer_id", ctx.customerId);
645
- }
646
- log(
647
- " Added session context:",
648
- ctx.configKey,
649
- ctx.sessionId,
650
- ctx.customerId
651
- );
652
- } else {
653
- log(" No session context available");
654
- }
646
+ onStart(span, _parentContext) {
647
+ log("\u{1F4CD} Span started:", span.name || "unknown");
655
648
  },
656
- onEnd(span2) {
657
- log("\u2705 Span ended:", span2.name, "duration:", span2.duration);
649
+ onEnd(span) {
650
+ log("\u2705 Span ended:", span.name, "duration:", span.duration);
658
651
  },
659
652
  shutdown() {
660
653
  return Promise.resolve();
@@ -663,47 +656,6 @@ var fallomSpanProcessor = {
663
656
  return Promise.resolve();
664
657
  }
665
658
  };
666
- async function init2(options = {}) {
667
- if (initialized) return;
668
- debugMode = options.debug ?? false;
669
- log("\u{1F680} Initializing Fallom tracing...");
670
- apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
671
- baseUrl = options.baseUrl || process.env.FALLOM_TRACES_URL || process.env.FALLOM_BASE_URL || "https://traces.fallom.com";
672
- const envCapture = process.env.FALLOM_CAPTURE_CONTENT?.toLowerCase();
673
- if (envCapture === "false" || envCapture === "0" || envCapture === "no") {
674
- captureContent = false;
675
- } else {
676
- captureContent = options.captureContent ?? true;
677
- }
678
- if (!apiKey) {
679
- throw new Error(
680
- "No API key provided. Set FALLOM_API_KEY environment variable or pass apiKey parameter."
681
- );
682
- }
683
- initialized = true;
684
- log("\u{1F4E1} Exporter URL:", `${baseUrl}/v1/traces`);
685
- const exporter = new OTLPTraceExporter({
686
- url: `${baseUrl}/v1/traces`,
687
- headers: {
688
- Authorization: `Bearer ${apiKey}`
689
- }
690
- });
691
- const instrumentations = await getInstrumentations();
692
- log("\u{1F527} Loaded instrumentations:", instrumentations.length);
693
- sdk = new NodeSDK({
694
- resource: new Resource({
695
- "service.name": "fallom-traced-app"
696
- }),
697
- traceExporter: exporter,
698
- spanProcessor: fallomSpanProcessor,
699
- instrumentations
700
- });
701
- sdk.start();
702
- log("\u2705 SDK started");
703
- process.on("SIGTERM", () => {
704
- sdk?.shutdown().catch(console.error);
705
- });
706
- }
707
659
  async function getInstrumentations() {
708
660
  const instrumentations = [];
709
661
  await tryAddInstrumentation(
@@ -758,75 +710,90 @@ async function tryAddInstrumentation(instrumentations, pkg, className) {
758
710
  Object.keys(mod)
759
711
  );
760
712
  }
761
- } catch (e) {
713
+ } catch {
762
714
  log(` \u274C ${pkg} not installed`);
763
715
  }
764
716
  }
765
- function setSession(configKey, sessionId, customerId) {
766
- const store = sessionStorage.getStore();
767
- if (store) {
768
- store.configKey = configKey;
769
- store.sessionId = sessionId;
770
- store.customerId = customerId;
771
- }
772
- fallbackSession = { configKey, sessionId, customerId };
773
- }
774
- function runWithSession(configKey, sessionId, customerIdOrFn, fn) {
775
- if (typeof customerIdOrFn === "function") {
776
- return sessionStorage.run({ configKey, sessionId }, customerIdOrFn);
777
- }
778
- return sessionStorage.run(
779
- { configKey, sessionId, customerId: customerIdOrFn },
780
- fn
781
- );
782
- }
783
- function getSession() {
784
- return sessionStorage.getStore() || fallbackSession || void 0;
785
- }
786
- function clearSession() {
787
- fallbackSession = null;
788
- }
789
- function span(data, options = {}) {
790
- if (!initialized) {
791
- throw new Error("Fallom not initialized. Call trace.init() first.");
717
+ async function init2(options = {}) {
718
+ if (initialized) return;
719
+ debugMode = options.debug ?? false;
720
+ log("\u{1F680} Initializing Fallom tracing...");
721
+ apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
722
+ baseUrl = options.baseUrl || process.env.FALLOM_TRACES_URL || process.env.FALLOM_BASE_URL || "https://traces.fallom.com";
723
+ const envCapture = process.env.FALLOM_CAPTURE_CONTENT?.toLowerCase();
724
+ if (envCapture === "false" || envCapture === "0" || envCapture === "no") {
725
+ captureContent = false;
726
+ } else {
727
+ captureContent = options.captureContent ?? true;
792
728
  }
793
- const ctx = sessionStorage.getStore() || fallbackSession;
794
- const configKey = options.configKey || ctx?.configKey;
795
- const sessionId = options.sessionId || ctx?.sessionId;
796
- if (!configKey || !sessionId) {
729
+ if (!apiKey) {
797
730
  throw new Error(
798
- "No session context. Either call setSession() first, or pass configKey and sessionId explicitly."
731
+ "No API key provided. Set FALLOM_API_KEY environment variable or pass apiKey parameter."
799
732
  );
800
733
  }
801
- sendSpan(configKey, sessionId, data).catch(() => {
734
+ initialized = true;
735
+ log("\u{1F4E1} Exporter URL:", `${baseUrl}/v1/traces`);
736
+ const exporter = new OTLPTraceExporter({
737
+ url: `${baseUrl}/v1/traces`,
738
+ headers: {
739
+ Authorization: `Bearer ${apiKey}`
740
+ }
802
741
  });
742
+ const instrumentations = await getInstrumentations();
743
+ log("\u{1F527} Loaded instrumentations:", instrumentations.length);
744
+ sdk = new NodeSDK({
745
+ resource: new Resource({
746
+ "service.name": "fallom-traced-app"
747
+ }),
748
+ traceExporter: exporter,
749
+ spanProcessor: fallomSpanProcessor,
750
+ instrumentations
751
+ });
752
+ sdk.start();
753
+ log("\u2705 SDK started");
754
+ process.on("SIGTERM", () => {
755
+ sdk?.shutdown().catch(console.error);
756
+ });
757
+ }
758
+ async function shutdown() {
759
+ if (sdk) {
760
+ await sdk.shutdown();
761
+ initialized = false;
762
+ }
803
763
  }
804
- async function sendSpan(configKey, sessionId, data) {
764
+ async function sendTrace(trace) {
765
+ const url = `${baseUrl}/v1/traces`;
766
+ log("\u{1F4E4} Sending trace to:", url);
767
+ log(" Session:", trace.session_id, "Config:", trace.config_key);
805
768
  try {
806
769
  const controller = new AbortController();
807
770
  const timeoutId = setTimeout(() => controller.abort(), 5e3);
808
- await fetch(`${baseUrl}/spans`, {
771
+ const response = await fetch(url, {
809
772
  method: "POST",
810
773
  headers: {
811
774
  Authorization: `Bearer ${apiKey}`,
812
775
  "Content-Type": "application/json"
813
776
  },
814
- body: JSON.stringify({
815
- config_key: configKey,
816
- session_id: sessionId,
817
- data
818
- }),
777
+ body: JSON.stringify(trace),
819
778
  signal: controller.signal
820
779
  });
821
780
  clearTimeout(timeoutId);
822
- } catch {
781
+ if (!response.ok) {
782
+ const text = await response.text();
783
+ log("\u274C Trace send failed:", response.status, text);
784
+ } else {
785
+ log("\u2705 Trace sent:", trace.name, trace.model);
786
+ }
787
+ } catch (err) {
788
+ log("\u274C Trace send error:", err instanceof Error ? err.message : err);
823
789
  }
824
790
  }
825
- async function shutdown() {
826
- if (sdk) {
827
- await sdk.shutdown();
828
- initialized = false;
829
- }
791
+
792
+ // src/trace/utils.ts
793
+ function generateHexId(length) {
794
+ const bytes = new Uint8Array(length / 2);
795
+ crypto.getRandomValues(bytes);
796
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
830
797
  }
831
798
  function messagesToOtelAttributes(messages, completion, model, responseId) {
832
799
  const attrs = {};
@@ -854,70 +821,39 @@ function messagesToOtelAttributes(messages, completion, model, responseId) {
854
821
  }
855
822
  return attrs;
856
823
  }
857
- function generateHexId(length) {
858
- const bytes = new Uint8Array(length / 2);
859
- crypto.getRandomValues(bytes);
860
- return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
861
- }
862
- var traceContextStorage = new AsyncLocalStorage();
863
- var fallbackTraceContext = null;
864
- async function sendTrace(trace) {
865
- const url = `${baseUrl}/v1/traces`;
866
- log("\u{1F4E4} Sending trace to:", url);
867
- log(" Session:", trace.session_id, "Config:", trace.config_key);
868
- try {
869
- const controller = new AbortController();
870
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
871
- const response = await fetch(url, {
872
- method: "POST",
873
- headers: {
874
- Authorization: `Bearer ${apiKey}`,
875
- "Content-Type": "application/json"
876
- },
877
- body: JSON.stringify(trace),
878
- signal: controller.signal
879
- });
880
- clearTimeout(timeoutId);
881
- if (!response.ok) {
882
- const text = await response.text();
883
- log("\u274C Trace send failed:", response.status, text);
884
- } else {
885
- log("\u2705 Trace sent:", trace.name, trace.model);
886
- }
887
- } catch (err) {
888
- log("\u274C Trace send error:", err instanceof Error ? err.message : err);
889
- }
890
- }
891
- function wrapOpenAI(client) {
824
+
825
+ // src/trace/wrappers/openai.ts
826
+ function wrapOpenAI(client, sessionCtx) {
892
827
  const originalCreate = client.chat.completions.create.bind(
893
828
  client.chat.completions
894
829
  );
830
+ const ctx = sessionCtx;
895
831
  client.chat.completions.create = async function(...args) {
896
- const ctx = sessionStorage.getStore() || fallbackSession;
897
- if (!ctx || !initialized) {
832
+ if (!isInitialized()) {
898
833
  return originalCreate(...args);
899
834
  }
900
- let promptCtx = null;
901
- try {
902
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
903
- promptCtx = getPromptContext();
904
- } catch {
905
- }
906
- const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
835
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
907
836
  const traceId = traceCtx?.traceId || generateHexId(32);
908
837
  const spanId = generateHexId(16);
909
838
  const parentSpanId = traceCtx?.parentSpanId;
910
839
  const params = args[0] || {};
911
840
  const startTime = Date.now();
841
+ const captureContent2 = shouldCaptureContent();
912
842
  try {
913
843
  const response = await originalCreate(...args);
914
844
  const endTime = Date.now();
915
- const attributes = captureContent ? messagesToOtelAttributes(
845
+ const attributes = captureContent2 ? messagesToOtelAttributes(
916
846
  params?.messages,
917
847
  response?.choices?.[0]?.message,
918
848
  response?.model || params?.model,
919
849
  response?.id
920
- ) : void 0;
850
+ ) : {};
851
+ if (response?.usage) {
852
+ attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
853
+ }
854
+ if (response?.choices?.[0]?.finish_reason) {
855
+ attributes["gen_ai.response.finish_reason"] = response.choices[0].finish_reason;
856
+ }
921
857
  sendTrace({
922
858
  config_key: ctx.configKey,
923
859
  session_id: ctx.sessionId,
@@ -935,17 +871,13 @@ function wrapOpenAI(client) {
935
871
  prompt_tokens: response?.usage?.prompt_tokens,
936
872
  completion_tokens: response?.usage?.completion_tokens,
937
873
  total_tokens: response?.usage?.total_tokens,
938
- attributes,
939
- prompt_key: promptCtx?.promptKey,
940
- prompt_version: promptCtx?.promptVersion,
941
- prompt_ab_test_key: promptCtx?.abTestKey,
942
- prompt_variant_index: promptCtx?.variantIndex
874
+ attributes: Object.keys(attributes).length > 0 ? attributes : void 0
943
875
  }).catch(() => {
944
876
  });
945
877
  return response;
946
878
  } catch (error) {
947
879
  const endTime = Date.now();
948
- const attributes = captureContent ? messagesToOtelAttributes(
880
+ const attributes = captureContent2 ? messagesToOtelAttributes(
949
881
  params?.messages,
950
882
  void 0,
951
883
  params?.model,
@@ -969,11 +901,7 @@ function wrapOpenAI(client) {
969
901
  duration_ms: endTime - startTime,
970
902
  status: "ERROR",
971
903
  error_message: error?.message,
972
- attributes,
973
- prompt_key: promptCtx?.promptKey,
974
- prompt_version: promptCtx?.promptVersion,
975
- prompt_ab_test_key: promptCtx?.abTestKey,
976
- prompt_variant_index: promptCtx?.variantIndex
904
+ attributes
977
905
  }).catch(() => {
978
906
  });
979
907
  throw error;
@@ -981,37 +909,40 @@ function wrapOpenAI(client) {
981
909
  };
982
910
  return client;
983
911
  }
984
- function wrapAnthropic(client) {
912
+
913
+ // src/trace/wrappers/anthropic.ts
914
+ function wrapAnthropic(client, sessionCtx) {
985
915
  const originalCreate = client.messages.create.bind(client.messages);
916
+ const ctx = sessionCtx;
986
917
  client.messages.create = async function(...args) {
987
- const ctx = sessionStorage.getStore() || fallbackSession;
988
- if (!ctx || !initialized) {
918
+ if (!isInitialized()) {
989
919
  return originalCreate(...args);
990
920
  }
991
- let promptCtx = null;
992
- try {
993
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
994
- promptCtx = getPromptContext();
995
- } catch {
996
- }
997
- const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
921
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
998
922
  const traceId = traceCtx?.traceId || generateHexId(32);
999
923
  const spanId = generateHexId(16);
1000
924
  const parentSpanId = traceCtx?.parentSpanId;
1001
925
  const params = args[0] || {};
1002
926
  const startTime = Date.now();
927
+ const captureContent2 = shouldCaptureContent();
1003
928
  try {
1004
929
  const response = await originalCreate(...args);
1005
930
  const endTime = Date.now();
1006
- const attributes = captureContent ? messagesToOtelAttributes(
931
+ const attributes = captureContent2 ? messagesToOtelAttributes(
1007
932
  params?.messages,
1008
933
  { role: "assistant", content: response?.content?.[0]?.text || "" },
1009
934
  response?.model || params?.model,
1010
935
  response?.id
1011
- ) : void 0;
1012
- if (attributes && params?.system) {
936
+ ) : {};
937
+ if (params?.system) {
1013
938
  attributes["gen_ai.system_prompt"] = params.system;
1014
939
  }
940
+ if (response?.usage) {
941
+ attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
942
+ }
943
+ if (response?.stop_reason) {
944
+ attributes["gen_ai.response.finish_reason"] = response.stop_reason;
945
+ }
1015
946
  sendTrace({
1016
947
  config_key: ctx.configKey,
1017
948
  session_id: ctx.sessionId,
@@ -1029,17 +960,13 @@ function wrapAnthropic(client) {
1029
960
  prompt_tokens: response?.usage?.input_tokens,
1030
961
  completion_tokens: response?.usage?.output_tokens,
1031
962
  total_tokens: (response?.usage?.input_tokens || 0) + (response?.usage?.output_tokens || 0),
1032
- attributes,
1033
- prompt_key: promptCtx?.promptKey,
1034
- prompt_version: promptCtx?.promptVersion,
1035
- prompt_ab_test_key: promptCtx?.abTestKey,
1036
- prompt_variant_index: promptCtx?.variantIndex
963
+ attributes: Object.keys(attributes).length > 0 ? attributes : void 0
1037
964
  }).catch(() => {
1038
965
  });
1039
966
  return response;
1040
967
  } catch (error) {
1041
968
  const endTime = Date.now();
1042
- const attributes = captureContent ? messagesToOtelAttributes(
969
+ const attributes = captureContent2 ? messagesToOtelAttributes(
1043
970
  params?.messages,
1044
971
  void 0,
1045
972
  params?.model,
@@ -1066,11 +993,7 @@ function wrapAnthropic(client) {
1066
993
  duration_ms: endTime - startTime,
1067
994
  status: "ERROR",
1068
995
  error_message: error?.message,
1069
- attributes,
1070
- prompt_key: promptCtx?.promptKey,
1071
- prompt_version: promptCtx?.promptVersion,
1072
- prompt_ab_test_key: promptCtx?.abTestKey,
1073
- prompt_variant_index: promptCtx?.variantIndex
996
+ attributes
1074
997
  }).catch(() => {
1075
998
  });
1076
999
  throw error;
@@ -1078,24 +1001,21 @@ function wrapAnthropic(client) {
1078
1001
  };
1079
1002
  return client;
1080
1003
  }
1081
- function wrapGoogleAI(model) {
1004
+
1005
+ // src/trace/wrappers/google-ai.ts
1006
+ function wrapGoogleAI(model, sessionCtx) {
1082
1007
  const originalGenerate = model.generateContent.bind(model);
1008
+ const ctx = sessionCtx;
1083
1009
  model.generateContent = async function(...args) {
1084
- const ctx = sessionStorage.getStore() || fallbackSession;
1085
- if (!ctx || !initialized) {
1010
+ if (!isInitialized()) {
1086
1011
  return originalGenerate(...args);
1087
1012
  }
1088
- let promptCtx = null;
1089
- try {
1090
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
1091
- promptCtx = getPromptContext();
1092
- } catch {
1093
- }
1094
- const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1013
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1095
1014
  const traceId = traceCtx?.traceId || generateHexId(32);
1096
1015
  const spanId = generateHexId(16);
1097
1016
  const parentSpanId = traceCtx?.parentSpanId;
1098
1017
  const startTime = Date.now();
1018
+ const captureContent2 = shouldCaptureContent();
1099
1019
  try {
1100
1020
  const response = await originalGenerate(...args);
1101
1021
  const endTime = Date.now();
@@ -1103,7 +1023,7 @@ function wrapGoogleAI(model) {
1103
1023
  const usage = result?.usageMetadata;
1104
1024
  const modelName = model?.model || "gemini";
1105
1025
  const attributes = {};
1106
- if (captureContent) {
1026
+ if (captureContent2) {
1107
1027
  attributes["gen_ai.request.model"] = modelName;
1108
1028
  attributes["gen_ai.response.model"] = modelName;
1109
1029
  const input = args[0];
@@ -1122,6 +1042,13 @@ function wrapGoogleAI(model) {
1122
1042
  attributes["gen_ai.completion.0.content"] = outputText;
1123
1043
  }
1124
1044
  }
1045
+ if (usage) {
1046
+ attributes["fallom.raw.usage"] = JSON.stringify(usage);
1047
+ }
1048
+ const candidate = result?.candidates?.[0];
1049
+ if (candidate?.finishReason) {
1050
+ attributes["gen_ai.response.finish_reason"] = candidate.finishReason;
1051
+ }
1125
1052
  sendTrace({
1126
1053
  config_key: ctx.configKey,
1127
1054
  session_id: ctx.sessionId,
@@ -1139,11 +1066,7 @@ function wrapGoogleAI(model) {
1139
1066
  prompt_tokens: usage?.promptTokenCount,
1140
1067
  completion_tokens: usage?.candidatesTokenCount,
1141
1068
  total_tokens: usage?.totalTokenCount,
1142
- attributes: captureContent ? attributes : void 0,
1143
- prompt_key: promptCtx?.promptKey,
1144
- prompt_version: promptCtx?.promptVersion,
1145
- prompt_ab_test_key: promptCtx?.abTestKey,
1146
- prompt_variant_index: promptCtx?.variantIndex
1069
+ attributes: Object.keys(attributes).length > 0 ? attributes : void 0
1147
1070
  }).catch(() => {
1148
1071
  });
1149
1072
  return response;
@@ -1151,7 +1074,7 @@ function wrapGoogleAI(model) {
1151
1074
  const endTime = Date.now();
1152
1075
  const modelName = model?.model || "gemini";
1153
1076
  const attributes = {};
1154
- if (captureContent) {
1077
+ if (captureContent2) {
1155
1078
  attributes["gen_ai.request.model"] = modelName;
1156
1079
  attributes["error.message"] = error?.message;
1157
1080
  const input = args[0];
@@ -1175,11 +1098,7 @@ function wrapGoogleAI(model) {
1175
1098
  duration_ms: endTime - startTime,
1176
1099
  status: "ERROR",
1177
1100
  error_message: error?.message,
1178
- attributes: captureContent ? attributes : void 0,
1179
- prompt_key: promptCtx?.promptKey,
1180
- prompt_version: promptCtx?.promptVersion,
1181
- prompt_ab_test_key: promptCtx?.abTestKey,
1182
- prompt_variant_index: promptCtx?.variantIndex
1101
+ attributes: captureContent2 ? attributes : void 0
1183
1102
  }).catch(() => {
1184
1103
  });
1185
1104
  throw error;
@@ -1187,39 +1106,70 @@ function wrapGoogleAI(model) {
1187
1106
  };
1188
1107
  return model;
1189
1108
  }
1190
- function wrapAISDK(ai) {
1191
- const aiModule = ai;
1192
- return {
1193
- generateText: createGenerateTextWrapper(aiModule),
1194
- streamText: createStreamTextWrapper(aiModule),
1195
- generateObject: aiModule.generateObject ? createGenerateObjectWrapper(aiModule) : void 0,
1196
- streamObject: aiModule.streamObject ? createStreamObjectWrapper(aiModule) : void 0
1197
- };
1109
+
1110
+ // src/trace/wrappers/vercel-ai/utils.ts
1111
+ function extractUsageFromResult(result, directUsage) {
1112
+ let usage = directUsage ?? result?.usage;
1113
+ const isValidNumber = (v) => v !== null && v !== void 0 && !Number.isNaN(v);
1114
+ let promptTokens = isValidNumber(usage?.promptTokens) ? usage.promptTokens : void 0;
1115
+ let completionTokens = isValidNumber(usage?.completionTokens) ? usage.completionTokens : void 0;
1116
+ let totalTokens = isValidNumber(usage?.totalTokens) ? usage.totalTokens : void 0;
1117
+ let cost;
1118
+ const orUsage = result?.experimental_providerMetadata?.openrouter?.usage;
1119
+ if (orUsage) {
1120
+ if (promptTokens === void 0 && isValidNumber(orUsage.promptTokens)) {
1121
+ promptTokens = orUsage.promptTokens;
1122
+ }
1123
+ if (completionTokens === void 0 && isValidNumber(orUsage.completionTokens)) {
1124
+ completionTokens = orUsage.completionTokens;
1125
+ }
1126
+ if (totalTokens === void 0 && isValidNumber(orUsage.totalTokens)) {
1127
+ totalTokens = orUsage.totalTokens;
1128
+ }
1129
+ if (isValidNumber(orUsage.cost)) {
1130
+ cost = orUsage.cost;
1131
+ }
1132
+ }
1133
+ if (totalTokens === void 0 && (promptTokens !== void 0 || completionTokens !== void 0)) {
1134
+ totalTokens = (promptTokens ?? 0) + (completionTokens ?? 0);
1135
+ }
1136
+ return { promptTokens, completionTokens, totalTokens, cost };
1198
1137
  }
1199
- function createGenerateTextWrapper(aiModule) {
1138
+
1139
+ // src/trace/wrappers/vercel-ai/generate-text.ts
1140
+ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
1141
+ const ctx = sessionCtx;
1200
1142
  return async (...args) => {
1201
- const ctx = sessionStorage.getStore() || fallbackSession;
1202
- if (!ctx || !initialized) {
1143
+ if (!isInitialized()) {
1203
1144
  return aiModule.generateText(...args);
1204
1145
  }
1205
- let promptCtx = null;
1206
- try {
1207
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
1208
- promptCtx = getPromptContext();
1209
- } catch {
1210
- }
1211
- const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1146
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1212
1147
  const traceId = traceCtx?.traceId || generateHexId(32);
1213
1148
  const spanId = generateHexId(16);
1214
1149
  const parentSpanId = traceCtx?.parentSpanId;
1215
1150
  const params = args[0] || {};
1216
1151
  const startTime = Date.now();
1152
+ const captureContent2 = shouldCaptureContent();
1217
1153
  try {
1218
1154
  const result = await aiModule.generateText(...args);
1219
1155
  const endTime = Date.now();
1156
+ if (debug || isDebugMode()) {
1157
+ console.log(
1158
+ "\n\u{1F50D} [Fallom Debug] generateText result keys:",
1159
+ Object.keys(result || {})
1160
+ );
1161
+ console.log(
1162
+ "\u{1F50D} [Fallom Debug] result.usage:",
1163
+ JSON.stringify(result?.usage, null, 2)
1164
+ );
1165
+ console.log(
1166
+ "\u{1F50D} [Fallom Debug] result.experimental_providerMetadata:",
1167
+ JSON.stringify(result?.experimental_providerMetadata, null, 2)
1168
+ );
1169
+ }
1220
1170
  const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
1221
1171
  const attributes = {};
1222
- if (captureContent) {
1172
+ if (captureContent2) {
1223
1173
  attributes["gen_ai.request.model"] = modelId;
1224
1174
  attributes["gen_ai.response.model"] = modelId;
1225
1175
  if (params?.prompt) {
@@ -1240,6 +1190,18 @@ function createGenerateTextWrapper(aiModule) {
1240
1190
  attributes["gen_ai.response.id"] = result.response.id;
1241
1191
  }
1242
1192
  }
1193
+ if (result?.usage) {
1194
+ attributes["fallom.raw.usage"] = JSON.stringify(result.usage);
1195
+ }
1196
+ if (result?.experimental_providerMetadata) {
1197
+ attributes["fallom.raw.providerMetadata"] = JSON.stringify(
1198
+ result.experimental_providerMetadata
1199
+ );
1200
+ }
1201
+ if (result?.finishReason) {
1202
+ attributes["gen_ai.response.finish_reason"] = result.finishReason;
1203
+ }
1204
+ const usage = extractUsageFromResult(result);
1243
1205
  sendTrace({
1244
1206
  config_key: ctx.configKey,
1245
1207
  session_id: ctx.sessionId,
@@ -1254,14 +1216,10 @@ function createGenerateTextWrapper(aiModule) {
1254
1216
  end_time: new Date(endTime).toISOString(),
1255
1217
  duration_ms: endTime - startTime,
1256
1218
  status: "OK",
1257
- prompt_tokens: result?.usage?.promptTokens,
1258
- completion_tokens: result?.usage?.completionTokens,
1259
- total_tokens: result?.usage?.totalTokens,
1260
- attributes: captureContent ? attributes : void 0,
1261
- prompt_key: promptCtx?.promptKey,
1262
- prompt_version: promptCtx?.promptVersion,
1263
- prompt_ab_test_key: promptCtx?.abTestKey,
1264
- prompt_variant_index: promptCtx?.variantIndex
1219
+ prompt_tokens: usage.promptTokens,
1220
+ completion_tokens: usage.completionTokens,
1221
+ total_tokens: usage.totalTokens,
1222
+ attributes: captureContent2 ? attributes : void 0
1265
1223
  }).catch(() => {
1266
1224
  });
1267
1225
  return result;
@@ -1282,44 +1240,58 @@ function createGenerateTextWrapper(aiModule) {
1282
1240
  end_time: new Date(endTime).toISOString(),
1283
1241
  duration_ms: endTime - startTime,
1284
1242
  status: "ERROR",
1285
- error_message: error?.message,
1286
- prompt_key: promptCtx?.promptKey,
1287
- prompt_version: promptCtx?.promptVersion,
1288
- prompt_ab_test_key: promptCtx?.abTestKey,
1289
- prompt_variant_index: promptCtx?.variantIndex
1243
+ error_message: error?.message
1290
1244
  }).catch(() => {
1291
1245
  });
1292
1246
  throw error;
1293
1247
  }
1294
1248
  };
1295
1249
  }
1296
- function createStreamTextWrapper(aiModule) {
1250
+
1251
+ // src/trace/wrappers/vercel-ai/stream-text.ts
1252
+ function log2(...args) {
1253
+ if (isDebugMode()) console.log("[Fallom]", ...args);
1254
+ }
1255
+ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
1256
+ const ctx = sessionCtx;
1297
1257
  return async (...args) => {
1298
- const ctx = sessionStorage.getStore() || fallbackSession;
1299
1258
  const params = args[0] || {};
1300
1259
  const startTime = Date.now();
1260
+ const captureContent2 = shouldCaptureContent();
1301
1261
  const result = await aiModule.streamText(...args);
1302
- if (!ctx || !initialized) {
1262
+ if (!isInitialized()) {
1303
1263
  return result;
1304
1264
  }
1305
- const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1265
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1306
1266
  const traceId = traceCtx?.traceId || generateHexId(32);
1307
1267
  const spanId = generateHexId(16);
1308
1268
  const parentSpanId = traceCtx?.parentSpanId;
1309
1269
  let firstTokenTime = null;
1310
1270
  const modelId = params?.model?.modelId || String(params?.model || "unknown");
1311
- let promptCtx = null;
1312
- try {
1313
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
1314
- promptCtx = getPromptContext();
1315
- } catch {
1316
- }
1317
1271
  if (result?.usage) {
1318
- result.usage.then((usage) => {
1272
+ result.usage.then(async (rawUsage) => {
1319
1273
  const endTime = Date.now();
1320
- log("\u{1F4CA} streamText usage:", JSON.stringify(usage, null, 2));
1274
+ if (debug || isDebugMode()) {
1275
+ console.log(
1276
+ "\n\u{1F50D} [Fallom Debug] streamText usage:",
1277
+ JSON.stringify(rawUsage, null, 2)
1278
+ );
1279
+ }
1280
+ log2("\u{1F4CA} streamText usage:", JSON.stringify(rawUsage, null, 2));
1281
+ let providerMetadata = result?.experimental_providerMetadata;
1282
+ if (providerMetadata && typeof providerMetadata.then === "function") {
1283
+ try {
1284
+ providerMetadata = await providerMetadata;
1285
+ } catch {
1286
+ providerMetadata = void 0;
1287
+ }
1288
+ }
1289
+ const usage = extractUsageFromResult(
1290
+ { experimental_providerMetadata: providerMetadata },
1291
+ rawUsage
1292
+ );
1321
1293
  const attributes = {};
1322
- if (captureContent) {
1294
+ if (captureContent2) {
1323
1295
  attributes["gen_ai.request.model"] = modelId;
1324
1296
  if (params?.prompt) {
1325
1297
  attributes["gen_ai.prompt.0.role"] = "user";
@@ -1329,6 +1301,12 @@ function createStreamTextWrapper(aiModule) {
1329
1301
  if (firstTokenTime) {
1330
1302
  attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
1331
1303
  }
1304
+ if (rawUsage) {
1305
+ attributes["fallom.raw.usage"] = JSON.stringify(rawUsage);
1306
+ }
1307
+ if (providerMetadata) {
1308
+ attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
1309
+ }
1332
1310
  const tracePayload = {
1333
1311
  config_key: ctx.configKey,
1334
1312
  session_id: ctx.sessionId,
@@ -1343,21 +1321,17 @@ function createStreamTextWrapper(aiModule) {
1343
1321
  end_time: new Date(endTime).toISOString(),
1344
1322
  duration_ms: endTime - startTime,
1345
1323
  status: "OK",
1346
- prompt_tokens: usage?.promptTokens,
1347
- completion_tokens: usage?.completionTokens,
1348
- total_tokens: usage?.totalTokens,
1324
+ prompt_tokens: usage.promptTokens,
1325
+ completion_tokens: usage.completionTokens,
1326
+ total_tokens: usage.totalTokens,
1349
1327
  time_to_first_token_ms: firstTokenTime ? firstTokenTime - startTime : void 0,
1350
- attributes: captureContent ? attributes : void 0,
1351
- prompt_key: promptCtx?.promptKey,
1352
- prompt_version: promptCtx?.promptVersion,
1353
- prompt_ab_test_key: promptCtx?.abTestKey,
1354
- prompt_variant_index: promptCtx?.variantIndex
1328
+ attributes: captureContent2 ? attributes : void 0
1355
1329
  };
1356
1330
  sendTrace(tracePayload).catch(() => {
1357
1331
  });
1358
1332
  }).catch((error) => {
1359
1333
  const endTime = Date.now();
1360
- log("\u274C streamText error:", error?.message);
1334
+ log2("\u274C streamText error:", error?.message);
1361
1335
  sendTrace({
1362
1336
  config_key: ctx.configKey,
1363
1337
  session_id: ctx.sessionId,
@@ -1372,11 +1346,7 @@ function createStreamTextWrapper(aiModule) {
1372
1346
  end_time: new Date(endTime).toISOString(),
1373
1347
  duration_ms: endTime - startTime,
1374
1348
  status: "ERROR",
1375
- error_message: error?.message,
1376
- prompt_key: promptCtx?.promptKey,
1377
- prompt_version: promptCtx?.promptVersion,
1378
- prompt_ab_test_key: promptCtx?.abTestKey,
1379
- prompt_variant_index: promptCtx?.variantIndex
1349
+ error_message: error?.message
1380
1350
  }).catch(() => {
1381
1351
  });
1382
1352
  });
@@ -1387,7 +1357,7 @@ function createStreamTextWrapper(aiModule) {
1387
1357
  for await (const chunk of originalTextStream) {
1388
1358
  if (!firstTokenTime) {
1389
1359
  firstTokenTime = Date.now();
1390
- log("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
1360
+ log2("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
1391
1361
  }
1392
1362
  yield chunk;
1393
1363
  }
@@ -1404,30 +1374,37 @@ function createStreamTextWrapper(aiModule) {
1404
1374
  return result;
1405
1375
  };
1406
1376
  }
1407
- function createGenerateObjectWrapper(aiModule) {
1377
+
1378
+ // src/trace/wrappers/vercel-ai/generate-object.ts
1379
+ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
1380
+ const ctx = sessionCtx;
1408
1381
  return async (...args) => {
1409
- const ctx = sessionStorage.getStore() || fallbackSession;
1410
- if (!ctx || !initialized) {
1382
+ if (!isInitialized()) {
1411
1383
  return aiModule.generateObject(...args);
1412
1384
  }
1413
- let promptCtx = null;
1414
- try {
1415
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
1416
- promptCtx = getPromptContext();
1417
- } catch {
1418
- }
1419
- const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1385
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1420
1386
  const traceId = traceCtx?.traceId || generateHexId(32);
1421
1387
  const spanId = generateHexId(16);
1422
1388
  const parentSpanId = traceCtx?.parentSpanId;
1423
1389
  const params = args[0] || {};
1424
1390
  const startTime = Date.now();
1391
+ const captureContent2 = shouldCaptureContent();
1425
1392
  try {
1426
1393
  const result = await aiModule.generateObject(...args);
1427
1394
  const endTime = Date.now();
1395
+ if (debug || isDebugMode()) {
1396
+ console.log(
1397
+ "\n\u{1F50D} [Fallom Debug] generateObject result keys:",
1398
+ Object.keys(result || {})
1399
+ );
1400
+ console.log(
1401
+ "\u{1F50D} [Fallom Debug] result.usage:",
1402
+ JSON.stringify(result?.usage, null, 2)
1403
+ );
1404
+ }
1428
1405
  const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
1429
1406
  const attributes = {};
1430
- if (captureContent) {
1407
+ if (captureContent2) {
1431
1408
  attributes["gen_ai.request.model"] = modelId;
1432
1409
  attributes["gen_ai.response.model"] = modelId;
1433
1410
  if (result?.object) {
@@ -1437,6 +1414,18 @@ function createGenerateObjectWrapper(aiModule) {
1437
1414
  );
1438
1415
  }
1439
1416
  }
1417
+ if (result?.usage) {
1418
+ attributes["fallom.raw.usage"] = JSON.stringify(result.usage);
1419
+ }
1420
+ if (result?.experimental_providerMetadata) {
1421
+ attributes["fallom.raw.providerMetadata"] = JSON.stringify(
1422
+ result.experimental_providerMetadata
1423
+ );
1424
+ }
1425
+ if (result?.finishReason) {
1426
+ attributes["gen_ai.response.finish_reason"] = result.finishReason;
1427
+ }
1428
+ const usage = extractUsageFromResult(result);
1440
1429
  sendTrace({
1441
1430
  config_key: ctx.configKey,
1442
1431
  session_id: ctx.sessionId,
@@ -1451,14 +1440,10 @@ function createGenerateObjectWrapper(aiModule) {
1451
1440
  end_time: new Date(endTime).toISOString(),
1452
1441
  duration_ms: endTime - startTime,
1453
1442
  status: "OK",
1454
- prompt_tokens: result?.usage?.promptTokens,
1455
- completion_tokens: result?.usage?.completionTokens,
1456
- total_tokens: result?.usage?.totalTokens,
1457
- attributes: captureContent ? attributes : void 0,
1458
- prompt_key: promptCtx?.promptKey,
1459
- prompt_version: promptCtx?.promptVersion,
1460
- prompt_ab_test_key: promptCtx?.abTestKey,
1461
- prompt_variant_index: promptCtx?.variantIndex
1443
+ prompt_tokens: usage.promptTokens,
1444
+ completion_tokens: usage.completionTokens,
1445
+ total_tokens: usage.totalTokens,
1446
+ attributes: captureContent2 ? attributes : void 0
1462
1447
  }).catch(() => {
1463
1448
  });
1464
1449
  return result;
@@ -1479,50 +1464,70 @@ function createGenerateObjectWrapper(aiModule) {
1479
1464
  end_time: new Date(endTime).toISOString(),
1480
1465
  duration_ms: endTime - startTime,
1481
1466
  status: "ERROR",
1482
- error_message: error?.message,
1483
- prompt_key: promptCtx?.promptKey,
1484
- prompt_version: promptCtx?.promptVersion,
1485
- prompt_ab_test_key: promptCtx?.abTestKey,
1486
- prompt_variant_index: promptCtx?.variantIndex
1467
+ error_message: error?.message
1487
1468
  }).catch(() => {
1488
1469
  });
1489
1470
  throw error;
1490
1471
  }
1491
1472
  };
1492
1473
  }
1493
- function createStreamObjectWrapper(aiModule) {
1474
+
1475
+ // src/trace/wrappers/vercel-ai/stream-object.ts
1476
+ function log3(...args) {
1477
+ if (isDebugMode()) console.log("[Fallom]", ...args);
1478
+ }
1479
+ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
1480
+ const ctx = sessionCtx;
1494
1481
  return async (...args) => {
1495
- const ctx = sessionStorage.getStore() || fallbackSession;
1496
1482
  const params = args[0] || {};
1497
1483
  const startTime = Date.now();
1484
+ const captureContent2 = shouldCaptureContent();
1498
1485
  const result = await aiModule.streamObject(...args);
1499
- log("\u{1F50D} streamObject result keys:", Object.keys(result || {}));
1500
- if (!ctx || !initialized) {
1486
+ log3("\u{1F50D} streamObject result keys:", Object.keys(result || {}));
1487
+ if (!isInitialized()) {
1501
1488
  return result;
1502
1489
  }
1503
- const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1490
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1504
1491
  const traceId = traceCtx?.traceId || generateHexId(32);
1505
1492
  const spanId = generateHexId(16);
1506
1493
  const parentSpanId = traceCtx?.parentSpanId;
1507
1494
  let firstTokenTime = null;
1508
1495
  const modelId = params?.model?.modelId || String(params?.model || "unknown");
1509
- let promptCtx = null;
1510
- try {
1511
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
1512
- promptCtx = getPromptContext();
1513
- } catch {
1514
- }
1515
1496
  if (result?.usage) {
1516
- result.usage.then((usage) => {
1497
+ result.usage.then(async (rawUsage) => {
1517
1498
  const endTime = Date.now();
1518
- log("\u{1F4CA} streamObject usage:", JSON.stringify(usage, null, 2));
1499
+ if (debug || isDebugMode()) {
1500
+ console.log(
1501
+ "\n\u{1F50D} [Fallom Debug] streamObject usage:",
1502
+ JSON.stringify(rawUsage, null, 2)
1503
+ );
1504
+ }
1505
+ log3("\u{1F4CA} streamObject usage:", JSON.stringify(rawUsage, null, 2));
1506
+ let providerMetadata = result?.experimental_providerMetadata;
1507
+ if (providerMetadata && typeof providerMetadata.then === "function") {
1508
+ try {
1509
+ providerMetadata = await providerMetadata;
1510
+ } catch {
1511
+ providerMetadata = void 0;
1512
+ }
1513
+ }
1514
+ const usage = extractUsageFromResult(
1515
+ { experimental_providerMetadata: providerMetadata },
1516
+ rawUsage
1517
+ );
1519
1518
  const attributes = {};
1520
- if (captureContent) {
1519
+ if (captureContent2) {
1521
1520
  attributes["gen_ai.request.model"] = modelId;
1522
1521
  }
1523
1522
  if (firstTokenTime) {
1524
1523
  attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
1525
1524
  }
1525
+ if (rawUsage) {
1526
+ attributes["fallom.raw.usage"] = JSON.stringify(rawUsage);
1527
+ }
1528
+ if (providerMetadata) {
1529
+ attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
1530
+ }
1526
1531
  sendTrace({
1527
1532
  config_key: ctx.configKey,
1528
1533
  session_id: ctx.sessionId,
@@ -1537,14 +1542,10 @@ function createStreamObjectWrapper(aiModule) {
1537
1542
  end_time: new Date(endTime).toISOString(),
1538
1543
  duration_ms: endTime - startTime,
1539
1544
  status: "OK",
1540
- prompt_tokens: usage?.promptTokens,
1541
- completion_tokens: usage?.completionTokens,
1542
- total_tokens: usage?.totalTokens,
1543
- attributes: captureContent ? attributes : void 0,
1544
- prompt_key: promptCtx?.promptKey,
1545
- prompt_version: promptCtx?.promptVersion,
1546
- prompt_ab_test_key: promptCtx?.abTestKey,
1547
- prompt_variant_index: promptCtx?.variantIndex
1545
+ prompt_tokens: usage.promptTokens,
1546
+ completion_tokens: usage.completionTokens,
1547
+ total_tokens: usage.totalTokens,
1548
+ attributes: captureContent2 ? attributes : void 0
1548
1549
  }).catch(() => {
1549
1550
  });
1550
1551
  }).catch((error) => {
@@ -1563,11 +1564,7 @@ function createStreamObjectWrapper(aiModule) {
1563
1564
  end_time: new Date(endTime).toISOString(),
1564
1565
  duration_ms: endTime - startTime,
1565
1566
  status: "ERROR",
1566
- error_message: error?.message,
1567
- prompt_key: promptCtx?.promptKey,
1568
- prompt_version: promptCtx?.promptVersion,
1569
- prompt_ab_test_key: promptCtx?.abTestKey,
1570
- prompt_variant_index: promptCtx?.variantIndex
1567
+ error_message: error?.message
1571
1568
  }).catch(() => {
1572
1569
  });
1573
1570
  });
@@ -1578,7 +1575,7 @@ function createStreamObjectWrapper(aiModule) {
1578
1575
  for await (const chunk of originalStream) {
1579
1576
  if (!firstTokenTime) {
1580
1577
  firstTokenTime = Date.now();
1581
- log("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
1578
+ log3("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
1582
1579
  }
1583
1580
  yield chunk;
1584
1581
  }
@@ -1595,20 +1592,27 @@ function createStreamObjectWrapper(aiModule) {
1595
1592
  return result;
1596
1593
  };
1597
1594
  }
1598
- function wrapMastraAgent(agent) {
1595
+
1596
+ // src/trace/wrappers/vercel-ai/index.ts
1597
+ function wrapAISDK(ai, sessionCtx, options) {
1598
+ const debug = options?.debug ?? false;
1599
+ return {
1600
+ generateText: createGenerateTextWrapper(ai, sessionCtx, debug),
1601
+ streamText: createStreamTextWrapper(ai, sessionCtx, debug),
1602
+ generateObject: ai.generateObject ? createGenerateObjectWrapper(ai, sessionCtx, debug) : void 0,
1603
+ streamObject: ai.streamObject ? createStreamObjectWrapper(ai, sessionCtx, debug) : void 0
1604
+ };
1605
+ }
1606
+
1607
+ // src/trace/wrappers/mastra.ts
1608
+ function wrapMastraAgent(agent, sessionCtx) {
1599
1609
  const originalGenerate = agent.generate.bind(agent);
1600
1610
  const agentName = agent.name || "MastraAgent";
1611
+ const ctx = sessionCtx;
1601
1612
  agent.generate = async function(...args) {
1602
- const ctx = sessionStorage.getStore() || fallbackSession;
1603
- if (!ctx || !initialized) {
1613
+ if (!isInitialized()) {
1604
1614
  return originalGenerate(...args);
1605
1615
  }
1606
- let promptCtx = null;
1607
- try {
1608
- const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
1609
- promptCtx = getPromptContext();
1610
- } catch {
1611
- }
1612
1616
  const traceId = generateHexId(32);
1613
1617
  const spanId = generateHexId(16);
1614
1618
  const startTime = Date.now();
@@ -1680,11 +1684,7 @@ function wrapMastraAgent(agent) {
1680
1684
  prompt_tokens: result?.usage?.promptTokens,
1681
1685
  completion_tokens: result?.usage?.completionTokens,
1682
1686
  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
1687
+ attributes
1688
1688
  };
1689
1689
  sendTrace(traceData).catch(() => {
1690
1690
  });
@@ -1703,11 +1703,7 @@ function wrapMastraAgent(agent) {
1703
1703
  end_time: new Date(endTime).toISOString(),
1704
1704
  duration_ms: endTime - startTime,
1705
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
1706
+ error_message: error instanceof Error ? error.message : String(error)
1711
1707
  };
1712
1708
  sendTrace(traceData).catch(() => {
1713
1709
  });
@@ -1717,38 +1713,213 @@ function wrapMastraAgent(agent) {
1717
1713
  return agent;
1718
1714
  }
1719
1715
 
1720
- // src/models.ts
1721
- var models_exports = {};
1722
- __export(models_exports, {
1716
+ // src/trace/session.ts
1717
+ var FallomSession = class {
1718
+ constructor(options) {
1719
+ this.ctx = {
1720
+ configKey: options.configKey,
1721
+ sessionId: options.sessionId,
1722
+ customerId: options.customerId
1723
+ };
1724
+ }
1725
+ /** Get the session context. */
1726
+ getContext() {
1727
+ return { ...this.ctx };
1728
+ }
1729
+ /**
1730
+ * Get model assignment for this session (A/B testing).
1731
+ */
1732
+ async getModel(configKeyOrOptions, options) {
1733
+ let configKey;
1734
+ let opts;
1735
+ if (typeof configKeyOrOptions === "string") {
1736
+ configKey = configKeyOrOptions;
1737
+ opts = options || {};
1738
+ } else {
1739
+ configKey = this.ctx.configKey;
1740
+ opts = configKeyOrOptions || {};
1741
+ }
1742
+ const { get: get2 } = await import("./models-JKMOBZUO.mjs");
1743
+ return get2(configKey, this.ctx.sessionId, opts);
1744
+ }
1745
+ /**
1746
+ * Wrap a Vercel AI SDK model to trace all calls.
1747
+ */
1748
+ traceModel(model) {
1749
+ const ctx = this.ctx;
1750
+ const tracedModel = Object.create(model);
1751
+ if (model.doGenerate) {
1752
+ const originalDoGenerate = model.doGenerate.bind(model);
1753
+ tracedModel.doGenerate = async function(...args) {
1754
+ if (!isInitialized()) return originalDoGenerate(...args);
1755
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1756
+ const traceId = traceCtx?.traceId || generateHexId(32);
1757
+ const spanId = generateHexId(16);
1758
+ const startTime = Date.now();
1759
+ try {
1760
+ const result = await originalDoGenerate(...args);
1761
+ const endTime = Date.now();
1762
+ const modelId = model.modelId || "unknown";
1763
+ const usage = result?.usage || result?.rawResponse?.usage;
1764
+ sendTrace({
1765
+ config_key: ctx.configKey,
1766
+ session_id: ctx.sessionId,
1767
+ customer_id: ctx.customerId,
1768
+ trace_id: traceId,
1769
+ span_id: spanId,
1770
+ parent_span_id: traceCtx?.parentSpanId,
1771
+ name: "generateText",
1772
+ kind: "llm",
1773
+ model: modelId,
1774
+ start_time: new Date(startTime).toISOString(),
1775
+ end_time: new Date(endTime).toISOString(),
1776
+ duration_ms: endTime - startTime,
1777
+ status: "OK",
1778
+ prompt_tokens: usage?.promptTokens,
1779
+ completion_tokens: usage?.completionTokens,
1780
+ total_tokens: usage?.totalTokens,
1781
+ attributes: shouldCaptureContent() && usage ? { "fallom.raw.usage": JSON.stringify(usage) } : void 0
1782
+ }).catch(() => {
1783
+ });
1784
+ return result;
1785
+ } catch (error) {
1786
+ const endTime = Date.now();
1787
+ sendTrace({
1788
+ config_key: ctx.configKey,
1789
+ session_id: ctx.sessionId,
1790
+ customer_id: ctx.customerId,
1791
+ trace_id: traceId,
1792
+ span_id: spanId,
1793
+ parent_span_id: traceCtx?.parentSpanId,
1794
+ name: "generateText",
1795
+ kind: "llm",
1796
+ model: model.modelId || "unknown",
1797
+ start_time: new Date(startTime).toISOString(),
1798
+ end_time: new Date(endTime).toISOString(),
1799
+ duration_ms: endTime - startTime,
1800
+ status: "ERROR",
1801
+ error_message: error instanceof Error ? error.message : String(error)
1802
+ }).catch(() => {
1803
+ });
1804
+ throw error;
1805
+ }
1806
+ };
1807
+ }
1808
+ if (model.doStream) {
1809
+ const originalDoStream = model.doStream.bind(model);
1810
+ tracedModel.doStream = async function(...args) {
1811
+ if (!isInitialized()) return originalDoStream(...args);
1812
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1813
+ const traceId = traceCtx?.traceId || generateHexId(32);
1814
+ const spanId = generateHexId(16);
1815
+ const startTime = Date.now();
1816
+ const modelId = model.modelId || "unknown";
1817
+ try {
1818
+ const result = await originalDoStream(...args);
1819
+ sendTrace({
1820
+ config_key: ctx.configKey,
1821
+ session_id: ctx.sessionId,
1822
+ customer_id: ctx.customerId,
1823
+ trace_id: traceId,
1824
+ span_id: spanId,
1825
+ parent_span_id: traceCtx?.parentSpanId,
1826
+ name: "streamText",
1827
+ kind: "llm",
1828
+ model: modelId,
1829
+ start_time: new Date(startTime).toISOString(),
1830
+ end_time: new Date(Date.now()).toISOString(),
1831
+ duration_ms: Date.now() - startTime,
1832
+ status: "OK",
1833
+ is_streaming: true
1834
+ }).catch(() => {
1835
+ });
1836
+ return result;
1837
+ } catch (error) {
1838
+ sendTrace({
1839
+ config_key: ctx.configKey,
1840
+ session_id: ctx.sessionId,
1841
+ customer_id: ctx.customerId,
1842
+ trace_id: traceId,
1843
+ span_id: spanId,
1844
+ parent_span_id: traceCtx?.parentSpanId,
1845
+ name: "streamText",
1846
+ kind: "llm",
1847
+ model: modelId,
1848
+ start_time: new Date(startTime).toISOString(),
1849
+ end_time: new Date(Date.now()).toISOString(),
1850
+ duration_ms: Date.now() - startTime,
1851
+ status: "ERROR",
1852
+ error_message: error instanceof Error ? error.message : String(error),
1853
+ is_streaming: true
1854
+ }).catch(() => {
1855
+ });
1856
+ throw error;
1857
+ }
1858
+ };
1859
+ }
1860
+ return tracedModel;
1861
+ }
1862
+ /** Wrap OpenAI client. Delegates to shared wrapper. */
1863
+ wrapOpenAI(client) {
1864
+ return wrapOpenAI(client, this.ctx);
1865
+ }
1866
+ /** Wrap Anthropic client. Delegates to shared wrapper. */
1867
+ wrapAnthropic(client) {
1868
+ return wrapAnthropic(client, this.ctx);
1869
+ }
1870
+ /** Wrap Google AI model. Delegates to shared wrapper. */
1871
+ wrapGoogleAI(model) {
1872
+ return wrapGoogleAI(model, this.ctx);
1873
+ }
1874
+ /** Wrap Vercel AI SDK. Delegates to shared wrapper. */
1875
+ wrapAISDK(ai, options) {
1876
+ return wrapAISDK(ai, this.ctx, options);
1877
+ }
1878
+ /** Wrap Mastra agent. Delegates to shared wrapper. */
1879
+ wrapMastraAgent(agent) {
1880
+ return wrapMastraAgent(agent, this.ctx);
1881
+ }
1882
+ };
1883
+ function session(options) {
1884
+ return new FallomSession(options);
1885
+ }
1886
+
1887
+ // src/prompts.ts
1888
+ var prompts_exports = {};
1889
+ __export(prompts_exports, {
1890
+ clearPromptContext: () => clearPromptContext,
1723
1891
  get: () => get,
1892
+ getAB: () => getAB,
1893
+ getPromptContext: () => getPromptContext,
1724
1894
  init: () => init3
1725
1895
  });
1726
1896
  import { createHash } from "crypto";
1727
1897
  var apiKey2 = null;
1728
- var baseUrl2 = "https://configs.fallom.com";
1898
+ var baseUrl2 = "https://prompts.fallom.com";
1729
1899
  var initialized2 = false;
1730
1900
  var syncInterval = null;
1731
1901
  var debugMode2 = false;
1732
- var configCache = /* @__PURE__ */ new Map();
1902
+ var promptCache = /* @__PURE__ */ new Map();
1903
+ var promptABCache = /* @__PURE__ */ new Map();
1904
+ var promptContext = null;
1733
1905
  var SYNC_TIMEOUT = 2e3;
1734
- var RECORD_TIMEOUT = 1e3;
1735
- function log2(msg) {
1906
+ function log4(msg) {
1736
1907
  if (debugMode2) {
1737
- console.log(`[Fallom] ${msg}`);
1908
+ console.log(`[Fallom Prompts] ${msg}`);
1738
1909
  }
1739
1910
  }
1740
1911
  function init3(options = {}) {
1741
1912
  apiKey2 = options.apiKey || process.env.FALLOM_API_KEY || null;
1742
- baseUrl2 = options.baseUrl || process.env.FALLOM_CONFIGS_URL || process.env.FALLOM_BASE_URL || "https://configs.fallom.com";
1913
+ baseUrl2 = options.baseUrl || process.env.FALLOM_PROMPTS_URL || process.env.FALLOM_BASE_URL || "https://prompts.fallom.com";
1743
1914
  initialized2 = true;
1744
1915
  if (!apiKey2) {
1745
1916
  return;
1746
1917
  }
1747
- fetchConfigs().catch(() => {
1918
+ fetchAll().catch(() => {
1748
1919
  });
1749
1920
  if (!syncInterval) {
1750
1921
  syncInterval = setInterval(() => {
1751
- fetchConfigs().catch(() => {
1922
+ fetchAll().catch(() => {
1752
1923
  });
1753
1924
  }, 3e4);
1754
1925
  syncInterval.unref();
@@ -1762,202 +1933,195 @@ function ensureInit() {
1762
1933
  }
1763
1934
  }
1764
1935
  }
1765
- async function fetchConfigs(timeout = SYNC_TIMEOUT) {
1766
- if (!apiKey2) {
1767
- log2("_fetchConfigs: No API key, skipping");
1768
- return;
1769
- }
1936
+ async function fetchAll() {
1937
+ await Promise.all([fetchPrompts(), fetchPromptABTests()]);
1938
+ }
1939
+ async function fetchPrompts(timeout = SYNC_TIMEOUT) {
1940
+ if (!apiKey2) return;
1770
1941
  try {
1771
- log2(`Fetching configs from ${baseUrl2}/configs`);
1772
1942
  const controller = new AbortController();
1773
1943
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1774
- const resp = await fetch(`${baseUrl2}/configs`, {
1944
+ const resp = await fetch(`${baseUrl2}/prompts`, {
1775
1945
  headers: { Authorization: `Bearer ${apiKey2}` },
1776
1946
  signal: controller.signal
1777
1947
  });
1778
1948
  clearTimeout(timeoutId);
1779
- log2(`Response status: ${resp.status}`);
1780
1949
  if (resp.ok) {
1781
1950
  const data = await resp.json();
1782
- const configs = data.configs || [];
1783
- log2(`Got ${configs.length} configs: ${configs.map((c) => c.key)}`);
1784
- for (const c of configs) {
1785
- const key = c.key;
1786
- const version = c.version || 1;
1787
- log2(`Config '${key}' v${version}: ${JSON.stringify(c.variants)}`);
1788
- if (!configCache.has(key)) {
1789
- configCache.set(key, { versions: /* @__PURE__ */ new Map(), latest: null });
1951
+ for (const p of data.prompts || []) {
1952
+ if (!promptCache.has(p.key)) {
1953
+ promptCache.set(p.key, { versions: /* @__PURE__ */ new Map(), current: null });
1790
1954
  }
1791
- const cached = configCache.get(key);
1792
- cached.versions.set(version, c);
1793
- cached.latest = version;
1955
+ const cached = promptCache.get(p.key);
1956
+ cached.versions.set(p.version, {
1957
+ systemPrompt: p.system_prompt,
1958
+ userTemplate: p.user_template
1959
+ });
1960
+ cached.current = p.version;
1794
1961
  }
1795
- } else {
1796
- log2(`Fetch failed: ${resp.statusText}`);
1797
1962
  }
1798
- } catch (e) {
1799
- log2(`Fetch exception: ${e}`);
1963
+ } catch {
1800
1964
  }
1801
1965
  }
1802
- async function fetchSpecificVersion(configKey, version, timeout = SYNC_TIMEOUT) {
1803
- if (!apiKey2) return null;
1966
+ async function fetchPromptABTests(timeout = SYNC_TIMEOUT) {
1967
+ if (!apiKey2) return;
1804
1968
  try {
1805
1969
  const controller = new AbortController();
1806
1970
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1807
- const resp = await fetch(
1808
- `${baseUrl2}/configs/${configKey}/version/${version}`,
1809
- {
1810
- headers: { Authorization: `Bearer ${apiKey2}` },
1811
- signal: controller.signal
1812
- }
1813
- );
1971
+ const resp = await fetch(`${baseUrl2}/prompt-ab-tests`, {
1972
+ headers: { Authorization: `Bearer ${apiKey2}` },
1973
+ signal: controller.signal
1974
+ });
1814
1975
  clearTimeout(timeoutId);
1815
1976
  if (resp.ok) {
1816
- const config = await resp.json();
1817
- if (!configCache.has(configKey)) {
1818
- configCache.set(configKey, { versions: /* @__PURE__ */ new Map(), latest: null });
1977
+ const data = await resp.json();
1978
+ for (const t of data.prompt_ab_tests || []) {
1979
+ if (!promptABCache.has(t.key)) {
1980
+ promptABCache.set(t.key, { versions: /* @__PURE__ */ new Map(), current: null });
1981
+ }
1982
+ const cached = promptABCache.get(t.key);
1983
+ cached.versions.set(t.version, { variants: t.variants });
1984
+ cached.current = t.version;
1819
1985
  }
1820
- configCache.get(configKey).versions.set(version, config);
1821
- return config;
1822
1986
  }
1823
1987
  } catch {
1824
1988
  }
1825
- return null;
1826
1989
  }
1827
- async function get(configKey, sessionId, options = {}) {
1828
- const { version, fallback, debug = false } = options;
1990
+ function replaceVariables(template, variables) {
1991
+ if (!variables) return template;
1992
+ return template.replace(/\{\{(\s*\w+\s*)\}\}/g, (match, varName) => {
1993
+ const key = varName.trim();
1994
+ return key in variables ? String(variables[key]) : match;
1995
+ });
1996
+ }
1997
+ function setPromptContext(ctx) {
1998
+ promptContext = ctx;
1999
+ }
2000
+ function getPromptContext() {
2001
+ const ctx = promptContext;
2002
+ promptContext = null;
2003
+ return ctx;
2004
+ }
2005
+ async function get(promptKey, options = {}) {
2006
+ const { variables, version, debug = false } = options;
1829
2007
  debugMode2 = debug;
1830
2008
  ensureInit();
1831
- log2(
1832
- `get() called: configKey=${configKey}, sessionId=${sessionId}, fallback=${fallback}`
1833
- );
1834
- try {
1835
- let configData = configCache.get(configKey);
1836
- log2(
1837
- `Cache lookup for '${configKey}': ${configData ? "found" : "not found"}`
2009
+ log4(`get() called: promptKey=${promptKey}`);
2010
+ let promptData = promptCache.get(promptKey);
2011
+ if (!promptData) {
2012
+ log4("Not in cache, fetching...");
2013
+ await fetchPrompts(SYNC_TIMEOUT);
2014
+ promptData = promptCache.get(promptKey);
2015
+ }
2016
+ if (!promptData) {
2017
+ throw new Error(
2018
+ `Prompt '${promptKey}' not found. Check that it exists in your Fallom dashboard.`
1838
2019
  );
1839
- if (!configData) {
1840
- log2("Not in cache, fetching...");
1841
- await fetchConfigs(SYNC_TIMEOUT);
1842
- configData = configCache.get(configKey);
1843
- log2(
1844
- `After fetch, cache lookup: ${configData ? "found" : "still not found"}`
1845
- );
1846
- }
1847
- if (!configData) {
1848
- log2(`Config not found, using fallback: ${fallback}`);
1849
- if (fallback) {
1850
- console.warn(
1851
- `[Fallom WARNING] Config '${configKey}' not found, using fallback model: ${fallback}`
1852
- );
1853
- return returnWithTrace(configKey, sessionId, fallback, 0);
1854
- }
1855
- throw new Error(
1856
- `Config '${configKey}' not found. Check that it exists in your Fallom dashboard.`
1857
- );
1858
- }
1859
- let config;
1860
- let targetVersion;
1861
- if (version !== void 0) {
1862
- config = configData.versions.get(version);
1863
- if (!config) {
1864
- config = await fetchSpecificVersion(configKey, version, SYNC_TIMEOUT) || void 0;
1865
- }
1866
- if (!config) {
1867
- if (fallback) {
1868
- console.warn(
1869
- `[Fallom WARNING] Config '${configKey}' version ${version} not found, using fallback: ${fallback}`
1870
- );
1871
- return returnWithTrace(configKey, sessionId, fallback, 0);
1872
- }
1873
- throw new Error(`Config '${configKey}' version ${version} not found.`);
1874
- }
1875
- targetVersion = version;
1876
- } else {
1877
- targetVersion = configData.latest;
1878
- config = configData.versions.get(targetVersion);
1879
- if (!config) {
1880
- if (fallback) {
1881
- console.warn(
1882
- `[Fallom WARNING] Config '${configKey}' has no cached version, using fallback: ${fallback}`
1883
- );
1884
- return returnWithTrace(configKey, sessionId, fallback, 0);
1885
- }
1886
- throw new Error(`Config '${configKey}' has no cached version.`);
1887
- }
1888
- }
1889
- const variantsRaw = config.variants;
1890
- const configVersion = config.version || targetVersion;
1891
- const variants = Array.isArray(variantsRaw) ? variantsRaw : Object.values(variantsRaw);
1892
- log2(
1893
- `Config found! Version: ${configVersion}, Variants: ${JSON.stringify(
1894
- variants
1895
- )}`
2020
+ }
2021
+ const targetVersion = version ?? promptData.current;
2022
+ const content = promptData.versions.get(targetVersion);
2023
+ if (!content) {
2024
+ throw new Error(
2025
+ `Prompt '${promptKey}' version ${targetVersion} not found.`
1896
2026
  );
1897
- const hashBytes = createHash("md5").update(sessionId).digest();
1898
- const hashVal = hashBytes.readUInt32BE(0) % 1e6;
1899
- log2(`Session hash: ${hashVal} (out of 1,000,000)`);
1900
- let cumulative = 0;
1901
- let assignedModel = variants[variants.length - 1].model;
1902
- for (const v of variants) {
1903
- const oldCumulative = cumulative;
1904
- cumulative += v.weight * 1e4;
1905
- log2(
1906
- `Variant ${v.model}: weight=${v.weight}%, range=${oldCumulative}-${cumulative}, hash=${hashVal}, match=${hashVal < cumulative}`
1907
- );
1908
- if (hashVal < cumulative) {
1909
- assignedModel = v.model;
1910
- break;
1911
- }
1912
- }
1913
- log2(`\u2705 Assigned model: ${assignedModel}`);
1914
- return returnWithTrace(configKey, sessionId, assignedModel, configVersion);
1915
- } catch (e) {
1916
- if (e instanceof Error && e.message.includes("not found")) {
1917
- throw e;
1918
- }
1919
- if (fallback) {
1920
- console.warn(
1921
- `[Fallom WARNING] Error getting model for '${configKey}': ${e}. Using fallback: ${fallback}`
1922
- );
1923
- return returnWithTrace(configKey, sessionId, fallback, 0);
1924
- }
1925
- throw e;
1926
2027
  }
2028
+ const system = replaceVariables(content.systemPrompt, variables);
2029
+ const user = replaceVariables(content.userTemplate, variables);
2030
+ setPromptContext({
2031
+ promptKey,
2032
+ promptVersion: targetVersion
2033
+ });
2034
+ log4(`\u2705 Got prompt: ${promptKey} v${targetVersion}`);
2035
+ return {
2036
+ key: promptKey,
2037
+ version: targetVersion,
2038
+ system,
2039
+ user
2040
+ };
1927
2041
  }
1928
- function returnWithTrace(configKey, sessionId, model, version) {
1929
- try {
1930
- setSession(configKey, sessionId);
1931
- } catch {
2042
+ async function getAB(abTestKey, sessionId, options = {}) {
2043
+ const { variables, debug = false } = options;
2044
+ debugMode2 = debug;
2045
+ ensureInit();
2046
+ log4(`getAB() called: abTestKey=${abTestKey}, sessionId=${sessionId}`);
2047
+ let abData = promptABCache.get(abTestKey);
2048
+ if (!abData) {
2049
+ log4("Not in cache, fetching...");
2050
+ await fetchPromptABTests(SYNC_TIMEOUT);
2051
+ abData = promptABCache.get(abTestKey);
1932
2052
  }
1933
- if (version > 0) {
1934
- recordSession(configKey, version, sessionId, model).catch(() => {
1935
- });
2053
+ if (!abData) {
2054
+ throw new Error(
2055
+ `Prompt A/B test '${abTestKey}' not found. Check that it exists in your Fallom dashboard.`
2056
+ );
1936
2057
  }
1937
- return model;
1938
- }
1939
- async function recordSession(configKey, version, sessionId, model) {
1940
- if (!apiKey2) return;
1941
- try {
1942
- const controller = new AbortController();
1943
- const timeoutId = setTimeout(() => controller.abort(), RECORD_TIMEOUT);
1944
- await fetch(`${baseUrl2}/sessions`, {
1945
- method: "POST",
1946
- headers: {
1947
- Authorization: `Bearer ${apiKey2}`,
1948
- "Content-Type": "application/json"
1949
- },
1950
- body: JSON.stringify({
1951
- config_key: configKey,
1952
- config_version: version,
1953
- session_id: sessionId,
1954
- assigned_model: model
1955
- }),
1956
- signal: controller.signal
1957
- });
1958
- clearTimeout(timeoutId);
1959
- } catch {
2058
+ const currentVersion = abData.current;
2059
+ const versionData = abData.versions.get(currentVersion);
2060
+ if (!versionData) {
2061
+ throw new Error(`Prompt A/B test '${abTestKey}' has no current version.`);
1960
2062
  }
2063
+ const { variants } = versionData;
2064
+ log4(`A/B test '${abTestKey}' has ${variants?.length ?? 0} variants`);
2065
+ log4(`Version data: ${JSON.stringify(versionData, null, 2)}`);
2066
+ if (!variants || variants.length === 0) {
2067
+ throw new Error(
2068
+ `Prompt A/B test '${abTestKey}' has no variants configured.`
2069
+ );
2070
+ }
2071
+ const hashBytes = createHash("md5").update(sessionId).digest();
2072
+ const hashVal = hashBytes.readUInt32BE(0) % 1e6;
2073
+ let cumulative = 0;
2074
+ let selectedVariant = variants[variants.length - 1];
2075
+ let selectedIndex = variants.length - 1;
2076
+ for (let i = 0; i < variants.length; i++) {
2077
+ cumulative += variants[i].weight * 1e4;
2078
+ if (hashVal < cumulative) {
2079
+ selectedVariant = variants[i];
2080
+ selectedIndex = i;
2081
+ break;
2082
+ }
2083
+ }
2084
+ const promptKey = selectedVariant.prompt_key;
2085
+ const promptVersion = selectedVariant.prompt_version;
2086
+ let promptData = promptCache.get(promptKey);
2087
+ if (!promptData) {
2088
+ await fetchPrompts(SYNC_TIMEOUT);
2089
+ promptData = promptCache.get(promptKey);
2090
+ }
2091
+ if (!promptData) {
2092
+ throw new Error(
2093
+ `Prompt '${promptKey}' (from A/B test '${abTestKey}') not found.`
2094
+ );
2095
+ }
2096
+ const targetVersion = promptVersion ?? promptData.current;
2097
+ const content = promptData.versions.get(targetVersion);
2098
+ if (!content) {
2099
+ throw new Error(
2100
+ `Prompt '${promptKey}' version ${targetVersion} not found.`
2101
+ );
2102
+ }
2103
+ const system = replaceVariables(content.systemPrompt, variables);
2104
+ const user = replaceVariables(content.userTemplate, variables);
2105
+ setPromptContext({
2106
+ promptKey,
2107
+ promptVersion: targetVersion,
2108
+ abTestKey,
2109
+ variantIndex: selectedIndex
2110
+ });
2111
+ log4(
2112
+ `\u2705 Got prompt from A/B: ${promptKey} v${targetVersion} (variant ${selectedIndex})`
2113
+ );
2114
+ return {
2115
+ key: promptKey,
2116
+ version: targetVersion,
2117
+ system,
2118
+ user,
2119
+ abTestKey,
2120
+ variantIndex: selectedIndex
2121
+ };
2122
+ }
2123
+ function clearPromptContext() {
2124
+ promptContext = null;
1961
2125
  }
1962
2126
 
1963
2127
  // src/init.ts
@@ -1971,11 +2135,11 @@ async function init4(options = {}) {
1971
2135
  captureContent: options.captureContent,
1972
2136
  debug: options.debug
1973
2137
  });
1974
- init3({
2138
+ init({
1975
2139
  apiKey: options.apiKey,
1976
2140
  baseUrl: configsUrl
1977
2141
  });
1978
- init({
2142
+ init3({
1979
2143
  apiKey: options.apiKey,
1980
2144
  baseUrl: promptsUrl
1981
2145
  });
@@ -1983,9 +2147,9 @@ async function init4(options = {}) {
1983
2147
 
1984
2148
  // src/mastra.ts
1985
2149
  import { ExportResultCode } from "@opentelemetry/core";
1986
- var promptContext = {};
2150
+ var promptContext2 = {};
1987
2151
  function setMastraPrompt(promptKey, version) {
1988
- promptContext = {
2152
+ promptContext2 = {
1989
2153
  promptKey,
1990
2154
  promptVersion: version,
1991
2155
  promptAbTestKey: void 0,
@@ -1993,7 +2157,7 @@ function setMastraPrompt(promptKey, version) {
1993
2157
  };
1994
2158
  }
1995
2159
  function setMastraPromptAB(abTestKey, variantIndex) {
1996
- promptContext = {
2160
+ promptContext2 = {
1997
2161
  promptKey: void 0,
1998
2162
  promptVersion: void 0,
1999
2163
  promptAbTestKey: abTestKey,
@@ -2001,7 +2165,7 @@ function setMastraPromptAB(abTestKey, variantIndex) {
2001
2165
  };
2002
2166
  }
2003
2167
  function clearMastraPrompt() {
2004
- promptContext = {};
2168
+ promptContext2 = {};
2005
2169
  }
2006
2170
  var FallomExporter = class {
2007
2171
  constructor(options = {}) {
@@ -2009,9 +2173,13 @@ var FallomExporter = class {
2009
2173
  this.apiKey = options.apiKey ?? process.env.FALLOM_API_KEY ?? "";
2010
2174
  this.baseUrl = options.baseUrl ?? "https://traces.fallom.com";
2011
2175
  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);
2176
+ this.session = options.session;
2177
+ if (this.debug) {
2178
+ console.log("[FallomExporter] Constructor called");
2179
+ console.log("[FallomExporter] API key present:", !!this.apiKey);
2180
+ console.log("[FallomExporter] Base URL:", this.baseUrl);
2181
+ console.log("[FallomExporter] Session:", this.session);
2182
+ }
2015
2183
  if (!this.apiKey) {
2016
2184
  console.warn(
2017
2185
  "[FallomExporter] No API key provided. Set FALLOM_API_KEY env var or pass apiKey option."
@@ -2033,10 +2201,10 @@ var FallomExporter = class {
2033
2201
  }
2034
2202
  this.log(`Exporting ${spans.length} spans...`);
2035
2203
  if (this.debug) {
2036
- for (const span2 of spans) {
2037
- this.log(` - ${span2.name}`, {
2204
+ for (const span of spans) {
2205
+ this.log(` - ${span.name}`, {
2038
2206
  attributes: Object.fromEntries(
2039
- Object.entries(span2.attributes).filter(
2207
+ Object.entries(span.attributes).filter(
2040
2208
  ([k]) => k.startsWith("gen_ai") || k.startsWith("llm")
2041
2209
  )
2042
2210
  )
@@ -2072,33 +2240,32 @@ var FallomExporter = class {
2072
2240
  * Send spans to Fallom's OTLP endpoint.
2073
2241
  */
2074
2242
  async sendSpans(spans) {
2075
- const session = getSession();
2076
2243
  const resourceSpans = this.spansToOtlpJson(spans);
2077
2244
  const headers = {
2078
2245
  "Content-Type": "application/json",
2079
2246
  Authorization: `Bearer ${this.apiKey}`
2080
2247
  };
2081
- if (session?.configKey) {
2082
- headers["X-Fallom-Config-Key"] = session.configKey;
2248
+ if (this.session?.configKey) {
2249
+ headers["X-Fallom-Config-Key"] = this.session.configKey;
2083
2250
  }
2084
- if (session?.sessionId) {
2085
- headers["X-Fallom-Session-Id"] = session.sessionId;
2251
+ if (this.session?.sessionId) {
2252
+ headers["X-Fallom-Session-Id"] = this.session.sessionId;
2086
2253
  }
2087
- if (session?.customerId) {
2088
- headers["X-Fallom-Customer-Id"] = session.customerId;
2254
+ if (this.session?.customerId) {
2255
+ headers["X-Fallom-Customer-Id"] = this.session.customerId;
2089
2256
  }
2090
- if (promptContext.promptKey) {
2091
- headers["X-Fallom-Prompt-Key"] = promptContext.promptKey;
2257
+ if (promptContext2.promptKey) {
2258
+ headers["X-Fallom-Prompt-Key"] = promptContext2.promptKey;
2092
2259
  }
2093
- if (promptContext.promptVersion !== void 0) {
2094
- headers["X-Fallom-Prompt-Version"] = String(promptContext.promptVersion);
2260
+ if (promptContext2.promptVersion !== void 0) {
2261
+ headers["X-Fallom-Prompt-Version"] = String(promptContext2.promptVersion);
2095
2262
  }
2096
- if (promptContext.promptAbTestKey) {
2097
- headers["X-Fallom-Prompt-AB-Test"] = promptContext.promptAbTestKey;
2263
+ if (promptContext2.promptAbTestKey) {
2264
+ headers["X-Fallom-Prompt-AB-Test"] = promptContext2.promptAbTestKey;
2098
2265
  }
2099
- if (promptContext.promptVariantIndex !== void 0) {
2266
+ if (promptContext2.promptVariantIndex !== void 0) {
2100
2267
  headers["X-Fallom-Prompt-Variant"] = String(
2101
- promptContext.promptVariantIndex
2268
+ promptContext2.promptVariantIndex
2102
2269
  );
2103
2270
  }
2104
2271
  const endpoint = `${this.baseUrl}/v1/traces`;
@@ -2122,12 +2289,12 @@ var FallomExporter = class {
2122
2289
  */
2123
2290
  spansToOtlpJson(spans) {
2124
2291
  const resourceMap = /* @__PURE__ */ new Map();
2125
- for (const span2 of spans) {
2126
- const resourceKey = JSON.stringify(span2.resource.attributes);
2292
+ for (const span of spans) {
2293
+ const resourceKey = JSON.stringify(span.resource.attributes);
2127
2294
  if (!resourceMap.has(resourceKey)) {
2128
2295
  resourceMap.set(resourceKey, []);
2129
2296
  }
2130
- resourceMap.get(resourceKey).push(span2);
2297
+ resourceMap.get(resourceKey).push(span);
2131
2298
  }
2132
2299
  const resourceSpans = [];
2133
2300
  for (const [_resourceKey, resourceSpanList] of resourceMap) {
@@ -2142,7 +2309,7 @@ var FallomExporter = class {
2142
2309
  name: firstSpan.instrumentationLibrary.name,
2143
2310
  version: firstSpan.instrumentationLibrary.version
2144
2311
  },
2145
- spans: resourceSpanList.map((span2) => this.spanToOtlp(span2))
2312
+ spans: resourceSpanList.map((span) => this.spanToOtlp(span))
2146
2313
  }
2147
2314
  ]
2148
2315
  });
@@ -2152,21 +2319,21 @@ var FallomExporter = class {
2152
2319
  /**
2153
2320
  * Convert a single span to OTLP format.
2154
2321
  */
2155
- spanToOtlp(span2) {
2322
+ spanToOtlp(span) {
2156
2323
  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),
2324
+ traceId: span.spanContext().traceId,
2325
+ spanId: span.spanContext().spanId,
2326
+ parentSpanId: span.parentSpanId,
2327
+ name: span.name,
2328
+ kind: span.kind,
2329
+ startTimeUnixNano: this.hrTimeToNanos(span.startTime),
2330
+ endTimeUnixNano: this.hrTimeToNanos(span.endTime),
2331
+ attributes: this.attributesToOtlp(span.attributes),
2165
2332
  status: {
2166
- code: span2.status.code,
2167
- message: span2.status.message
2333
+ code: span.status.code,
2334
+ message: span.status.message
2168
2335
  },
2169
- events: span2.events.map((event) => ({
2336
+ events: span.events.map((event) => ({
2170
2337
  timeUnixNano: this.hrTimeToNanos(event.time),
2171
2338
  name: event.name,
2172
2339
  attributes: this.attributesToOtlp(event.attributes || {})
@@ -2221,15 +2388,18 @@ var index_default = {
2221
2388
  init: init4,
2222
2389
  trace: trace_exports,
2223
2390
  models: models_exports,
2224
- prompts: prompts_exports
2391
+ prompts: prompts_exports,
2392
+ session
2225
2393
  };
2226
2394
  export {
2227
2395
  FallomExporter,
2396
+ FallomSession,
2228
2397
  clearMastraPrompt,
2229
2398
  index_default as default,
2230
2399
  init4 as init,
2231
2400
  models_exports as models,
2232
2401
  prompts_exports as prompts,
2402
+ session,
2233
2403
  setMastraPrompt,
2234
2404
  setMastraPromptAB,
2235
2405
  trace_exports as trace