@fallom/trace 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -17,12 +20,252 @@ var __copyProps = (to, from, except, desc) => {
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
22
 
23
+ // src/prompts.ts
24
+ var prompts_exports = {};
25
+ __export(prompts_exports, {
26
+ clearPromptContext: () => clearPromptContext,
27
+ get: () => get,
28
+ getAB: () => getAB,
29
+ getPromptContext: () => getPromptContext,
30
+ init: () => init
31
+ });
32
+ function log(msg) {
33
+ if (debugMode) {
34
+ console.log(`[Fallom Prompts] ${msg}`);
35
+ }
36
+ }
37
+ function init(options = {}) {
38
+ apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
39
+ baseUrl = options.baseUrl || process.env.FALLOM_BASE_URL || "https://spans.fallom.com";
40
+ initialized = true;
41
+ if (!apiKey) {
42
+ return;
43
+ }
44
+ fetchAll().catch(() => {
45
+ });
46
+ if (!syncInterval) {
47
+ syncInterval = setInterval(() => {
48
+ fetchAll().catch(() => {
49
+ });
50
+ }, 3e4);
51
+ syncInterval.unref();
52
+ }
53
+ }
54
+ function ensureInit() {
55
+ if (!initialized) {
56
+ try {
57
+ init();
58
+ } catch {
59
+ }
60
+ }
61
+ }
62
+ async function fetchAll() {
63
+ await Promise.all([fetchPrompts(), fetchPromptABTests()]);
64
+ }
65
+ async function fetchPrompts(timeout = SYNC_TIMEOUT) {
66
+ if (!apiKey) return;
67
+ try {
68
+ const controller = new AbortController();
69
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
70
+ const resp = await fetch(`${baseUrl}/prompts`, {
71
+ headers: { Authorization: `Bearer ${apiKey}` },
72
+ signal: controller.signal
73
+ });
74
+ clearTimeout(timeoutId);
75
+ if (resp.ok) {
76
+ const data = await resp.json();
77
+ for (const p of data.prompts || []) {
78
+ if (!promptCache.has(p.key)) {
79
+ promptCache.set(p.key, { versions: /* @__PURE__ */ new Map(), current: null });
80
+ }
81
+ const cached = promptCache.get(p.key);
82
+ cached.versions.set(p.version, {
83
+ systemPrompt: p.system_prompt,
84
+ userTemplate: p.user_template
85
+ });
86
+ cached.current = p.version;
87
+ }
88
+ }
89
+ } catch {
90
+ }
91
+ }
92
+ async function fetchPromptABTests(timeout = SYNC_TIMEOUT) {
93
+ if (!apiKey) return;
94
+ try {
95
+ const controller = new AbortController();
96
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
97
+ const resp = await fetch(`${baseUrl}/prompt-ab-tests`, {
98
+ headers: { Authorization: `Bearer ${apiKey}` },
99
+ signal: controller.signal
100
+ });
101
+ clearTimeout(timeoutId);
102
+ if (resp.ok) {
103
+ const data = await resp.json();
104
+ for (const t of data.prompt_ab_tests || []) {
105
+ if (!promptABCache.has(t.key)) {
106
+ promptABCache.set(t.key, { versions: /* @__PURE__ */ new Map(), current: null });
107
+ }
108
+ const cached = promptABCache.get(t.key);
109
+ cached.versions.set(t.version, { variants: t.variants });
110
+ cached.current = t.version;
111
+ }
112
+ }
113
+ } catch {
114
+ }
115
+ }
116
+ function replaceVariables(template, variables) {
117
+ if (!variables) return template;
118
+ return template.replace(/\{\{(\s*\w+\s*)\}\}/g, (match, varName) => {
119
+ const key = varName.trim();
120
+ return key in variables ? String(variables[key]) : match;
121
+ });
122
+ }
123
+ function setPromptContext(ctx) {
124
+ promptContext = ctx;
125
+ }
126
+ function getPromptContext() {
127
+ const ctx = promptContext;
128
+ promptContext = null;
129
+ return ctx;
130
+ }
131
+ async function get(promptKey, options = {}) {
132
+ const { variables, version, debug = false } = options;
133
+ debugMode = debug;
134
+ ensureInit();
135
+ log(`get() called: promptKey=${promptKey}`);
136
+ let promptData = promptCache.get(promptKey);
137
+ if (!promptData) {
138
+ log("Not in cache, fetching...");
139
+ await fetchPrompts(SYNC_TIMEOUT);
140
+ promptData = promptCache.get(promptKey);
141
+ }
142
+ if (!promptData) {
143
+ throw new Error(
144
+ `Prompt '${promptKey}' not found. Check that it exists in your Fallom dashboard.`
145
+ );
146
+ }
147
+ const targetVersion = version ?? promptData.current;
148
+ const content = promptData.versions.get(targetVersion);
149
+ if (!content) {
150
+ throw new Error(
151
+ `Prompt '${promptKey}' version ${targetVersion} not found.`
152
+ );
153
+ }
154
+ const system = replaceVariables(content.systemPrompt, variables);
155
+ const user = replaceVariables(content.userTemplate, variables);
156
+ setPromptContext({
157
+ promptKey,
158
+ promptVersion: targetVersion
159
+ });
160
+ log(`\u2705 Got prompt: ${promptKey} v${targetVersion}`);
161
+ return {
162
+ key: promptKey,
163
+ version: targetVersion,
164
+ system,
165
+ user
166
+ };
167
+ }
168
+ async function getAB(abTestKey, sessionId, options = {}) {
169
+ const { variables, debug = false } = options;
170
+ debugMode = debug;
171
+ ensureInit();
172
+ log(`getAB() called: abTestKey=${abTestKey}, sessionId=${sessionId}`);
173
+ let abData = promptABCache.get(abTestKey);
174
+ if (!abData) {
175
+ log("Not in cache, fetching...");
176
+ await fetchPromptABTests(SYNC_TIMEOUT);
177
+ abData = promptABCache.get(abTestKey);
178
+ }
179
+ if (!abData) {
180
+ throw new Error(
181
+ `Prompt A/B test '${abTestKey}' not found. Check that it exists in your Fallom dashboard.`
182
+ );
183
+ }
184
+ const currentVersion = abData.current;
185
+ const versionData = abData.versions.get(currentVersion);
186
+ if (!versionData) {
187
+ throw new Error(`Prompt A/B test '${abTestKey}' has no current version.`);
188
+ }
189
+ const { variants } = versionData;
190
+ const hashBytes = (0, import_crypto.createHash)("md5").update(sessionId).digest();
191
+ const hashVal = hashBytes.readUInt32BE(0) % 1e6;
192
+ let cumulative = 0;
193
+ let selectedVariant = variants[variants.length - 1];
194
+ let selectedIndex = variants.length - 1;
195
+ for (let i = 0; i < variants.length; i++) {
196
+ cumulative += variants[i].weight * 1e4;
197
+ if (hashVal < cumulative) {
198
+ selectedVariant = variants[i];
199
+ selectedIndex = i;
200
+ break;
201
+ }
202
+ }
203
+ const promptKey = selectedVariant.prompt_key;
204
+ const promptVersion = selectedVariant.prompt_version;
205
+ let promptData = promptCache.get(promptKey);
206
+ if (!promptData) {
207
+ await fetchPrompts(SYNC_TIMEOUT);
208
+ promptData = promptCache.get(promptKey);
209
+ }
210
+ if (!promptData) {
211
+ throw new Error(
212
+ `Prompt '${promptKey}' (from A/B test '${abTestKey}') not found.`
213
+ );
214
+ }
215
+ const targetVersion = promptVersion ?? promptData.current;
216
+ const content = promptData.versions.get(targetVersion);
217
+ if (!content) {
218
+ throw new Error(
219
+ `Prompt '${promptKey}' version ${targetVersion} not found.`
220
+ );
221
+ }
222
+ const system = replaceVariables(content.systemPrompt, variables);
223
+ const user = replaceVariables(content.userTemplate, variables);
224
+ setPromptContext({
225
+ promptKey,
226
+ promptVersion: targetVersion,
227
+ abTestKey,
228
+ variantIndex: selectedIndex
229
+ });
230
+ log(
231
+ `\u2705 Got prompt from A/B: ${promptKey} v${targetVersion} (variant ${selectedIndex})`
232
+ );
233
+ return {
234
+ key: promptKey,
235
+ version: targetVersion,
236
+ system,
237
+ user,
238
+ abTestKey,
239
+ variantIndex: selectedIndex
240
+ };
241
+ }
242
+ function clearPromptContext() {
243
+ promptContext = null;
244
+ }
245
+ var import_crypto, apiKey, baseUrl, initialized, syncInterval, debugMode, promptCache, promptABCache, promptContext, SYNC_TIMEOUT;
246
+ var init_prompts = __esm({
247
+ "src/prompts.ts"() {
248
+ "use strict";
249
+ import_crypto = require("crypto");
250
+ apiKey = null;
251
+ baseUrl = "https://spans.fallom.com";
252
+ initialized = false;
253
+ syncInterval = null;
254
+ debugMode = false;
255
+ promptCache = /* @__PURE__ */ new Map();
256
+ promptABCache = /* @__PURE__ */ new Map();
257
+ promptContext = null;
258
+ SYNC_TIMEOUT = 2e3;
259
+ }
260
+ });
261
+
20
262
  // src/index.ts
21
263
  var index_exports = {};
22
264
  __export(index_exports, {
23
265
  default: () => index_default,
24
- init: () => init3,
266
+ init: () => init4,
25
267
  models: () => models_exports,
268
+ prompts: () => prompts_exports,
26
269
  trace: () => trace_exports
27
270
  });
28
271
  module.exports = __toCommonJS(index_exports);
@@ -32,7 +275,7 @@ var trace_exports = {};
32
275
  __export(trace_exports, {
33
276
  clearSession: () => clearSession,
34
277
  getSession: () => getSession,
35
- init: () => init,
278
+ init: () => init2,
36
279
  runWithSession: () => runWithSession,
37
280
  setSession: () => setSession,
38
281
  shutdown: () => shutdown,
@@ -671,29 +914,29 @@ var Resource = (
671
914
  // src/trace.ts
672
915
  var sessionStorage = new import_async_hooks.AsyncLocalStorage();
673
916
  var fallbackSession = null;
674
- var apiKey = null;
675
- var baseUrl = "https://spans.fallom.com";
676
- var initialized = false;
917
+ var apiKey2 = null;
918
+ var baseUrl2 = "https://spans.fallom.com";
919
+ var initialized2 = false;
677
920
  var captureContent = true;
678
- var debugMode = false;
921
+ var debugMode2 = false;
679
922
  var sdk = null;
680
- function log(...args) {
681
- if (debugMode) console.log("[Fallom]", ...args);
923
+ function log2(...args) {
924
+ if (debugMode2) console.log("[Fallom]", ...args);
682
925
  }
683
926
  var fallomSpanProcessor = {
684
927
  onStart(span2, _parentContext) {
685
- log("\u{1F4CD} Span started:", span2.name || "unknown");
928
+ log2("\u{1F4CD} Span started:", span2.name || "unknown");
686
929
  const ctx = sessionStorage.getStore() || fallbackSession;
687
930
  if (ctx) {
688
931
  span2.setAttribute("fallom.config_key", ctx.configKey);
689
932
  span2.setAttribute("fallom.session_id", ctx.sessionId);
690
- log(" Added session context:", ctx.configKey, ctx.sessionId);
933
+ log2(" Added session context:", ctx.configKey, ctx.sessionId);
691
934
  } else {
692
- log(" No session context available");
935
+ log2(" No session context available");
693
936
  }
694
937
  },
695
938
  onEnd(span2) {
696
- log("\u2705 Span ended:", span2.name, "duration:", span2.duration);
939
+ log2("\u2705 Span ended:", span2.name, "duration:", span2.duration);
697
940
  },
698
941
  shutdown() {
699
942
  return Promise.resolve();
@@ -702,33 +945,33 @@ var fallomSpanProcessor = {
702
945
  return Promise.resolve();
703
946
  }
704
947
  };
705
- async function init(options = {}) {
706
- if (initialized) return;
707
- debugMode = options.debug ?? false;
708
- log("\u{1F680} Initializing Fallom tracing...");
709
- apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
710
- baseUrl = options.baseUrl || process.env.FALLOM_BASE_URL || "https://spans.fallom.com";
948
+ async function init2(options = {}) {
949
+ if (initialized2) return;
950
+ debugMode2 = options.debug ?? false;
951
+ log2("\u{1F680} Initializing Fallom tracing...");
952
+ apiKey2 = options.apiKey || process.env.FALLOM_API_KEY || null;
953
+ baseUrl2 = options.baseUrl || process.env.FALLOM_BASE_URL || "https://spans.fallom.com";
711
954
  const envCapture = process.env.FALLOM_CAPTURE_CONTENT?.toLowerCase();
712
955
  if (envCapture === "false" || envCapture === "0" || envCapture === "no") {
713
956
  captureContent = false;
714
957
  } else {
715
958
  captureContent = options.captureContent ?? true;
716
959
  }
717
- if (!apiKey) {
960
+ if (!apiKey2) {
718
961
  throw new Error(
719
962
  "No API key provided. Set FALLOM_API_KEY environment variable or pass apiKey parameter."
720
963
  );
721
964
  }
722
- initialized = true;
723
- log("\u{1F4E1} Exporter URL:", `${baseUrl}/v1/traces`);
965
+ initialized2 = true;
966
+ log2("\u{1F4E1} Exporter URL:", `${baseUrl2}/v1/traces`);
724
967
  const exporter = new import_exporter_trace_otlp_http.OTLPTraceExporter({
725
- url: `${baseUrl}/v1/traces`,
968
+ url: `${baseUrl2}/v1/traces`,
726
969
  headers: {
727
- Authorization: `Bearer ${apiKey}`
970
+ Authorization: `Bearer ${apiKey2}`
728
971
  }
729
972
  });
730
973
  const instrumentations = await getInstrumentations();
731
- log("\u{1F527} Loaded instrumentations:", instrumentations.length);
974
+ log2("\u{1F527} Loaded instrumentations:", instrumentations.length);
732
975
  sdk = new import_sdk_node.NodeSDK({
733
976
  resource: new Resource({
734
977
  "service.name": "fallom-traced-app"
@@ -738,20 +981,48 @@ async function init(options = {}) {
738
981
  instrumentations
739
982
  });
740
983
  sdk.start();
741
- log("\u2705 SDK started");
984
+ log2("\u2705 SDK started");
742
985
  process.on("SIGTERM", () => {
743
986
  sdk?.shutdown().catch(console.error);
744
987
  });
745
988
  }
746
989
  async function getInstrumentations() {
747
990
  const instrumentations = [];
748
- await tryAddInstrumentation(instrumentations, "@traceloop/instrumentation-openai", "OpenAIInstrumentation");
749
- await tryAddInstrumentation(instrumentations, "@traceloop/instrumentation-anthropic", "AnthropicInstrumentation");
750
- await tryAddInstrumentation(instrumentations, "@traceloop/instrumentation-cohere", "CohereInstrumentation");
751
- await tryAddInstrumentation(instrumentations, "@traceloop/instrumentation-bedrock", "BedrockInstrumentation");
752
- await tryAddInstrumentation(instrumentations, "@traceloop/instrumentation-google-generativeai", "GoogleGenerativeAIInstrumentation");
753
- await tryAddInstrumentation(instrumentations, "@traceloop/instrumentation-azure", "AzureOpenAIInstrumentation");
754
- await tryAddInstrumentation(instrumentations, "@traceloop/instrumentation-vertexai", "VertexAIInstrumentation");
991
+ await tryAddInstrumentation(
992
+ instrumentations,
993
+ "@traceloop/instrumentation-openai",
994
+ "OpenAIInstrumentation"
995
+ );
996
+ await tryAddInstrumentation(
997
+ instrumentations,
998
+ "@traceloop/instrumentation-anthropic",
999
+ "AnthropicInstrumentation"
1000
+ );
1001
+ await tryAddInstrumentation(
1002
+ instrumentations,
1003
+ "@traceloop/instrumentation-cohere",
1004
+ "CohereInstrumentation"
1005
+ );
1006
+ await tryAddInstrumentation(
1007
+ instrumentations,
1008
+ "@traceloop/instrumentation-bedrock",
1009
+ "BedrockInstrumentation"
1010
+ );
1011
+ await tryAddInstrumentation(
1012
+ instrumentations,
1013
+ "@traceloop/instrumentation-google-generativeai",
1014
+ "GoogleGenerativeAIInstrumentation"
1015
+ );
1016
+ await tryAddInstrumentation(
1017
+ instrumentations,
1018
+ "@traceloop/instrumentation-azure",
1019
+ "AzureOpenAIInstrumentation"
1020
+ );
1021
+ await tryAddInstrumentation(
1022
+ instrumentations,
1023
+ "@traceloop/instrumentation-vertexai",
1024
+ "VertexAIInstrumentation"
1025
+ );
755
1026
  return instrumentations;
756
1027
  }
757
1028
  async function tryAddInstrumentation(instrumentations, pkg, className) {
@@ -759,13 +1030,18 @@ async function tryAddInstrumentation(instrumentations, pkg, className) {
759
1030
  const mod = await import(pkg);
760
1031
  const InstrumentationClass = mod[className] || mod.default?.[className];
761
1032
  if (InstrumentationClass) {
762
- instrumentations.push(new InstrumentationClass({ traceContent: captureContent }));
763
- log(` \u2705 Loaded ${pkg}`);
1033
+ instrumentations.push(
1034
+ new InstrumentationClass({ traceContent: captureContent })
1035
+ );
1036
+ log2(` \u2705 Loaded ${pkg}`);
764
1037
  } else {
765
- log(` \u26A0\uFE0F ${pkg} loaded but ${className} not found. Available:`, Object.keys(mod));
1038
+ log2(
1039
+ ` \u26A0\uFE0F ${pkg} loaded but ${className} not found. Available:`,
1040
+ Object.keys(mod)
1041
+ );
766
1042
  }
767
1043
  } catch (e) {
768
- log(` \u274C ${pkg} not installed`);
1044
+ log2(` \u274C ${pkg} not installed`);
769
1045
  }
770
1046
  }
771
1047
  function setSession(configKey, sessionId) {
@@ -786,7 +1062,7 @@ function clearSession() {
786
1062
  fallbackSession = null;
787
1063
  }
788
1064
  function span(data, options = {}) {
789
- if (!initialized) {
1065
+ if (!initialized2) {
790
1066
  throw new Error("Fallom not initialized. Call trace.init() first.");
791
1067
  }
792
1068
  const ctx = sessionStorage.getStore() || fallbackSession;
@@ -804,10 +1080,10 @@ async function sendSpan(configKey, sessionId, data) {
804
1080
  try {
805
1081
  const controller = new AbortController();
806
1082
  const timeoutId = setTimeout(() => controller.abort(), 5e3);
807
- await fetch(`${baseUrl}/spans`, {
1083
+ await fetch(`${baseUrl2}/spans`, {
808
1084
  method: "POST",
809
1085
  headers: {
810
- Authorization: `Bearer ${apiKey}`,
1086
+ Authorization: `Bearer ${apiKey2}`,
811
1087
  "Content-Type": "application/json"
812
1088
  },
813
1089
  body: JSON.stringify({
@@ -824,34 +1100,42 @@ async function sendSpan(configKey, sessionId, data) {
824
1100
  async function shutdown() {
825
1101
  if (sdk) {
826
1102
  await sdk.shutdown();
827
- initialized = false;
1103
+ initialized2 = false;
828
1104
  }
829
1105
  }
830
1106
  async function sendTrace(trace) {
831
1107
  try {
832
1108
  const controller = new AbortController();
833
1109
  const timeoutId = setTimeout(() => controller.abort(), 5e3);
834
- await fetch(`${baseUrl}/v1/traces`, {
1110
+ await fetch(`${baseUrl2}/v1/traces`, {
835
1111
  method: "POST",
836
1112
  headers: {
837
- Authorization: `Bearer ${apiKey}`,
1113
+ Authorization: `Bearer ${apiKey2}`,
838
1114
  "Content-Type": "application/json"
839
1115
  },
840
1116
  body: JSON.stringify(trace),
841
1117
  signal: controller.signal
842
1118
  });
843
1119
  clearTimeout(timeoutId);
844
- log("\u{1F4E4} Trace sent:", trace.name, trace.model);
1120
+ log2("\u{1F4E4} Trace sent:", trace.name, trace.model);
845
1121
  } catch {
846
1122
  }
847
1123
  }
848
1124
  function wrapOpenAI(client) {
849
- const originalCreate = client.chat.completions.create.bind(client.chat.completions);
1125
+ const originalCreate = client.chat.completions.create.bind(
1126
+ client.chat.completions
1127
+ );
850
1128
  client.chat.completions.create = async function(...args) {
851
1129
  const ctx = sessionStorage.getStore() || fallbackSession;
852
- if (!ctx || !initialized) {
1130
+ if (!ctx || !initialized2) {
853
1131
  return originalCreate(...args);
854
1132
  }
1133
+ let promptCtx = null;
1134
+ try {
1135
+ const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
1136
+ promptCtx = getPromptContext2();
1137
+ } catch {
1138
+ }
855
1139
  const params = args[0] || {};
856
1140
  const startTime = Date.now();
857
1141
  try {
@@ -870,7 +1154,11 @@ function wrapOpenAI(client) {
870
1154
  completion_tokens: response?.usage?.completion_tokens,
871
1155
  total_tokens: response?.usage?.total_tokens,
872
1156
  input: captureContent ? JSON.stringify(params?.messages) : void 0,
873
- output: captureContent ? response?.choices?.[0]?.message?.content : void 0
1157
+ output: captureContent ? response?.choices?.[0]?.message?.content : void 0,
1158
+ prompt_key: promptCtx?.promptKey,
1159
+ prompt_version: promptCtx?.promptVersion,
1160
+ prompt_ab_test_key: promptCtx?.abTestKey,
1161
+ prompt_variant_index: promptCtx?.variantIndex
874
1162
  }).catch(() => {
875
1163
  });
876
1164
  return response;
@@ -885,7 +1173,11 @@ function wrapOpenAI(client) {
885
1173
  end_time: new Date(endTime).toISOString(),
886
1174
  duration_ms: endTime - startTime,
887
1175
  status: "ERROR",
888
- error_message: error?.message
1176
+ error_message: error?.message,
1177
+ prompt_key: promptCtx?.promptKey,
1178
+ prompt_version: promptCtx?.promptVersion,
1179
+ prompt_ab_test_key: promptCtx?.abTestKey,
1180
+ prompt_variant_index: promptCtx?.variantIndex
889
1181
  }).catch(() => {
890
1182
  });
891
1183
  throw error;
@@ -897,9 +1189,15 @@ function wrapAnthropic(client) {
897
1189
  const originalCreate = client.messages.create.bind(client.messages);
898
1190
  client.messages.create = async function(...args) {
899
1191
  const ctx = sessionStorage.getStore() || fallbackSession;
900
- if (!ctx || !initialized) {
1192
+ if (!ctx || !initialized2) {
901
1193
  return originalCreate(...args);
902
1194
  }
1195
+ let promptCtx = null;
1196
+ try {
1197
+ const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
1198
+ promptCtx = getPromptContext2();
1199
+ } catch {
1200
+ }
903
1201
  const params = args[0] || {};
904
1202
  const startTime = Date.now();
905
1203
  try {
@@ -918,7 +1216,11 @@ function wrapAnthropic(client) {
918
1216
  completion_tokens: response?.usage?.output_tokens,
919
1217
  total_tokens: (response?.usage?.input_tokens || 0) + (response?.usage?.output_tokens || 0),
920
1218
  input: captureContent ? JSON.stringify(params?.messages) : void 0,
921
- output: captureContent ? response?.content?.[0]?.text : void 0
1219
+ output: captureContent ? response?.content?.[0]?.text : void 0,
1220
+ prompt_key: promptCtx?.promptKey,
1221
+ prompt_version: promptCtx?.promptVersion,
1222
+ prompt_ab_test_key: promptCtx?.abTestKey,
1223
+ prompt_variant_index: promptCtx?.variantIndex
922
1224
  }).catch(() => {
923
1225
  });
924
1226
  return response;
@@ -933,7 +1235,11 @@ function wrapAnthropic(client) {
933
1235
  end_time: new Date(endTime).toISOString(),
934
1236
  duration_ms: endTime - startTime,
935
1237
  status: "ERROR",
936
- error_message: error?.message
1238
+ error_message: error?.message,
1239
+ prompt_key: promptCtx?.promptKey,
1240
+ prompt_version: promptCtx?.promptVersion,
1241
+ prompt_ab_test_key: promptCtx?.abTestKey,
1242
+ prompt_variant_index: promptCtx?.variantIndex
937
1243
  }).catch(() => {
938
1244
  });
939
1245
  throw error;
@@ -945,9 +1251,15 @@ function wrapGoogleAI(model) {
945
1251
  const originalGenerate = model.generateContent.bind(model);
946
1252
  model.generateContent = async function(...args) {
947
1253
  const ctx = sessionStorage.getStore() || fallbackSession;
948
- if (!ctx || !initialized) {
1254
+ if (!ctx || !initialized2) {
949
1255
  return originalGenerate(...args);
950
1256
  }
1257
+ let promptCtx = null;
1258
+ try {
1259
+ const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
1260
+ promptCtx = getPromptContext2();
1261
+ } catch {
1262
+ }
951
1263
  const startTime = Date.now();
952
1264
  try {
953
1265
  const response = await originalGenerate(...args);
@@ -967,7 +1279,11 @@ function wrapGoogleAI(model) {
967
1279
  completion_tokens: usage?.candidatesTokenCount,
968
1280
  total_tokens: usage?.totalTokenCount,
969
1281
  input: captureContent ? JSON.stringify(args[0]) : void 0,
970
- output: captureContent ? result?.text?.() : void 0
1282
+ output: captureContent ? result?.text?.() : void 0,
1283
+ prompt_key: promptCtx?.promptKey,
1284
+ prompt_version: promptCtx?.promptVersion,
1285
+ prompt_ab_test_key: promptCtx?.abTestKey,
1286
+ prompt_variant_index: promptCtx?.variantIndex
971
1287
  }).catch(() => {
972
1288
  });
973
1289
  return response;
@@ -982,7 +1298,11 @@ function wrapGoogleAI(model) {
982
1298
  end_time: new Date(endTime).toISOString(),
983
1299
  duration_ms: endTime - startTime,
984
1300
  status: "ERROR",
985
- error_message: error?.message
1301
+ error_message: error?.message,
1302
+ prompt_key: promptCtx?.promptKey,
1303
+ prompt_version: promptCtx?.promptVersion,
1304
+ prompt_ab_test_key: promptCtx?.abTestKey,
1305
+ prompt_variant_index: promptCtx?.variantIndex
986
1306
  }).catch(() => {
987
1307
  });
988
1308
  throw error;
@@ -994,71 +1314,71 @@ function wrapGoogleAI(model) {
994
1314
  // src/models.ts
995
1315
  var models_exports = {};
996
1316
  __export(models_exports, {
997
- get: () => get,
998
- init: () => init2
1317
+ get: () => get2,
1318
+ init: () => init3
999
1319
  });
1000
- var import_crypto = require("crypto");
1001
- var apiKey2 = null;
1002
- var baseUrl2 = "https://spans.fallom.com";
1003
- var initialized2 = false;
1004
- var syncInterval = null;
1005
- var debugMode2 = false;
1320
+ var import_crypto2 = require("crypto");
1321
+ var apiKey3 = null;
1322
+ var baseUrl3 = "https://spans.fallom.com";
1323
+ var initialized3 = false;
1324
+ var syncInterval2 = null;
1325
+ var debugMode3 = false;
1006
1326
  var configCache = /* @__PURE__ */ new Map();
1007
- var SYNC_TIMEOUT = 2e3;
1327
+ var SYNC_TIMEOUT2 = 2e3;
1008
1328
  var RECORD_TIMEOUT = 1e3;
1009
- function log2(msg) {
1010
- if (debugMode2) {
1329
+ function log3(msg) {
1330
+ if (debugMode3) {
1011
1331
  console.log(`[Fallom] ${msg}`);
1012
1332
  }
1013
1333
  }
1014
- function init2(options = {}) {
1015
- apiKey2 = options.apiKey || process.env.FALLOM_API_KEY || null;
1016
- baseUrl2 = options.baseUrl || process.env.FALLOM_BASE_URL || "https://spans.fallom.com";
1017
- initialized2 = true;
1018
- if (!apiKey2) {
1334
+ function init3(options = {}) {
1335
+ apiKey3 = options.apiKey || process.env.FALLOM_API_KEY || null;
1336
+ baseUrl3 = options.baseUrl || process.env.FALLOM_BASE_URL || "https://spans.fallom.com";
1337
+ initialized3 = true;
1338
+ if (!apiKey3) {
1019
1339
  return;
1020
1340
  }
1021
1341
  fetchConfigs().catch(() => {
1022
1342
  });
1023
- if (!syncInterval) {
1024
- syncInterval = setInterval(() => {
1343
+ if (!syncInterval2) {
1344
+ syncInterval2 = setInterval(() => {
1025
1345
  fetchConfigs().catch(() => {
1026
1346
  });
1027
1347
  }, 3e4);
1028
- syncInterval.unref();
1348
+ syncInterval2.unref();
1029
1349
  }
1030
1350
  }
1031
- function ensureInit() {
1032
- if (!initialized2) {
1351
+ function ensureInit2() {
1352
+ if (!initialized3) {
1033
1353
  try {
1034
- init2();
1354
+ init3();
1035
1355
  } catch {
1036
1356
  }
1037
1357
  }
1038
1358
  }
1039
- async function fetchConfigs(timeout = SYNC_TIMEOUT) {
1040
- if (!apiKey2) {
1041
- log2("_fetchConfigs: No API key, skipping");
1359
+ async function fetchConfigs(timeout = SYNC_TIMEOUT2) {
1360
+ if (!apiKey3) {
1361
+ log3("_fetchConfigs: No API key, skipping");
1042
1362
  return;
1043
1363
  }
1044
1364
  try {
1045
- log2(`Fetching configs from ${baseUrl2}/configs`);
1365
+ log3(`Fetching configs from ${baseUrl3}/configs`);
1046
1366
  const controller = new AbortController();
1047
1367
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1048
- const resp = await fetch(`${baseUrl2}/configs`, {
1049
- headers: { Authorization: `Bearer ${apiKey2}` },
1368
+ const resp = await fetch(`${baseUrl3}/configs`, {
1369
+ headers: { Authorization: `Bearer ${apiKey3}` },
1050
1370
  signal: controller.signal
1051
1371
  });
1052
1372
  clearTimeout(timeoutId);
1053
- log2(`Response status: ${resp.status}`);
1373
+ log3(`Response status: ${resp.status}`);
1054
1374
  if (resp.ok) {
1055
1375
  const data = await resp.json();
1056
1376
  const configs = data.configs || [];
1057
- log2(`Got ${configs.length} configs: ${configs.map((c) => c.key)}`);
1377
+ log3(`Got ${configs.length} configs: ${configs.map((c) => c.key)}`);
1058
1378
  for (const c of configs) {
1059
1379
  const key = c.key;
1060
1380
  const version = c.version || 1;
1061
- log2(`Config '${key}' v${version}: ${JSON.stringify(c.variants)}`);
1381
+ log3(`Config '${key}' v${version}: ${JSON.stringify(c.variants)}`);
1062
1382
  if (!configCache.has(key)) {
1063
1383
  configCache.set(key, { versions: /* @__PURE__ */ new Map(), latest: null });
1064
1384
  }
@@ -1067,21 +1387,21 @@ async function fetchConfigs(timeout = SYNC_TIMEOUT) {
1067
1387
  cached.latest = version;
1068
1388
  }
1069
1389
  } else {
1070
- log2(`Fetch failed: ${resp.statusText}`);
1390
+ log3(`Fetch failed: ${resp.statusText}`);
1071
1391
  }
1072
1392
  } catch (e) {
1073
- log2(`Fetch exception: ${e}`);
1393
+ log3(`Fetch exception: ${e}`);
1074
1394
  }
1075
1395
  }
1076
- async function fetchSpecificVersion(configKey, version, timeout = SYNC_TIMEOUT) {
1077
- if (!apiKey2) return null;
1396
+ async function fetchSpecificVersion(configKey, version, timeout = SYNC_TIMEOUT2) {
1397
+ if (!apiKey3) return null;
1078
1398
  try {
1079
1399
  const controller = new AbortController();
1080
1400
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1081
1401
  const resp = await fetch(
1082
- `${baseUrl2}/configs/${configKey}/version/${version}`,
1402
+ `${baseUrl3}/configs/${configKey}/version/${version}`,
1083
1403
  {
1084
- headers: { Authorization: `Bearer ${apiKey2}` },
1404
+ headers: { Authorization: `Bearer ${apiKey3}` },
1085
1405
  signal: controller.signal
1086
1406
  }
1087
1407
  );
@@ -1098,22 +1418,22 @@ async function fetchSpecificVersion(configKey, version, timeout = SYNC_TIMEOUT)
1098
1418
  }
1099
1419
  return null;
1100
1420
  }
1101
- async function get(configKey, sessionId, options = {}) {
1421
+ async function get2(configKey, sessionId, options = {}) {
1102
1422
  const { version, fallback, debug = false } = options;
1103
- debugMode2 = debug;
1104
- ensureInit();
1105
- log2(`get() called: configKey=${configKey}, sessionId=${sessionId}, fallback=${fallback}`);
1423
+ debugMode3 = debug;
1424
+ ensureInit2();
1425
+ log3(`get() called: configKey=${configKey}, sessionId=${sessionId}, fallback=${fallback}`);
1106
1426
  try {
1107
1427
  let configData = configCache.get(configKey);
1108
- log2(`Cache lookup for '${configKey}': ${configData ? "found" : "not found"}`);
1428
+ log3(`Cache lookup for '${configKey}': ${configData ? "found" : "not found"}`);
1109
1429
  if (!configData) {
1110
- log2("Not in cache, fetching...");
1111
- await fetchConfigs(SYNC_TIMEOUT);
1430
+ log3("Not in cache, fetching...");
1431
+ await fetchConfigs(SYNC_TIMEOUT2);
1112
1432
  configData = configCache.get(configKey);
1113
- log2(`After fetch, cache lookup: ${configData ? "found" : "still not found"}`);
1433
+ log3(`After fetch, cache lookup: ${configData ? "found" : "still not found"}`);
1114
1434
  }
1115
1435
  if (!configData) {
1116
- log2(`Config not found, using fallback: ${fallback}`);
1436
+ log3(`Config not found, using fallback: ${fallback}`);
1117
1437
  if (fallback) {
1118
1438
  console.warn(`[Fallom WARNING] Config '${configKey}' not found, using fallback model: ${fallback}`);
1119
1439
  return returnWithTrace(configKey, sessionId, fallback, 0);
@@ -1127,7 +1447,7 @@ async function get(configKey, sessionId, options = {}) {
1127
1447
  if (version !== void 0) {
1128
1448
  config = configData.versions.get(version);
1129
1449
  if (!config) {
1130
- config = await fetchSpecificVersion(configKey, version, SYNC_TIMEOUT) || void 0;
1450
+ config = await fetchSpecificVersion(configKey, version, SYNC_TIMEOUT2) || void 0;
1131
1451
  }
1132
1452
  if (!config) {
1133
1453
  if (fallback) {
@@ -1151,22 +1471,22 @@ async function get(configKey, sessionId, options = {}) {
1151
1471
  const variantsRaw = config.variants;
1152
1472
  const configVersion = config.version || targetVersion;
1153
1473
  const variants = Array.isArray(variantsRaw) ? variantsRaw : Object.values(variantsRaw);
1154
- log2(`Config found! Version: ${configVersion}, Variants: ${JSON.stringify(variants)}`);
1155
- const hashBytes = (0, import_crypto.createHash)("md5").update(sessionId).digest();
1474
+ log3(`Config found! Version: ${configVersion}, Variants: ${JSON.stringify(variants)}`);
1475
+ const hashBytes = (0, import_crypto2.createHash)("md5").update(sessionId).digest();
1156
1476
  const hashVal = hashBytes.readUInt32BE(0) % 1e6;
1157
- log2(`Session hash: ${hashVal} (out of 1,000,000)`);
1477
+ log3(`Session hash: ${hashVal} (out of 1,000,000)`);
1158
1478
  let cumulative = 0;
1159
1479
  let assignedModel = variants[variants.length - 1].model;
1160
1480
  for (const v of variants) {
1161
1481
  const oldCumulative = cumulative;
1162
1482
  cumulative += v.weight * 1e4;
1163
- log2(`Variant ${v.model}: weight=${v.weight}%, range=${oldCumulative}-${cumulative}, hash=${hashVal}, match=${hashVal < cumulative}`);
1483
+ log3(`Variant ${v.model}: weight=${v.weight}%, range=${oldCumulative}-${cumulative}, hash=${hashVal}, match=${hashVal < cumulative}`);
1164
1484
  if (hashVal < cumulative) {
1165
1485
  assignedModel = v.model;
1166
1486
  break;
1167
1487
  }
1168
1488
  }
1169
- log2(`\u2705 Assigned model: ${assignedModel}`);
1489
+ log3(`\u2705 Assigned model: ${assignedModel}`);
1170
1490
  return returnWithTrace(configKey, sessionId, assignedModel, configVersion);
1171
1491
  } catch (e) {
1172
1492
  if (e instanceof Error && e.message.includes("not found")) {
@@ -1191,14 +1511,14 @@ function returnWithTrace(configKey, sessionId, model, version) {
1191
1511
  return model;
1192
1512
  }
1193
1513
  async function recordSession(configKey, version, sessionId, model) {
1194
- if (!apiKey2) return;
1514
+ if (!apiKey3) return;
1195
1515
  try {
1196
1516
  const controller = new AbortController();
1197
1517
  const timeoutId = setTimeout(() => controller.abort(), RECORD_TIMEOUT);
1198
- await fetch(`${baseUrl2}/sessions`, {
1518
+ await fetch(`${baseUrl3}/sessions`, {
1199
1519
  method: "POST",
1200
1520
  headers: {
1201
- Authorization: `Bearer ${apiKey2}`,
1521
+ Authorization: `Bearer ${apiKey3}`,
1202
1522
  "Content-Type": "application/json"
1203
1523
  },
1204
1524
  body: JSON.stringify({
@@ -1214,30 +1534,41 @@ async function recordSession(configKey, version, sessionId, model) {
1214
1534
  }
1215
1535
  }
1216
1536
 
1537
+ // src/index.ts
1538
+ init_prompts();
1539
+
1217
1540
  // src/init.ts
1218
- async function init3(options = {}) {
1219
- const baseUrl3 = options.baseUrl || process.env.FALLOM_BASE_URL || "https://spans.fallom.com";
1220
- await init({
1541
+ init_prompts();
1542
+ async function init4(options = {}) {
1543
+ const baseUrl4 = options.baseUrl || process.env.FALLOM_BASE_URL || "https://spans.fallom.com";
1544
+ await init2({
1221
1545
  apiKey: options.apiKey,
1222
- baseUrl: baseUrl3,
1546
+ baseUrl: baseUrl4,
1223
1547
  captureContent: options.captureContent,
1224
1548
  debug: options.debug
1225
1549
  });
1226
- init2({
1550
+ init3({
1551
+ apiKey: options.apiKey,
1552
+ baseUrl: baseUrl4
1553
+ });
1554
+ init({
1227
1555
  apiKey: options.apiKey,
1228
- baseUrl: baseUrl3
1556
+ baseUrl: baseUrl4
1229
1557
  });
1230
1558
  }
1231
1559
 
1232
1560
  // src/index.ts
1561
+ init_prompts();
1233
1562
  var index_default = {
1234
- init: init3,
1563
+ init: init4,
1235
1564
  trace: trace_exports,
1236
- models: models_exports
1565
+ models: models_exports,
1566
+ prompts: prompts_exports
1237
1567
  };
1238
1568
  // Annotate the CommonJS export names for ESM import in node:
1239
1569
  0 && (module.exports = {
1240
1570
  init,
1241
1571
  models,
1572
+ prompts,
1242
1573
  trace
1243
1574
  });