@fallom/trace 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -7,6 +7,7 @@
7
7
  interface SessionContext {
8
8
  configKey: string;
9
9
  sessionId: string;
10
+ customerId?: string;
10
11
  }
11
12
  /**
12
13
  * Initialize Fallom tracing. Auto-instruments all LLM calls.
@@ -42,34 +43,36 @@ declare function init$3(options?: {
42
43
  * Set the current session context.
43
44
  *
44
45
  * All subsequent LLM calls in this async context will be
45
- * automatically tagged with this configKey and sessionId.
46
+ * automatically tagged with this configKey, sessionId, and customerId.
46
47
  *
47
48
  * @param configKey - Your config name (e.g., "linkedin-agent")
48
49
  * @param sessionId - Your session/conversation ID
50
+ * @param customerId - Optional customer/user identifier for analytics
49
51
  *
50
52
  * @example
51
53
  * ```typescript
52
- * trace.setSession("linkedin-agent", sessionId);
53
- * await agent.run(message); // Automatically traced with session
54
+ * trace.setSession("linkedin-agent", sessionId, "user_123");
55
+ * await agent.run(message); // Automatically traced with session + customer
54
56
  * ```
55
57
  */
56
- declare function setSession(configKey: string, sessionId: string): void;
58
+ declare function setSession(configKey: string, sessionId: string, customerId?: string): void;
57
59
  /**
58
60
  * Run a function with session context.
59
61
  * Use this to ensure session context propagates across async boundaries.
60
62
  *
61
63
  * @param configKey - Your config name
62
64
  * @param sessionId - Your session ID
65
+ * @param customerId - Optional customer/user identifier
63
66
  * @param fn - Function to run with session context
64
67
  *
65
68
  * @example
66
69
  * ```typescript
67
- * await trace.runWithSession("my-agent", sessionId, async () => {
70
+ * await trace.runWithSession("my-agent", sessionId, "user_123", async () => {
68
71
  * await agent.run(message); // Has session context
69
72
  * });
70
73
  * ```
71
74
  */
72
- declare function runWithSession<T>(configKey: string, sessionId: string, fn: () => T): T;
75
+ declare function runWithSession<T>(configKey: string, sessionId: string, customerIdOrFn: string | (() => T), fn?: () => T): T;
73
76
  /**
74
77
  * Get current session context, if any.
75
78
  */
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  interface SessionContext {
8
8
  configKey: string;
9
9
  sessionId: string;
10
+ customerId?: string;
10
11
  }
11
12
  /**
12
13
  * Initialize Fallom tracing. Auto-instruments all LLM calls.
@@ -42,34 +43,36 @@ declare function init$3(options?: {
42
43
  * Set the current session context.
43
44
  *
44
45
  * All subsequent LLM calls in this async context will be
45
- * automatically tagged with this configKey and sessionId.
46
+ * automatically tagged with this configKey, sessionId, and customerId.
46
47
  *
47
48
  * @param configKey - Your config name (e.g., "linkedin-agent")
48
49
  * @param sessionId - Your session/conversation ID
50
+ * @param customerId - Optional customer/user identifier for analytics
49
51
  *
50
52
  * @example
51
53
  * ```typescript
52
- * trace.setSession("linkedin-agent", sessionId);
53
- * await agent.run(message); // Automatically traced with session
54
+ * trace.setSession("linkedin-agent", sessionId, "user_123");
55
+ * await agent.run(message); // Automatically traced with session + customer
54
56
  * ```
55
57
  */
56
- declare function setSession(configKey: string, sessionId: string): void;
58
+ declare function setSession(configKey: string, sessionId: string, customerId?: string): void;
57
59
  /**
58
60
  * Run a function with session context.
59
61
  * Use this to ensure session context propagates across async boundaries.
60
62
  *
61
63
  * @param configKey - Your config name
62
64
  * @param sessionId - Your session ID
65
+ * @param customerId - Optional customer/user identifier
63
66
  * @param fn - Function to run with session context
64
67
  *
65
68
  * @example
66
69
  * ```typescript
67
- * await trace.runWithSession("my-agent", sessionId, async () => {
70
+ * await trace.runWithSession("my-agent", sessionId, "user_123", async () => {
68
71
  * await agent.run(message); // Has session context
69
72
  * });
70
73
  * ```
71
74
  */
72
- declare function runWithSession<T>(configKey: string, sessionId: string, fn: () => T): T;
75
+ declare function runWithSession<T>(configKey: string, sessionId: string, customerIdOrFn: string | (() => T), fn?: () => T): T;
73
76
  /**
74
77
  * Get current session context, if any.
75
78
  */
package/dist/index.js CHANGED
@@ -930,7 +930,15 @@ var fallomSpanProcessor = {
930
930
  if (ctx) {
931
931
  span2.setAttribute("fallom.config_key", ctx.configKey);
932
932
  span2.setAttribute("fallom.session_id", ctx.sessionId);
933
- log2(" Added session context:", ctx.configKey, ctx.sessionId);
933
+ if (ctx.customerId) {
934
+ span2.setAttribute("fallom.customer_id", ctx.customerId);
935
+ }
936
+ log2(
937
+ " Added session context:",
938
+ ctx.configKey,
939
+ ctx.sessionId,
940
+ ctx.customerId
941
+ );
934
942
  } else {
935
943
  log2(" No session context available");
936
944
  }
@@ -1044,16 +1052,23 @@ async function tryAddInstrumentation(instrumentations, pkg, className) {
1044
1052
  log2(` \u274C ${pkg} not installed`);
1045
1053
  }
1046
1054
  }
1047
- function setSession(configKey, sessionId) {
1055
+ function setSession(configKey, sessionId, customerId) {
1048
1056
  const store = sessionStorage.getStore();
1049
1057
  if (store) {
1050
1058
  store.configKey = configKey;
1051
1059
  store.sessionId = sessionId;
1060
+ store.customerId = customerId;
1052
1061
  }
1053
- fallbackSession = { configKey, sessionId };
1062
+ fallbackSession = { configKey, sessionId, customerId };
1054
1063
  }
1055
- function runWithSession(configKey, sessionId, fn) {
1056
- return sessionStorage.run({ configKey, sessionId }, fn);
1064
+ function runWithSession(configKey, sessionId, customerIdOrFn, fn) {
1065
+ if (typeof customerIdOrFn === "function") {
1066
+ return sessionStorage.run({ configKey, sessionId }, customerIdOrFn);
1067
+ }
1068
+ return sessionStorage.run(
1069
+ { configKey, sessionId, customerId: customerIdOrFn },
1070
+ fn
1071
+ );
1057
1072
  }
1058
1073
  function getSession() {
1059
1074
  return sessionStorage.getStore() || fallbackSession || void 0;
@@ -1103,6 +1118,39 @@ async function shutdown() {
1103
1118
  initialized2 = false;
1104
1119
  }
1105
1120
  }
1121
+ function messagesToOtelAttributes(messages, completion, model, responseId) {
1122
+ const attrs = {};
1123
+ if (model) {
1124
+ attrs["gen_ai.request.model"] = model;
1125
+ attrs["gen_ai.response.model"] = model;
1126
+ }
1127
+ if (responseId) {
1128
+ attrs["gen_ai.response.id"] = responseId;
1129
+ }
1130
+ if (messages) {
1131
+ messages.forEach((msg, i) => {
1132
+ attrs[`gen_ai.prompt.${i}.role`] = msg.role;
1133
+ attrs[`gen_ai.prompt.${i}.content`] = msg.content;
1134
+ });
1135
+ }
1136
+ if (completion) {
1137
+ attrs["gen_ai.completion.0.role"] = completion.role;
1138
+ attrs["gen_ai.completion.0.content"] = completion.content;
1139
+ if (completion.tool_calls) {
1140
+ attrs["gen_ai.completion.0.tool_calls"] = JSON.stringify(
1141
+ completion.tool_calls
1142
+ );
1143
+ }
1144
+ }
1145
+ return attrs;
1146
+ }
1147
+ function generateHexId(length) {
1148
+ const bytes = new Uint8Array(length / 2);
1149
+ crypto.getRandomValues(bytes);
1150
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1151
+ }
1152
+ var traceContextStorage = new import_async_hooks.AsyncLocalStorage();
1153
+ var fallbackTraceContext = null;
1106
1154
  async function sendTrace(trace) {
1107
1155
  try {
1108
1156
  const controller = new AbortController();
@@ -1136,15 +1184,30 @@ function wrapOpenAI(client) {
1136
1184
  promptCtx = getPromptContext2();
1137
1185
  } catch {
1138
1186
  }
1187
+ const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1188
+ const traceId = traceCtx?.traceId || generateHexId(32);
1189
+ const spanId = generateHexId(16);
1190
+ const parentSpanId = traceCtx?.parentSpanId;
1139
1191
  const params = args[0] || {};
1140
1192
  const startTime = Date.now();
1141
1193
  try {
1142
1194
  const response = await originalCreate(...args);
1143
1195
  const endTime = Date.now();
1196
+ const attributes = captureContent ? messagesToOtelAttributes(
1197
+ params?.messages,
1198
+ response?.choices?.[0]?.message,
1199
+ response?.model || params?.model,
1200
+ response?.id
1201
+ ) : void 0;
1144
1202
  sendTrace({
1145
1203
  config_key: ctx.configKey,
1146
1204
  session_id: ctx.sessionId,
1205
+ customer_id: ctx.customerId,
1206
+ trace_id: traceId,
1207
+ span_id: spanId,
1208
+ parent_span_id: parentSpanId,
1147
1209
  name: "chat.completions.create",
1210
+ kind: "llm",
1148
1211
  model: response?.model || params?.model,
1149
1212
  start_time: new Date(startTime).toISOString(),
1150
1213
  end_time: new Date(endTime).toISOString(),
@@ -1153,8 +1216,7 @@ function wrapOpenAI(client) {
1153
1216
  prompt_tokens: response?.usage?.prompt_tokens,
1154
1217
  completion_tokens: response?.usage?.completion_tokens,
1155
1218
  total_tokens: response?.usage?.total_tokens,
1156
- input: captureContent ? JSON.stringify(params?.messages) : void 0,
1157
- output: captureContent ? response?.choices?.[0]?.message?.content : void 0,
1219
+ attributes,
1158
1220
  prompt_key: promptCtx?.promptKey,
1159
1221
  prompt_version: promptCtx?.promptVersion,
1160
1222
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -1164,16 +1226,31 @@ function wrapOpenAI(client) {
1164
1226
  return response;
1165
1227
  } catch (error) {
1166
1228
  const endTime = Date.now();
1229
+ const attributes = captureContent ? messagesToOtelAttributes(
1230
+ params?.messages,
1231
+ void 0,
1232
+ params?.model,
1233
+ void 0
1234
+ ) : void 0;
1235
+ if (attributes) {
1236
+ attributes["error.message"] = error?.message;
1237
+ }
1167
1238
  sendTrace({
1168
1239
  config_key: ctx.configKey,
1169
1240
  session_id: ctx.sessionId,
1241
+ customer_id: ctx.customerId,
1242
+ trace_id: traceId,
1243
+ span_id: spanId,
1244
+ parent_span_id: parentSpanId,
1170
1245
  name: "chat.completions.create",
1246
+ kind: "llm",
1171
1247
  model: params?.model,
1172
1248
  start_time: new Date(startTime).toISOString(),
1173
1249
  end_time: new Date(endTime).toISOString(),
1174
1250
  duration_ms: endTime - startTime,
1175
1251
  status: "ERROR",
1176
1252
  error_message: error?.message,
1253
+ attributes,
1177
1254
  prompt_key: promptCtx?.promptKey,
1178
1255
  prompt_version: promptCtx?.promptVersion,
1179
1256
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -1198,15 +1275,33 @@ function wrapAnthropic(client) {
1198
1275
  promptCtx = getPromptContext2();
1199
1276
  } catch {
1200
1277
  }
1278
+ const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1279
+ const traceId = traceCtx?.traceId || generateHexId(32);
1280
+ const spanId = generateHexId(16);
1281
+ const parentSpanId = traceCtx?.parentSpanId;
1201
1282
  const params = args[0] || {};
1202
1283
  const startTime = Date.now();
1203
1284
  try {
1204
1285
  const response = await originalCreate(...args);
1205
1286
  const endTime = Date.now();
1287
+ const attributes = captureContent ? messagesToOtelAttributes(
1288
+ params?.messages,
1289
+ { role: "assistant", content: response?.content?.[0]?.text || "" },
1290
+ response?.model || params?.model,
1291
+ response?.id
1292
+ ) : void 0;
1293
+ if (attributes && params?.system) {
1294
+ attributes["gen_ai.system_prompt"] = params.system;
1295
+ }
1206
1296
  sendTrace({
1207
1297
  config_key: ctx.configKey,
1208
1298
  session_id: ctx.sessionId,
1299
+ customer_id: ctx.customerId,
1300
+ trace_id: traceId,
1301
+ span_id: spanId,
1302
+ parent_span_id: parentSpanId,
1209
1303
  name: "messages.create",
1304
+ kind: "llm",
1210
1305
  model: response?.model || params?.model,
1211
1306
  start_time: new Date(startTime).toISOString(),
1212
1307
  end_time: new Date(endTime).toISOString(),
@@ -1215,8 +1310,7 @@ function wrapAnthropic(client) {
1215
1310
  prompt_tokens: response?.usage?.input_tokens,
1216
1311
  completion_tokens: response?.usage?.output_tokens,
1217
1312
  total_tokens: (response?.usage?.input_tokens || 0) + (response?.usage?.output_tokens || 0),
1218
- input: captureContent ? JSON.stringify(params?.messages) : void 0,
1219
- output: captureContent ? response?.content?.[0]?.text : void 0,
1313
+ attributes,
1220
1314
  prompt_key: promptCtx?.promptKey,
1221
1315
  prompt_version: promptCtx?.promptVersion,
1222
1316
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -1226,16 +1320,34 @@ function wrapAnthropic(client) {
1226
1320
  return response;
1227
1321
  } catch (error) {
1228
1322
  const endTime = Date.now();
1323
+ const attributes = captureContent ? messagesToOtelAttributes(
1324
+ params?.messages,
1325
+ void 0,
1326
+ params?.model,
1327
+ void 0
1328
+ ) : void 0;
1329
+ if (attributes) {
1330
+ attributes["error.message"] = error?.message;
1331
+ if (params?.system) {
1332
+ attributes["gen_ai.system_prompt"] = params.system;
1333
+ }
1334
+ }
1229
1335
  sendTrace({
1230
1336
  config_key: ctx.configKey,
1231
1337
  session_id: ctx.sessionId,
1338
+ customer_id: ctx.customerId,
1339
+ trace_id: traceId,
1340
+ span_id: spanId,
1341
+ parent_span_id: parentSpanId,
1232
1342
  name: "messages.create",
1343
+ kind: "llm",
1233
1344
  model: params?.model,
1234
1345
  start_time: new Date(startTime).toISOString(),
1235
1346
  end_time: new Date(endTime).toISOString(),
1236
1347
  duration_ms: endTime - startTime,
1237
1348
  status: "ERROR",
1238
1349
  error_message: error?.message,
1350
+ attributes,
1239
1351
  prompt_key: promptCtx?.promptKey,
1240
1352
  prompt_version: promptCtx?.promptVersion,
1241
1353
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -1260,17 +1372,47 @@ function wrapGoogleAI(model) {
1260
1372
  promptCtx = getPromptContext2();
1261
1373
  } catch {
1262
1374
  }
1375
+ const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1376
+ const traceId = traceCtx?.traceId || generateHexId(32);
1377
+ const spanId = generateHexId(16);
1378
+ const parentSpanId = traceCtx?.parentSpanId;
1263
1379
  const startTime = Date.now();
1264
1380
  try {
1265
1381
  const response = await originalGenerate(...args);
1266
1382
  const endTime = Date.now();
1267
1383
  const result = response?.response;
1268
1384
  const usage = result?.usageMetadata;
1385
+ const modelName = model?.model || "gemini";
1386
+ const attributes = {};
1387
+ if (captureContent) {
1388
+ attributes["gen_ai.request.model"] = modelName;
1389
+ attributes["gen_ai.response.model"] = modelName;
1390
+ const input = args[0];
1391
+ if (typeof input === "string") {
1392
+ attributes["gen_ai.prompt.0.role"] = "user";
1393
+ attributes["gen_ai.prompt.0.content"] = input;
1394
+ } else if (input?.contents) {
1395
+ input.contents.forEach((content, i) => {
1396
+ attributes[`gen_ai.prompt.${i}.role`] = content.role || "user";
1397
+ attributes[`gen_ai.prompt.${i}.content`] = content.parts?.[0]?.text || JSON.stringify(content.parts);
1398
+ });
1399
+ }
1400
+ const outputText = result?.text?.();
1401
+ if (outputText) {
1402
+ attributes["gen_ai.completion.0.role"] = "assistant";
1403
+ attributes["gen_ai.completion.0.content"] = outputText;
1404
+ }
1405
+ }
1269
1406
  sendTrace({
1270
1407
  config_key: ctx.configKey,
1271
1408
  session_id: ctx.sessionId,
1409
+ customer_id: ctx.customerId,
1410
+ trace_id: traceId,
1411
+ span_id: spanId,
1412
+ parent_span_id: parentSpanId,
1272
1413
  name: "generateContent",
1273
- model: model?.model || "gemini",
1414
+ kind: "llm",
1415
+ model: modelName,
1274
1416
  start_time: new Date(startTime).toISOString(),
1275
1417
  end_time: new Date(endTime).toISOString(),
1276
1418
  duration_ms: endTime - startTime,
@@ -1278,8 +1420,7 @@ function wrapGoogleAI(model) {
1278
1420
  prompt_tokens: usage?.promptTokenCount,
1279
1421
  completion_tokens: usage?.candidatesTokenCount,
1280
1422
  total_tokens: usage?.totalTokenCount,
1281
- input: captureContent ? JSON.stringify(args[0]) : void 0,
1282
- output: captureContent ? result?.text?.() : void 0,
1423
+ attributes: captureContent ? attributes : void 0,
1283
1424
  prompt_key: promptCtx?.promptKey,
1284
1425
  prompt_version: promptCtx?.promptVersion,
1285
1426
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -1289,16 +1430,33 @@ function wrapGoogleAI(model) {
1289
1430
  return response;
1290
1431
  } catch (error) {
1291
1432
  const endTime = Date.now();
1433
+ const modelName = model?.model || "gemini";
1434
+ const attributes = {};
1435
+ if (captureContent) {
1436
+ attributes["gen_ai.request.model"] = modelName;
1437
+ attributes["error.message"] = error?.message;
1438
+ const input = args[0];
1439
+ if (typeof input === "string") {
1440
+ attributes["gen_ai.prompt.0.role"] = "user";
1441
+ attributes["gen_ai.prompt.0.content"] = input;
1442
+ }
1443
+ }
1292
1444
  sendTrace({
1293
1445
  config_key: ctx.configKey,
1294
1446
  session_id: ctx.sessionId,
1447
+ customer_id: ctx.customerId,
1448
+ trace_id: traceId,
1449
+ span_id: spanId,
1450
+ parent_span_id: parentSpanId,
1295
1451
  name: "generateContent",
1296
- model: model?.model || "gemini",
1452
+ kind: "llm",
1453
+ model: modelName,
1297
1454
  start_time: new Date(startTime).toISOString(),
1298
1455
  end_time: new Date(endTime).toISOString(),
1299
1456
  duration_ms: endTime - startTime,
1300
1457
  status: "ERROR",
1301
1458
  error_message: error?.message,
1459
+ attributes: captureContent ? attributes : void 0,
1302
1460
  prompt_key: promptCtx?.promptKey,
1303
1461
  prompt_version: promptCtx?.promptVersion,
1304
1462
  prompt_ab_test_key: promptCtx?.abTestKey,
package/dist/index.mjs CHANGED
@@ -664,7 +664,15 @@ var fallomSpanProcessor = {
664
664
  if (ctx) {
665
665
  span2.setAttribute("fallom.config_key", ctx.configKey);
666
666
  span2.setAttribute("fallom.session_id", ctx.sessionId);
667
- log(" Added session context:", ctx.configKey, ctx.sessionId);
667
+ if (ctx.customerId) {
668
+ span2.setAttribute("fallom.customer_id", ctx.customerId);
669
+ }
670
+ log(
671
+ " Added session context:",
672
+ ctx.configKey,
673
+ ctx.sessionId,
674
+ ctx.customerId
675
+ );
668
676
  } else {
669
677
  log(" No session context available");
670
678
  }
@@ -778,16 +786,23 @@ async function tryAddInstrumentation(instrumentations, pkg, className) {
778
786
  log(` \u274C ${pkg} not installed`);
779
787
  }
780
788
  }
781
- function setSession(configKey, sessionId) {
789
+ function setSession(configKey, sessionId, customerId) {
782
790
  const store = sessionStorage.getStore();
783
791
  if (store) {
784
792
  store.configKey = configKey;
785
793
  store.sessionId = sessionId;
794
+ store.customerId = customerId;
786
795
  }
787
- fallbackSession = { configKey, sessionId };
796
+ fallbackSession = { configKey, sessionId, customerId };
788
797
  }
789
- function runWithSession(configKey, sessionId, fn) {
790
- return sessionStorage.run({ configKey, sessionId }, fn);
798
+ function runWithSession(configKey, sessionId, customerIdOrFn, fn) {
799
+ if (typeof customerIdOrFn === "function") {
800
+ return sessionStorage.run({ configKey, sessionId }, customerIdOrFn);
801
+ }
802
+ return sessionStorage.run(
803
+ { configKey, sessionId, customerId: customerIdOrFn },
804
+ fn
805
+ );
791
806
  }
792
807
  function getSession() {
793
808
  return sessionStorage.getStore() || fallbackSession || void 0;
@@ -837,6 +852,39 @@ async function shutdown() {
837
852
  initialized = false;
838
853
  }
839
854
  }
855
+ function messagesToOtelAttributes(messages, completion, model, responseId) {
856
+ const attrs = {};
857
+ if (model) {
858
+ attrs["gen_ai.request.model"] = model;
859
+ attrs["gen_ai.response.model"] = model;
860
+ }
861
+ if (responseId) {
862
+ attrs["gen_ai.response.id"] = responseId;
863
+ }
864
+ if (messages) {
865
+ messages.forEach((msg, i) => {
866
+ attrs[`gen_ai.prompt.${i}.role`] = msg.role;
867
+ attrs[`gen_ai.prompt.${i}.content`] = msg.content;
868
+ });
869
+ }
870
+ if (completion) {
871
+ attrs["gen_ai.completion.0.role"] = completion.role;
872
+ attrs["gen_ai.completion.0.content"] = completion.content;
873
+ if (completion.tool_calls) {
874
+ attrs["gen_ai.completion.0.tool_calls"] = JSON.stringify(
875
+ completion.tool_calls
876
+ );
877
+ }
878
+ }
879
+ return attrs;
880
+ }
881
+ function generateHexId(length) {
882
+ const bytes = new Uint8Array(length / 2);
883
+ crypto.getRandomValues(bytes);
884
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
885
+ }
886
+ var traceContextStorage = new AsyncLocalStorage();
887
+ var fallbackTraceContext = null;
840
888
  async function sendTrace(trace) {
841
889
  try {
842
890
  const controller = new AbortController();
@@ -870,15 +918,30 @@ function wrapOpenAI(client) {
870
918
  promptCtx = getPromptContext();
871
919
  } catch {
872
920
  }
921
+ const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
922
+ const traceId = traceCtx?.traceId || generateHexId(32);
923
+ const spanId = generateHexId(16);
924
+ const parentSpanId = traceCtx?.parentSpanId;
873
925
  const params = args[0] || {};
874
926
  const startTime = Date.now();
875
927
  try {
876
928
  const response = await originalCreate(...args);
877
929
  const endTime = Date.now();
930
+ const attributes = captureContent ? messagesToOtelAttributes(
931
+ params?.messages,
932
+ response?.choices?.[0]?.message,
933
+ response?.model || params?.model,
934
+ response?.id
935
+ ) : void 0;
878
936
  sendTrace({
879
937
  config_key: ctx.configKey,
880
938
  session_id: ctx.sessionId,
939
+ customer_id: ctx.customerId,
940
+ trace_id: traceId,
941
+ span_id: spanId,
942
+ parent_span_id: parentSpanId,
881
943
  name: "chat.completions.create",
944
+ kind: "llm",
882
945
  model: response?.model || params?.model,
883
946
  start_time: new Date(startTime).toISOString(),
884
947
  end_time: new Date(endTime).toISOString(),
@@ -887,8 +950,7 @@ function wrapOpenAI(client) {
887
950
  prompt_tokens: response?.usage?.prompt_tokens,
888
951
  completion_tokens: response?.usage?.completion_tokens,
889
952
  total_tokens: response?.usage?.total_tokens,
890
- input: captureContent ? JSON.stringify(params?.messages) : void 0,
891
- output: captureContent ? response?.choices?.[0]?.message?.content : void 0,
953
+ attributes,
892
954
  prompt_key: promptCtx?.promptKey,
893
955
  prompt_version: promptCtx?.promptVersion,
894
956
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -898,16 +960,31 @@ function wrapOpenAI(client) {
898
960
  return response;
899
961
  } catch (error) {
900
962
  const endTime = Date.now();
963
+ const attributes = captureContent ? messagesToOtelAttributes(
964
+ params?.messages,
965
+ void 0,
966
+ params?.model,
967
+ void 0
968
+ ) : void 0;
969
+ if (attributes) {
970
+ attributes["error.message"] = error?.message;
971
+ }
901
972
  sendTrace({
902
973
  config_key: ctx.configKey,
903
974
  session_id: ctx.sessionId,
975
+ customer_id: ctx.customerId,
976
+ trace_id: traceId,
977
+ span_id: spanId,
978
+ parent_span_id: parentSpanId,
904
979
  name: "chat.completions.create",
980
+ kind: "llm",
905
981
  model: params?.model,
906
982
  start_time: new Date(startTime).toISOString(),
907
983
  end_time: new Date(endTime).toISOString(),
908
984
  duration_ms: endTime - startTime,
909
985
  status: "ERROR",
910
986
  error_message: error?.message,
987
+ attributes,
911
988
  prompt_key: promptCtx?.promptKey,
912
989
  prompt_version: promptCtx?.promptVersion,
913
990
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -932,15 +1009,33 @@ function wrapAnthropic(client) {
932
1009
  promptCtx = getPromptContext();
933
1010
  } catch {
934
1011
  }
1012
+ const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1013
+ const traceId = traceCtx?.traceId || generateHexId(32);
1014
+ const spanId = generateHexId(16);
1015
+ const parentSpanId = traceCtx?.parentSpanId;
935
1016
  const params = args[0] || {};
936
1017
  const startTime = Date.now();
937
1018
  try {
938
1019
  const response = await originalCreate(...args);
939
1020
  const endTime = Date.now();
1021
+ const attributes = captureContent ? messagesToOtelAttributes(
1022
+ params?.messages,
1023
+ { role: "assistant", content: response?.content?.[0]?.text || "" },
1024
+ response?.model || params?.model,
1025
+ response?.id
1026
+ ) : void 0;
1027
+ if (attributes && params?.system) {
1028
+ attributes["gen_ai.system_prompt"] = params.system;
1029
+ }
940
1030
  sendTrace({
941
1031
  config_key: ctx.configKey,
942
1032
  session_id: ctx.sessionId,
1033
+ customer_id: ctx.customerId,
1034
+ trace_id: traceId,
1035
+ span_id: spanId,
1036
+ parent_span_id: parentSpanId,
943
1037
  name: "messages.create",
1038
+ kind: "llm",
944
1039
  model: response?.model || params?.model,
945
1040
  start_time: new Date(startTime).toISOString(),
946
1041
  end_time: new Date(endTime).toISOString(),
@@ -949,8 +1044,7 @@ function wrapAnthropic(client) {
949
1044
  prompt_tokens: response?.usage?.input_tokens,
950
1045
  completion_tokens: response?.usage?.output_tokens,
951
1046
  total_tokens: (response?.usage?.input_tokens || 0) + (response?.usage?.output_tokens || 0),
952
- input: captureContent ? JSON.stringify(params?.messages) : void 0,
953
- output: captureContent ? response?.content?.[0]?.text : void 0,
1047
+ attributes,
954
1048
  prompt_key: promptCtx?.promptKey,
955
1049
  prompt_version: promptCtx?.promptVersion,
956
1050
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -960,16 +1054,34 @@ function wrapAnthropic(client) {
960
1054
  return response;
961
1055
  } catch (error) {
962
1056
  const endTime = Date.now();
1057
+ const attributes = captureContent ? messagesToOtelAttributes(
1058
+ params?.messages,
1059
+ void 0,
1060
+ params?.model,
1061
+ void 0
1062
+ ) : void 0;
1063
+ if (attributes) {
1064
+ attributes["error.message"] = error?.message;
1065
+ if (params?.system) {
1066
+ attributes["gen_ai.system_prompt"] = params.system;
1067
+ }
1068
+ }
963
1069
  sendTrace({
964
1070
  config_key: ctx.configKey,
965
1071
  session_id: ctx.sessionId,
1072
+ customer_id: ctx.customerId,
1073
+ trace_id: traceId,
1074
+ span_id: spanId,
1075
+ parent_span_id: parentSpanId,
966
1076
  name: "messages.create",
1077
+ kind: "llm",
967
1078
  model: params?.model,
968
1079
  start_time: new Date(startTime).toISOString(),
969
1080
  end_time: new Date(endTime).toISOString(),
970
1081
  duration_ms: endTime - startTime,
971
1082
  status: "ERROR",
972
1083
  error_message: error?.message,
1084
+ attributes,
973
1085
  prompt_key: promptCtx?.promptKey,
974
1086
  prompt_version: promptCtx?.promptVersion,
975
1087
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -994,17 +1106,47 @@ function wrapGoogleAI(model) {
994
1106
  promptCtx = getPromptContext();
995
1107
  } catch {
996
1108
  }
1109
+ const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
1110
+ const traceId = traceCtx?.traceId || generateHexId(32);
1111
+ const spanId = generateHexId(16);
1112
+ const parentSpanId = traceCtx?.parentSpanId;
997
1113
  const startTime = Date.now();
998
1114
  try {
999
1115
  const response = await originalGenerate(...args);
1000
1116
  const endTime = Date.now();
1001
1117
  const result = response?.response;
1002
1118
  const usage = result?.usageMetadata;
1119
+ const modelName = model?.model || "gemini";
1120
+ const attributes = {};
1121
+ if (captureContent) {
1122
+ attributes["gen_ai.request.model"] = modelName;
1123
+ attributes["gen_ai.response.model"] = modelName;
1124
+ const input = args[0];
1125
+ if (typeof input === "string") {
1126
+ attributes["gen_ai.prompt.0.role"] = "user";
1127
+ attributes["gen_ai.prompt.0.content"] = input;
1128
+ } else if (input?.contents) {
1129
+ input.contents.forEach((content, i) => {
1130
+ attributes[`gen_ai.prompt.${i}.role`] = content.role || "user";
1131
+ attributes[`gen_ai.prompt.${i}.content`] = content.parts?.[0]?.text || JSON.stringify(content.parts);
1132
+ });
1133
+ }
1134
+ const outputText = result?.text?.();
1135
+ if (outputText) {
1136
+ attributes["gen_ai.completion.0.role"] = "assistant";
1137
+ attributes["gen_ai.completion.0.content"] = outputText;
1138
+ }
1139
+ }
1003
1140
  sendTrace({
1004
1141
  config_key: ctx.configKey,
1005
1142
  session_id: ctx.sessionId,
1143
+ customer_id: ctx.customerId,
1144
+ trace_id: traceId,
1145
+ span_id: spanId,
1146
+ parent_span_id: parentSpanId,
1006
1147
  name: "generateContent",
1007
- model: model?.model || "gemini",
1148
+ kind: "llm",
1149
+ model: modelName,
1008
1150
  start_time: new Date(startTime).toISOString(),
1009
1151
  end_time: new Date(endTime).toISOString(),
1010
1152
  duration_ms: endTime - startTime,
@@ -1012,8 +1154,7 @@ function wrapGoogleAI(model) {
1012
1154
  prompt_tokens: usage?.promptTokenCount,
1013
1155
  completion_tokens: usage?.candidatesTokenCount,
1014
1156
  total_tokens: usage?.totalTokenCount,
1015
- input: captureContent ? JSON.stringify(args[0]) : void 0,
1016
- output: captureContent ? result?.text?.() : void 0,
1157
+ attributes: captureContent ? attributes : void 0,
1017
1158
  prompt_key: promptCtx?.promptKey,
1018
1159
  prompt_version: promptCtx?.promptVersion,
1019
1160
  prompt_ab_test_key: promptCtx?.abTestKey,
@@ -1023,16 +1164,33 @@ function wrapGoogleAI(model) {
1023
1164
  return response;
1024
1165
  } catch (error) {
1025
1166
  const endTime = Date.now();
1167
+ const modelName = model?.model || "gemini";
1168
+ const attributes = {};
1169
+ if (captureContent) {
1170
+ attributes["gen_ai.request.model"] = modelName;
1171
+ attributes["error.message"] = error?.message;
1172
+ const input = args[0];
1173
+ if (typeof input === "string") {
1174
+ attributes["gen_ai.prompt.0.role"] = "user";
1175
+ attributes["gen_ai.prompt.0.content"] = input;
1176
+ }
1177
+ }
1026
1178
  sendTrace({
1027
1179
  config_key: ctx.configKey,
1028
1180
  session_id: ctx.sessionId,
1181
+ customer_id: ctx.customerId,
1182
+ trace_id: traceId,
1183
+ span_id: spanId,
1184
+ parent_span_id: parentSpanId,
1029
1185
  name: "generateContent",
1030
- model: model?.model || "gemini",
1186
+ kind: "llm",
1187
+ model: modelName,
1031
1188
  start_time: new Date(startTime).toISOString(),
1032
1189
  end_time: new Date(endTime).toISOString(),
1033
1190
  duration_ms: endTime - startTime,
1034
1191
  status: "ERROR",
1035
1192
  error_message: error?.message,
1193
+ attributes: captureContent ? attributes : void 0,
1036
1194
  prompt_key: promptCtx?.promptKey,
1037
1195
  prompt_version: promptCtx?.promptVersion,
1038
1196
  prompt_ab_test_key: promptCtx?.abTestKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fallom/trace",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Model A/B testing and tracing for LLM applications. Zero latency, production-ready.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",