@fallom/trace 0.1.6 → 0.1.10
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/README.md +3 -1
- package/dist/chunk-6MSTRIK4.mjs +255 -0
- package/dist/chunk-H2EACSBT.mjs +255 -0
- package/dist/index.d.mts +53 -5
- package/dist/index.d.ts +53 -5
- package/dist/index.js +471 -24
- package/dist/index.mjs +466 -26
- package/dist/prompts-VAN5E3L4.mjs +14 -0
- package/dist/prompts-ZSLS4DHO.mjs +14 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ function log(msg) {
|
|
|
36
36
|
}
|
|
37
37
|
function init(options = {}) {
|
|
38
38
|
apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
|
|
39
|
-
baseUrl = options.baseUrl || process.env.FALLOM_BASE_URL || "https://
|
|
39
|
+
baseUrl = options.baseUrl || process.env.FALLOM_PROMPTS_URL || process.env.FALLOM_BASE_URL || "https://prompts.fallom.com";
|
|
40
40
|
initialized = true;
|
|
41
41
|
if (!apiKey) {
|
|
42
42
|
return;
|
|
@@ -187,6 +187,13 @@ async function getAB(abTestKey, sessionId, options = {}) {
|
|
|
187
187
|
throw new Error(`Prompt A/B test '${abTestKey}' has no current version.`);
|
|
188
188
|
}
|
|
189
189
|
const { variants } = versionData;
|
|
190
|
+
log(`A/B test '${abTestKey}' has ${variants?.length ?? 0} variants`);
|
|
191
|
+
log(`Version data: ${JSON.stringify(versionData, null, 2)}`);
|
|
192
|
+
if (!variants || variants.length === 0) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Prompt A/B test '${abTestKey}' has no variants configured.`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
190
197
|
const hashBytes = (0, import_crypto.createHash)("md5").update(sessionId).digest();
|
|
191
198
|
const hashVal = hashBytes.readUInt32BE(0) % 1e6;
|
|
192
199
|
let cumulative = 0;
|
|
@@ -248,7 +255,7 @@ var init_prompts = __esm({
|
|
|
248
255
|
"use strict";
|
|
249
256
|
import_crypto = require("crypto");
|
|
250
257
|
apiKey = null;
|
|
251
|
-
baseUrl = "https://
|
|
258
|
+
baseUrl = "https://prompts.fallom.com";
|
|
252
259
|
initialized = false;
|
|
253
260
|
syncInterval = null;
|
|
254
261
|
debugMode = false;
|
|
@@ -280,6 +287,7 @@ __export(trace_exports, {
|
|
|
280
287
|
setSession: () => setSession,
|
|
281
288
|
shutdown: () => shutdown,
|
|
282
289
|
span: () => span,
|
|
290
|
+
wrapAISDK: () => wrapAISDK,
|
|
283
291
|
wrapAnthropic: () => wrapAnthropic,
|
|
284
292
|
wrapGoogleAI: () => wrapGoogleAI,
|
|
285
293
|
wrapOpenAI: () => wrapOpenAI
|
|
@@ -915,7 +923,7 @@ var Resource = (
|
|
|
915
923
|
var sessionStorage = new import_async_hooks.AsyncLocalStorage();
|
|
916
924
|
var fallbackSession = null;
|
|
917
925
|
var apiKey2 = null;
|
|
918
|
-
var baseUrl2 = "https://
|
|
926
|
+
var baseUrl2 = "https://traces.fallom.com";
|
|
919
927
|
var initialized2 = false;
|
|
920
928
|
var captureContent = true;
|
|
921
929
|
var debugMode2 = false;
|
|
@@ -958,7 +966,7 @@ async function init2(options = {}) {
|
|
|
958
966
|
debugMode2 = options.debug ?? false;
|
|
959
967
|
log2("\u{1F680} Initializing Fallom tracing...");
|
|
960
968
|
apiKey2 = options.apiKey || process.env.FALLOM_API_KEY || null;
|
|
961
|
-
baseUrl2 = options.baseUrl || process.env.FALLOM_BASE_URL || "https://
|
|
969
|
+
baseUrl2 = options.baseUrl || process.env.FALLOM_TRACES_URL || process.env.FALLOM_BASE_URL || "https://traces.fallom.com";
|
|
962
970
|
const envCapture = process.env.FALLOM_CAPTURE_CONTENT?.toLowerCase();
|
|
963
971
|
if (envCapture === "false" || envCapture === "0" || envCapture === "no") {
|
|
964
972
|
captureContent = false;
|
|
@@ -1130,12 +1138,12 @@ function messagesToOtelAttributes(messages, completion, model, responseId) {
|
|
|
1130
1138
|
if (messages) {
|
|
1131
1139
|
messages.forEach((msg, i) => {
|
|
1132
1140
|
attrs[`gen_ai.prompt.${i}.role`] = msg.role;
|
|
1133
|
-
attrs[`gen_ai.prompt.${i}.content`] = msg.content;
|
|
1141
|
+
attrs[`gen_ai.prompt.${i}.content`] = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
1134
1142
|
});
|
|
1135
1143
|
}
|
|
1136
1144
|
if (completion) {
|
|
1137
1145
|
attrs["gen_ai.completion.0.role"] = completion.role;
|
|
1138
|
-
attrs["gen_ai.completion.0.content"] = completion.content;
|
|
1146
|
+
attrs["gen_ai.completion.0.content"] = typeof completion.content === "string" ? completion.content : JSON.stringify(completion.content);
|
|
1139
1147
|
if (completion.tool_calls) {
|
|
1140
1148
|
attrs["gen_ai.completion.0.tool_calls"] = JSON.stringify(
|
|
1141
1149
|
completion.tool_calls
|
|
@@ -1152,10 +1160,13 @@ function generateHexId(length) {
|
|
|
1152
1160
|
var traceContextStorage = new import_async_hooks.AsyncLocalStorage();
|
|
1153
1161
|
var fallbackTraceContext = null;
|
|
1154
1162
|
async function sendTrace(trace) {
|
|
1163
|
+
const url = `${baseUrl2}/v1/traces`;
|
|
1164
|
+
log2("\u{1F4E4} Sending trace to:", url);
|
|
1165
|
+
log2(" Session:", trace.session_id, "Config:", trace.config_key);
|
|
1155
1166
|
try {
|
|
1156
1167
|
const controller = new AbortController();
|
|
1157
1168
|
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
1158
|
-
await fetch(
|
|
1169
|
+
const response = await fetch(url, {
|
|
1159
1170
|
method: "POST",
|
|
1160
1171
|
headers: {
|
|
1161
1172
|
Authorization: `Bearer ${apiKey2}`,
|
|
@@ -1165,8 +1176,14 @@ async function sendTrace(trace) {
|
|
|
1165
1176
|
signal: controller.signal
|
|
1166
1177
|
});
|
|
1167
1178
|
clearTimeout(timeoutId);
|
|
1168
|
-
|
|
1169
|
-
|
|
1179
|
+
if (!response.ok) {
|
|
1180
|
+
const text = await response.text();
|
|
1181
|
+
log2("\u274C Trace send failed:", response.status, text);
|
|
1182
|
+
} else {
|
|
1183
|
+
log2("\u2705 Trace sent:", trace.name, trace.model);
|
|
1184
|
+
}
|
|
1185
|
+
} catch (err) {
|
|
1186
|
+
log2("\u274C Trace send error:", err instanceof Error ? err.message : err);
|
|
1170
1187
|
}
|
|
1171
1188
|
}
|
|
1172
1189
|
function wrapOpenAI(client) {
|
|
@@ -1468,6 +1485,414 @@ function wrapGoogleAI(model) {
|
|
|
1468
1485
|
};
|
|
1469
1486
|
return model;
|
|
1470
1487
|
}
|
|
1488
|
+
function wrapAISDK(ai) {
|
|
1489
|
+
const aiModule = ai;
|
|
1490
|
+
return {
|
|
1491
|
+
generateText: createGenerateTextWrapper(aiModule),
|
|
1492
|
+
streamText: createStreamTextWrapper(aiModule),
|
|
1493
|
+
generateObject: aiModule.generateObject ? createGenerateObjectWrapper(aiModule) : void 0,
|
|
1494
|
+
streamObject: aiModule.streamObject ? createStreamObjectWrapper(aiModule) : void 0
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
function createGenerateTextWrapper(aiModule) {
|
|
1498
|
+
return async (...args) => {
|
|
1499
|
+
const ctx = sessionStorage.getStore() || fallbackSession;
|
|
1500
|
+
if (!ctx || !initialized2) {
|
|
1501
|
+
return aiModule.generateText(...args);
|
|
1502
|
+
}
|
|
1503
|
+
let promptCtx = null;
|
|
1504
|
+
try {
|
|
1505
|
+
const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
|
|
1506
|
+
promptCtx = getPromptContext2();
|
|
1507
|
+
} catch {
|
|
1508
|
+
}
|
|
1509
|
+
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
1510
|
+
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1511
|
+
const spanId = generateHexId(16);
|
|
1512
|
+
const parentSpanId = traceCtx?.parentSpanId;
|
|
1513
|
+
const params = args[0] || {};
|
|
1514
|
+
const startTime = Date.now();
|
|
1515
|
+
try {
|
|
1516
|
+
const result = await aiModule.generateText(...args);
|
|
1517
|
+
const endTime = Date.now();
|
|
1518
|
+
const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
|
|
1519
|
+
const attributes = {};
|
|
1520
|
+
if (captureContent) {
|
|
1521
|
+
attributes["gen_ai.request.model"] = modelId;
|
|
1522
|
+
attributes["gen_ai.response.model"] = modelId;
|
|
1523
|
+
if (params?.prompt) {
|
|
1524
|
+
attributes["gen_ai.prompt.0.role"] = "user";
|
|
1525
|
+
attributes["gen_ai.prompt.0.content"] = params.prompt;
|
|
1526
|
+
}
|
|
1527
|
+
if (params?.messages) {
|
|
1528
|
+
params.messages.forEach((msg, i) => {
|
|
1529
|
+
attributes[`gen_ai.prompt.${i}.role`] = msg.role;
|
|
1530
|
+
attributes[`gen_ai.prompt.${i}.content`] = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
if (result?.text) {
|
|
1534
|
+
attributes["gen_ai.completion.0.role"] = "assistant";
|
|
1535
|
+
attributes["gen_ai.completion.0.content"] = result.text;
|
|
1536
|
+
}
|
|
1537
|
+
if (result?.response?.id) {
|
|
1538
|
+
attributes["gen_ai.response.id"] = result.response.id;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
sendTrace({
|
|
1542
|
+
config_key: ctx.configKey,
|
|
1543
|
+
session_id: ctx.sessionId,
|
|
1544
|
+
customer_id: ctx.customerId,
|
|
1545
|
+
trace_id: traceId,
|
|
1546
|
+
span_id: spanId,
|
|
1547
|
+
parent_span_id: parentSpanId,
|
|
1548
|
+
name: "generateText",
|
|
1549
|
+
kind: "llm",
|
|
1550
|
+
model: modelId,
|
|
1551
|
+
start_time: new Date(startTime).toISOString(),
|
|
1552
|
+
end_time: new Date(endTime).toISOString(),
|
|
1553
|
+
duration_ms: endTime - startTime,
|
|
1554
|
+
status: "OK",
|
|
1555
|
+
prompt_tokens: result?.usage?.promptTokens,
|
|
1556
|
+
completion_tokens: result?.usage?.completionTokens,
|
|
1557
|
+
total_tokens: result?.usage?.totalTokens,
|
|
1558
|
+
attributes: captureContent ? attributes : void 0,
|
|
1559
|
+
prompt_key: promptCtx?.promptKey,
|
|
1560
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1561
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1562
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1563
|
+
}).catch(() => {
|
|
1564
|
+
});
|
|
1565
|
+
return result;
|
|
1566
|
+
} catch (error) {
|
|
1567
|
+
const endTime = Date.now();
|
|
1568
|
+
const modelId = params?.model?.modelId || String(params?.model || "unknown");
|
|
1569
|
+
sendTrace({
|
|
1570
|
+
config_key: ctx.configKey,
|
|
1571
|
+
session_id: ctx.sessionId,
|
|
1572
|
+
customer_id: ctx.customerId,
|
|
1573
|
+
trace_id: traceId,
|
|
1574
|
+
span_id: spanId,
|
|
1575
|
+
parent_span_id: parentSpanId,
|
|
1576
|
+
name: "generateText",
|
|
1577
|
+
kind: "llm",
|
|
1578
|
+
model: modelId,
|
|
1579
|
+
start_time: new Date(startTime).toISOString(),
|
|
1580
|
+
end_time: new Date(endTime).toISOString(),
|
|
1581
|
+
duration_ms: endTime - startTime,
|
|
1582
|
+
status: "ERROR",
|
|
1583
|
+
error_message: error?.message,
|
|
1584
|
+
prompt_key: promptCtx?.promptKey,
|
|
1585
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1586
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1587
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1588
|
+
}).catch(() => {
|
|
1589
|
+
});
|
|
1590
|
+
throw error;
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
function createStreamTextWrapper(aiModule) {
|
|
1595
|
+
return async (...args) => {
|
|
1596
|
+
const ctx = sessionStorage.getStore() || fallbackSession;
|
|
1597
|
+
const params = args[0] || {};
|
|
1598
|
+
const startTime = Date.now();
|
|
1599
|
+
const result = await aiModule.streamText(...args);
|
|
1600
|
+
if (!ctx || !initialized2) {
|
|
1601
|
+
return result;
|
|
1602
|
+
}
|
|
1603
|
+
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
1604
|
+
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1605
|
+
const spanId = generateHexId(16);
|
|
1606
|
+
const parentSpanId = traceCtx?.parentSpanId;
|
|
1607
|
+
let firstTokenTime = null;
|
|
1608
|
+
const modelId = params?.model?.modelId || String(params?.model || "unknown");
|
|
1609
|
+
let promptCtx = null;
|
|
1610
|
+
try {
|
|
1611
|
+
const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
|
|
1612
|
+
promptCtx = getPromptContext2();
|
|
1613
|
+
} catch {
|
|
1614
|
+
}
|
|
1615
|
+
if (result?.usage) {
|
|
1616
|
+
result.usage.then((usage) => {
|
|
1617
|
+
const endTime = Date.now();
|
|
1618
|
+
log2("\u{1F4CA} streamText usage:", JSON.stringify(usage, null, 2));
|
|
1619
|
+
const attributes = {};
|
|
1620
|
+
if (captureContent) {
|
|
1621
|
+
attributes["gen_ai.request.model"] = modelId;
|
|
1622
|
+
if (params?.prompt) {
|
|
1623
|
+
attributes["gen_ai.prompt.0.role"] = "user";
|
|
1624
|
+
attributes["gen_ai.prompt.0.content"] = params.prompt;
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
if (firstTokenTime) {
|
|
1628
|
+
attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
|
|
1629
|
+
}
|
|
1630
|
+
const tracePayload = {
|
|
1631
|
+
config_key: ctx.configKey,
|
|
1632
|
+
session_id: ctx.sessionId,
|
|
1633
|
+
customer_id: ctx.customerId,
|
|
1634
|
+
trace_id: traceId,
|
|
1635
|
+
span_id: spanId,
|
|
1636
|
+
parent_span_id: parentSpanId,
|
|
1637
|
+
name: "streamText",
|
|
1638
|
+
kind: "llm",
|
|
1639
|
+
model: modelId,
|
|
1640
|
+
start_time: new Date(startTime).toISOString(),
|
|
1641
|
+
end_time: new Date(endTime).toISOString(),
|
|
1642
|
+
duration_ms: endTime - startTime,
|
|
1643
|
+
status: "OK",
|
|
1644
|
+
prompt_tokens: usage?.promptTokens,
|
|
1645
|
+
completion_tokens: usage?.completionTokens,
|
|
1646
|
+
total_tokens: usage?.totalTokens,
|
|
1647
|
+
time_to_first_token_ms: firstTokenTime ? firstTokenTime - startTime : void 0,
|
|
1648
|
+
attributes: captureContent ? attributes : void 0,
|
|
1649
|
+
prompt_key: promptCtx?.promptKey,
|
|
1650
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1651
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1652
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1653
|
+
};
|
|
1654
|
+
sendTrace(tracePayload).catch(() => {
|
|
1655
|
+
});
|
|
1656
|
+
}).catch((error) => {
|
|
1657
|
+
const endTime = Date.now();
|
|
1658
|
+
log2("\u274C streamText error:", error?.message);
|
|
1659
|
+
sendTrace({
|
|
1660
|
+
config_key: ctx.configKey,
|
|
1661
|
+
session_id: ctx.sessionId,
|
|
1662
|
+
customer_id: ctx.customerId,
|
|
1663
|
+
trace_id: traceId,
|
|
1664
|
+
span_id: spanId,
|
|
1665
|
+
parent_span_id: parentSpanId,
|
|
1666
|
+
name: "streamText",
|
|
1667
|
+
kind: "llm",
|
|
1668
|
+
model: modelId,
|
|
1669
|
+
start_time: new Date(startTime).toISOString(),
|
|
1670
|
+
end_time: new Date(endTime).toISOString(),
|
|
1671
|
+
duration_ms: endTime - startTime,
|
|
1672
|
+
status: "ERROR",
|
|
1673
|
+
error_message: error?.message,
|
|
1674
|
+
prompt_key: promptCtx?.promptKey,
|
|
1675
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1676
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1677
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1678
|
+
}).catch(() => {
|
|
1679
|
+
});
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
if (result?.textStream) {
|
|
1683
|
+
const originalTextStream = result.textStream;
|
|
1684
|
+
const wrappedTextStream = (async function* () {
|
|
1685
|
+
for await (const chunk of originalTextStream) {
|
|
1686
|
+
if (!firstTokenTime) {
|
|
1687
|
+
firstTokenTime = Date.now();
|
|
1688
|
+
log2("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
|
|
1689
|
+
}
|
|
1690
|
+
yield chunk;
|
|
1691
|
+
}
|
|
1692
|
+
})();
|
|
1693
|
+
return new Proxy(result, {
|
|
1694
|
+
get(target, prop) {
|
|
1695
|
+
if (prop === "textStream") {
|
|
1696
|
+
return wrappedTextStream;
|
|
1697
|
+
}
|
|
1698
|
+
return target[prop];
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
return result;
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
function createGenerateObjectWrapper(aiModule) {
|
|
1706
|
+
return async (...args) => {
|
|
1707
|
+
const ctx = sessionStorage.getStore() || fallbackSession;
|
|
1708
|
+
if (!ctx || !initialized2) {
|
|
1709
|
+
return aiModule.generateObject(...args);
|
|
1710
|
+
}
|
|
1711
|
+
let promptCtx = null;
|
|
1712
|
+
try {
|
|
1713
|
+
const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
|
|
1714
|
+
promptCtx = getPromptContext2();
|
|
1715
|
+
} catch {
|
|
1716
|
+
}
|
|
1717
|
+
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
1718
|
+
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1719
|
+
const spanId = generateHexId(16);
|
|
1720
|
+
const parentSpanId = traceCtx?.parentSpanId;
|
|
1721
|
+
const params = args[0] || {};
|
|
1722
|
+
const startTime = Date.now();
|
|
1723
|
+
try {
|
|
1724
|
+
const result = await aiModule.generateObject(...args);
|
|
1725
|
+
const endTime = Date.now();
|
|
1726
|
+
const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
|
|
1727
|
+
const attributes = {};
|
|
1728
|
+
if (captureContent) {
|
|
1729
|
+
attributes["gen_ai.request.model"] = modelId;
|
|
1730
|
+
attributes["gen_ai.response.model"] = modelId;
|
|
1731
|
+
if (result?.object) {
|
|
1732
|
+
attributes["gen_ai.completion.0.role"] = "assistant";
|
|
1733
|
+
attributes["gen_ai.completion.0.content"] = JSON.stringify(
|
|
1734
|
+
result.object
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
sendTrace({
|
|
1739
|
+
config_key: ctx.configKey,
|
|
1740
|
+
session_id: ctx.sessionId,
|
|
1741
|
+
customer_id: ctx.customerId,
|
|
1742
|
+
trace_id: traceId,
|
|
1743
|
+
span_id: spanId,
|
|
1744
|
+
parent_span_id: parentSpanId,
|
|
1745
|
+
name: "generateObject",
|
|
1746
|
+
kind: "llm",
|
|
1747
|
+
model: modelId,
|
|
1748
|
+
start_time: new Date(startTime).toISOString(),
|
|
1749
|
+
end_time: new Date(endTime).toISOString(),
|
|
1750
|
+
duration_ms: endTime - startTime,
|
|
1751
|
+
status: "OK",
|
|
1752
|
+
prompt_tokens: result?.usage?.promptTokens,
|
|
1753
|
+
completion_tokens: result?.usage?.completionTokens,
|
|
1754
|
+
total_tokens: result?.usage?.totalTokens,
|
|
1755
|
+
attributes: captureContent ? attributes : void 0,
|
|
1756
|
+
prompt_key: promptCtx?.promptKey,
|
|
1757
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1758
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1759
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1760
|
+
}).catch(() => {
|
|
1761
|
+
});
|
|
1762
|
+
return result;
|
|
1763
|
+
} catch (error) {
|
|
1764
|
+
const endTime = Date.now();
|
|
1765
|
+
const modelId = params?.model?.modelId || String(params?.model || "unknown");
|
|
1766
|
+
sendTrace({
|
|
1767
|
+
config_key: ctx.configKey,
|
|
1768
|
+
session_id: ctx.sessionId,
|
|
1769
|
+
customer_id: ctx.customerId,
|
|
1770
|
+
trace_id: traceId,
|
|
1771
|
+
span_id: spanId,
|
|
1772
|
+
parent_span_id: parentSpanId,
|
|
1773
|
+
name: "generateObject",
|
|
1774
|
+
kind: "llm",
|
|
1775
|
+
model: modelId,
|
|
1776
|
+
start_time: new Date(startTime).toISOString(),
|
|
1777
|
+
end_time: new Date(endTime).toISOString(),
|
|
1778
|
+
duration_ms: endTime - startTime,
|
|
1779
|
+
status: "ERROR",
|
|
1780
|
+
error_message: error?.message,
|
|
1781
|
+
prompt_key: promptCtx?.promptKey,
|
|
1782
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1783
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1784
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1785
|
+
}).catch(() => {
|
|
1786
|
+
});
|
|
1787
|
+
throw error;
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function createStreamObjectWrapper(aiModule) {
|
|
1792
|
+
return async (...args) => {
|
|
1793
|
+
const ctx = sessionStorage.getStore() || fallbackSession;
|
|
1794
|
+
const params = args[0] || {};
|
|
1795
|
+
const startTime = Date.now();
|
|
1796
|
+
const result = await aiModule.streamObject(...args);
|
|
1797
|
+
log2("\u{1F50D} streamObject result keys:", Object.keys(result || {}));
|
|
1798
|
+
if (!ctx || !initialized2) {
|
|
1799
|
+
return result;
|
|
1800
|
+
}
|
|
1801
|
+
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
1802
|
+
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1803
|
+
const spanId = generateHexId(16);
|
|
1804
|
+
const parentSpanId = traceCtx?.parentSpanId;
|
|
1805
|
+
let firstTokenTime = null;
|
|
1806
|
+
const modelId = params?.model?.modelId || String(params?.model || "unknown");
|
|
1807
|
+
let promptCtx = null;
|
|
1808
|
+
try {
|
|
1809
|
+
const { getPromptContext: getPromptContext2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
|
|
1810
|
+
promptCtx = getPromptContext2();
|
|
1811
|
+
} catch {
|
|
1812
|
+
}
|
|
1813
|
+
if (result?.usage) {
|
|
1814
|
+
result.usage.then((usage) => {
|
|
1815
|
+
const endTime = Date.now();
|
|
1816
|
+
log2("\u{1F4CA} streamObject usage:", JSON.stringify(usage, null, 2));
|
|
1817
|
+
const attributes = {};
|
|
1818
|
+
if (captureContent) {
|
|
1819
|
+
attributes["gen_ai.request.model"] = modelId;
|
|
1820
|
+
}
|
|
1821
|
+
if (firstTokenTime) {
|
|
1822
|
+
attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
|
|
1823
|
+
}
|
|
1824
|
+
sendTrace({
|
|
1825
|
+
config_key: ctx.configKey,
|
|
1826
|
+
session_id: ctx.sessionId,
|
|
1827
|
+
customer_id: ctx.customerId,
|
|
1828
|
+
trace_id: traceId,
|
|
1829
|
+
span_id: spanId,
|
|
1830
|
+
parent_span_id: parentSpanId,
|
|
1831
|
+
name: "streamObject",
|
|
1832
|
+
kind: "llm",
|
|
1833
|
+
model: modelId,
|
|
1834
|
+
start_time: new Date(startTime).toISOString(),
|
|
1835
|
+
end_time: new Date(endTime).toISOString(),
|
|
1836
|
+
duration_ms: endTime - startTime,
|
|
1837
|
+
status: "OK",
|
|
1838
|
+
prompt_tokens: usage?.promptTokens,
|
|
1839
|
+
completion_tokens: usage?.completionTokens,
|
|
1840
|
+
total_tokens: usage?.totalTokens,
|
|
1841
|
+
attributes: captureContent ? attributes : void 0,
|
|
1842
|
+
prompt_key: promptCtx?.promptKey,
|
|
1843
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1844
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1845
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1846
|
+
}).catch(() => {
|
|
1847
|
+
});
|
|
1848
|
+
}).catch((error) => {
|
|
1849
|
+
const endTime = Date.now();
|
|
1850
|
+
sendTrace({
|
|
1851
|
+
config_key: ctx.configKey,
|
|
1852
|
+
session_id: ctx.sessionId,
|
|
1853
|
+
customer_id: ctx.customerId,
|
|
1854
|
+
trace_id: traceId,
|
|
1855
|
+
span_id: spanId,
|
|
1856
|
+
parent_span_id: parentSpanId,
|
|
1857
|
+
name: "streamObject",
|
|
1858
|
+
kind: "llm",
|
|
1859
|
+
model: modelId,
|
|
1860
|
+
start_time: new Date(startTime).toISOString(),
|
|
1861
|
+
end_time: new Date(endTime).toISOString(),
|
|
1862
|
+
duration_ms: endTime - startTime,
|
|
1863
|
+
status: "ERROR",
|
|
1864
|
+
error_message: error?.message,
|
|
1865
|
+
prompt_key: promptCtx?.promptKey,
|
|
1866
|
+
prompt_version: promptCtx?.promptVersion,
|
|
1867
|
+
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1868
|
+
prompt_variant_index: promptCtx?.variantIndex
|
|
1869
|
+
}).catch(() => {
|
|
1870
|
+
});
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
if (result?.partialObjectStream) {
|
|
1874
|
+
const originalStream = result.partialObjectStream;
|
|
1875
|
+
const wrappedStream = (async function* () {
|
|
1876
|
+
for await (const chunk of originalStream) {
|
|
1877
|
+
if (!firstTokenTime) {
|
|
1878
|
+
firstTokenTime = Date.now();
|
|
1879
|
+
log2("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
|
|
1880
|
+
}
|
|
1881
|
+
yield chunk;
|
|
1882
|
+
}
|
|
1883
|
+
})();
|
|
1884
|
+
return new Proxy(result, {
|
|
1885
|
+
get(target, prop) {
|
|
1886
|
+
if (prop === "partialObjectStream") {
|
|
1887
|
+
return wrappedStream;
|
|
1888
|
+
}
|
|
1889
|
+
return target[prop];
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
return result;
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1471
1896
|
|
|
1472
1897
|
// src/models.ts
|
|
1473
1898
|
var models_exports = {};
|
|
@@ -1477,7 +1902,7 @@ __export(models_exports, {
|
|
|
1477
1902
|
});
|
|
1478
1903
|
var import_crypto2 = require("crypto");
|
|
1479
1904
|
var apiKey3 = null;
|
|
1480
|
-
var baseUrl3 = "https://
|
|
1905
|
+
var baseUrl3 = "https://configs.fallom.com";
|
|
1481
1906
|
var initialized3 = false;
|
|
1482
1907
|
var syncInterval2 = null;
|
|
1483
1908
|
var debugMode3 = false;
|
|
@@ -1491,7 +1916,7 @@ function log3(msg) {
|
|
|
1491
1916
|
}
|
|
1492
1917
|
function init3(options = {}) {
|
|
1493
1918
|
apiKey3 = options.apiKey || process.env.FALLOM_API_KEY || null;
|
|
1494
|
-
baseUrl3 = options.baseUrl || process.env.FALLOM_BASE_URL || "https://
|
|
1919
|
+
baseUrl3 = options.baseUrl || process.env.FALLOM_CONFIGS_URL || process.env.FALLOM_BASE_URL || "https://configs.fallom.com";
|
|
1495
1920
|
initialized3 = true;
|
|
1496
1921
|
if (!apiKey3) {
|
|
1497
1922
|
return;
|
|
@@ -1580,20 +2005,28 @@ async function get2(configKey, sessionId, options = {}) {
|
|
|
1580
2005
|
const { version, fallback, debug = false } = options;
|
|
1581
2006
|
debugMode3 = debug;
|
|
1582
2007
|
ensureInit2();
|
|
1583
|
-
log3(
|
|
2008
|
+
log3(
|
|
2009
|
+
`get() called: configKey=${configKey}, sessionId=${sessionId}, fallback=${fallback}`
|
|
2010
|
+
);
|
|
1584
2011
|
try {
|
|
1585
2012
|
let configData = configCache.get(configKey);
|
|
1586
|
-
log3(
|
|
2013
|
+
log3(
|
|
2014
|
+
`Cache lookup for '${configKey}': ${configData ? "found" : "not found"}`
|
|
2015
|
+
);
|
|
1587
2016
|
if (!configData) {
|
|
1588
2017
|
log3("Not in cache, fetching...");
|
|
1589
2018
|
await fetchConfigs(SYNC_TIMEOUT2);
|
|
1590
2019
|
configData = configCache.get(configKey);
|
|
1591
|
-
log3(
|
|
2020
|
+
log3(
|
|
2021
|
+
`After fetch, cache lookup: ${configData ? "found" : "still not found"}`
|
|
2022
|
+
);
|
|
1592
2023
|
}
|
|
1593
2024
|
if (!configData) {
|
|
1594
2025
|
log3(`Config not found, using fallback: ${fallback}`);
|
|
1595
2026
|
if (fallback) {
|
|
1596
|
-
console.warn(
|
|
2027
|
+
console.warn(
|
|
2028
|
+
`[Fallom WARNING] Config '${configKey}' not found, using fallback model: ${fallback}`
|
|
2029
|
+
);
|
|
1597
2030
|
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1598
2031
|
}
|
|
1599
2032
|
throw new Error(
|
|
@@ -1609,7 +2042,9 @@ async function get2(configKey, sessionId, options = {}) {
|
|
|
1609
2042
|
}
|
|
1610
2043
|
if (!config) {
|
|
1611
2044
|
if (fallback) {
|
|
1612
|
-
console.warn(
|
|
2045
|
+
console.warn(
|
|
2046
|
+
`[Fallom WARNING] Config '${configKey}' version ${version} not found, using fallback: ${fallback}`
|
|
2047
|
+
);
|
|
1613
2048
|
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1614
2049
|
}
|
|
1615
2050
|
throw new Error(`Config '${configKey}' version ${version} not found.`);
|
|
@@ -1620,7 +2055,9 @@ async function get2(configKey, sessionId, options = {}) {
|
|
|
1620
2055
|
config = configData.versions.get(targetVersion);
|
|
1621
2056
|
if (!config) {
|
|
1622
2057
|
if (fallback) {
|
|
1623
|
-
console.warn(
|
|
2058
|
+
console.warn(
|
|
2059
|
+
`[Fallom WARNING] Config '${configKey}' has no cached version, using fallback: ${fallback}`
|
|
2060
|
+
);
|
|
1624
2061
|
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1625
2062
|
}
|
|
1626
2063
|
throw new Error(`Config '${configKey}' has no cached version.`);
|
|
@@ -1629,7 +2066,11 @@ async function get2(configKey, sessionId, options = {}) {
|
|
|
1629
2066
|
const variantsRaw = config.variants;
|
|
1630
2067
|
const configVersion = config.version || targetVersion;
|
|
1631
2068
|
const variants = Array.isArray(variantsRaw) ? variantsRaw : Object.values(variantsRaw);
|
|
1632
|
-
log3(
|
|
2069
|
+
log3(
|
|
2070
|
+
`Config found! Version: ${configVersion}, Variants: ${JSON.stringify(
|
|
2071
|
+
variants
|
|
2072
|
+
)}`
|
|
2073
|
+
);
|
|
1633
2074
|
const hashBytes = (0, import_crypto2.createHash)("md5").update(sessionId).digest();
|
|
1634
2075
|
const hashVal = hashBytes.readUInt32BE(0) % 1e6;
|
|
1635
2076
|
log3(`Session hash: ${hashVal} (out of 1,000,000)`);
|
|
@@ -1638,7 +2079,9 @@ async function get2(configKey, sessionId, options = {}) {
|
|
|
1638
2079
|
for (const v of variants) {
|
|
1639
2080
|
const oldCumulative = cumulative;
|
|
1640
2081
|
cumulative += v.weight * 1e4;
|
|
1641
|
-
log3(
|
|
2082
|
+
log3(
|
|
2083
|
+
`Variant ${v.model}: weight=${v.weight}%, range=${oldCumulative}-${cumulative}, hash=${hashVal}, match=${hashVal < cumulative}`
|
|
2084
|
+
);
|
|
1642
2085
|
if (hashVal < cumulative) {
|
|
1643
2086
|
assignedModel = v.model;
|
|
1644
2087
|
break;
|
|
@@ -1651,7 +2094,9 @@ async function get2(configKey, sessionId, options = {}) {
|
|
|
1651
2094
|
throw e;
|
|
1652
2095
|
}
|
|
1653
2096
|
if (fallback) {
|
|
1654
|
-
console.warn(
|
|
2097
|
+
console.warn(
|
|
2098
|
+
`[Fallom WARNING] Error getting model for '${configKey}': ${e}. Using fallback: ${fallback}`
|
|
2099
|
+
);
|
|
1655
2100
|
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1656
2101
|
}
|
|
1657
2102
|
throw e;
|
|
@@ -1698,20 +2143,22 @@ init_prompts();
|
|
|
1698
2143
|
// src/init.ts
|
|
1699
2144
|
init_prompts();
|
|
1700
2145
|
async function init4(options = {}) {
|
|
1701
|
-
const
|
|
2146
|
+
const tracesUrl = options.tracesUrl || process.env.FALLOM_TRACES_URL || "https://traces.fallom.com";
|
|
2147
|
+
const configsUrl = options.configsUrl || process.env.FALLOM_CONFIGS_URL || "https://configs.fallom.com";
|
|
2148
|
+
const promptsUrl = options.promptsUrl || process.env.FALLOM_PROMPTS_URL || "https://prompts.fallom.com";
|
|
1702
2149
|
await init2({
|
|
1703
2150
|
apiKey: options.apiKey,
|
|
1704
|
-
baseUrl:
|
|
2151
|
+
baseUrl: tracesUrl,
|
|
1705
2152
|
captureContent: options.captureContent,
|
|
1706
2153
|
debug: options.debug
|
|
1707
2154
|
});
|
|
1708
2155
|
init3({
|
|
1709
2156
|
apiKey: options.apiKey,
|
|
1710
|
-
baseUrl:
|
|
2157
|
+
baseUrl: configsUrl
|
|
1711
2158
|
});
|
|
1712
2159
|
init({
|
|
1713
2160
|
apiKey: options.apiKey,
|
|
1714
|
-
baseUrl:
|
|
2161
|
+
baseUrl: promptsUrl
|
|
1715
2162
|
});
|
|
1716
2163
|
}
|
|
1717
2164
|
|