@fallom/trace 0.2.16 → 0.2.18
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 +222 -32
- package/dist/index.mjs +222 -32
- 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,
|
|
@@ -2658,20 +2727,36 @@ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2658
2727
|
tools: params?.tools ? Object.keys(params.tools) : void 0,
|
|
2659
2728
|
maxSteps: params?.maxSteps
|
|
2660
2729
|
});
|
|
2661
|
-
const mapToolCall = (tc) =>
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2730
|
+
const mapToolCall = (tc) => {
|
|
2731
|
+
let args2 = tc?.args ?? tc?.input;
|
|
2732
|
+
if (args2 === void 0 && tc) {
|
|
2733
|
+
const { type, toolCallId, toolName, providerExecuted, dynamic, invalid, error, providerMetadata, ...rest } = tc;
|
|
2734
|
+
if (Object.keys(rest).length > 0) {
|
|
2735
|
+
args2 = rest;
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
return {
|
|
2739
|
+
toolCallId: tc?.toolCallId,
|
|
2740
|
+
toolName: tc?.toolName,
|
|
2741
|
+
args: args2,
|
|
2742
|
+
type: tc?.type
|
|
2743
|
+
};
|
|
2744
|
+
};
|
|
2745
|
+
const mapToolResult = (tr) => {
|
|
2746
|
+
let result2 = tr?.result ?? tr?.output;
|
|
2747
|
+
if (result2 === void 0 && tr) {
|
|
2748
|
+
const { type, toolCallId, toolName, ...rest } = tr;
|
|
2749
|
+
if (Object.keys(rest).length > 0) {
|
|
2750
|
+
result2 = rest;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
return {
|
|
2754
|
+
toolCallId: tr?.toolCallId,
|
|
2755
|
+
toolName: tr?.toolName,
|
|
2756
|
+
result: result2,
|
|
2757
|
+
type: tr?.type
|
|
2758
|
+
};
|
|
2759
|
+
};
|
|
2675
2760
|
attributes["fallom.raw.response"] = JSON.stringify({
|
|
2676
2761
|
text: result?.text,
|
|
2677
2762
|
finishReason: result?.finishReason,
|
|
@@ -2705,6 +2790,13 @@ function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2705
2790
|
result.experimental_providerMetadata
|
|
2706
2791
|
);
|
|
2707
2792
|
}
|
|
2793
|
+
try {
|
|
2794
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
2795
|
+
result,
|
|
2796
|
+
sanitizeMetadataOnly
|
|
2797
|
+
);
|
|
2798
|
+
} catch {
|
|
2799
|
+
}
|
|
2708
2800
|
const totalDurationMs = endTime - startTime;
|
|
2709
2801
|
const sortedToolTimings = Array.from(toolTimings.values()).sort(
|
|
2710
2802
|
(a, b) => a.startTime - b.startTime
|
|
@@ -2877,7 +2969,9 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2877
2969
|
let wrappedParams = params;
|
|
2878
2970
|
if (params.tools && typeof params.tools === "object") {
|
|
2879
2971
|
const wrappedTools = {};
|
|
2880
|
-
for (const [toolName, tool] of Object.entries(
|
|
2972
|
+
for (const [toolName, tool] of Object.entries(
|
|
2973
|
+
params.tools
|
|
2974
|
+
)) {
|
|
2881
2975
|
if (tool && typeof tool.execute === "function") {
|
|
2882
2976
|
const originalExecute = tool.execute;
|
|
2883
2977
|
wrappedTools[toolName] = {
|
|
@@ -2960,10 +3054,54 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2960
3054
|
"\u{1F50D} [Fallom Debug] streamText toolCalls:",
|
|
2961
3055
|
JSON.stringify(toolCalls, null, 2)
|
|
2962
3056
|
);
|
|
3057
|
+
if (toolCalls?.[0]) {
|
|
3058
|
+
console.log(
|
|
3059
|
+
"\u{1F50D} [Fallom Debug] streamText toolCalls[0] keys:",
|
|
3060
|
+
Object.keys(toolCalls[0])
|
|
3061
|
+
);
|
|
3062
|
+
console.log(
|
|
3063
|
+
"\u{1F50D} [Fallom Debug] streamText toolCalls[0] full:",
|
|
3064
|
+
JSON.stringify(
|
|
3065
|
+
toolCalls[0],
|
|
3066
|
+
Object.getOwnPropertyNames(toolCalls[0]),
|
|
3067
|
+
2
|
|
3068
|
+
)
|
|
3069
|
+
);
|
|
3070
|
+
}
|
|
2963
3071
|
console.log(
|
|
2964
3072
|
"\u{1F50D} [Fallom Debug] streamText steps count:",
|
|
2965
3073
|
steps?.length
|
|
2966
3074
|
);
|
|
3075
|
+
if (steps?.[0]?.toolCalls?.[0]) {
|
|
3076
|
+
const tc = steps[0].toolCalls[0];
|
|
3077
|
+
console.log(
|
|
3078
|
+
"\u{1F50D} [Fallom Debug] steps[0].toolCalls[0] keys:",
|
|
3079
|
+
Object.keys(tc)
|
|
3080
|
+
);
|
|
3081
|
+
console.log(
|
|
3082
|
+
"\u{1F50D} [Fallom Debug] steps[0].toolCalls[0].args (v4):",
|
|
3083
|
+
tc.args
|
|
3084
|
+
);
|
|
3085
|
+
console.log(
|
|
3086
|
+
"\u{1F50D} [Fallom Debug] steps[0].toolCalls[0].input (v5):",
|
|
3087
|
+
tc.input
|
|
3088
|
+
);
|
|
3089
|
+
}
|
|
3090
|
+
if (steps?.[0]?.toolResults?.[0]) {
|
|
3091
|
+
const tr = steps[0].toolResults[0];
|
|
3092
|
+
console.log(
|
|
3093
|
+
"\u{1F50D} [Fallom Debug] steps[0].toolResults[0] keys:",
|
|
3094
|
+
Object.keys(tr)
|
|
3095
|
+
);
|
|
3096
|
+
console.log(
|
|
3097
|
+
"\u{1F50D} [Fallom Debug] steps[0].toolResults[0].result (v4):",
|
|
3098
|
+
typeof tr.result === "string" ? tr.result.slice(0, 200) : tr.result
|
|
3099
|
+
);
|
|
3100
|
+
console.log(
|
|
3101
|
+
"\u{1F50D} [Fallom Debug] steps[0].toolResults[0].output (v5):",
|
|
3102
|
+
typeof tr.output === "string" ? tr.output.slice(0, 200) : tr.output
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
2967
3105
|
}
|
|
2968
3106
|
let providerMetadata = result?.experimental_providerMetadata;
|
|
2969
3107
|
if (providerMetadata && typeof providerMetadata.then === "function") {
|
|
@@ -2979,20 +3117,46 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
2979
3117
|
"fallom.is_streaming": true
|
|
2980
3118
|
};
|
|
2981
3119
|
if (captureContent2) {
|
|
2982
|
-
const mapToolCall = (tc) =>
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3120
|
+
const mapToolCall = (tc) => {
|
|
3121
|
+
let args2 = tc?.args ?? tc?.input;
|
|
3122
|
+
if (args2 === void 0 && tc) {
|
|
3123
|
+
const {
|
|
3124
|
+
type,
|
|
3125
|
+
toolCallId,
|
|
3126
|
+
toolName,
|
|
3127
|
+
providerExecuted,
|
|
3128
|
+
dynamic,
|
|
3129
|
+
invalid,
|
|
3130
|
+
error,
|
|
3131
|
+
providerMetadata: providerMetadata2,
|
|
3132
|
+
...rest
|
|
3133
|
+
} = tc;
|
|
3134
|
+
if (Object.keys(rest).length > 0) {
|
|
3135
|
+
args2 = rest;
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
return {
|
|
3139
|
+
toolCallId: tc?.toolCallId,
|
|
3140
|
+
toolName: tc?.toolName,
|
|
3141
|
+
args: args2,
|
|
3142
|
+
type: tc?.type
|
|
3143
|
+
};
|
|
3144
|
+
};
|
|
3145
|
+
const mapToolResult = (tr) => {
|
|
3146
|
+
let result2 = tr?.result ?? tr?.output;
|
|
3147
|
+
if (result2 === void 0 && tr) {
|
|
3148
|
+
const { type, toolCallId, toolName, ...rest } = tr;
|
|
3149
|
+
if (Object.keys(rest).length > 0) {
|
|
3150
|
+
result2 = rest;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
return {
|
|
3154
|
+
toolCallId: tr?.toolCallId,
|
|
3155
|
+
toolName: tr?.toolName,
|
|
3156
|
+
result: result2,
|
|
3157
|
+
type: tr?.type
|
|
3158
|
+
};
|
|
3159
|
+
};
|
|
2996
3160
|
attributes["fallom.raw.request"] = JSON.stringify({
|
|
2997
3161
|
prompt: params?.prompt,
|
|
2998
3162
|
messages: params?.messages,
|
|
@@ -3033,6 +3197,13 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
3033
3197
|
if (firstTokenTime) {
|
|
3034
3198
|
attributes["fallom.time_to_first_token_ms"] = firstTokenTime - startTime;
|
|
3035
3199
|
}
|
|
3200
|
+
try {
|
|
3201
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
3202
|
+
result,
|
|
3203
|
+
sanitizeMetadataOnly
|
|
3204
|
+
);
|
|
3205
|
+
} catch {
|
|
3206
|
+
}
|
|
3036
3207
|
const totalDurationMs = endTime - startTime;
|
|
3037
3208
|
const sortedToolTimings = Array.from(toolTimings.values()).sort(
|
|
3038
3209
|
(a, b) => a.startTime - b.startTime
|
|
@@ -3057,8 +3228,12 @@ function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
3057
3228
|
});
|
|
3058
3229
|
}
|
|
3059
3230
|
if (sortedToolTimings.length > 0) {
|
|
3060
|
-
const firstToolStart = Math.min(
|
|
3061
|
-
|
|
3231
|
+
const firstToolStart = Math.min(
|
|
3232
|
+
...sortedToolTimings.map((t) => t.startTime)
|
|
3233
|
+
);
|
|
3234
|
+
const lastToolEnd = Math.max(
|
|
3235
|
+
...sortedToolTimings.map((t) => t.endTime)
|
|
3236
|
+
);
|
|
3062
3237
|
if (firstToolStart > 10) {
|
|
3063
3238
|
waterfallTimings.phases.push({
|
|
3064
3239
|
type: "llm",
|
|
@@ -3238,6 +3413,10 @@ function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
3238
3413
|
result.experimental_providerMetadata
|
|
3239
3414
|
);
|
|
3240
3415
|
}
|
|
3416
|
+
try {
|
|
3417
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
3418
|
+
} catch {
|
|
3419
|
+
}
|
|
3241
3420
|
const promptCtx = getPromptContext();
|
|
3242
3421
|
sendTrace({
|
|
3243
3422
|
config_key: ctx.configKey,
|
|
@@ -3357,6 +3536,10 @@ function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
|
3357
3536
|
if (providerMetadata) {
|
|
3358
3537
|
attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
|
|
3359
3538
|
}
|
|
3539
|
+
try {
|
|
3540
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(result, sanitizeMetadataOnly);
|
|
3541
|
+
} catch {
|
|
3542
|
+
}
|
|
3360
3543
|
const promptCtx = getPromptContext();
|
|
3361
3544
|
sendTrace({
|
|
3362
3545
|
config_key: ctx.configKey,
|
|
@@ -3453,6 +3636,13 @@ function wrapMastraAgent(agent, sessionCtx) {
|
|
|
3453
3636
|
attributes["fallom.raw.request"] = JSON.stringify(input);
|
|
3454
3637
|
attributes["fallom.raw.response"] = JSON.stringify(result);
|
|
3455
3638
|
}
|
|
3639
|
+
try {
|
|
3640
|
+
attributes["fallom.raw.metadata"] = JSON.stringify(
|
|
3641
|
+
result,
|
|
3642
|
+
sanitizeMetadataOnly
|
|
3643
|
+
);
|
|
3644
|
+
} catch {
|
|
3645
|
+
}
|
|
3456
3646
|
sendTrace({
|
|
3457
3647
|
config_key: ctx.configKey,
|
|
3458
3648
|
session_id: ctx.sessionId,
|