@fallom/trace 0.2.16 → 0.2.17
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/chunk-KFD5AQ7V.mjs +308 -0
- package/dist/index.js +96 -1
- package/dist/index.mjs +96 -1
- package/dist/models-SEFDGZU2.mjs +8 -0
- package/package.json +1 -1
- package/dist/chunk-XBZ3ESNV.mjs +0 -824
- package/dist/core-JLHYFVYS.mjs +0 -21
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/models.ts
|
|
8
|
+
var models_exports = {};
|
|
9
|
+
__export(models_exports, {
|
|
10
|
+
get: () => get,
|
|
11
|
+
init: () => init
|
|
12
|
+
});
|
|
13
|
+
import { createHash } from "crypto";
|
|
14
|
+
var apiKey = null;
|
|
15
|
+
var baseUrl = "https://configs.fallom.com";
|
|
16
|
+
var initialized = false;
|
|
17
|
+
var syncInterval = null;
|
|
18
|
+
var debugMode = false;
|
|
19
|
+
var configCache = /* @__PURE__ */ new Map();
|
|
20
|
+
var SYNC_TIMEOUT = 2e3;
|
|
21
|
+
var RECORD_TIMEOUT = 1e3;
|
|
22
|
+
function log(msg) {
|
|
23
|
+
if (debugMode) {
|
|
24
|
+
console.log(`[Fallom] ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function evaluateTargeting(targeting, customerId, context) {
|
|
28
|
+
if (!targeting || targeting.enabled === false) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const evalContext = {
|
|
32
|
+
...context || {},
|
|
33
|
+
...customerId ? { customerId } : {}
|
|
34
|
+
};
|
|
35
|
+
log(`Evaluating targeting with context: ${JSON.stringify(evalContext)}`);
|
|
36
|
+
if (targeting.individualTargets) {
|
|
37
|
+
for (const target of targeting.individualTargets) {
|
|
38
|
+
const fieldValue = evalContext[target.field];
|
|
39
|
+
if (fieldValue === target.value) {
|
|
40
|
+
log(`Individual target matched: ${target.field}=${target.value} -> variant ${target.variantIndex}`);
|
|
41
|
+
return target.variantIndex;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (targeting.rules) {
|
|
46
|
+
for (const rule of targeting.rules) {
|
|
47
|
+
const allConditionsMatch = rule.conditions.every((condition) => {
|
|
48
|
+
const fieldValue = evalContext[condition.field];
|
|
49
|
+
if (fieldValue === void 0) return false;
|
|
50
|
+
switch (condition.operator) {
|
|
51
|
+
case "eq":
|
|
52
|
+
return fieldValue === condition.value;
|
|
53
|
+
case "neq":
|
|
54
|
+
return fieldValue !== condition.value;
|
|
55
|
+
case "in":
|
|
56
|
+
return Array.isArray(condition.value) && condition.value.includes(fieldValue);
|
|
57
|
+
case "nin":
|
|
58
|
+
return Array.isArray(condition.value) && !condition.value.includes(fieldValue);
|
|
59
|
+
case "contains":
|
|
60
|
+
return typeof condition.value === "string" && fieldValue.includes(condition.value);
|
|
61
|
+
case "startsWith":
|
|
62
|
+
return typeof condition.value === "string" && fieldValue.startsWith(condition.value);
|
|
63
|
+
case "endsWith":
|
|
64
|
+
return typeof condition.value === "string" && fieldValue.endsWith(condition.value);
|
|
65
|
+
default:
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
if (allConditionsMatch) {
|
|
70
|
+
log(`Rule matched: ${JSON.stringify(rule.conditions)} -> variant ${rule.variantIndex}`);
|
|
71
|
+
return rule.variantIndex;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
log("No targeting rules matched, falling back to weighted random");
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function init(options = {}) {
|
|
79
|
+
apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
|
|
80
|
+
baseUrl = options.baseUrl || process.env.FALLOM_CONFIGS_URL || process.env.FALLOM_BASE_URL || "https://configs.fallom.com";
|
|
81
|
+
initialized = true;
|
|
82
|
+
if (!apiKey) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
fetchConfigs().catch(() => {
|
|
86
|
+
});
|
|
87
|
+
if (!syncInterval) {
|
|
88
|
+
syncInterval = setInterval(() => {
|
|
89
|
+
fetchConfigs().catch(() => {
|
|
90
|
+
});
|
|
91
|
+
}, 3e4);
|
|
92
|
+
syncInterval.unref();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function ensureInit() {
|
|
96
|
+
if (!initialized) {
|
|
97
|
+
try {
|
|
98
|
+
init();
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function fetchConfigs(timeout = SYNC_TIMEOUT) {
|
|
104
|
+
if (!apiKey) {
|
|
105
|
+
log("_fetchConfigs: No API key, skipping");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
log(`Fetching configs from ${baseUrl}/configs`);
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
112
|
+
const resp = await fetch(`${baseUrl}/configs`, {
|
|
113
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
114
|
+
signal: controller.signal
|
|
115
|
+
});
|
|
116
|
+
clearTimeout(timeoutId);
|
|
117
|
+
log(`Response status: ${resp.status}`);
|
|
118
|
+
if (resp.ok) {
|
|
119
|
+
const data = await resp.json();
|
|
120
|
+
const configs = data.configs || [];
|
|
121
|
+
log(`Got ${configs.length} configs: ${configs.map((c) => c.key)}`);
|
|
122
|
+
for (const c of configs) {
|
|
123
|
+
const key = c.key;
|
|
124
|
+
const version = c.version || 1;
|
|
125
|
+
log(`Config '${key}' v${version}: ${JSON.stringify(c.variants)}`);
|
|
126
|
+
if (!configCache.has(key)) {
|
|
127
|
+
configCache.set(key, { versions: /* @__PURE__ */ new Map(), latest: null });
|
|
128
|
+
}
|
|
129
|
+
const cached = configCache.get(key);
|
|
130
|
+
cached.versions.set(version, c);
|
|
131
|
+
cached.latest = version;
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
log(`Fetch failed: ${resp.statusText}`);
|
|
135
|
+
}
|
|
136
|
+
} catch (e) {
|
|
137
|
+
log(`Fetch exception: ${e}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function fetchSpecificVersion(configKey, version, timeout = SYNC_TIMEOUT) {
|
|
141
|
+
if (!apiKey) return null;
|
|
142
|
+
try {
|
|
143
|
+
const controller = new AbortController();
|
|
144
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
145
|
+
const resp = await fetch(
|
|
146
|
+
`${baseUrl}/configs/${configKey}/version/${version}`,
|
|
147
|
+
{
|
|
148
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
149
|
+
signal: controller.signal
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
clearTimeout(timeoutId);
|
|
153
|
+
if (resp.ok) {
|
|
154
|
+
const config = await resp.json();
|
|
155
|
+
if (!configCache.has(configKey)) {
|
|
156
|
+
configCache.set(configKey, { versions: /* @__PURE__ */ new Map(), latest: null });
|
|
157
|
+
}
|
|
158
|
+
configCache.get(configKey).versions.set(version, config);
|
|
159
|
+
return config;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
async function get(configKey, sessionId, options = {}) {
|
|
166
|
+
const { version, fallback, customerId, context, debug = false } = options;
|
|
167
|
+
debugMode = debug;
|
|
168
|
+
ensureInit();
|
|
169
|
+
log(
|
|
170
|
+
`get() called: configKey=${configKey}, sessionId=${sessionId}, fallback=${fallback}`
|
|
171
|
+
);
|
|
172
|
+
try {
|
|
173
|
+
let configData = configCache.get(configKey);
|
|
174
|
+
log(
|
|
175
|
+
`Cache lookup for '${configKey}': ${configData ? "found" : "not found"}`
|
|
176
|
+
);
|
|
177
|
+
if (!configData) {
|
|
178
|
+
log("Not in cache, fetching...");
|
|
179
|
+
await fetchConfigs(SYNC_TIMEOUT);
|
|
180
|
+
configData = configCache.get(configKey);
|
|
181
|
+
log(
|
|
182
|
+
`After fetch, cache lookup: ${configData ? "found" : "still not found"}`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (!configData) {
|
|
186
|
+
log(`Config not found, using fallback: ${fallback}`);
|
|
187
|
+
if (fallback) {
|
|
188
|
+
console.warn(
|
|
189
|
+
`[Fallom WARNING] Config '${configKey}' not found, using fallback model: ${fallback}`
|
|
190
|
+
);
|
|
191
|
+
return returnModel(configKey, sessionId, fallback, 0);
|
|
192
|
+
}
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Config '${configKey}' not found. Check that it exists in your Fallom dashboard.`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
let config;
|
|
198
|
+
let targetVersion;
|
|
199
|
+
if (version !== void 0) {
|
|
200
|
+
config = configData.versions.get(version);
|
|
201
|
+
if (!config) {
|
|
202
|
+
config = await fetchSpecificVersion(configKey, version, SYNC_TIMEOUT) || void 0;
|
|
203
|
+
}
|
|
204
|
+
if (!config) {
|
|
205
|
+
if (fallback) {
|
|
206
|
+
console.warn(
|
|
207
|
+
`[Fallom WARNING] Config '${configKey}' version ${version} not found, using fallback: ${fallback}`
|
|
208
|
+
);
|
|
209
|
+
return returnModel(configKey, sessionId, fallback, 0);
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`Config '${configKey}' version ${version} not found.`);
|
|
212
|
+
}
|
|
213
|
+
targetVersion = version;
|
|
214
|
+
} else {
|
|
215
|
+
targetVersion = configData.latest;
|
|
216
|
+
config = configData.versions.get(targetVersion);
|
|
217
|
+
if (!config) {
|
|
218
|
+
if (fallback) {
|
|
219
|
+
console.warn(
|
|
220
|
+
`[Fallom WARNING] Config '${configKey}' has no cached version, using fallback: ${fallback}`
|
|
221
|
+
);
|
|
222
|
+
return returnModel(configKey, sessionId, fallback, 0);
|
|
223
|
+
}
|
|
224
|
+
throw new Error(`Config '${configKey}' has no cached version.`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const variantsRaw = config.variants;
|
|
228
|
+
const configVersion = config.version || targetVersion;
|
|
229
|
+
const variants = Array.isArray(variantsRaw) ? variantsRaw : Object.values(variantsRaw);
|
|
230
|
+
log(
|
|
231
|
+
`Config found! Version: ${configVersion}, Variants: ${JSON.stringify(
|
|
232
|
+
variants
|
|
233
|
+
)}`
|
|
234
|
+
);
|
|
235
|
+
const targetedVariantIndex = evaluateTargeting(config.targeting, customerId, context);
|
|
236
|
+
if (targetedVariantIndex !== null && variants[targetedVariantIndex]) {
|
|
237
|
+
const assignedModel2 = variants[targetedVariantIndex].model;
|
|
238
|
+
log(`\u2705 Assigned model via targeting: ${assignedModel2}`);
|
|
239
|
+
return returnModel(configKey, sessionId, assignedModel2, configVersion);
|
|
240
|
+
}
|
|
241
|
+
const hashBytes = createHash("md5").update(sessionId).digest();
|
|
242
|
+
const hashVal = hashBytes.readUInt32BE(0) % 1e6;
|
|
243
|
+
log(`Session hash: ${hashVal} (out of 1,000,000)`);
|
|
244
|
+
let cumulative = 0;
|
|
245
|
+
let assignedModel = variants[variants.length - 1].model;
|
|
246
|
+
for (const v of variants) {
|
|
247
|
+
const oldCumulative = cumulative;
|
|
248
|
+
cumulative += v.weight * 1e4;
|
|
249
|
+
log(
|
|
250
|
+
`Variant ${v.model}: weight=${v.weight}%, range=${oldCumulative}-${cumulative}, hash=${hashVal}, match=${hashVal < cumulative}`
|
|
251
|
+
);
|
|
252
|
+
if (hashVal < cumulative) {
|
|
253
|
+
assignedModel = v.model;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
log(`\u2705 Assigned model via weighted random: ${assignedModel}`);
|
|
258
|
+
return returnModel(configKey, sessionId, assignedModel, configVersion);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
if (e instanceof Error && e.message.includes("not found")) {
|
|
261
|
+
throw e;
|
|
262
|
+
}
|
|
263
|
+
if (fallback) {
|
|
264
|
+
console.warn(
|
|
265
|
+
`[Fallom WARNING] Error getting model for '${configKey}': ${e}. Using fallback: ${fallback}`
|
|
266
|
+
);
|
|
267
|
+
return returnModel(configKey, sessionId, fallback, 0);
|
|
268
|
+
}
|
|
269
|
+
throw e;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function returnModel(configKey, sessionId, model, version) {
|
|
273
|
+
if (version > 0) {
|
|
274
|
+
recordSession(configKey, version, sessionId, model).catch(() => {
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return model;
|
|
278
|
+
}
|
|
279
|
+
async function recordSession(configKey, version, sessionId, model) {
|
|
280
|
+
if (!apiKey) return;
|
|
281
|
+
try {
|
|
282
|
+
const controller = new AbortController();
|
|
283
|
+
const timeoutId = setTimeout(() => controller.abort(), RECORD_TIMEOUT);
|
|
284
|
+
await fetch(`${baseUrl}/sessions`, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
Authorization: `Bearer ${apiKey}`,
|
|
288
|
+
"Content-Type": "application/json"
|
|
289
|
+
},
|
|
290
|
+
body: JSON.stringify({
|
|
291
|
+
config_key: configKey,
|
|
292
|
+
config_version: version,
|
|
293
|
+
session_id: sessionId,
|
|
294
|
+
assigned_model: model
|
|
295
|
+
}),
|
|
296
|
+
signal: controller.signal
|
|
297
|
+
});
|
|
298
|
+
clearTimeout(timeoutId);
|
|
299
|
+
} catch {
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export {
|
|
304
|
+
__export,
|
|
305
|
+
init,
|
|
306
|
+
get,
|
|
307
|
+
models_exports
|
|
308
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1201,7 +1201,7 @@ var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otl
|
|
|
1201
1201
|
// node_modules/@opentelemetry/resources/build/esm/Resource.js
|
|
1202
1202
|
var import_api = require("@opentelemetry/api");
|
|
1203
1203
|
|
|
1204
|
-
// node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
|
|
1204
|
+
// node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
|
|
1205
1205
|
var SemanticResourceAttributes = {
|
|
1206
1206
|
/**
|
|
1207
1207
|
* Name of the cloud provider.
|
|
@@ -2216,6 +2216,54 @@ function clearPromptContext() {
|
|
|
2216
2216
|
promptContext = null;
|
|
2217
2217
|
}
|
|
2218
2218
|
|
|
2219
|
+
// src/trace/wrappers/shared-utils.ts
|
|
2220
|
+
function sanitizeMetadataOnly(key, value) {
|
|
2221
|
+
const contentKeys = [
|
|
2222
|
+
"text",
|
|
2223
|
+
"content",
|
|
2224
|
+
"message",
|
|
2225
|
+
"messages",
|
|
2226
|
+
"object",
|
|
2227
|
+
"prompt",
|
|
2228
|
+
"system",
|
|
2229
|
+
"input",
|
|
2230
|
+
"output",
|
|
2231
|
+
"response",
|
|
2232
|
+
"toolCalls",
|
|
2233
|
+
"toolResults",
|
|
2234
|
+
"steps",
|
|
2235
|
+
"reasoning",
|
|
2236
|
+
"rawResponse",
|
|
2237
|
+
"rawCall",
|
|
2238
|
+
"body",
|
|
2239
|
+
"candidates",
|
|
2240
|
+
"parts"
|
|
2241
|
+
];
|
|
2242
|
+
if (contentKeys.includes(key)) {
|
|
2243
|
+
if (typeof value === "string") {
|
|
2244
|
+
return `[content omitted: ${value.length} chars]`;
|
|
2245
|
+
}
|
|
2246
|
+
if (Array.isArray(value)) {
|
|
2247
|
+
return `[content omitted: ${value.length} items]`;
|
|
2248
|
+
}
|
|
2249
|
+
if (typeof value === "object" && value !== null) {
|
|
2250
|
+
return "[content omitted]";
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
if (typeof value === "string") {
|
|
2254
|
+
if (value.startsWith("data:image/")) {
|
|
2255
|
+
return "[base64 image omitted]";
|
|
2256
|
+
}
|
|
2257
|
+
if (value.length > 1e3) {
|
|
2258
|
+
return `[large string omitted: ${value.length} chars]`;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
if (value instanceof Uint8Array || value && value.type === "Buffer") {
|
|
2262
|
+
return "[binary data omitted]";
|
|
2263
|
+
}
|
|
2264
|
+
return value;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2219
2267
|
// src/trace/wrappers/openai.ts
|
|
2220
2268
|
function wrapOpenAI(client, sessionCtx) {
|
|
2221
2269
|
const originalCreate = client.chat.completions.create.bind(
|
|
@@ -2263,6 +2311,13 @@ function wrapOpenAI(client, sessionCtx) {
|
|
|
2263
2311
|
if (response?.usage) {
|
|
2264
2312
|
attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
|
|
2265
2313
|
}
|
|
2314
|
+
try {
|
|
2315
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
2316
|
+
response,
|
|
2317
|
+
sanitizeMetadataOnly
|
|
2318
|
+
);
|
|
2319
|
+
} catch {
|
|
2320
|
+
}
|
|
2266
2321
|
const waterfallTimings = {
|
|
2267
2322
|
requestStart: 0,
|
|
2268
2323
|
requestEnd: endTime - startTime,
|
|
@@ -2389,6 +2444,13 @@ function wrapAnthropic(client, sessionCtx) {
|
|
|
2389
2444
|
if (response?.usage) {
|
|
2390
2445
|
attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
|
|
2391
2446
|
}
|
|
2447
|
+
try {
|
|
2448
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
2449
|
+
response,
|
|
2450
|
+
sanitizeMetadataOnly
|
|
2451
|
+
);
|
|
2452
|
+
} catch {
|
|
2453
|
+
}
|
|
2392
2454
|
const waterfallTimings = {
|
|
2393
2455
|
requestStart: 0,
|
|
2394
2456
|
requestEnd: endTime - startTime,
|
|
@@ -2509,6 +2571,13 @@ function wrapGoogleAI(model, sessionCtx) {
|
|
|
2509
2571
|
if (result?.usageMetadata) {
|
|
2510
2572
|
attributes["fallom.raw.usage"] = JSON.stringify(result.usageMetadata);
|
|
2511
2573
|
}
|
|
2574
|
+
try {
|
|
2575
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
2576
|
+
result,
|
|
2577
|
+
sanitizeMetadataOnly
|
|
2578
|
+
);
|
|
2579
|
+
} catch {
|
|
2580
|
+
}
|
|
2512
2581
|
const waterfallTimings = {
|
|
2513
2582
|
requestStart: 0,
|
|
2514
2583
|
requestEnd: endTime - startTime,
|
|
@@ -2705,6 +2774,13 @@ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2705
2774
|
result.experimental_providerMetadata
|
|
2706
2775
|
);
|
|
2707
2776
|
}
|
|
2777
|
+
try {
|
|
2778
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
2779
|
+
result,
|
|
2780
|
+
sanitizeMetadataOnly
|
|
2781
|
+
);
|
|
2782
|
+
} catch {
|
|
2783
|
+
}
|
|
2708
2784
|
const totalDurationMs = endTime - startTime;
|
|
2709
2785
|
const sortedToolTimings = Array.from(toolTimings.values()).sort(
|
|
2710
2786
|
(a, b) => a.startTime - b.startTime
|
|
@@ -3033,6 +3109,10 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
3033
3109
|
if (firstTokenTime) {
|
|
3034
3110
|
attributes["fallom.time_to_first_token_ms"] = firstTokenTime - startTime;
|
|
3035
3111
|
}
|
|
3112
|
+
try {
|
|
3113
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
3114
|
+
} catch {
|
|
3115
|
+
}
|
|
3036
3116
|
const totalDurationMs = endTime - startTime;
|
|
3037
3117
|
const sortedToolTimings = Array.from(toolTimings.values()).sort(
|
|
3038
3118
|
(a, b) => a.startTime - b.startTime
|
|
@@ -3238,6 +3318,10 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
3238
3318
|
result.experimental_providerMetadata
|
|
3239
3319
|
);
|
|
3240
3320
|
}
|
|
3321
|
+
try {
|
|
3322
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
3323
|
+
} catch {
|
|
3324
|
+
}
|
|
3241
3325
|
const promptCtx = getPromptContext();
|
|
3242
3326
|
sendTrace({
|
|
3243
3327
|
config_key: ctx.configKey,
|
|
@@ -3357,6 +3441,10 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
3357
3441
|
if (providerMetadata) {
|
|
3358
3442
|
attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
|
|
3359
3443
|
}
|
|
3444
|
+
try {
|
|
3445
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
3446
|
+
} catch {
|
|
3447
|
+
}
|
|
3360
3448
|
const promptCtx = getPromptContext();
|
|
3361
3449
|
sendTrace({
|
|
3362
3450
|
config_key: ctx.configKey,
|
|
@@ -3453,6 +3541,13 @@ function wrapMastraAgent(agent, sessionCtx) {
|
|
|
3453
3541
|
attributes["fallom.raw.request"] = JSON.stringify(input);
|
|
3454
3542
|
attributes["fallom.raw.response"] = JSON.stringify(result);
|
|
3455
3543
|
}
|
|
3544
|
+
try {
|
|
3545
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
3546
|
+
result,
|
|
3547
|
+
sanitizeMetadataOnly
|
|
3548
|
+
);
|
|
3549
|
+
} catch {
|
|
3550
|
+
}
|
|
3456
3551
|
sendTrace({
|
|
3457
3552
|
config_key: ctx.configKey,
|
|
3458
3553
|
session_id: ctx.sessionId,
|
package/dist/index.mjs
CHANGED
|
@@ -41,7 +41,7 @@ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
|
41
41
|
// node_modules/@opentelemetry/resources/build/esm/Resource.js
|
|
42
42
|
import { diag } from "@opentelemetry/api";
|
|
43
43
|
|
|
44
|
-
// node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
|
|
44
|
+
// node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions/build/esm/resource/SemanticResourceAttributes.js
|
|
45
45
|
var SemanticResourceAttributes = {
|
|
46
46
|
/**
|
|
47
47
|
* Name of the cloud provider.
|
|
@@ -1056,6 +1056,54 @@ function clearPromptContext() {
|
|
|
1056
1056
|
promptContext = null;
|
|
1057
1057
|
}
|
|
1058
1058
|
|
|
1059
|
+
// src/trace/wrappers/shared-utils.ts
|
|
1060
|
+
function sanitizeMetadataOnly(key, value) {
|
|
1061
|
+
const contentKeys = [
|
|
1062
|
+
"text",
|
|
1063
|
+
"content",
|
|
1064
|
+
"message",
|
|
1065
|
+
"messages",
|
|
1066
|
+
"object",
|
|
1067
|
+
"prompt",
|
|
1068
|
+
"system",
|
|
1069
|
+
"input",
|
|
1070
|
+
"output",
|
|
1071
|
+
"response",
|
|
1072
|
+
"toolCalls",
|
|
1073
|
+
"toolResults",
|
|
1074
|
+
"steps",
|
|
1075
|
+
"reasoning",
|
|
1076
|
+
"rawResponse",
|
|
1077
|
+
"rawCall",
|
|
1078
|
+
"body",
|
|
1079
|
+
"candidates",
|
|
1080
|
+
"parts"
|
|
1081
|
+
];
|
|
1082
|
+
if (contentKeys.includes(key)) {
|
|
1083
|
+
if (typeof value === "string") {
|
|
1084
|
+
return `[content omitted: ${value.length} chars]`;
|
|
1085
|
+
}
|
|
1086
|
+
if (Array.isArray(value)) {
|
|
1087
|
+
return `[content omitted: ${value.length} items]`;
|
|
1088
|
+
}
|
|
1089
|
+
if (typeof value === "object" && value !== null) {
|
|
1090
|
+
return "[content omitted]";
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (typeof value === "string") {
|
|
1094
|
+
if (value.startsWith("data:image/")) {
|
|
1095
|
+
return "[base64 image omitted]";
|
|
1096
|
+
}
|
|
1097
|
+
if (value.length > 1e3) {
|
|
1098
|
+
return `[large string omitted: ${value.length} chars]`;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
if (value instanceof Uint8Array || value && value.type === "Buffer") {
|
|
1102
|
+
return "[binary data omitted]";
|
|
1103
|
+
}
|
|
1104
|
+
return value;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1059
1107
|
// src/trace/wrappers/openai.ts
|
|
1060
1108
|
function wrapOpenAI(client, sessionCtx) {
|
|
1061
1109
|
const originalCreate = client.chat.completions.create.bind(
|
|
@@ -1103,6 +1151,13 @@ function wrapOpenAI(client, sessionCtx) {
|
|
|
1103
1151
|
if (response?.usage) {
|
|
1104
1152
|
attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
|
|
1105
1153
|
}
|
|
1154
|
+
try {
|
|
1155
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
1156
|
+
response,
|
|
1157
|
+
sanitizeMetadataOnly
|
|
1158
|
+
);
|
|
1159
|
+
} catch {
|
|
1160
|
+
}
|
|
1106
1161
|
const waterfallTimings = {
|
|
1107
1162
|
requestStart: 0,
|
|
1108
1163
|
requestEnd: endTime - startTime,
|
|
@@ -1229,6 +1284,13 @@ function wrapAnthropic(client, sessionCtx) {
|
|
|
1229
1284
|
if (response?.usage) {
|
|
1230
1285
|
attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
|
|
1231
1286
|
}
|
|
1287
|
+
try {
|
|
1288
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
1289
|
+
response,
|
|
1290
|
+
sanitizeMetadataOnly
|
|
1291
|
+
);
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1232
1294
|
const waterfallTimings = {
|
|
1233
1295
|
requestStart: 0,
|
|
1234
1296
|
requestEnd: endTime - startTime,
|
|
@@ -1349,6 +1411,13 @@ function wrapGoogleAI(model, sessionCtx) {
|
|
|
1349
1411
|
if (result?.usageMetadata) {
|
|
1350
1412
|
attributes["fallom.raw.usage"] = JSON.stringify(result.usageMetadata);
|
|
1351
1413
|
}
|
|
1414
|
+
try {
|
|
1415
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
1416
|
+
result,
|
|
1417
|
+
sanitizeMetadataOnly
|
|
1418
|
+
);
|
|
1419
|
+
} catch {
|
|
1420
|
+
}
|
|
1352
1421
|
const waterfallTimings = {
|
|
1353
1422
|
requestStart: 0,
|
|
1354
1423
|
requestEnd: endTime - startTime,
|
|
@@ -1545,6 +1614,13 @@ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
1545
1614
|
result.experimental_providerMetadata
|
|
1546
1615
|
);
|
|
1547
1616
|
}
|
|
1617
|
+
try {
|
|
1618
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
1619
|
+
result,
|
|
1620
|
+
sanitizeMetadataOnly
|
|
1621
|
+
);
|
|
1622
|
+
} catch {
|
|
1623
|
+
}
|
|
1548
1624
|
const totalDurationMs = endTime - startTime;
|
|
1549
1625
|
const sortedToolTimings = Array.from(toolTimings.values()).sort(
|
|
1550
1626
|
(a, b) => a.startTime - b.startTime
|
|
@@ -1873,6 +1949,10 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
1873
1949
|
if (firstTokenTime) {
|
|
1874
1950
|
attributes["fallom.time_to_first_token_ms"] = firstTokenTime - startTime;
|
|
1875
1951
|
}
|
|
1952
|
+
try {
|
|
1953
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
1954
|
+
} catch {
|
|
1955
|
+
}
|
|
1876
1956
|
const totalDurationMs = endTime - startTime;
|
|
1877
1957
|
const sortedToolTimings = Array.from(toolTimings.values()).sort(
|
|
1878
1958
|
(a, b) => a.startTime - b.startTime
|
|
@@ -2078,6 +2158,10 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2078
2158
|
result.experimental_providerMetadata
|
|
2079
2159
|
);
|
|
2080
2160
|
}
|
|
2161
|
+
try {
|
|
2162
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
2163
|
+
} catch {
|
|
2164
|
+
}
|
|
2081
2165
|
const promptCtx = getPromptContext();
|
|
2082
2166
|
sendTrace({
|
|
2083
2167
|
config_key: ctx.configKey,
|
|
@@ -2197,6 +2281,10 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2197
2281
|
if (providerMetadata) {
|
|
2198
2282
|
attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
|
|
2199
2283
|
}
|
|
2284
|
+
try {
|
|
2285
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
2286
|
+
} catch {
|
|
2287
|
+
}
|
|
2200
2288
|
const promptCtx = getPromptContext();
|
|
2201
2289
|
sendTrace({
|
|
2202
2290
|
config_key: ctx.configKey,
|
|
@@ -2293,6 +2381,13 @@ function wrapMastraAgent(agent, sessionCtx) {
|
|
|
2293
2381
|
attributes["fallom.raw.request"] = JSON.stringify(input);
|
|
2294
2382
|
attributes["fallom.raw.response"] = JSON.stringify(result);
|
|
2295
2383
|
}
|
|
2384
|
+
try {
|
|
2385
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
2386
|
+
result,
|
|
2387
|
+
sanitizeMetadataOnly
|
|
2388
|
+
);
|
|
2389
|
+
} catch {
|
|
2390
|
+
}
|
|
2296
2391
|
sendTrace({
|
|
2297
2392
|
config_key: ctx.configKey,
|
|
2298
2393
|
session_id: ctx.sessionId,
|
package/package.json
CHANGED