@fallom/trace 0.2.1 → 0.2.3

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
@@ -795,32 +795,6 @@ function generateHexId(length) {
795
795
  crypto.getRandomValues(bytes);
796
796
  return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
797
797
  }
798
- function messagesToOtelAttributes(messages, completion, model, responseId) {
799
- const attrs = {};
800
- if (model) {
801
- attrs["gen_ai.request.model"] = model;
802
- attrs["gen_ai.response.model"] = model;
803
- }
804
- if (responseId) {
805
- attrs["gen_ai.response.id"] = responseId;
806
- }
807
- if (messages) {
808
- messages.forEach((msg, i) => {
809
- attrs[`gen_ai.prompt.${i}.role`] = msg.role;
810
- attrs[`gen_ai.prompt.${i}.content`] = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
811
- });
812
- }
813
- if (completion) {
814
- attrs["gen_ai.completion.0.role"] = completion.role;
815
- attrs["gen_ai.completion.0.content"] = typeof completion.content === "string" ? completion.content : JSON.stringify(completion.content);
816
- if (completion.tool_calls) {
817
- attrs["gen_ai.completion.0.tool_calls"] = JSON.stringify(
818
- completion.tool_calls
819
- );
820
- }
821
- }
822
- return attrs;
823
- }
824
798
 
825
799
  // src/trace/wrappers/openai.ts
826
800
  function wrapOpenAI(client, sessionCtx) {
@@ -842,18 +816,25 @@ function wrapOpenAI(client, sessionCtx) {
842
816
  try {
843
817
  const response = await originalCreate(...args);
844
818
  const endTime = Date.now();
845
- const attributes = captureContent2 ? messagesToOtelAttributes(
846
- params?.messages,
847
- response?.choices?.[0]?.message,
848
- response?.model || params?.model,
849
- response?.id
850
- ) : {};
819
+ const attributes = {
820
+ "fallom.sdk_version": "2",
821
+ "fallom.method": "chat.completions.create"
822
+ };
823
+ if (captureContent2) {
824
+ attributes["fallom.raw.request"] = JSON.stringify({
825
+ messages: params?.messages,
826
+ model: params?.model
827
+ });
828
+ attributes["fallom.raw.response"] = JSON.stringify({
829
+ text: response?.choices?.[0]?.message?.content,
830
+ finishReason: response?.choices?.[0]?.finish_reason,
831
+ responseId: response?.id,
832
+ model: response?.model
833
+ });
834
+ }
851
835
  if (response?.usage) {
852
836
  attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
853
837
  }
854
- if (response?.choices?.[0]?.finish_reason) {
855
- attributes["gen_ai.response.finish_reason"] = response.choices[0].finish_reason;
856
- }
857
838
  sendTrace({
858
839
  config_key: ctx.configKey,
859
840
  session_id: ctx.sessionId,
@@ -868,24 +849,12 @@ function wrapOpenAI(client, sessionCtx) {
868
849
  end_time: new Date(endTime).toISOString(),
869
850
  duration_ms: endTime - startTime,
870
851
  status: "OK",
871
- prompt_tokens: response?.usage?.prompt_tokens,
872
- completion_tokens: response?.usage?.completion_tokens,
873
- total_tokens: response?.usage?.total_tokens,
874
- attributes: Object.keys(attributes).length > 0 ? attributes : void 0
852
+ attributes
875
853
  }).catch(() => {
876
854
  });
877
855
  return response;
878
856
  } catch (error) {
879
857
  const endTime = Date.now();
880
- const attributes = captureContent2 ? messagesToOtelAttributes(
881
- params?.messages,
882
- void 0,
883
- params?.model,
884
- void 0
885
- ) : void 0;
886
- if (attributes) {
887
- attributes["error.message"] = error?.message;
888
- }
889
858
  sendTrace({
890
859
  config_key: ctx.configKey,
891
860
  session_id: ctx.sessionId,
@@ -901,7 +870,10 @@ function wrapOpenAI(client, sessionCtx) {
901
870
  duration_ms: endTime - startTime,
902
871
  status: "ERROR",
903
872
  error_message: error?.message,
904
- attributes
873
+ attributes: {
874
+ "fallom.sdk_version": "2",
875
+ "fallom.method": "chat.completions.create"
876
+ }
905
877
  }).catch(() => {
906
878
  });
907
879
  throw error;
@@ -928,21 +900,26 @@ function wrapAnthropic(client, sessionCtx) {
928
900
  try {
929
901
  const response = await originalCreate(...args);
930
902
  const endTime = Date.now();
931
- const attributes = captureContent2 ? messagesToOtelAttributes(
932
- params?.messages,
933
- { role: "assistant", content: response?.content?.[0]?.text || "" },
934
- response?.model || params?.model,
935
- response?.id
936
- ) : {};
937
- if (params?.system) {
938
- attributes["gen_ai.system_prompt"] = params.system;
903
+ const attributes = {
904
+ "fallom.sdk_version": "2",
905
+ "fallom.method": "messages.create"
906
+ };
907
+ if (captureContent2) {
908
+ attributes["fallom.raw.request"] = JSON.stringify({
909
+ messages: params?.messages,
910
+ system: params?.system,
911
+ model: params?.model
912
+ });
913
+ attributes["fallom.raw.response"] = JSON.stringify({
914
+ text: response?.content?.[0]?.text,
915
+ finishReason: response?.stop_reason,
916
+ responseId: response?.id,
917
+ model: response?.model
918
+ });
939
919
  }
940
920
  if (response?.usage) {
941
921
  attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
942
922
  }
943
- if (response?.stop_reason) {
944
- attributes["gen_ai.response.finish_reason"] = response.stop_reason;
945
- }
946
923
  sendTrace({
947
924
  config_key: ctx.configKey,
948
925
  session_id: ctx.sessionId,
@@ -957,27 +934,12 @@ function wrapAnthropic(client, sessionCtx) {
957
934
  end_time: new Date(endTime).toISOString(),
958
935
  duration_ms: endTime - startTime,
959
936
  status: "OK",
960
- prompt_tokens: response?.usage?.input_tokens,
961
- completion_tokens: response?.usage?.output_tokens,
962
- total_tokens: (response?.usage?.input_tokens || 0) + (response?.usage?.output_tokens || 0),
963
- attributes: Object.keys(attributes).length > 0 ? attributes : void 0
937
+ attributes
964
938
  }).catch(() => {
965
939
  });
966
940
  return response;
967
941
  } catch (error) {
968
942
  const endTime = Date.now();
969
- const attributes = captureContent2 ? messagesToOtelAttributes(
970
- params?.messages,
971
- void 0,
972
- params?.model,
973
- void 0
974
- ) : void 0;
975
- if (attributes) {
976
- attributes["error.message"] = error?.message;
977
- if (params?.system) {
978
- attributes["gen_ai.system_prompt"] = params.system;
979
- }
980
- }
981
943
  sendTrace({
982
944
  config_key: ctx.configKey,
983
945
  session_id: ctx.sessionId,
@@ -993,7 +955,10 @@ function wrapAnthropic(client, sessionCtx) {
993
955
  duration_ms: endTime - startTime,
994
956
  status: "ERROR",
995
957
  error_message: error?.message,
996
- attributes
958
+ attributes: {
959
+ "fallom.sdk_version": "2",
960
+ "fallom.method": "messages.create"
961
+ }
997
962
  }).catch(() => {
998
963
  });
999
964
  throw error;
@@ -1004,50 +969,36 @@ function wrapAnthropic(client, sessionCtx) {
1004
969
 
1005
970
  // src/trace/wrappers/google-ai.ts
1006
971
  function wrapGoogleAI(model, sessionCtx) {
1007
- const originalGenerate = model.generateContent.bind(model);
972
+ const originalGenerateContent = model.generateContent.bind(model);
1008
973
  const ctx = sessionCtx;
1009
974
  model.generateContent = async function(...args) {
1010
975
  if (!isInitialized()) {
1011
- return originalGenerate(...args);
976
+ return originalGenerateContent(...args);
1012
977
  }
1013
978
  const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1014
979
  const traceId = traceCtx?.traceId || generateHexId(32);
1015
980
  const spanId = generateHexId(16);
1016
981
  const parentSpanId = traceCtx?.parentSpanId;
982
+ const request = args[0];
1017
983
  const startTime = Date.now();
1018
984
  const captureContent2 = shouldCaptureContent();
1019
985
  try {
1020
- const response = await originalGenerate(...args);
986
+ const response = await originalGenerateContent(...args);
1021
987
  const endTime = Date.now();
1022
- const result = response?.response;
1023
- const usage = result?.usageMetadata;
1024
- const modelName = model?.model || "gemini";
1025
- const attributes = {};
988
+ const result = response?.response || response;
989
+ const attributes = {
990
+ "fallom.sdk_version": "2",
991
+ "fallom.method": "generateContent"
992
+ };
1026
993
  if (captureContent2) {
1027
- attributes["gen_ai.request.model"] = modelName;
1028
- attributes["gen_ai.response.model"] = modelName;
1029
- const input = args[0];
1030
- if (typeof input === "string") {
1031
- attributes["gen_ai.prompt.0.role"] = "user";
1032
- attributes["gen_ai.prompt.0.content"] = input;
1033
- } else if (input?.contents) {
1034
- input.contents.forEach((content, i) => {
1035
- attributes[`gen_ai.prompt.${i}.role`] = content.role || "user";
1036
- attributes[`gen_ai.prompt.${i}.content`] = content.parts?.[0]?.text || JSON.stringify(content.parts);
1037
- });
1038
- }
1039
- const outputText = result?.text?.();
1040
- if (outputText) {
1041
- attributes["gen_ai.completion.0.role"] = "assistant";
1042
- attributes["gen_ai.completion.0.content"] = outputText;
1043
- }
1044
- }
1045
- if (usage) {
1046
- attributes["fallom.raw.usage"] = JSON.stringify(usage);
994
+ attributes["fallom.raw.request"] = JSON.stringify(request);
995
+ attributes["fallom.raw.response"] = JSON.stringify({
996
+ text: result?.text?.(),
997
+ candidates: result?.candidates
998
+ });
1047
999
  }
1048
- const candidate = result?.candidates?.[0];
1049
- if (candidate?.finishReason) {
1050
- attributes["gen_ai.response.finish_reason"] = candidate.finishReason;
1000
+ if (result?.usageMetadata) {
1001
+ attributes["fallom.raw.usage"] = JSON.stringify(result.usageMetadata);
1051
1002
  }
1052
1003
  sendTrace({
1053
1004
  config_key: ctx.configKey,
@@ -1058,31 +1009,17 @@ function wrapGoogleAI(model, sessionCtx) {
1058
1009
  parent_span_id: parentSpanId,
1059
1010
  name: "generateContent",
1060
1011
  kind: "llm",
1061
- model: modelName,
1012
+ model: model.model || "gemini",
1062
1013
  start_time: new Date(startTime).toISOString(),
1063
1014
  end_time: new Date(endTime).toISOString(),
1064
1015
  duration_ms: endTime - startTime,
1065
1016
  status: "OK",
1066
- prompt_tokens: usage?.promptTokenCount,
1067
- completion_tokens: usage?.candidatesTokenCount,
1068
- total_tokens: usage?.totalTokenCount,
1069
- attributes: Object.keys(attributes).length > 0 ? attributes : void 0
1017
+ attributes
1070
1018
  }).catch(() => {
1071
1019
  });
1072
1020
  return response;
1073
1021
  } catch (error) {
1074
1022
  const endTime = Date.now();
1075
- const modelName = model?.model || "gemini";
1076
- const attributes = {};
1077
- if (captureContent2) {
1078
- attributes["gen_ai.request.model"] = modelName;
1079
- attributes["error.message"] = error?.message;
1080
- const input = args[0];
1081
- if (typeof input === "string") {
1082
- attributes["gen_ai.prompt.0.role"] = "user";
1083
- attributes["gen_ai.prompt.0.content"] = input;
1084
- }
1085
- }
1086
1023
  sendTrace({
1087
1024
  config_key: ctx.configKey,
1088
1025
  session_id: ctx.sessionId,
@@ -1092,13 +1029,16 @@ function wrapGoogleAI(model, sessionCtx) {
1092
1029
  parent_span_id: parentSpanId,
1093
1030
  name: "generateContent",
1094
1031
  kind: "llm",
1095
- model: modelName,
1032
+ model: model.model || "gemini",
1096
1033
  start_time: new Date(startTime).toISOString(),
1097
1034
  end_time: new Date(endTime).toISOString(),
1098
1035
  duration_ms: endTime - startTime,
1099
1036
  status: "ERROR",
1100
1037
  error_message: error?.message,
1101
- attributes: captureContent2 ? attributes : void 0
1038
+ attributes: {
1039
+ "fallom.sdk_version": "2",
1040
+ "fallom.method": "generateContent"
1041
+ }
1102
1042
  }).catch(() => {
1103
1043
  });
1104
1044
  throw error;
@@ -1107,35 +1047,6 @@ function wrapGoogleAI(model, sessionCtx) {
1107
1047
  return model;
1108
1048
  }
1109
1049
 
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 };
1137
- }
1138
-
1139
1050
  // src/trace/wrappers/vercel-ai/generate-text.ts
1140
1051
  function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
1141
1052
  const ctx = sessionCtx;
@@ -1154,54 +1065,33 @@ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
1154
1065
  const result = await aiModule.generateText(...args);
1155
1066
  const endTime = Date.now();
1156
1067
  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
- );
1068
+ console.log("\n\u{1F50D} [Fallom Debug] generateText raw result:", JSON.stringify(result, null, 2));
1169
1069
  }
1170
1070
  const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
1171
- const attributes = {};
1071
+ const attributes = {
1072
+ "fallom.sdk_version": "2",
1073
+ "fallom.method": "generateText"
1074
+ };
1172
1075
  if (captureContent2) {
1173
- attributes["gen_ai.request.model"] = modelId;
1174
- attributes["gen_ai.response.model"] = modelId;
1175
- if (params?.prompt) {
1176
- attributes["gen_ai.prompt.0.role"] = "user";
1177
- attributes["gen_ai.prompt.0.content"] = params.prompt;
1178
- }
1179
- if (params?.messages) {
1180
- params.messages.forEach((msg, i) => {
1181
- attributes[`gen_ai.prompt.${i}.role`] = msg.role;
1182
- attributes[`gen_ai.prompt.${i}.content`] = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1183
- });
1184
- }
1185
- if (result?.text) {
1186
- attributes["gen_ai.completion.0.role"] = "assistant";
1187
- attributes["gen_ai.completion.0.content"] = result.text;
1188
- }
1189
- if (result?.response?.id) {
1190
- attributes["gen_ai.response.id"] = result.response.id;
1191
- }
1076
+ attributes["fallom.raw.request"] = JSON.stringify({
1077
+ prompt: params?.prompt,
1078
+ messages: params?.messages,
1079
+ system: params?.system,
1080
+ model: modelId
1081
+ });
1082
+ attributes["fallom.raw.response"] = JSON.stringify({
1083
+ text: result?.text,
1084
+ finishReason: result?.finishReason,
1085
+ responseId: result?.response?.id,
1086
+ modelId: result?.response?.modelId
1087
+ });
1192
1088
  }
1193
1089
  if (result?.usage) {
1194
1090
  attributes["fallom.raw.usage"] = JSON.stringify(result.usage);
1195
1091
  }
1196
1092
  if (result?.experimental_providerMetadata) {
1197
- attributes["fallom.raw.providerMetadata"] = JSON.stringify(
1198
- result.experimental_providerMetadata
1199
- );
1093
+ attributes["fallom.raw.providerMetadata"] = JSON.stringify(result.experimental_providerMetadata);
1200
1094
  }
1201
- if (result?.finishReason) {
1202
- attributes["gen_ai.response.finish_reason"] = result.finishReason;
1203
- }
1204
- const usage = extractUsageFromResult(result);
1205
1095
  sendTrace({
1206
1096
  config_key: ctx.configKey,
1207
1097
  session_id: ctx.sessionId,
@@ -1216,10 +1106,7 @@ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
1216
1106
  end_time: new Date(endTime).toISOString(),
1217
1107
  duration_ms: endTime - startTime,
1218
1108
  status: "OK",
1219
- prompt_tokens: usage.promptTokens,
1220
- completion_tokens: usage.completionTokens,
1221
- total_tokens: usage.totalTokens,
1222
- attributes: captureContent2 ? attributes : void 0
1109
+ attributes
1223
1110
  }).catch(() => {
1224
1111
  });
1225
1112
  return result;
@@ -1240,7 +1127,17 @@ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
1240
1127
  end_time: new Date(endTime).toISOString(),
1241
1128
  duration_ms: endTime - startTime,
1242
1129
  status: "ERROR",
1243
- error_message: error?.message
1130
+ error_message: error?.message,
1131
+ attributes: {
1132
+ "fallom.sdk_version": "2",
1133
+ "fallom.method": "generateText",
1134
+ "fallom.raw.request": JSON.stringify({
1135
+ prompt: params?.prompt,
1136
+ messages: params?.messages,
1137
+ system: params?.system,
1138
+ model: modelId
1139
+ })
1140
+ }
1244
1141
  }).catch(() => {
1245
1142
  });
1246
1143
  throw error;
@@ -1272,12 +1169,8 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
1272
1169
  result.usage.then(async (rawUsage) => {
1273
1170
  const endTime = Date.now();
1274
1171
  if (debug || isDebugMode()) {
1275
- console.log(
1276
- "\n\u{1F50D} [Fallom Debug] streamText usage:",
1277
- JSON.stringify(rawUsage, null, 2)
1278
- );
1172
+ console.log("\n\u{1F50D} [Fallom Debug] streamText raw usage:", JSON.stringify(rawUsage, null, 2));
1279
1173
  }
1280
- log2("\u{1F4CA} streamText usage:", JSON.stringify(rawUsage, null, 2));
1281
1174
  let providerMetadata = result?.experimental_providerMetadata;
1282
1175
  if (providerMetadata && typeof providerMetadata.then === "function") {
1283
1176
  try {
@@ -1286,20 +1179,18 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
1286
1179
  providerMetadata = void 0;
1287
1180
  }
1288
1181
  }
1289
- const usage = extractUsageFromResult(
1290
- { experimental_providerMetadata: providerMetadata },
1291
- rawUsage
1292
- );
1293
- const attributes = {};
1182
+ const attributes = {
1183
+ "fallom.sdk_version": "2",
1184
+ "fallom.method": "streamText",
1185
+ "fallom.is_streaming": true
1186
+ };
1294
1187
  if (captureContent2) {
1295
- attributes["gen_ai.request.model"] = modelId;
1296
- if (params?.prompt) {
1297
- attributes["gen_ai.prompt.0.role"] = "user";
1298
- attributes["gen_ai.prompt.0.content"] = params.prompt;
1299
- }
1300
- }
1301
- if (firstTokenTime) {
1302
- attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
1188
+ attributes["fallom.raw.request"] = JSON.stringify({
1189
+ prompt: params?.prompt,
1190
+ messages: params?.messages,
1191
+ system: params?.system,
1192
+ model: modelId
1193
+ });
1303
1194
  }
1304
1195
  if (rawUsage) {
1305
1196
  attributes["fallom.raw.usage"] = JSON.stringify(rawUsage);
@@ -1307,7 +1198,10 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
1307
1198
  if (providerMetadata) {
1308
1199
  attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
1309
1200
  }
1310
- const tracePayload = {
1201
+ if (firstTokenTime) {
1202
+ attributes["fallom.time_to_first_token_ms"] = firstTokenTime - startTime;
1203
+ }
1204
+ sendTrace({
1311
1205
  config_key: ctx.configKey,
1312
1206
  session_id: ctx.sessionId,
1313
1207
  customer_id: ctx.customerId,
@@ -1321,13 +1215,10 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
1321
1215
  end_time: new Date(endTime).toISOString(),
1322
1216
  duration_ms: endTime - startTime,
1323
1217
  status: "OK",
1324
- prompt_tokens: usage.promptTokens,
1325
- completion_tokens: usage.completionTokens,
1326
- total_tokens: usage.totalTokens,
1327
1218
  time_to_first_token_ms: firstTokenTime ? firstTokenTime - startTime : void 0,
1328
- attributes: captureContent2 ? attributes : void 0
1329
- };
1330
- sendTrace(tracePayload).catch(() => {
1219
+ is_streaming: true,
1220
+ attributes
1221
+ }).catch(() => {
1331
1222
  });
1332
1223
  }).catch((error) => {
1333
1224
  const endTime = Date.now();
@@ -1346,7 +1237,12 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
1346
1237
  end_time: new Date(endTime).toISOString(),
1347
1238
  duration_ms: endTime - startTime,
1348
1239
  status: "ERROR",
1349
- error_message: error?.message
1240
+ error_message: error?.message,
1241
+ attributes: {
1242
+ "fallom.sdk_version": "2",
1243
+ "fallom.method": "streamText",
1244
+ "fallom.is_streaming": true
1245
+ }
1350
1246
  }).catch(() => {
1351
1247
  });
1352
1248
  });
@@ -1394,25 +1290,30 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
1394
1290
  const endTime = Date.now();
1395
1291
  if (debug || isDebugMode()) {
1396
1292
  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)
1293
+ "\n\u{1F50D} [Fallom Debug] generateObject raw result:",
1294
+ JSON.stringify(result, null, 2)
1403
1295
  );
1404
1296
  }
1405
1297
  const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
1406
- const attributes = {};
1298
+ const attributes = {
1299
+ "fallom.sdk_version": "2",
1300
+ "fallom.method": "generateObject"
1301
+ };
1407
1302
  if (captureContent2) {
1408
- attributes["gen_ai.request.model"] = modelId;
1409
- attributes["gen_ai.response.model"] = modelId;
1410
- if (result?.object) {
1411
- attributes["gen_ai.completion.0.role"] = "assistant";
1412
- attributes["gen_ai.completion.0.content"] = JSON.stringify(
1413
- result.object
1414
- );
1415
- }
1303
+ attributes["fallom.raw.request"] = JSON.stringify({
1304
+ prompt: params?.prompt,
1305
+ messages: params?.messages,
1306
+ system: params?.system,
1307
+ model: modelId,
1308
+ schema: params?.schema ? "provided" : void 0
1309
+ // Don't send full schema, just note if present
1310
+ });
1311
+ attributes["fallom.raw.response"] = JSON.stringify({
1312
+ object: result?.object,
1313
+ finishReason: result?.finishReason,
1314
+ responseId: result?.response?.id,
1315
+ modelId: result?.response?.modelId
1316
+ });
1416
1317
  }
1417
1318
  if (result?.usage) {
1418
1319
  attributes["fallom.raw.usage"] = JSON.stringify(result.usage);
@@ -1422,10 +1323,6 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
1422
1323
  result.experimental_providerMetadata
1423
1324
  );
1424
1325
  }
1425
- if (result?.finishReason) {
1426
- attributes["gen_ai.response.finish_reason"] = result.finishReason;
1427
- }
1428
- const usage = extractUsageFromResult(result);
1429
1326
  sendTrace({
1430
1327
  config_key: ctx.configKey,
1431
1328
  session_id: ctx.sessionId,
@@ -1440,10 +1337,7 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
1440
1337
  end_time: new Date(endTime).toISOString(),
1441
1338
  duration_ms: endTime - startTime,
1442
1339
  status: "OK",
1443
- prompt_tokens: usage.promptTokens,
1444
- completion_tokens: usage.completionTokens,
1445
- total_tokens: usage.totalTokens,
1446
- attributes: captureContent2 ? attributes : void 0
1340
+ attributes
1447
1341
  }).catch(() => {
1448
1342
  });
1449
1343
  return result;
@@ -1464,7 +1358,11 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
1464
1358
  end_time: new Date(endTime).toISOString(),
1465
1359
  duration_ms: endTime - startTime,
1466
1360
  status: "ERROR",
1467
- error_message: error?.message
1361
+ error_message: error?.message,
1362
+ attributes: {
1363
+ "fallom.sdk_version": "2",
1364
+ "fallom.method": "generateObject"
1365
+ }
1468
1366
  }).catch(() => {
1469
1367
  });
1470
1368
  throw error;
@@ -1473,9 +1371,6 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
1473
1371
  }
1474
1372
 
1475
1373
  // src/trace/wrappers/vercel-ai/stream-object.ts
1476
- function log3(...args) {
1477
- if (isDebugMode()) console.log("[Fallom]", ...args);
1478
- }
1479
1374
  function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
1480
1375
  const ctx = sessionCtx;
1481
1376
  return async (...args) => {
@@ -1483,7 +1378,6 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
1483
1378
  const startTime = Date.now();
1484
1379
  const captureContent2 = shouldCaptureContent();
1485
1380
  const result = await aiModule.streamObject(...args);
1486
- log3("\u{1F50D} streamObject result keys:", Object.keys(result || {}));
1487
1381
  if (!isInitialized()) {
1488
1382
  return result;
1489
1383
  }
@@ -1491,18 +1385,13 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
1491
1385
  const traceId = traceCtx?.traceId || generateHexId(32);
1492
1386
  const spanId = generateHexId(16);
1493
1387
  const parentSpanId = traceCtx?.parentSpanId;
1494
- let firstTokenTime = null;
1495
1388
  const modelId = params?.model?.modelId || String(params?.model || "unknown");
1496
1389
  if (result?.usage) {
1497
1390
  result.usage.then(async (rawUsage) => {
1498
1391
  const endTime = Date.now();
1499
1392
  if (debug || isDebugMode()) {
1500
- console.log(
1501
- "\n\u{1F50D} [Fallom Debug] streamObject usage:",
1502
- JSON.stringify(rawUsage, null, 2)
1503
- );
1393
+ console.log("\n\u{1F50D} [Fallom Debug] streamObject raw usage:", JSON.stringify(rawUsage, null, 2));
1504
1394
  }
1505
- log3("\u{1F4CA} streamObject usage:", JSON.stringify(rawUsage, null, 2));
1506
1395
  let providerMetadata = result?.experimental_providerMetadata;
1507
1396
  if (providerMetadata && typeof providerMetadata.then === "function") {
1508
1397
  try {
@@ -1511,16 +1400,19 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
1511
1400
  providerMetadata = void 0;
1512
1401
  }
1513
1402
  }
1514
- const usage = extractUsageFromResult(
1515
- { experimental_providerMetadata: providerMetadata },
1516
- rawUsage
1517
- );
1518
- const attributes = {};
1403
+ const attributes = {
1404
+ "fallom.sdk_version": "2",
1405
+ "fallom.method": "streamObject",
1406
+ "fallom.is_streaming": true
1407
+ };
1519
1408
  if (captureContent2) {
1520
- attributes["gen_ai.request.model"] = modelId;
1521
- }
1522
- if (firstTokenTime) {
1523
- attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
1409
+ attributes["fallom.raw.request"] = JSON.stringify({
1410
+ prompt: params?.prompt,
1411
+ messages: params?.messages,
1412
+ system: params?.system,
1413
+ model: modelId,
1414
+ schema: params?.schema ? "provided" : void 0
1415
+ });
1524
1416
  }
1525
1417
  if (rawUsage) {
1526
1418
  attributes["fallom.raw.usage"] = JSON.stringify(rawUsage);
@@ -1542,10 +1434,8 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
1542
1434
  end_time: new Date(endTime).toISOString(),
1543
1435
  duration_ms: endTime - startTime,
1544
1436
  status: "OK",
1545
- prompt_tokens: usage.promptTokens,
1546
- completion_tokens: usage.completionTokens,
1547
- total_tokens: usage.totalTokens,
1548
- attributes: captureContent2 ? attributes : void 0
1437
+ is_streaming: true,
1438
+ attributes
1549
1439
  }).catch(() => {
1550
1440
  });
1551
1441
  }).catch((error) => {
@@ -1564,31 +1454,16 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
1564
1454
  end_time: new Date(endTime).toISOString(),
1565
1455
  duration_ms: endTime - startTime,
1566
1456
  status: "ERROR",
1567
- error_message: error?.message
1457
+ error_message: error?.message,
1458
+ attributes: {
1459
+ "fallom.sdk_version": "2",
1460
+ "fallom.method": "streamObject",
1461
+ "fallom.is_streaming": true
1462
+ }
1568
1463
  }).catch(() => {
1569
1464
  });
1570
1465
  });
1571
1466
  }
1572
- if (result?.partialObjectStream) {
1573
- const originalStream = result.partialObjectStream;
1574
- const wrappedStream = (async function* () {
1575
- for await (const chunk of originalStream) {
1576
- if (!firstTokenTime) {
1577
- firstTokenTime = Date.now();
1578
- log3("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
1579
- }
1580
- yield chunk;
1581
- }
1582
- })();
1583
- return new Proxy(result, {
1584
- get(target, prop) {
1585
- if (prop === "partialObjectStream") {
1586
- return wrappedStream;
1587
- }
1588
- return target[prop];
1589
- }
1590
- });
1591
- }
1592
1467
  return result;
1593
1468
  };
1594
1469
  }
@@ -1607,105 +1482,69 @@ function wrapAISDK(ai, sessionCtx, options) {
1607
1482
  // src/trace/wrappers/mastra.ts
1608
1483
  function wrapMastraAgent(agent, sessionCtx) {
1609
1484
  const originalGenerate = agent.generate.bind(agent);
1610
- const agentName = agent.name || "MastraAgent";
1611
1485
  const ctx = sessionCtx;
1612
1486
  agent.generate = async function(...args) {
1613
1487
  if (!isInitialized()) {
1614
1488
  return originalGenerate(...args);
1615
1489
  }
1616
- const traceId = generateHexId(32);
1490
+ const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
1491
+ const traceId = traceCtx?.traceId || generateHexId(32);
1617
1492
  const spanId = generateHexId(16);
1493
+ const parentSpanId = traceCtx?.parentSpanId;
1494
+ const input = args[0];
1618
1495
  const startTime = Date.now();
1619
- const messages = args[0] || [];
1496
+ const captureContent2 = shouldCaptureContent();
1620
1497
  try {
1621
1498
  const result = await originalGenerate(...args);
1622
1499
  const endTime = Date.now();
1623
- const model = result?.model?.modelId || "unknown";
1624
- const toolCalls = [];
1625
- if (result?.steps?.length) {
1626
- for (const step of result.steps) {
1627
- if (step.toolCalls?.length) {
1628
- for (let i = 0; i < step.toolCalls.length; i++) {
1629
- const tc = step.toolCalls[i];
1630
- const tr = step.toolResults?.[i];
1631
- toolCalls.push({
1632
- name: tc.toolName,
1633
- arguments: tc.args,
1634
- result: tr?.result
1635
- });
1636
- }
1637
- }
1638
- }
1639
- }
1640
1500
  const attributes = {
1641
- "gen_ai.system": "Mastra",
1642
- "gen_ai.request.model": model,
1643
- "gen_ai.response.model": model,
1644
- "fallom.source": "mastra-agent",
1645
- "llm.request.type": "chat"
1501
+ "fallom.sdk_version": "2",
1502
+ "fallom.method": "agent.generate",
1503
+ "fallom.agent_name": agent.name || "unknown"
1646
1504
  };
1647
- if (Array.isArray(messages)) {
1648
- messages.forEach((msg, i) => {
1649
- attributes[`gen_ai.prompt.${i}.role`] = msg.role || "user";
1650
- attributes[`gen_ai.prompt.${i}.content`] = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1651
- });
1652
- }
1653
- if (result?.text) {
1654
- attributes["gen_ai.completion.0.role"] = "assistant";
1655
- attributes["gen_ai.completion.0.content"] = result.text;
1656
- attributes["gen_ai.completion.0.finish_reason"] = "stop";
1657
- }
1658
- if (toolCalls.length > 0) {
1659
- attributes["fallom.tool_calls"] = JSON.stringify(toolCalls);
1660
- toolCalls.forEach((tc, i) => {
1661
- attributes[`gen_ai.completion.0.tool_calls.${i}.name`] = tc.name;
1662
- attributes[`gen_ai.completion.0.tool_calls.${i}.type`] = "function";
1663
- attributes[`gen_ai.completion.0.tool_calls.${i}.arguments`] = JSON.stringify(tc.arguments);
1664
- });
1665
- }
1666
- if (result?.usage) {
1667
- attributes["gen_ai.usage.prompt_tokens"] = result.usage.promptTokens;
1668
- attributes["gen_ai.usage.completion_tokens"] = result.usage.completionTokens;
1669
- attributes["llm.usage.total_tokens"] = result.usage.totalTokens;
1505
+ if (captureContent2) {
1506
+ attributes["fallom.raw.request"] = JSON.stringify(input);
1507
+ attributes["fallom.raw.response"] = JSON.stringify(result);
1670
1508
  }
1671
- const traceData = {
1509
+ sendTrace({
1672
1510
  config_key: ctx.configKey,
1673
1511
  session_id: ctx.sessionId,
1674
1512
  customer_id: ctx.customerId,
1675
1513
  trace_id: traceId,
1676
1514
  span_id: spanId,
1677
- name: `mastra.${agentName}.generate`,
1678
- kind: "client",
1679
- model,
1515
+ parent_span_id: parentSpanId,
1516
+ name: `agent.${agent.name || "unknown"}.generate`,
1517
+ kind: "agent",
1680
1518
  start_time: new Date(startTime).toISOString(),
1681
1519
  end_time: new Date(endTime).toISOString(),
1682
1520
  duration_ms: endTime - startTime,
1683
1521
  status: "OK",
1684
- prompt_tokens: result?.usage?.promptTokens,
1685
- completion_tokens: result?.usage?.completionTokens,
1686
- total_tokens: result?.usage?.totalTokens,
1687
1522
  attributes
1688
- };
1689
- sendTrace(traceData).catch(() => {
1523
+ }).catch(() => {
1690
1524
  });
1691
1525
  return result;
1692
1526
  } catch (error) {
1693
1527
  const endTime = Date.now();
1694
- const traceData = {
1528
+ sendTrace({
1695
1529
  config_key: ctx.configKey,
1696
1530
  session_id: ctx.sessionId,
1697
1531
  customer_id: ctx.customerId,
1698
1532
  trace_id: traceId,
1699
1533
  span_id: spanId,
1700
- name: `mastra.${agentName}.generate`,
1701
- kind: "client",
1534
+ parent_span_id: parentSpanId,
1535
+ name: `agent.${agent.name || "unknown"}.generate`,
1536
+ kind: "agent",
1702
1537
  start_time: new Date(startTime).toISOString(),
1703
1538
  end_time: new Date(endTime).toISOString(),
1704
1539
  duration_ms: endTime - startTime,
1705
1540
  status: "ERROR",
1706
- error_message: error instanceof Error ? error.message : String(error)
1707
- };
1708
- sendTrace(traceData).catch(() => {
1541
+ error_message: error?.message,
1542
+ attributes: {
1543
+ "fallom.sdk_version": "2",
1544
+ "fallom.method": "agent.generate",
1545
+ "fallom.agent_name": agent.name || "unknown"
1546
+ }
1547
+ }).catch(() => {
1709
1548
  });
1710
1549
  throw error;
1711
1550
  }
@@ -1745,6 +1584,9 @@ var FallomSession = class {
1745
1584
  /**
1746
1585
  * Wrap a Vercel AI SDK model to trace all calls (PostHog style).
1747
1586
  * Returns the same model type with tracing injected.
1587
+ *
1588
+ * Note: This only captures tokens/timing, not prompt/completion content.
1589
+ * Use wrapAISDK for full content tracing.
1748
1590
  */
1749
1591
  traceModel(model) {
1750
1592
  const ctx = this.ctx;
@@ -1770,17 +1612,18 @@ var FallomSession = class {
1770
1612
  trace_id: traceId,
1771
1613
  span_id: spanId,
1772
1614
  parent_span_id: traceCtx?.parentSpanId,
1773
- name: "generateText",
1615
+ name: "doGenerate",
1774
1616
  kind: "llm",
1775
1617
  model: modelId,
1776
1618
  start_time: new Date(startTime).toISOString(),
1777
1619
  end_time: new Date(endTime).toISOString(),
1778
1620
  duration_ms: endTime - startTime,
1779
1621
  status: "OK",
1780
- prompt_tokens: usage?.promptTokens,
1781
- completion_tokens: usage?.completionTokens,
1782
- total_tokens: usage?.totalTokens,
1783
- attributes: shouldCaptureContent() && usage ? { "fallom.raw.usage": JSON.stringify(usage) } : void 0
1622
+ attributes: {
1623
+ "fallom.sdk_version": "2",
1624
+ "fallom.method": "traceModel.doGenerate",
1625
+ ...usage ? { "fallom.raw.usage": JSON.stringify(usage) } : {}
1626
+ }
1784
1627
  }).catch(() => {
1785
1628
  });
1786
1629
  return result;
@@ -1793,14 +1636,15 @@ var FallomSession = class {
1793
1636
  trace_id: traceId,
1794
1637
  span_id: spanId,
1795
1638
  parent_span_id: traceCtx?.parentSpanId,
1796
- name: "generateText",
1639
+ name: "doGenerate",
1797
1640
  kind: "llm",
1798
1641
  model: model.modelId || "unknown",
1799
1642
  start_time: new Date(startTime).toISOString(),
1800
1643
  end_time: new Date(endTime).toISOString(),
1801
1644
  duration_ms: endTime - startTime,
1802
1645
  status: "ERROR",
1803
- error_message: error instanceof Error ? error.message : String(error)
1646
+ error_message: error instanceof Error ? error.message : String(error),
1647
+ attributes: { "fallom.sdk_version": "2", "fallom.method": "traceModel.doGenerate" }
1804
1648
  }).catch(() => {
1805
1649
  });
1806
1650
  throw error;
@@ -1825,14 +1669,19 @@ var FallomSession = class {
1825
1669
  trace_id: traceId,
1826
1670
  span_id: spanId,
1827
1671
  parent_span_id: traceCtx?.parentSpanId,
1828
- name: "streamText",
1672
+ name: "doStream",
1829
1673
  kind: "llm",
1830
1674
  model: modelId,
1831
1675
  start_time: new Date(startTime).toISOString(),
1832
1676
  end_time: new Date(Date.now()).toISOString(),
1833
1677
  duration_ms: Date.now() - startTime,
1834
1678
  status: "OK",
1835
- is_streaming: true
1679
+ is_streaming: true,
1680
+ attributes: {
1681
+ "fallom.sdk_version": "2",
1682
+ "fallom.method": "traceModel.doStream",
1683
+ "fallom.is_streaming": true
1684
+ }
1836
1685
  }).catch(() => {
1837
1686
  });
1838
1687
  return result;
@@ -1844,7 +1693,7 @@ var FallomSession = class {
1844
1693
  trace_id: traceId,
1845
1694
  span_id: spanId,
1846
1695
  parent_span_id: traceCtx?.parentSpanId,
1847
- name: "streamText",
1696
+ name: "doStream",
1848
1697
  kind: "llm",
1849
1698
  model: modelId,
1850
1699
  start_time: new Date(startTime).toISOString(),
@@ -1852,7 +1701,12 @@ var FallomSession = class {
1852
1701
  duration_ms: Date.now() - startTime,
1853
1702
  status: "ERROR",
1854
1703
  error_message: error instanceof Error ? error.message : String(error),
1855
- is_streaming: true
1704
+ is_streaming: true,
1705
+ attributes: {
1706
+ "fallom.sdk_version": "2",
1707
+ "fallom.method": "traceModel.doStream",
1708
+ "fallom.is_streaming": true
1709
+ }
1856
1710
  }).catch(() => {
1857
1711
  });
1858
1712
  throw error;
@@ -1905,7 +1759,7 @@ var promptCache = /* @__PURE__ */ new Map();
1905
1759
  var promptABCache = /* @__PURE__ */ new Map();
1906
1760
  var promptContext = null;
1907
1761
  var SYNC_TIMEOUT = 2e3;
1908
- function log4(msg) {
1762
+ function log3(msg) {
1909
1763
  if (debugMode2) {
1910
1764
  console.log(`[Fallom Prompts] ${msg}`);
1911
1765
  }
@@ -2008,10 +1862,10 @@ async function get(promptKey, options = {}) {
2008
1862
  const { variables, version, debug = false } = options;
2009
1863
  debugMode2 = debug;
2010
1864
  ensureInit();
2011
- log4(`get() called: promptKey=${promptKey}`);
1865
+ log3(`get() called: promptKey=${promptKey}`);
2012
1866
  let promptData = promptCache.get(promptKey);
2013
1867
  if (!promptData) {
2014
- log4("Not in cache, fetching...");
1868
+ log3("Not in cache, fetching...");
2015
1869
  await fetchPrompts(SYNC_TIMEOUT);
2016
1870
  promptData = promptCache.get(promptKey);
2017
1871
  }
@@ -2033,7 +1887,7 @@ async function get(promptKey, options = {}) {
2033
1887
  promptKey,
2034
1888
  promptVersion: targetVersion
2035
1889
  });
2036
- log4(`\u2705 Got prompt: ${promptKey} v${targetVersion}`);
1890
+ log3(`\u2705 Got prompt: ${promptKey} v${targetVersion}`);
2037
1891
  return {
2038
1892
  key: promptKey,
2039
1893
  version: targetVersion,
@@ -2045,10 +1899,10 @@ async function getAB(abTestKey, sessionId, options = {}) {
2045
1899
  const { variables, debug = false } = options;
2046
1900
  debugMode2 = debug;
2047
1901
  ensureInit();
2048
- log4(`getAB() called: abTestKey=${abTestKey}, sessionId=${sessionId}`);
1902
+ log3(`getAB() called: abTestKey=${abTestKey}, sessionId=${sessionId}`);
2049
1903
  let abData = promptABCache.get(abTestKey);
2050
1904
  if (!abData) {
2051
- log4("Not in cache, fetching...");
1905
+ log3("Not in cache, fetching...");
2052
1906
  await fetchPromptABTests(SYNC_TIMEOUT);
2053
1907
  abData = promptABCache.get(abTestKey);
2054
1908
  }
@@ -2063,8 +1917,8 @@ async function getAB(abTestKey, sessionId, options = {}) {
2063
1917
  throw new Error(`Prompt A/B test '${abTestKey}' has no current version.`);
2064
1918
  }
2065
1919
  const { variants } = versionData;
2066
- log4(`A/B test '${abTestKey}' has ${variants?.length ?? 0} variants`);
2067
- log4(`Version data: ${JSON.stringify(versionData, null, 2)}`);
1920
+ log3(`A/B test '${abTestKey}' has ${variants?.length ?? 0} variants`);
1921
+ log3(`Version data: ${JSON.stringify(versionData, null, 2)}`);
2068
1922
  if (!variants || variants.length === 0) {
2069
1923
  throw new Error(
2070
1924
  `Prompt A/B test '${abTestKey}' has no variants configured.`
@@ -2110,7 +1964,7 @@ async function getAB(abTestKey, sessionId, options = {}) {
2110
1964
  abTestKey,
2111
1965
  variantIndex: selectedIndex
2112
1966
  });
2113
- log4(
1967
+ log3(
2114
1968
  `\u2705 Got prompt from A/B: ${promptKey} v${targetVersion} (variant ${selectedIndex})`
2115
1969
  );
2116
1970
  return {