@fallom/trace 0.1.11 → 0.2.0
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 +215 -178
- package/dist/chunk-2BP4H4AD.mjs +3012 -0
- package/dist/chunk-7P6ASYW6.mjs +9 -0
- package/dist/chunk-K7HYYE4Y.mjs +2930 -0
- package/dist/chunk-KAZ5NEU2.mjs +2237 -0
- package/dist/chunk-KMA4IPED.mjs +252 -0
- package/dist/chunk-W6M2RQ3W.mjs +251 -0
- package/dist/index.d.mts +210 -248
- package/dist/index.d.ts +210 -248
- package/dist/index.js +947 -776
- package/dist/index.mjs +746 -576
- package/dist/models-2Y6DRQPS.mjs +9 -0
- package/dist/models-BUHMMTWK.mjs +9 -0
- package/dist/models-JIO5LVMB.mjs +8 -0
- package/dist/models-JKMOBZUO.mjs +8 -0
- package/dist/prompts-XSZHTCX7.mjs +15 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__export,
|
|
3
3
|
init,
|
|
4
|
-
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
models_exports
|
|
5
|
+
} from "./chunk-W6M2RQ3W.mjs";
|
|
6
6
|
|
|
7
7
|
// src/trace.ts
|
|
8
8
|
var trace_exports = {};
|
|
9
9
|
__export(trace_exports, {
|
|
10
|
-
|
|
11
|
-
getSession: () => getSession,
|
|
10
|
+
FallomSession: () => FallomSession,
|
|
12
11
|
init: () => init2,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
shutdown: () => shutdown,
|
|
16
|
-
span: () => span,
|
|
17
|
-
wrapAISDK: () => wrapAISDK,
|
|
18
|
-
wrapAnthropic: () => wrapAnthropic,
|
|
19
|
-
wrapGoogleAI: () => wrapGoogleAI,
|
|
20
|
-
wrapMastraAgent: () => wrapMastraAgent,
|
|
21
|
-
wrapOpenAI: () => wrapOpenAI
|
|
12
|
+
session: () => session,
|
|
13
|
+
shutdown: () => shutdown
|
|
22
14
|
});
|
|
15
|
+
|
|
16
|
+
// src/trace/core.ts
|
|
23
17
|
import { AsyncLocalStorage } from "async_hooks";
|
|
24
18
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
25
19
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
@@ -621,9 +615,9 @@ var Resource = (
|
|
|
621
615
|
})()
|
|
622
616
|
);
|
|
623
617
|
|
|
624
|
-
// src/trace.ts
|
|
625
|
-
var
|
|
626
|
-
var
|
|
618
|
+
// src/trace/core.ts
|
|
619
|
+
var traceContextStorage = new AsyncLocalStorage();
|
|
620
|
+
var fallbackTraceContext = null;
|
|
627
621
|
var apiKey = null;
|
|
628
622
|
var baseUrl = "https://traces.fallom.com";
|
|
629
623
|
var initialized = false;
|
|
@@ -633,28 +627,27 @@ var sdk = null;
|
|
|
633
627
|
function log(...args) {
|
|
634
628
|
if (debugMode) console.log("[Fallom]", ...args);
|
|
635
629
|
}
|
|
630
|
+
function getTraceContextStorage() {
|
|
631
|
+
return traceContextStorage;
|
|
632
|
+
}
|
|
633
|
+
function getFallbackTraceContext() {
|
|
634
|
+
return fallbackTraceContext;
|
|
635
|
+
}
|
|
636
|
+
function isInitialized() {
|
|
637
|
+
return initialized;
|
|
638
|
+
}
|
|
639
|
+
function shouldCaptureContent() {
|
|
640
|
+
return captureContent;
|
|
641
|
+
}
|
|
642
|
+
function isDebugMode() {
|
|
643
|
+
return debugMode;
|
|
644
|
+
}
|
|
636
645
|
var fallomSpanProcessor = {
|
|
637
|
-
onStart(
|
|
638
|
-
log("\u{1F4CD} Span started:",
|
|
639
|
-
const ctx = sessionStorage.getStore() || fallbackSession;
|
|
640
|
-
if (ctx) {
|
|
641
|
-
span2.setAttribute("fallom.config_key", ctx.configKey);
|
|
642
|
-
span2.setAttribute("fallom.session_id", ctx.sessionId);
|
|
643
|
-
if (ctx.customerId) {
|
|
644
|
-
span2.setAttribute("fallom.customer_id", ctx.customerId);
|
|
645
|
-
}
|
|
646
|
-
log(
|
|
647
|
-
" Added session context:",
|
|
648
|
-
ctx.configKey,
|
|
649
|
-
ctx.sessionId,
|
|
650
|
-
ctx.customerId
|
|
651
|
-
);
|
|
652
|
-
} else {
|
|
653
|
-
log(" No session context available");
|
|
654
|
-
}
|
|
646
|
+
onStart(span, _parentContext) {
|
|
647
|
+
log("\u{1F4CD} Span started:", span.name || "unknown");
|
|
655
648
|
},
|
|
656
|
-
onEnd(
|
|
657
|
-
log("\u2705 Span ended:",
|
|
649
|
+
onEnd(span) {
|
|
650
|
+
log("\u2705 Span ended:", span.name, "duration:", span.duration);
|
|
658
651
|
},
|
|
659
652
|
shutdown() {
|
|
660
653
|
return Promise.resolve();
|
|
@@ -663,47 +656,6 @@ var fallomSpanProcessor = {
|
|
|
663
656
|
return Promise.resolve();
|
|
664
657
|
}
|
|
665
658
|
};
|
|
666
|
-
async function init2(options = {}) {
|
|
667
|
-
if (initialized) return;
|
|
668
|
-
debugMode = options.debug ?? false;
|
|
669
|
-
log("\u{1F680} Initializing Fallom tracing...");
|
|
670
|
-
apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
|
|
671
|
-
baseUrl = options.baseUrl || process.env.FALLOM_TRACES_URL || process.env.FALLOM_BASE_URL || "https://traces.fallom.com";
|
|
672
|
-
const envCapture = process.env.FALLOM_CAPTURE_CONTENT?.toLowerCase();
|
|
673
|
-
if (envCapture === "false" || envCapture === "0" || envCapture === "no") {
|
|
674
|
-
captureContent = false;
|
|
675
|
-
} else {
|
|
676
|
-
captureContent = options.captureContent ?? true;
|
|
677
|
-
}
|
|
678
|
-
if (!apiKey) {
|
|
679
|
-
throw new Error(
|
|
680
|
-
"No API key provided. Set FALLOM_API_KEY environment variable or pass apiKey parameter."
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
initialized = true;
|
|
684
|
-
log("\u{1F4E1} Exporter URL:", `${baseUrl}/v1/traces`);
|
|
685
|
-
const exporter = new OTLPTraceExporter({
|
|
686
|
-
url: `${baseUrl}/v1/traces`,
|
|
687
|
-
headers: {
|
|
688
|
-
Authorization: `Bearer ${apiKey}`
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
const instrumentations = await getInstrumentations();
|
|
692
|
-
log("\u{1F527} Loaded instrumentations:", instrumentations.length);
|
|
693
|
-
sdk = new NodeSDK({
|
|
694
|
-
resource: new Resource({
|
|
695
|
-
"service.name": "fallom-traced-app"
|
|
696
|
-
}),
|
|
697
|
-
traceExporter: exporter,
|
|
698
|
-
spanProcessor: fallomSpanProcessor,
|
|
699
|
-
instrumentations
|
|
700
|
-
});
|
|
701
|
-
sdk.start();
|
|
702
|
-
log("\u2705 SDK started");
|
|
703
|
-
process.on("SIGTERM", () => {
|
|
704
|
-
sdk?.shutdown().catch(console.error);
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
659
|
async function getInstrumentations() {
|
|
708
660
|
const instrumentations = [];
|
|
709
661
|
await tryAddInstrumentation(
|
|
@@ -758,75 +710,90 @@ async function tryAddInstrumentation(instrumentations, pkg, className) {
|
|
|
758
710
|
Object.keys(mod)
|
|
759
711
|
);
|
|
760
712
|
}
|
|
761
|
-
} catch
|
|
713
|
+
} catch {
|
|
762
714
|
log(` \u274C ${pkg} not installed`);
|
|
763
715
|
}
|
|
764
716
|
}
|
|
765
|
-
function
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return sessionStorage.run({ configKey, sessionId }, customerIdOrFn);
|
|
777
|
-
}
|
|
778
|
-
return sessionStorage.run(
|
|
779
|
-
{ configKey, sessionId, customerId: customerIdOrFn },
|
|
780
|
-
fn
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
function getSession() {
|
|
784
|
-
return sessionStorage.getStore() || fallbackSession || void 0;
|
|
785
|
-
}
|
|
786
|
-
function clearSession() {
|
|
787
|
-
fallbackSession = null;
|
|
788
|
-
}
|
|
789
|
-
function span(data, options = {}) {
|
|
790
|
-
if (!initialized) {
|
|
791
|
-
throw new Error("Fallom not initialized. Call trace.init() first.");
|
|
717
|
+
async function init2(options = {}) {
|
|
718
|
+
if (initialized) return;
|
|
719
|
+
debugMode = options.debug ?? false;
|
|
720
|
+
log("\u{1F680} Initializing Fallom tracing...");
|
|
721
|
+
apiKey = options.apiKey || process.env.FALLOM_API_KEY || null;
|
|
722
|
+
baseUrl = options.baseUrl || process.env.FALLOM_TRACES_URL || process.env.FALLOM_BASE_URL || "https://traces.fallom.com";
|
|
723
|
+
const envCapture = process.env.FALLOM_CAPTURE_CONTENT?.toLowerCase();
|
|
724
|
+
if (envCapture === "false" || envCapture === "0" || envCapture === "no") {
|
|
725
|
+
captureContent = false;
|
|
726
|
+
} else {
|
|
727
|
+
captureContent = options.captureContent ?? true;
|
|
792
728
|
}
|
|
793
|
-
|
|
794
|
-
const configKey = options.configKey || ctx?.configKey;
|
|
795
|
-
const sessionId = options.sessionId || ctx?.sessionId;
|
|
796
|
-
if (!configKey || !sessionId) {
|
|
729
|
+
if (!apiKey) {
|
|
797
730
|
throw new Error(
|
|
798
|
-
"No
|
|
731
|
+
"No API key provided. Set FALLOM_API_KEY environment variable or pass apiKey parameter."
|
|
799
732
|
);
|
|
800
733
|
}
|
|
801
|
-
|
|
734
|
+
initialized = true;
|
|
735
|
+
log("\u{1F4E1} Exporter URL:", `${baseUrl}/v1/traces`);
|
|
736
|
+
const exporter = new OTLPTraceExporter({
|
|
737
|
+
url: `${baseUrl}/v1/traces`,
|
|
738
|
+
headers: {
|
|
739
|
+
Authorization: `Bearer ${apiKey}`
|
|
740
|
+
}
|
|
802
741
|
});
|
|
742
|
+
const instrumentations = await getInstrumentations();
|
|
743
|
+
log("\u{1F527} Loaded instrumentations:", instrumentations.length);
|
|
744
|
+
sdk = new NodeSDK({
|
|
745
|
+
resource: new Resource({
|
|
746
|
+
"service.name": "fallom-traced-app"
|
|
747
|
+
}),
|
|
748
|
+
traceExporter: exporter,
|
|
749
|
+
spanProcessor: fallomSpanProcessor,
|
|
750
|
+
instrumentations
|
|
751
|
+
});
|
|
752
|
+
sdk.start();
|
|
753
|
+
log("\u2705 SDK started");
|
|
754
|
+
process.on("SIGTERM", () => {
|
|
755
|
+
sdk?.shutdown().catch(console.error);
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
async function shutdown() {
|
|
759
|
+
if (sdk) {
|
|
760
|
+
await sdk.shutdown();
|
|
761
|
+
initialized = false;
|
|
762
|
+
}
|
|
803
763
|
}
|
|
804
|
-
async function
|
|
764
|
+
async function sendTrace(trace) {
|
|
765
|
+
const url = `${baseUrl}/v1/traces`;
|
|
766
|
+
log("\u{1F4E4} Sending trace to:", url);
|
|
767
|
+
log(" Session:", trace.session_id, "Config:", trace.config_key);
|
|
805
768
|
try {
|
|
806
769
|
const controller = new AbortController();
|
|
807
770
|
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
808
|
-
await fetch(
|
|
771
|
+
const response = await fetch(url, {
|
|
809
772
|
method: "POST",
|
|
810
773
|
headers: {
|
|
811
774
|
Authorization: `Bearer ${apiKey}`,
|
|
812
775
|
"Content-Type": "application/json"
|
|
813
776
|
},
|
|
814
|
-
body: JSON.stringify(
|
|
815
|
-
config_key: configKey,
|
|
816
|
-
session_id: sessionId,
|
|
817
|
-
data
|
|
818
|
-
}),
|
|
777
|
+
body: JSON.stringify(trace),
|
|
819
778
|
signal: controller.signal
|
|
820
779
|
});
|
|
821
780
|
clearTimeout(timeoutId);
|
|
822
|
-
|
|
781
|
+
if (!response.ok) {
|
|
782
|
+
const text = await response.text();
|
|
783
|
+
log("\u274C Trace send failed:", response.status, text);
|
|
784
|
+
} else {
|
|
785
|
+
log("\u2705 Trace sent:", trace.name, trace.model);
|
|
786
|
+
}
|
|
787
|
+
} catch (err) {
|
|
788
|
+
log("\u274C Trace send error:", err instanceof Error ? err.message : err);
|
|
823
789
|
}
|
|
824
790
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
791
|
+
|
|
792
|
+
// src/trace/utils.ts
|
|
793
|
+
function generateHexId(length) {
|
|
794
|
+
const bytes = new Uint8Array(length / 2);
|
|
795
|
+
crypto.getRandomValues(bytes);
|
|
796
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
830
797
|
}
|
|
831
798
|
function messagesToOtelAttributes(messages, completion, model, responseId) {
|
|
832
799
|
const attrs = {};
|
|
@@ -854,70 +821,39 @@ function messagesToOtelAttributes(messages, completion, model, responseId) {
|
|
|
854
821
|
}
|
|
855
822
|
return attrs;
|
|
856
823
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
861
|
-
}
|
|
862
|
-
var traceContextStorage = new AsyncLocalStorage();
|
|
863
|
-
var fallbackTraceContext = null;
|
|
864
|
-
async function sendTrace(trace) {
|
|
865
|
-
const url = `${baseUrl}/v1/traces`;
|
|
866
|
-
log("\u{1F4E4} Sending trace to:", url);
|
|
867
|
-
log(" Session:", trace.session_id, "Config:", trace.config_key);
|
|
868
|
-
try {
|
|
869
|
-
const controller = new AbortController();
|
|
870
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
871
|
-
const response = await fetch(url, {
|
|
872
|
-
method: "POST",
|
|
873
|
-
headers: {
|
|
874
|
-
Authorization: `Bearer ${apiKey}`,
|
|
875
|
-
"Content-Type": "application/json"
|
|
876
|
-
},
|
|
877
|
-
body: JSON.stringify(trace),
|
|
878
|
-
signal: controller.signal
|
|
879
|
-
});
|
|
880
|
-
clearTimeout(timeoutId);
|
|
881
|
-
if (!response.ok) {
|
|
882
|
-
const text = await response.text();
|
|
883
|
-
log("\u274C Trace send failed:", response.status, text);
|
|
884
|
-
} else {
|
|
885
|
-
log("\u2705 Trace sent:", trace.name, trace.model);
|
|
886
|
-
}
|
|
887
|
-
} catch (err) {
|
|
888
|
-
log("\u274C Trace send error:", err instanceof Error ? err.message : err);
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
function wrapOpenAI(client) {
|
|
824
|
+
|
|
825
|
+
// src/trace/wrappers/openai.ts
|
|
826
|
+
function wrapOpenAI(client, sessionCtx) {
|
|
892
827
|
const originalCreate = client.chat.completions.create.bind(
|
|
893
828
|
client.chat.completions
|
|
894
829
|
);
|
|
830
|
+
const ctx = sessionCtx;
|
|
895
831
|
client.chat.completions.create = async function(...args) {
|
|
896
|
-
|
|
897
|
-
if (!ctx || !initialized) {
|
|
832
|
+
if (!isInitialized()) {
|
|
898
833
|
return originalCreate(...args);
|
|
899
834
|
}
|
|
900
|
-
|
|
901
|
-
try {
|
|
902
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
903
|
-
promptCtx = getPromptContext();
|
|
904
|
-
} catch {
|
|
905
|
-
}
|
|
906
|
-
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
835
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
907
836
|
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
908
837
|
const spanId = generateHexId(16);
|
|
909
838
|
const parentSpanId = traceCtx?.parentSpanId;
|
|
910
839
|
const params = args[0] || {};
|
|
911
840
|
const startTime = Date.now();
|
|
841
|
+
const captureContent2 = shouldCaptureContent();
|
|
912
842
|
try {
|
|
913
843
|
const response = await originalCreate(...args);
|
|
914
844
|
const endTime = Date.now();
|
|
915
|
-
const attributes =
|
|
845
|
+
const attributes = captureContent2 ? messagesToOtelAttributes(
|
|
916
846
|
params?.messages,
|
|
917
847
|
response?.choices?.[0]?.message,
|
|
918
848
|
response?.model || params?.model,
|
|
919
849
|
response?.id
|
|
920
|
-
) :
|
|
850
|
+
) : {};
|
|
851
|
+
if (response?.usage) {
|
|
852
|
+
attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
|
|
853
|
+
}
|
|
854
|
+
if (response?.choices?.[0]?.finish_reason) {
|
|
855
|
+
attributes["gen_ai.response.finish_reason"] = response.choices[0].finish_reason;
|
|
856
|
+
}
|
|
921
857
|
sendTrace({
|
|
922
858
|
config_key: ctx.configKey,
|
|
923
859
|
session_id: ctx.sessionId,
|
|
@@ -935,17 +871,13 @@ function wrapOpenAI(client) {
|
|
|
935
871
|
prompt_tokens: response?.usage?.prompt_tokens,
|
|
936
872
|
completion_tokens: response?.usage?.completion_tokens,
|
|
937
873
|
total_tokens: response?.usage?.total_tokens,
|
|
938
|
-
attributes
|
|
939
|
-
prompt_key: promptCtx?.promptKey,
|
|
940
|
-
prompt_version: promptCtx?.promptVersion,
|
|
941
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
942
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
874
|
+
attributes: Object.keys(attributes).length > 0 ? attributes : void 0
|
|
943
875
|
}).catch(() => {
|
|
944
876
|
});
|
|
945
877
|
return response;
|
|
946
878
|
} catch (error) {
|
|
947
879
|
const endTime = Date.now();
|
|
948
|
-
const attributes =
|
|
880
|
+
const attributes = captureContent2 ? messagesToOtelAttributes(
|
|
949
881
|
params?.messages,
|
|
950
882
|
void 0,
|
|
951
883
|
params?.model,
|
|
@@ -969,11 +901,7 @@ function wrapOpenAI(client) {
|
|
|
969
901
|
duration_ms: endTime - startTime,
|
|
970
902
|
status: "ERROR",
|
|
971
903
|
error_message: error?.message,
|
|
972
|
-
attributes
|
|
973
|
-
prompt_key: promptCtx?.promptKey,
|
|
974
|
-
prompt_version: promptCtx?.promptVersion,
|
|
975
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
976
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
904
|
+
attributes
|
|
977
905
|
}).catch(() => {
|
|
978
906
|
});
|
|
979
907
|
throw error;
|
|
@@ -981,37 +909,40 @@ function wrapOpenAI(client) {
|
|
|
981
909
|
};
|
|
982
910
|
return client;
|
|
983
911
|
}
|
|
984
|
-
|
|
912
|
+
|
|
913
|
+
// src/trace/wrappers/anthropic.ts
|
|
914
|
+
function wrapAnthropic(client, sessionCtx) {
|
|
985
915
|
const originalCreate = client.messages.create.bind(client.messages);
|
|
916
|
+
const ctx = sessionCtx;
|
|
986
917
|
client.messages.create = async function(...args) {
|
|
987
|
-
|
|
988
|
-
if (!ctx || !initialized) {
|
|
918
|
+
if (!isInitialized()) {
|
|
989
919
|
return originalCreate(...args);
|
|
990
920
|
}
|
|
991
|
-
|
|
992
|
-
try {
|
|
993
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
994
|
-
promptCtx = getPromptContext();
|
|
995
|
-
} catch {
|
|
996
|
-
}
|
|
997
|
-
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
921
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
998
922
|
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
999
923
|
const spanId = generateHexId(16);
|
|
1000
924
|
const parentSpanId = traceCtx?.parentSpanId;
|
|
1001
925
|
const params = args[0] || {};
|
|
1002
926
|
const startTime = Date.now();
|
|
927
|
+
const captureContent2 = shouldCaptureContent();
|
|
1003
928
|
try {
|
|
1004
929
|
const response = await originalCreate(...args);
|
|
1005
930
|
const endTime = Date.now();
|
|
1006
|
-
const attributes =
|
|
931
|
+
const attributes = captureContent2 ? messagesToOtelAttributes(
|
|
1007
932
|
params?.messages,
|
|
1008
933
|
{ role: "assistant", content: response?.content?.[0]?.text || "" },
|
|
1009
934
|
response?.model || params?.model,
|
|
1010
935
|
response?.id
|
|
1011
|
-
) :
|
|
1012
|
-
if (
|
|
936
|
+
) : {};
|
|
937
|
+
if (params?.system) {
|
|
1013
938
|
attributes["gen_ai.system_prompt"] = params.system;
|
|
1014
939
|
}
|
|
940
|
+
if (response?.usage) {
|
|
941
|
+
attributes["fallom.raw.usage"] = JSON.stringify(response.usage);
|
|
942
|
+
}
|
|
943
|
+
if (response?.stop_reason) {
|
|
944
|
+
attributes["gen_ai.response.finish_reason"] = response.stop_reason;
|
|
945
|
+
}
|
|
1015
946
|
sendTrace({
|
|
1016
947
|
config_key: ctx.configKey,
|
|
1017
948
|
session_id: ctx.sessionId,
|
|
@@ -1029,17 +960,13 @@ function wrapAnthropic(client) {
|
|
|
1029
960
|
prompt_tokens: response?.usage?.input_tokens,
|
|
1030
961
|
completion_tokens: response?.usage?.output_tokens,
|
|
1031
962
|
total_tokens: (response?.usage?.input_tokens || 0) + (response?.usage?.output_tokens || 0),
|
|
1032
|
-
attributes
|
|
1033
|
-
prompt_key: promptCtx?.promptKey,
|
|
1034
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1035
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1036
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
963
|
+
attributes: Object.keys(attributes).length > 0 ? attributes : void 0
|
|
1037
964
|
}).catch(() => {
|
|
1038
965
|
});
|
|
1039
966
|
return response;
|
|
1040
967
|
} catch (error) {
|
|
1041
968
|
const endTime = Date.now();
|
|
1042
|
-
const attributes =
|
|
969
|
+
const attributes = captureContent2 ? messagesToOtelAttributes(
|
|
1043
970
|
params?.messages,
|
|
1044
971
|
void 0,
|
|
1045
972
|
params?.model,
|
|
@@ -1066,11 +993,7 @@ function wrapAnthropic(client) {
|
|
|
1066
993
|
duration_ms: endTime - startTime,
|
|
1067
994
|
status: "ERROR",
|
|
1068
995
|
error_message: error?.message,
|
|
1069
|
-
attributes
|
|
1070
|
-
prompt_key: promptCtx?.promptKey,
|
|
1071
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1072
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1073
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
996
|
+
attributes
|
|
1074
997
|
}).catch(() => {
|
|
1075
998
|
});
|
|
1076
999
|
throw error;
|
|
@@ -1078,24 +1001,21 @@ function wrapAnthropic(client) {
|
|
|
1078
1001
|
};
|
|
1079
1002
|
return client;
|
|
1080
1003
|
}
|
|
1081
|
-
|
|
1004
|
+
|
|
1005
|
+
// src/trace/wrappers/google-ai.ts
|
|
1006
|
+
function wrapGoogleAI(model, sessionCtx) {
|
|
1082
1007
|
const originalGenerate = model.generateContent.bind(model);
|
|
1008
|
+
const ctx = sessionCtx;
|
|
1083
1009
|
model.generateContent = async function(...args) {
|
|
1084
|
-
|
|
1085
|
-
if (!ctx || !initialized) {
|
|
1010
|
+
if (!isInitialized()) {
|
|
1086
1011
|
return originalGenerate(...args);
|
|
1087
1012
|
}
|
|
1088
|
-
|
|
1089
|
-
try {
|
|
1090
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
1091
|
-
promptCtx = getPromptContext();
|
|
1092
|
-
} catch {
|
|
1093
|
-
}
|
|
1094
|
-
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
1013
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
1095
1014
|
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1096
1015
|
const spanId = generateHexId(16);
|
|
1097
1016
|
const parentSpanId = traceCtx?.parentSpanId;
|
|
1098
1017
|
const startTime = Date.now();
|
|
1018
|
+
const captureContent2 = shouldCaptureContent();
|
|
1099
1019
|
try {
|
|
1100
1020
|
const response = await originalGenerate(...args);
|
|
1101
1021
|
const endTime = Date.now();
|
|
@@ -1103,7 +1023,7 @@ function wrapGoogleAI(model) {
|
|
|
1103
1023
|
const usage = result?.usageMetadata;
|
|
1104
1024
|
const modelName = model?.model || "gemini";
|
|
1105
1025
|
const attributes = {};
|
|
1106
|
-
if (
|
|
1026
|
+
if (captureContent2) {
|
|
1107
1027
|
attributes["gen_ai.request.model"] = modelName;
|
|
1108
1028
|
attributes["gen_ai.response.model"] = modelName;
|
|
1109
1029
|
const input = args[0];
|
|
@@ -1122,6 +1042,13 @@ function wrapGoogleAI(model) {
|
|
|
1122
1042
|
attributes["gen_ai.completion.0.content"] = outputText;
|
|
1123
1043
|
}
|
|
1124
1044
|
}
|
|
1045
|
+
if (usage) {
|
|
1046
|
+
attributes["fallom.raw.usage"] = JSON.stringify(usage);
|
|
1047
|
+
}
|
|
1048
|
+
const candidate = result?.candidates?.[0];
|
|
1049
|
+
if (candidate?.finishReason) {
|
|
1050
|
+
attributes["gen_ai.response.finish_reason"] = candidate.finishReason;
|
|
1051
|
+
}
|
|
1125
1052
|
sendTrace({
|
|
1126
1053
|
config_key: ctx.configKey,
|
|
1127
1054
|
session_id: ctx.sessionId,
|
|
@@ -1139,11 +1066,7 @@ function wrapGoogleAI(model) {
|
|
|
1139
1066
|
prompt_tokens: usage?.promptTokenCount,
|
|
1140
1067
|
completion_tokens: usage?.candidatesTokenCount,
|
|
1141
1068
|
total_tokens: usage?.totalTokenCount,
|
|
1142
|
-
attributes:
|
|
1143
|
-
prompt_key: promptCtx?.promptKey,
|
|
1144
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1145
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1146
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1069
|
+
attributes: Object.keys(attributes).length > 0 ? attributes : void 0
|
|
1147
1070
|
}).catch(() => {
|
|
1148
1071
|
});
|
|
1149
1072
|
return response;
|
|
@@ -1151,7 +1074,7 @@ function wrapGoogleAI(model) {
|
|
|
1151
1074
|
const endTime = Date.now();
|
|
1152
1075
|
const modelName = model?.model || "gemini";
|
|
1153
1076
|
const attributes = {};
|
|
1154
|
-
if (
|
|
1077
|
+
if (captureContent2) {
|
|
1155
1078
|
attributes["gen_ai.request.model"] = modelName;
|
|
1156
1079
|
attributes["error.message"] = error?.message;
|
|
1157
1080
|
const input = args[0];
|
|
@@ -1175,11 +1098,7 @@ function wrapGoogleAI(model) {
|
|
|
1175
1098
|
duration_ms: endTime - startTime,
|
|
1176
1099
|
status: "ERROR",
|
|
1177
1100
|
error_message: error?.message,
|
|
1178
|
-
attributes:
|
|
1179
|
-
prompt_key: promptCtx?.promptKey,
|
|
1180
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1181
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1182
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1101
|
+
attributes: captureContent2 ? attributes : void 0
|
|
1183
1102
|
}).catch(() => {
|
|
1184
1103
|
});
|
|
1185
1104
|
throw error;
|
|
@@ -1187,39 +1106,70 @@ function wrapGoogleAI(model) {
|
|
|
1187
1106
|
};
|
|
1188
1107
|
return model;
|
|
1189
1108
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1109
|
+
|
|
1110
|
+
// src/trace/wrappers/vercel-ai/utils.ts
|
|
1111
|
+
function extractUsageFromResult(result, directUsage) {
|
|
1112
|
+
let usage = directUsage ?? result?.usage;
|
|
1113
|
+
const isValidNumber = (v) => v !== null && v !== void 0 && !Number.isNaN(v);
|
|
1114
|
+
let promptTokens = isValidNumber(usage?.promptTokens) ? usage.promptTokens : void 0;
|
|
1115
|
+
let completionTokens = isValidNumber(usage?.completionTokens) ? usage.completionTokens : void 0;
|
|
1116
|
+
let totalTokens = isValidNumber(usage?.totalTokens) ? usage.totalTokens : void 0;
|
|
1117
|
+
let cost;
|
|
1118
|
+
const orUsage = result?.experimental_providerMetadata?.openrouter?.usage;
|
|
1119
|
+
if (orUsage) {
|
|
1120
|
+
if (promptTokens === void 0 && isValidNumber(orUsage.promptTokens)) {
|
|
1121
|
+
promptTokens = orUsage.promptTokens;
|
|
1122
|
+
}
|
|
1123
|
+
if (completionTokens === void 0 && isValidNumber(orUsage.completionTokens)) {
|
|
1124
|
+
completionTokens = orUsage.completionTokens;
|
|
1125
|
+
}
|
|
1126
|
+
if (totalTokens === void 0 && isValidNumber(orUsage.totalTokens)) {
|
|
1127
|
+
totalTokens = orUsage.totalTokens;
|
|
1128
|
+
}
|
|
1129
|
+
if (isValidNumber(orUsage.cost)) {
|
|
1130
|
+
cost = orUsage.cost;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (totalTokens === void 0 && (promptTokens !== void 0 || completionTokens !== void 0)) {
|
|
1134
|
+
totalTokens = (promptTokens ?? 0) + (completionTokens ?? 0);
|
|
1135
|
+
}
|
|
1136
|
+
return { promptTokens, completionTokens, totalTokens, cost };
|
|
1198
1137
|
}
|
|
1199
|
-
|
|
1138
|
+
|
|
1139
|
+
// src/trace/wrappers/vercel-ai/generate-text.ts
|
|
1140
|
+
function createGenerateTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
1141
|
+
const ctx = sessionCtx;
|
|
1200
1142
|
return async (...args) => {
|
|
1201
|
-
|
|
1202
|
-
if (!ctx || !initialized) {
|
|
1143
|
+
if (!isInitialized()) {
|
|
1203
1144
|
return aiModule.generateText(...args);
|
|
1204
1145
|
}
|
|
1205
|
-
|
|
1206
|
-
try {
|
|
1207
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
1208
|
-
promptCtx = getPromptContext();
|
|
1209
|
-
} catch {
|
|
1210
|
-
}
|
|
1211
|
-
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
1146
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
1212
1147
|
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1213
1148
|
const spanId = generateHexId(16);
|
|
1214
1149
|
const parentSpanId = traceCtx?.parentSpanId;
|
|
1215
1150
|
const params = args[0] || {};
|
|
1216
1151
|
const startTime = Date.now();
|
|
1152
|
+
const captureContent2 = shouldCaptureContent();
|
|
1217
1153
|
try {
|
|
1218
1154
|
const result = await aiModule.generateText(...args);
|
|
1219
1155
|
const endTime = Date.now();
|
|
1156
|
+
if (debug || isDebugMode()) {
|
|
1157
|
+
console.log(
|
|
1158
|
+
"\n\u{1F50D} [Fallom Debug] generateText result keys:",
|
|
1159
|
+
Object.keys(result || {})
|
|
1160
|
+
);
|
|
1161
|
+
console.log(
|
|
1162
|
+
"\u{1F50D} [Fallom Debug] result.usage:",
|
|
1163
|
+
JSON.stringify(result?.usage, null, 2)
|
|
1164
|
+
);
|
|
1165
|
+
console.log(
|
|
1166
|
+
"\u{1F50D} [Fallom Debug] result.experimental_providerMetadata:",
|
|
1167
|
+
JSON.stringify(result?.experimental_providerMetadata, null, 2)
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1220
1170
|
const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
|
|
1221
1171
|
const attributes = {};
|
|
1222
|
-
if (
|
|
1172
|
+
if (captureContent2) {
|
|
1223
1173
|
attributes["gen_ai.request.model"] = modelId;
|
|
1224
1174
|
attributes["gen_ai.response.model"] = modelId;
|
|
1225
1175
|
if (params?.prompt) {
|
|
@@ -1240,6 +1190,18 @@ function createGenerateTextWrapper(aiModule) {
|
|
|
1240
1190
|
attributes["gen_ai.response.id"] = result.response.id;
|
|
1241
1191
|
}
|
|
1242
1192
|
}
|
|
1193
|
+
if (result?.usage) {
|
|
1194
|
+
attributes["fallom.raw.usage"] = JSON.stringify(result.usage);
|
|
1195
|
+
}
|
|
1196
|
+
if (result?.experimental_providerMetadata) {
|
|
1197
|
+
attributes["fallom.raw.providerMetadata"] = JSON.stringify(
|
|
1198
|
+
result.experimental_providerMetadata
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
if (result?.finishReason) {
|
|
1202
|
+
attributes["gen_ai.response.finish_reason"] = result.finishReason;
|
|
1203
|
+
}
|
|
1204
|
+
const usage = extractUsageFromResult(result);
|
|
1243
1205
|
sendTrace({
|
|
1244
1206
|
config_key: ctx.configKey,
|
|
1245
1207
|
session_id: ctx.sessionId,
|
|
@@ -1254,14 +1216,10 @@ function createGenerateTextWrapper(aiModule) {
|
|
|
1254
1216
|
end_time: new Date(endTime).toISOString(),
|
|
1255
1217
|
duration_ms: endTime - startTime,
|
|
1256
1218
|
status: "OK",
|
|
1257
|
-
prompt_tokens:
|
|
1258
|
-
completion_tokens:
|
|
1259
|
-
total_tokens:
|
|
1260
|
-
attributes:
|
|
1261
|
-
prompt_key: promptCtx?.promptKey,
|
|
1262
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1263
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1264
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1219
|
+
prompt_tokens: usage.promptTokens,
|
|
1220
|
+
completion_tokens: usage.completionTokens,
|
|
1221
|
+
total_tokens: usage.totalTokens,
|
|
1222
|
+
attributes: captureContent2 ? attributes : void 0
|
|
1265
1223
|
}).catch(() => {
|
|
1266
1224
|
});
|
|
1267
1225
|
return result;
|
|
@@ -1282,44 +1240,58 @@ function createGenerateTextWrapper(aiModule) {
|
|
|
1282
1240
|
end_time: new Date(endTime).toISOString(),
|
|
1283
1241
|
duration_ms: endTime - startTime,
|
|
1284
1242
|
status: "ERROR",
|
|
1285
|
-
error_message: error?.message
|
|
1286
|
-
prompt_key: promptCtx?.promptKey,
|
|
1287
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1288
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1289
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1243
|
+
error_message: error?.message
|
|
1290
1244
|
}).catch(() => {
|
|
1291
1245
|
});
|
|
1292
1246
|
throw error;
|
|
1293
1247
|
}
|
|
1294
1248
|
};
|
|
1295
1249
|
}
|
|
1296
|
-
|
|
1250
|
+
|
|
1251
|
+
// src/trace/wrappers/vercel-ai/stream-text.ts
|
|
1252
|
+
function log2(...args) {
|
|
1253
|
+
if (isDebugMode()) console.log("[Fallom]", ...args);
|
|
1254
|
+
}
|
|
1255
|
+
function createStreamTextWrapper(aiModule, sessionCtx, debug = false) {
|
|
1256
|
+
const ctx = sessionCtx;
|
|
1297
1257
|
return async (...args) => {
|
|
1298
|
-
const ctx = sessionStorage.getStore() || fallbackSession;
|
|
1299
1258
|
const params = args[0] || {};
|
|
1300
1259
|
const startTime = Date.now();
|
|
1260
|
+
const captureContent2 = shouldCaptureContent();
|
|
1301
1261
|
const result = await aiModule.streamText(...args);
|
|
1302
|
-
if (!
|
|
1262
|
+
if (!isInitialized()) {
|
|
1303
1263
|
return result;
|
|
1304
1264
|
}
|
|
1305
|
-
const traceCtx =
|
|
1265
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
1306
1266
|
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1307
1267
|
const spanId = generateHexId(16);
|
|
1308
1268
|
const parentSpanId = traceCtx?.parentSpanId;
|
|
1309
1269
|
let firstTokenTime = null;
|
|
1310
1270
|
const modelId = params?.model?.modelId || String(params?.model || "unknown");
|
|
1311
|
-
let promptCtx = null;
|
|
1312
|
-
try {
|
|
1313
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
1314
|
-
promptCtx = getPromptContext();
|
|
1315
|
-
} catch {
|
|
1316
|
-
}
|
|
1317
1271
|
if (result?.usage) {
|
|
1318
|
-
result.usage.then((
|
|
1272
|
+
result.usage.then(async (rawUsage) => {
|
|
1319
1273
|
const endTime = Date.now();
|
|
1320
|
-
|
|
1274
|
+
if (debug || isDebugMode()) {
|
|
1275
|
+
console.log(
|
|
1276
|
+
"\n\u{1F50D} [Fallom Debug] streamText usage:",
|
|
1277
|
+
JSON.stringify(rawUsage, null, 2)
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
log2("\u{1F4CA} streamText usage:", JSON.stringify(rawUsage, null, 2));
|
|
1281
|
+
let providerMetadata = result?.experimental_providerMetadata;
|
|
1282
|
+
if (providerMetadata && typeof providerMetadata.then === "function") {
|
|
1283
|
+
try {
|
|
1284
|
+
providerMetadata = await providerMetadata;
|
|
1285
|
+
} catch {
|
|
1286
|
+
providerMetadata = void 0;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const usage = extractUsageFromResult(
|
|
1290
|
+
{ experimental_providerMetadata: providerMetadata },
|
|
1291
|
+
rawUsage
|
|
1292
|
+
);
|
|
1321
1293
|
const attributes = {};
|
|
1322
|
-
if (
|
|
1294
|
+
if (captureContent2) {
|
|
1323
1295
|
attributes["gen_ai.request.model"] = modelId;
|
|
1324
1296
|
if (params?.prompt) {
|
|
1325
1297
|
attributes["gen_ai.prompt.0.role"] = "user";
|
|
@@ -1329,6 +1301,12 @@ function createStreamTextWrapper(aiModule) {
|
|
|
1329
1301
|
if (firstTokenTime) {
|
|
1330
1302
|
attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
|
|
1331
1303
|
}
|
|
1304
|
+
if (rawUsage) {
|
|
1305
|
+
attributes["fallom.raw.usage"] = JSON.stringify(rawUsage);
|
|
1306
|
+
}
|
|
1307
|
+
if (providerMetadata) {
|
|
1308
|
+
attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
|
|
1309
|
+
}
|
|
1332
1310
|
const tracePayload = {
|
|
1333
1311
|
config_key: ctx.configKey,
|
|
1334
1312
|
session_id: ctx.sessionId,
|
|
@@ -1343,21 +1321,17 @@ function createStreamTextWrapper(aiModule) {
|
|
|
1343
1321
|
end_time: new Date(endTime).toISOString(),
|
|
1344
1322
|
duration_ms: endTime - startTime,
|
|
1345
1323
|
status: "OK",
|
|
1346
|
-
prompt_tokens: usage
|
|
1347
|
-
completion_tokens: usage
|
|
1348
|
-
total_tokens: usage
|
|
1324
|
+
prompt_tokens: usage.promptTokens,
|
|
1325
|
+
completion_tokens: usage.completionTokens,
|
|
1326
|
+
total_tokens: usage.totalTokens,
|
|
1349
1327
|
time_to_first_token_ms: firstTokenTime ? firstTokenTime - startTime : void 0,
|
|
1350
|
-
attributes:
|
|
1351
|
-
prompt_key: promptCtx?.promptKey,
|
|
1352
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1353
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1354
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1328
|
+
attributes: captureContent2 ? attributes : void 0
|
|
1355
1329
|
};
|
|
1356
1330
|
sendTrace(tracePayload).catch(() => {
|
|
1357
1331
|
});
|
|
1358
1332
|
}).catch((error) => {
|
|
1359
1333
|
const endTime = Date.now();
|
|
1360
|
-
|
|
1334
|
+
log2("\u274C streamText error:", error?.message);
|
|
1361
1335
|
sendTrace({
|
|
1362
1336
|
config_key: ctx.configKey,
|
|
1363
1337
|
session_id: ctx.sessionId,
|
|
@@ -1372,11 +1346,7 @@ function createStreamTextWrapper(aiModule) {
|
|
|
1372
1346
|
end_time: new Date(endTime).toISOString(),
|
|
1373
1347
|
duration_ms: endTime - startTime,
|
|
1374
1348
|
status: "ERROR",
|
|
1375
|
-
error_message: error?.message
|
|
1376
|
-
prompt_key: promptCtx?.promptKey,
|
|
1377
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1378
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1379
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1349
|
+
error_message: error?.message
|
|
1380
1350
|
}).catch(() => {
|
|
1381
1351
|
});
|
|
1382
1352
|
});
|
|
@@ -1387,7 +1357,7 @@ function createStreamTextWrapper(aiModule) {
|
|
|
1387
1357
|
for await (const chunk of originalTextStream) {
|
|
1388
1358
|
if (!firstTokenTime) {
|
|
1389
1359
|
firstTokenTime = Date.now();
|
|
1390
|
-
|
|
1360
|
+
log2("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
|
|
1391
1361
|
}
|
|
1392
1362
|
yield chunk;
|
|
1393
1363
|
}
|
|
@@ -1404,30 +1374,37 @@ function createStreamTextWrapper(aiModule) {
|
|
|
1404
1374
|
return result;
|
|
1405
1375
|
};
|
|
1406
1376
|
}
|
|
1407
|
-
|
|
1377
|
+
|
|
1378
|
+
// src/trace/wrappers/vercel-ai/generate-object.ts
|
|
1379
|
+
function createGenerateObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
1380
|
+
const ctx = sessionCtx;
|
|
1408
1381
|
return async (...args) => {
|
|
1409
|
-
|
|
1410
|
-
if (!ctx || !initialized) {
|
|
1382
|
+
if (!isInitialized()) {
|
|
1411
1383
|
return aiModule.generateObject(...args);
|
|
1412
1384
|
}
|
|
1413
|
-
|
|
1414
|
-
try {
|
|
1415
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
1416
|
-
promptCtx = getPromptContext();
|
|
1417
|
-
} catch {
|
|
1418
|
-
}
|
|
1419
|
-
const traceCtx = traceContextStorage.getStore() || fallbackTraceContext;
|
|
1385
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
1420
1386
|
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1421
1387
|
const spanId = generateHexId(16);
|
|
1422
1388
|
const parentSpanId = traceCtx?.parentSpanId;
|
|
1423
1389
|
const params = args[0] || {};
|
|
1424
1390
|
const startTime = Date.now();
|
|
1391
|
+
const captureContent2 = shouldCaptureContent();
|
|
1425
1392
|
try {
|
|
1426
1393
|
const result = await aiModule.generateObject(...args);
|
|
1427
1394
|
const endTime = Date.now();
|
|
1395
|
+
if (debug || isDebugMode()) {
|
|
1396
|
+
console.log(
|
|
1397
|
+
"\n\u{1F50D} [Fallom Debug] generateObject result keys:",
|
|
1398
|
+
Object.keys(result || {})
|
|
1399
|
+
);
|
|
1400
|
+
console.log(
|
|
1401
|
+
"\u{1F50D} [Fallom Debug] result.usage:",
|
|
1402
|
+
JSON.stringify(result?.usage, null, 2)
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1428
1405
|
const modelId = result?.response?.modelId || params?.model?.modelId || String(params?.model || "unknown");
|
|
1429
1406
|
const attributes = {};
|
|
1430
|
-
if (
|
|
1407
|
+
if (captureContent2) {
|
|
1431
1408
|
attributes["gen_ai.request.model"] = modelId;
|
|
1432
1409
|
attributes["gen_ai.response.model"] = modelId;
|
|
1433
1410
|
if (result?.object) {
|
|
@@ -1437,6 +1414,18 @@ function createGenerateObjectWrapper(aiModule) {
|
|
|
1437
1414
|
);
|
|
1438
1415
|
}
|
|
1439
1416
|
}
|
|
1417
|
+
if (result?.usage) {
|
|
1418
|
+
attributes["fallom.raw.usage"] = JSON.stringify(result.usage);
|
|
1419
|
+
}
|
|
1420
|
+
if (result?.experimental_providerMetadata) {
|
|
1421
|
+
attributes["fallom.raw.providerMetadata"] = JSON.stringify(
|
|
1422
|
+
result.experimental_providerMetadata
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
if (result?.finishReason) {
|
|
1426
|
+
attributes["gen_ai.response.finish_reason"] = result.finishReason;
|
|
1427
|
+
}
|
|
1428
|
+
const usage = extractUsageFromResult(result);
|
|
1440
1429
|
sendTrace({
|
|
1441
1430
|
config_key: ctx.configKey,
|
|
1442
1431
|
session_id: ctx.sessionId,
|
|
@@ -1451,14 +1440,10 @@ function createGenerateObjectWrapper(aiModule) {
|
|
|
1451
1440
|
end_time: new Date(endTime).toISOString(),
|
|
1452
1441
|
duration_ms: endTime - startTime,
|
|
1453
1442
|
status: "OK",
|
|
1454
|
-
prompt_tokens:
|
|
1455
|
-
completion_tokens:
|
|
1456
|
-
total_tokens:
|
|
1457
|
-
attributes:
|
|
1458
|
-
prompt_key: promptCtx?.promptKey,
|
|
1459
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1460
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1461
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1443
|
+
prompt_tokens: usage.promptTokens,
|
|
1444
|
+
completion_tokens: usage.completionTokens,
|
|
1445
|
+
total_tokens: usage.totalTokens,
|
|
1446
|
+
attributes: captureContent2 ? attributes : void 0
|
|
1462
1447
|
}).catch(() => {
|
|
1463
1448
|
});
|
|
1464
1449
|
return result;
|
|
@@ -1479,50 +1464,70 @@ function createGenerateObjectWrapper(aiModule) {
|
|
|
1479
1464
|
end_time: new Date(endTime).toISOString(),
|
|
1480
1465
|
duration_ms: endTime - startTime,
|
|
1481
1466
|
status: "ERROR",
|
|
1482
|
-
error_message: error?.message
|
|
1483
|
-
prompt_key: promptCtx?.promptKey,
|
|
1484
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1485
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1486
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1467
|
+
error_message: error?.message
|
|
1487
1468
|
}).catch(() => {
|
|
1488
1469
|
});
|
|
1489
1470
|
throw error;
|
|
1490
1471
|
}
|
|
1491
1472
|
};
|
|
1492
1473
|
}
|
|
1493
|
-
|
|
1474
|
+
|
|
1475
|
+
// src/trace/wrappers/vercel-ai/stream-object.ts
|
|
1476
|
+
function log3(...args) {
|
|
1477
|
+
if (isDebugMode()) console.log("[Fallom]", ...args);
|
|
1478
|
+
}
|
|
1479
|
+
function createStreamObjectWrapper(aiModule, sessionCtx, debug = false) {
|
|
1480
|
+
const ctx = sessionCtx;
|
|
1494
1481
|
return async (...args) => {
|
|
1495
|
-
const ctx = sessionStorage.getStore() || fallbackSession;
|
|
1496
1482
|
const params = args[0] || {};
|
|
1497
1483
|
const startTime = Date.now();
|
|
1484
|
+
const captureContent2 = shouldCaptureContent();
|
|
1498
1485
|
const result = await aiModule.streamObject(...args);
|
|
1499
|
-
|
|
1500
|
-
if (!
|
|
1486
|
+
log3("\u{1F50D} streamObject result keys:", Object.keys(result || {}));
|
|
1487
|
+
if (!isInitialized()) {
|
|
1501
1488
|
return result;
|
|
1502
1489
|
}
|
|
1503
|
-
const traceCtx =
|
|
1490
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
1504
1491
|
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1505
1492
|
const spanId = generateHexId(16);
|
|
1506
1493
|
const parentSpanId = traceCtx?.parentSpanId;
|
|
1507
1494
|
let firstTokenTime = null;
|
|
1508
1495
|
const modelId = params?.model?.modelId || String(params?.model || "unknown");
|
|
1509
|
-
let promptCtx = null;
|
|
1510
|
-
try {
|
|
1511
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
1512
|
-
promptCtx = getPromptContext();
|
|
1513
|
-
} catch {
|
|
1514
|
-
}
|
|
1515
1496
|
if (result?.usage) {
|
|
1516
|
-
result.usage.then((
|
|
1497
|
+
result.usage.then(async (rawUsage) => {
|
|
1517
1498
|
const endTime = Date.now();
|
|
1518
|
-
|
|
1499
|
+
if (debug || isDebugMode()) {
|
|
1500
|
+
console.log(
|
|
1501
|
+
"\n\u{1F50D} [Fallom Debug] streamObject usage:",
|
|
1502
|
+
JSON.stringify(rawUsage, null, 2)
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
log3("\u{1F4CA} streamObject usage:", JSON.stringify(rawUsage, null, 2));
|
|
1506
|
+
let providerMetadata = result?.experimental_providerMetadata;
|
|
1507
|
+
if (providerMetadata && typeof providerMetadata.then === "function") {
|
|
1508
|
+
try {
|
|
1509
|
+
providerMetadata = await providerMetadata;
|
|
1510
|
+
} catch {
|
|
1511
|
+
providerMetadata = void 0;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
const usage = extractUsageFromResult(
|
|
1515
|
+
{ experimental_providerMetadata: providerMetadata },
|
|
1516
|
+
rawUsage
|
|
1517
|
+
);
|
|
1519
1518
|
const attributes = {};
|
|
1520
|
-
if (
|
|
1519
|
+
if (captureContent2) {
|
|
1521
1520
|
attributes["gen_ai.request.model"] = modelId;
|
|
1522
1521
|
}
|
|
1523
1522
|
if (firstTokenTime) {
|
|
1524
1523
|
attributes["gen_ai.time_to_first_token_ms"] = firstTokenTime - startTime;
|
|
1525
1524
|
}
|
|
1525
|
+
if (rawUsage) {
|
|
1526
|
+
attributes["fallom.raw.usage"] = JSON.stringify(rawUsage);
|
|
1527
|
+
}
|
|
1528
|
+
if (providerMetadata) {
|
|
1529
|
+
attributes["fallom.raw.providerMetadata"] = JSON.stringify(providerMetadata);
|
|
1530
|
+
}
|
|
1526
1531
|
sendTrace({
|
|
1527
1532
|
config_key: ctx.configKey,
|
|
1528
1533
|
session_id: ctx.sessionId,
|
|
@@ -1537,14 +1542,10 @@ function createStreamObjectWrapper(aiModule) {
|
|
|
1537
1542
|
end_time: new Date(endTime).toISOString(),
|
|
1538
1543
|
duration_ms: endTime - startTime,
|
|
1539
1544
|
status: "OK",
|
|
1540
|
-
prompt_tokens: usage
|
|
1541
|
-
completion_tokens: usage
|
|
1542
|
-
total_tokens: usage
|
|
1543
|
-
attributes:
|
|
1544
|
-
prompt_key: promptCtx?.promptKey,
|
|
1545
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1546
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1547
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1545
|
+
prompt_tokens: usage.promptTokens,
|
|
1546
|
+
completion_tokens: usage.completionTokens,
|
|
1547
|
+
total_tokens: usage.totalTokens,
|
|
1548
|
+
attributes: captureContent2 ? attributes : void 0
|
|
1548
1549
|
}).catch(() => {
|
|
1549
1550
|
});
|
|
1550
1551
|
}).catch((error) => {
|
|
@@ -1563,11 +1564,7 @@ function createStreamObjectWrapper(aiModule) {
|
|
|
1563
1564
|
end_time: new Date(endTime).toISOString(),
|
|
1564
1565
|
duration_ms: endTime - startTime,
|
|
1565
1566
|
status: "ERROR",
|
|
1566
|
-
error_message: error?.message
|
|
1567
|
-
prompt_key: promptCtx?.promptKey,
|
|
1568
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1569
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1570
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1567
|
+
error_message: error?.message
|
|
1571
1568
|
}).catch(() => {
|
|
1572
1569
|
});
|
|
1573
1570
|
});
|
|
@@ -1578,7 +1575,7 @@ function createStreamObjectWrapper(aiModule) {
|
|
|
1578
1575
|
for await (const chunk of originalStream) {
|
|
1579
1576
|
if (!firstTokenTime) {
|
|
1580
1577
|
firstTokenTime = Date.now();
|
|
1581
|
-
|
|
1578
|
+
log3("\u23F1\uFE0F Time to first token:", firstTokenTime - startTime, "ms");
|
|
1582
1579
|
}
|
|
1583
1580
|
yield chunk;
|
|
1584
1581
|
}
|
|
@@ -1595,20 +1592,27 @@ function createStreamObjectWrapper(aiModule) {
|
|
|
1595
1592
|
return result;
|
|
1596
1593
|
};
|
|
1597
1594
|
}
|
|
1598
|
-
|
|
1595
|
+
|
|
1596
|
+
// src/trace/wrappers/vercel-ai/index.ts
|
|
1597
|
+
function wrapAISDK(ai, sessionCtx, options) {
|
|
1598
|
+
const debug = options?.debug ?? false;
|
|
1599
|
+
return {
|
|
1600
|
+
generateText: createGenerateTextWrapper(ai, sessionCtx, debug),
|
|
1601
|
+
streamText: createStreamTextWrapper(ai, sessionCtx, debug),
|
|
1602
|
+
generateObject: ai.generateObject ? createGenerateObjectWrapper(ai, sessionCtx, debug) : void 0,
|
|
1603
|
+
streamObject: ai.streamObject ? createStreamObjectWrapper(ai, sessionCtx, debug) : void 0
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/trace/wrappers/mastra.ts
|
|
1608
|
+
function wrapMastraAgent(agent, sessionCtx) {
|
|
1599
1609
|
const originalGenerate = agent.generate.bind(agent);
|
|
1600
1610
|
const agentName = agent.name || "MastraAgent";
|
|
1611
|
+
const ctx = sessionCtx;
|
|
1601
1612
|
agent.generate = async function(...args) {
|
|
1602
|
-
|
|
1603
|
-
if (!ctx || !initialized) {
|
|
1613
|
+
if (!isInitialized()) {
|
|
1604
1614
|
return originalGenerate(...args);
|
|
1605
1615
|
}
|
|
1606
|
-
let promptCtx = null;
|
|
1607
|
-
try {
|
|
1608
|
-
const { getPromptContext } = await import("./prompts-VAN5E3L4.mjs");
|
|
1609
|
-
promptCtx = getPromptContext();
|
|
1610
|
-
} catch {
|
|
1611
|
-
}
|
|
1612
1616
|
const traceId = generateHexId(32);
|
|
1613
1617
|
const spanId = generateHexId(16);
|
|
1614
1618
|
const startTime = Date.now();
|
|
@@ -1680,11 +1684,7 @@ function wrapMastraAgent(agent) {
|
|
|
1680
1684
|
prompt_tokens: result?.usage?.promptTokens,
|
|
1681
1685
|
completion_tokens: result?.usage?.completionTokens,
|
|
1682
1686
|
total_tokens: result?.usage?.totalTokens,
|
|
1683
|
-
attributes
|
|
1684
|
-
prompt_key: promptCtx?.promptKey,
|
|
1685
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1686
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1687
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1687
|
+
attributes
|
|
1688
1688
|
};
|
|
1689
1689
|
sendTrace(traceData).catch(() => {
|
|
1690
1690
|
});
|
|
@@ -1703,11 +1703,7 @@ function wrapMastraAgent(agent) {
|
|
|
1703
1703
|
end_time: new Date(endTime).toISOString(),
|
|
1704
1704
|
duration_ms: endTime - startTime,
|
|
1705
1705
|
status: "ERROR",
|
|
1706
|
-
error_message: error instanceof Error ? error.message : String(error)
|
|
1707
|
-
prompt_key: promptCtx?.promptKey,
|
|
1708
|
-
prompt_version: promptCtx?.promptVersion,
|
|
1709
|
-
prompt_ab_test_key: promptCtx?.abTestKey,
|
|
1710
|
-
prompt_variant_index: promptCtx?.variantIndex
|
|
1706
|
+
error_message: error instanceof Error ? error.message : String(error)
|
|
1711
1707
|
};
|
|
1712
1708
|
sendTrace(traceData).catch(() => {
|
|
1713
1709
|
});
|
|
@@ -1717,38 +1713,213 @@ function wrapMastraAgent(agent) {
|
|
|
1717
1713
|
return agent;
|
|
1718
1714
|
}
|
|
1719
1715
|
|
|
1720
|
-
// src/
|
|
1721
|
-
var
|
|
1722
|
-
|
|
1716
|
+
// src/trace/session.ts
|
|
1717
|
+
var FallomSession = class {
|
|
1718
|
+
constructor(options) {
|
|
1719
|
+
this.ctx = {
|
|
1720
|
+
configKey: options.configKey,
|
|
1721
|
+
sessionId: options.sessionId,
|
|
1722
|
+
customerId: options.customerId
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
/** Get the session context. */
|
|
1726
|
+
getContext() {
|
|
1727
|
+
return { ...this.ctx };
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Get model assignment for this session (A/B testing).
|
|
1731
|
+
*/
|
|
1732
|
+
async getModel(configKeyOrOptions, options) {
|
|
1733
|
+
let configKey;
|
|
1734
|
+
let opts;
|
|
1735
|
+
if (typeof configKeyOrOptions === "string") {
|
|
1736
|
+
configKey = configKeyOrOptions;
|
|
1737
|
+
opts = options || {};
|
|
1738
|
+
} else {
|
|
1739
|
+
configKey = this.ctx.configKey;
|
|
1740
|
+
opts = configKeyOrOptions || {};
|
|
1741
|
+
}
|
|
1742
|
+
const { get: get2 } = await import("./models-JKMOBZUO.mjs");
|
|
1743
|
+
return get2(configKey, this.ctx.sessionId, opts);
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Wrap a Vercel AI SDK model to trace all calls.
|
|
1747
|
+
*/
|
|
1748
|
+
traceModel(model) {
|
|
1749
|
+
const ctx = this.ctx;
|
|
1750
|
+
const tracedModel = Object.create(model);
|
|
1751
|
+
if (model.doGenerate) {
|
|
1752
|
+
const originalDoGenerate = model.doGenerate.bind(model);
|
|
1753
|
+
tracedModel.doGenerate = async function(...args) {
|
|
1754
|
+
if (!isInitialized()) return originalDoGenerate(...args);
|
|
1755
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
1756
|
+
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1757
|
+
const spanId = generateHexId(16);
|
|
1758
|
+
const startTime = Date.now();
|
|
1759
|
+
try {
|
|
1760
|
+
const result = await originalDoGenerate(...args);
|
|
1761
|
+
const endTime = Date.now();
|
|
1762
|
+
const modelId = model.modelId || "unknown";
|
|
1763
|
+
const usage = result?.usage || result?.rawResponse?.usage;
|
|
1764
|
+
sendTrace({
|
|
1765
|
+
config_key: ctx.configKey,
|
|
1766
|
+
session_id: ctx.sessionId,
|
|
1767
|
+
customer_id: ctx.customerId,
|
|
1768
|
+
trace_id: traceId,
|
|
1769
|
+
span_id: spanId,
|
|
1770
|
+
parent_span_id: traceCtx?.parentSpanId,
|
|
1771
|
+
name: "generateText",
|
|
1772
|
+
kind: "llm",
|
|
1773
|
+
model: modelId,
|
|
1774
|
+
start_time: new Date(startTime).toISOString(),
|
|
1775
|
+
end_time: new Date(endTime).toISOString(),
|
|
1776
|
+
duration_ms: endTime - startTime,
|
|
1777
|
+
status: "OK",
|
|
1778
|
+
prompt_tokens: usage?.promptTokens,
|
|
1779
|
+
completion_tokens: usage?.completionTokens,
|
|
1780
|
+
total_tokens: usage?.totalTokens,
|
|
1781
|
+
attributes: shouldCaptureContent() && usage ? { "fallom.raw.usage": JSON.stringify(usage) } : void 0
|
|
1782
|
+
}).catch(() => {
|
|
1783
|
+
});
|
|
1784
|
+
return result;
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
const endTime = Date.now();
|
|
1787
|
+
sendTrace({
|
|
1788
|
+
config_key: ctx.configKey,
|
|
1789
|
+
session_id: ctx.sessionId,
|
|
1790
|
+
customer_id: ctx.customerId,
|
|
1791
|
+
trace_id: traceId,
|
|
1792
|
+
span_id: spanId,
|
|
1793
|
+
parent_span_id: traceCtx?.parentSpanId,
|
|
1794
|
+
name: "generateText",
|
|
1795
|
+
kind: "llm",
|
|
1796
|
+
model: model.modelId || "unknown",
|
|
1797
|
+
start_time: new Date(startTime).toISOString(),
|
|
1798
|
+
end_time: new Date(endTime).toISOString(),
|
|
1799
|
+
duration_ms: endTime - startTime,
|
|
1800
|
+
status: "ERROR",
|
|
1801
|
+
error_message: error instanceof Error ? error.message : String(error)
|
|
1802
|
+
}).catch(() => {
|
|
1803
|
+
});
|
|
1804
|
+
throw error;
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
if (model.doStream) {
|
|
1809
|
+
const originalDoStream = model.doStream.bind(model);
|
|
1810
|
+
tracedModel.doStream = async function(...args) {
|
|
1811
|
+
if (!isInitialized()) return originalDoStream(...args);
|
|
1812
|
+
const traceCtx = getTraceContextStorage().getStore() || getFallbackTraceContext();
|
|
1813
|
+
const traceId = traceCtx?.traceId || generateHexId(32);
|
|
1814
|
+
const spanId = generateHexId(16);
|
|
1815
|
+
const startTime = Date.now();
|
|
1816
|
+
const modelId = model.modelId || "unknown";
|
|
1817
|
+
try {
|
|
1818
|
+
const result = await originalDoStream(...args);
|
|
1819
|
+
sendTrace({
|
|
1820
|
+
config_key: ctx.configKey,
|
|
1821
|
+
session_id: ctx.sessionId,
|
|
1822
|
+
customer_id: ctx.customerId,
|
|
1823
|
+
trace_id: traceId,
|
|
1824
|
+
span_id: spanId,
|
|
1825
|
+
parent_span_id: traceCtx?.parentSpanId,
|
|
1826
|
+
name: "streamText",
|
|
1827
|
+
kind: "llm",
|
|
1828
|
+
model: modelId,
|
|
1829
|
+
start_time: new Date(startTime).toISOString(),
|
|
1830
|
+
end_time: new Date(Date.now()).toISOString(),
|
|
1831
|
+
duration_ms: Date.now() - startTime,
|
|
1832
|
+
status: "OK",
|
|
1833
|
+
is_streaming: true
|
|
1834
|
+
}).catch(() => {
|
|
1835
|
+
});
|
|
1836
|
+
return result;
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
sendTrace({
|
|
1839
|
+
config_key: ctx.configKey,
|
|
1840
|
+
session_id: ctx.sessionId,
|
|
1841
|
+
customer_id: ctx.customerId,
|
|
1842
|
+
trace_id: traceId,
|
|
1843
|
+
span_id: spanId,
|
|
1844
|
+
parent_span_id: traceCtx?.parentSpanId,
|
|
1845
|
+
name: "streamText",
|
|
1846
|
+
kind: "llm",
|
|
1847
|
+
model: modelId,
|
|
1848
|
+
start_time: new Date(startTime).toISOString(),
|
|
1849
|
+
end_time: new Date(Date.now()).toISOString(),
|
|
1850
|
+
duration_ms: Date.now() - startTime,
|
|
1851
|
+
status: "ERROR",
|
|
1852
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
1853
|
+
is_streaming: true
|
|
1854
|
+
}).catch(() => {
|
|
1855
|
+
});
|
|
1856
|
+
throw error;
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
return tracedModel;
|
|
1861
|
+
}
|
|
1862
|
+
/** Wrap OpenAI client. Delegates to shared wrapper. */
|
|
1863
|
+
wrapOpenAI(client) {
|
|
1864
|
+
return wrapOpenAI(client, this.ctx);
|
|
1865
|
+
}
|
|
1866
|
+
/** Wrap Anthropic client. Delegates to shared wrapper. */
|
|
1867
|
+
wrapAnthropic(client) {
|
|
1868
|
+
return wrapAnthropic(client, this.ctx);
|
|
1869
|
+
}
|
|
1870
|
+
/** Wrap Google AI model. Delegates to shared wrapper. */
|
|
1871
|
+
wrapGoogleAI(model) {
|
|
1872
|
+
return wrapGoogleAI(model, this.ctx);
|
|
1873
|
+
}
|
|
1874
|
+
/** Wrap Vercel AI SDK. Delegates to shared wrapper. */
|
|
1875
|
+
wrapAISDK(ai, options) {
|
|
1876
|
+
return wrapAISDK(ai, this.ctx, options);
|
|
1877
|
+
}
|
|
1878
|
+
/** Wrap Mastra agent. Delegates to shared wrapper. */
|
|
1879
|
+
wrapMastraAgent(agent) {
|
|
1880
|
+
return wrapMastraAgent(agent, this.ctx);
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
function session(options) {
|
|
1884
|
+
return new FallomSession(options);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
// src/prompts.ts
|
|
1888
|
+
var prompts_exports = {};
|
|
1889
|
+
__export(prompts_exports, {
|
|
1890
|
+
clearPromptContext: () => clearPromptContext,
|
|
1723
1891
|
get: () => get,
|
|
1892
|
+
getAB: () => getAB,
|
|
1893
|
+
getPromptContext: () => getPromptContext,
|
|
1724
1894
|
init: () => init3
|
|
1725
1895
|
});
|
|
1726
1896
|
import { createHash } from "crypto";
|
|
1727
1897
|
var apiKey2 = null;
|
|
1728
|
-
var baseUrl2 = "https://
|
|
1898
|
+
var baseUrl2 = "https://prompts.fallom.com";
|
|
1729
1899
|
var initialized2 = false;
|
|
1730
1900
|
var syncInterval = null;
|
|
1731
1901
|
var debugMode2 = false;
|
|
1732
|
-
var
|
|
1902
|
+
var promptCache = /* @__PURE__ */ new Map();
|
|
1903
|
+
var promptABCache = /* @__PURE__ */ new Map();
|
|
1904
|
+
var promptContext = null;
|
|
1733
1905
|
var SYNC_TIMEOUT = 2e3;
|
|
1734
|
-
|
|
1735
|
-
function log2(msg) {
|
|
1906
|
+
function log4(msg) {
|
|
1736
1907
|
if (debugMode2) {
|
|
1737
|
-
console.log(`[Fallom] ${msg}`);
|
|
1908
|
+
console.log(`[Fallom Prompts] ${msg}`);
|
|
1738
1909
|
}
|
|
1739
1910
|
}
|
|
1740
1911
|
function init3(options = {}) {
|
|
1741
1912
|
apiKey2 = options.apiKey || process.env.FALLOM_API_KEY || null;
|
|
1742
|
-
baseUrl2 = options.baseUrl || process.env.
|
|
1913
|
+
baseUrl2 = options.baseUrl || process.env.FALLOM_PROMPTS_URL || process.env.FALLOM_BASE_URL || "https://prompts.fallom.com";
|
|
1743
1914
|
initialized2 = true;
|
|
1744
1915
|
if (!apiKey2) {
|
|
1745
1916
|
return;
|
|
1746
1917
|
}
|
|
1747
|
-
|
|
1918
|
+
fetchAll().catch(() => {
|
|
1748
1919
|
});
|
|
1749
1920
|
if (!syncInterval) {
|
|
1750
1921
|
syncInterval = setInterval(() => {
|
|
1751
|
-
|
|
1922
|
+
fetchAll().catch(() => {
|
|
1752
1923
|
});
|
|
1753
1924
|
}, 3e4);
|
|
1754
1925
|
syncInterval.unref();
|
|
@@ -1762,202 +1933,195 @@ function ensureInit() {
|
|
|
1762
1933
|
}
|
|
1763
1934
|
}
|
|
1764
1935
|
}
|
|
1765
|
-
async function
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1936
|
+
async function fetchAll() {
|
|
1937
|
+
await Promise.all([fetchPrompts(), fetchPromptABTests()]);
|
|
1938
|
+
}
|
|
1939
|
+
async function fetchPrompts(timeout = SYNC_TIMEOUT) {
|
|
1940
|
+
if (!apiKey2) return;
|
|
1770
1941
|
try {
|
|
1771
|
-
log2(`Fetching configs from ${baseUrl2}/configs`);
|
|
1772
1942
|
const controller = new AbortController();
|
|
1773
1943
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1774
|
-
const resp = await fetch(`${baseUrl2}/
|
|
1944
|
+
const resp = await fetch(`${baseUrl2}/prompts`, {
|
|
1775
1945
|
headers: { Authorization: `Bearer ${apiKey2}` },
|
|
1776
1946
|
signal: controller.signal
|
|
1777
1947
|
});
|
|
1778
1948
|
clearTimeout(timeoutId);
|
|
1779
|
-
log2(`Response status: ${resp.status}`);
|
|
1780
1949
|
if (resp.ok) {
|
|
1781
1950
|
const data = await resp.json();
|
|
1782
|
-
const
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
const key = c.key;
|
|
1786
|
-
const version = c.version || 1;
|
|
1787
|
-
log2(`Config '${key}' v${version}: ${JSON.stringify(c.variants)}`);
|
|
1788
|
-
if (!configCache.has(key)) {
|
|
1789
|
-
configCache.set(key, { versions: /* @__PURE__ */ new Map(), latest: null });
|
|
1951
|
+
for (const p of data.prompts || []) {
|
|
1952
|
+
if (!promptCache.has(p.key)) {
|
|
1953
|
+
promptCache.set(p.key, { versions: /* @__PURE__ */ new Map(), current: null });
|
|
1790
1954
|
}
|
|
1791
|
-
const cached =
|
|
1792
|
-
cached.versions.set(version,
|
|
1793
|
-
|
|
1955
|
+
const cached = promptCache.get(p.key);
|
|
1956
|
+
cached.versions.set(p.version, {
|
|
1957
|
+
systemPrompt: p.system_prompt,
|
|
1958
|
+
userTemplate: p.user_template
|
|
1959
|
+
});
|
|
1960
|
+
cached.current = p.version;
|
|
1794
1961
|
}
|
|
1795
|
-
} else {
|
|
1796
|
-
log2(`Fetch failed: ${resp.statusText}`);
|
|
1797
1962
|
}
|
|
1798
|
-
} catch
|
|
1799
|
-
log2(`Fetch exception: ${e}`);
|
|
1963
|
+
} catch {
|
|
1800
1964
|
}
|
|
1801
1965
|
}
|
|
1802
|
-
async function
|
|
1803
|
-
if (!apiKey2) return
|
|
1966
|
+
async function fetchPromptABTests(timeout = SYNC_TIMEOUT) {
|
|
1967
|
+
if (!apiKey2) return;
|
|
1804
1968
|
try {
|
|
1805
1969
|
const controller = new AbortController();
|
|
1806
1970
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1807
|
-
const resp = await fetch(
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
signal: controller.signal
|
|
1812
|
-
}
|
|
1813
|
-
);
|
|
1971
|
+
const resp = await fetch(`${baseUrl2}/prompt-ab-tests`, {
|
|
1972
|
+
headers: { Authorization: `Bearer ${apiKey2}` },
|
|
1973
|
+
signal: controller.signal
|
|
1974
|
+
});
|
|
1814
1975
|
clearTimeout(timeoutId);
|
|
1815
1976
|
if (resp.ok) {
|
|
1816
|
-
const
|
|
1817
|
-
|
|
1818
|
-
|
|
1977
|
+
const data = await resp.json();
|
|
1978
|
+
for (const t of data.prompt_ab_tests || []) {
|
|
1979
|
+
if (!promptABCache.has(t.key)) {
|
|
1980
|
+
promptABCache.set(t.key, { versions: /* @__PURE__ */ new Map(), current: null });
|
|
1981
|
+
}
|
|
1982
|
+
const cached = promptABCache.get(t.key);
|
|
1983
|
+
cached.versions.set(t.version, { variants: t.variants });
|
|
1984
|
+
cached.current = t.version;
|
|
1819
1985
|
}
|
|
1820
|
-
configCache.get(configKey).versions.set(version, config);
|
|
1821
|
-
return config;
|
|
1822
1986
|
}
|
|
1823
1987
|
} catch {
|
|
1824
1988
|
}
|
|
1825
|
-
return null;
|
|
1826
1989
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1990
|
+
function replaceVariables(template, variables) {
|
|
1991
|
+
if (!variables) return template;
|
|
1992
|
+
return template.replace(/\{\{(\s*\w+\s*)\}\}/g, (match, varName) => {
|
|
1993
|
+
const key = varName.trim();
|
|
1994
|
+
return key in variables ? String(variables[key]) : match;
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
function setPromptContext(ctx) {
|
|
1998
|
+
promptContext = ctx;
|
|
1999
|
+
}
|
|
2000
|
+
function getPromptContext() {
|
|
2001
|
+
const ctx = promptContext;
|
|
2002
|
+
promptContext = null;
|
|
2003
|
+
return ctx;
|
|
2004
|
+
}
|
|
2005
|
+
async function get(promptKey, options = {}) {
|
|
2006
|
+
const { variables, version, debug = false } = options;
|
|
1829
2007
|
debugMode2 = debug;
|
|
1830
2008
|
ensureInit();
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
)
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
2009
|
+
log4(`get() called: promptKey=${promptKey}`);
|
|
2010
|
+
let promptData = promptCache.get(promptKey);
|
|
2011
|
+
if (!promptData) {
|
|
2012
|
+
log4("Not in cache, fetching...");
|
|
2013
|
+
await fetchPrompts(SYNC_TIMEOUT);
|
|
2014
|
+
promptData = promptCache.get(promptKey);
|
|
2015
|
+
}
|
|
2016
|
+
if (!promptData) {
|
|
2017
|
+
throw new Error(
|
|
2018
|
+
`Prompt '${promptKey}' not found. Check that it exists in your Fallom dashboard.`
|
|
1838
2019
|
);
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
);
|
|
1846
|
-
}
|
|
1847
|
-
if (!configData) {
|
|
1848
|
-
log2(`Config not found, using fallback: ${fallback}`);
|
|
1849
|
-
if (fallback) {
|
|
1850
|
-
console.warn(
|
|
1851
|
-
`[Fallom WARNING] Config '${configKey}' not found, using fallback model: ${fallback}`
|
|
1852
|
-
);
|
|
1853
|
-
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1854
|
-
}
|
|
1855
|
-
throw new Error(
|
|
1856
|
-
`Config '${configKey}' not found. Check that it exists in your Fallom dashboard.`
|
|
1857
|
-
);
|
|
1858
|
-
}
|
|
1859
|
-
let config;
|
|
1860
|
-
let targetVersion;
|
|
1861
|
-
if (version !== void 0) {
|
|
1862
|
-
config = configData.versions.get(version);
|
|
1863
|
-
if (!config) {
|
|
1864
|
-
config = await fetchSpecificVersion(configKey, version, SYNC_TIMEOUT) || void 0;
|
|
1865
|
-
}
|
|
1866
|
-
if (!config) {
|
|
1867
|
-
if (fallback) {
|
|
1868
|
-
console.warn(
|
|
1869
|
-
`[Fallom WARNING] Config '${configKey}' version ${version} not found, using fallback: ${fallback}`
|
|
1870
|
-
);
|
|
1871
|
-
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1872
|
-
}
|
|
1873
|
-
throw new Error(`Config '${configKey}' version ${version} not found.`);
|
|
1874
|
-
}
|
|
1875
|
-
targetVersion = version;
|
|
1876
|
-
} else {
|
|
1877
|
-
targetVersion = configData.latest;
|
|
1878
|
-
config = configData.versions.get(targetVersion);
|
|
1879
|
-
if (!config) {
|
|
1880
|
-
if (fallback) {
|
|
1881
|
-
console.warn(
|
|
1882
|
-
`[Fallom WARNING] Config '${configKey}' has no cached version, using fallback: ${fallback}`
|
|
1883
|
-
);
|
|
1884
|
-
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1885
|
-
}
|
|
1886
|
-
throw new Error(`Config '${configKey}' has no cached version.`);
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
const variantsRaw = config.variants;
|
|
1890
|
-
const configVersion = config.version || targetVersion;
|
|
1891
|
-
const variants = Array.isArray(variantsRaw) ? variantsRaw : Object.values(variantsRaw);
|
|
1892
|
-
log2(
|
|
1893
|
-
`Config found! Version: ${configVersion}, Variants: ${JSON.stringify(
|
|
1894
|
-
variants
|
|
1895
|
-
)}`
|
|
2020
|
+
}
|
|
2021
|
+
const targetVersion = version ?? promptData.current;
|
|
2022
|
+
const content = promptData.versions.get(targetVersion);
|
|
2023
|
+
if (!content) {
|
|
2024
|
+
throw new Error(
|
|
2025
|
+
`Prompt '${promptKey}' version ${targetVersion} not found.`
|
|
1896
2026
|
);
|
|
1897
|
-
const hashBytes = createHash("md5").update(sessionId).digest();
|
|
1898
|
-
const hashVal = hashBytes.readUInt32BE(0) % 1e6;
|
|
1899
|
-
log2(`Session hash: ${hashVal} (out of 1,000,000)`);
|
|
1900
|
-
let cumulative = 0;
|
|
1901
|
-
let assignedModel = variants[variants.length - 1].model;
|
|
1902
|
-
for (const v of variants) {
|
|
1903
|
-
const oldCumulative = cumulative;
|
|
1904
|
-
cumulative += v.weight * 1e4;
|
|
1905
|
-
log2(
|
|
1906
|
-
`Variant ${v.model}: weight=${v.weight}%, range=${oldCumulative}-${cumulative}, hash=${hashVal}, match=${hashVal < cumulative}`
|
|
1907
|
-
);
|
|
1908
|
-
if (hashVal < cumulative) {
|
|
1909
|
-
assignedModel = v.model;
|
|
1910
|
-
break;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
log2(`\u2705 Assigned model: ${assignedModel}`);
|
|
1914
|
-
return returnWithTrace(configKey, sessionId, assignedModel, configVersion);
|
|
1915
|
-
} catch (e) {
|
|
1916
|
-
if (e instanceof Error && e.message.includes("not found")) {
|
|
1917
|
-
throw e;
|
|
1918
|
-
}
|
|
1919
|
-
if (fallback) {
|
|
1920
|
-
console.warn(
|
|
1921
|
-
`[Fallom WARNING] Error getting model for '${configKey}': ${e}. Using fallback: ${fallback}`
|
|
1922
|
-
);
|
|
1923
|
-
return returnWithTrace(configKey, sessionId, fallback, 0);
|
|
1924
|
-
}
|
|
1925
|
-
throw e;
|
|
1926
2027
|
}
|
|
2028
|
+
const system = replaceVariables(content.systemPrompt, variables);
|
|
2029
|
+
const user = replaceVariables(content.userTemplate, variables);
|
|
2030
|
+
setPromptContext({
|
|
2031
|
+
promptKey,
|
|
2032
|
+
promptVersion: targetVersion
|
|
2033
|
+
});
|
|
2034
|
+
log4(`\u2705 Got prompt: ${promptKey} v${targetVersion}`);
|
|
2035
|
+
return {
|
|
2036
|
+
key: promptKey,
|
|
2037
|
+
version: targetVersion,
|
|
2038
|
+
system,
|
|
2039
|
+
user
|
|
2040
|
+
};
|
|
1927
2041
|
}
|
|
1928
|
-
function
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2042
|
+
async function getAB(abTestKey, sessionId, options = {}) {
|
|
2043
|
+
const { variables, debug = false } = options;
|
|
2044
|
+
debugMode2 = debug;
|
|
2045
|
+
ensureInit();
|
|
2046
|
+
log4(`getAB() called: abTestKey=${abTestKey}, sessionId=${sessionId}`);
|
|
2047
|
+
let abData = promptABCache.get(abTestKey);
|
|
2048
|
+
if (!abData) {
|
|
2049
|
+
log4("Not in cache, fetching...");
|
|
2050
|
+
await fetchPromptABTests(SYNC_TIMEOUT);
|
|
2051
|
+
abData = promptABCache.get(abTestKey);
|
|
1932
2052
|
}
|
|
1933
|
-
if (
|
|
1934
|
-
|
|
1935
|
-
|
|
2053
|
+
if (!abData) {
|
|
2054
|
+
throw new Error(
|
|
2055
|
+
`Prompt A/B test '${abTestKey}' not found. Check that it exists in your Fallom dashboard.`
|
|
2056
|
+
);
|
|
1936
2057
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
try {
|
|
1942
|
-
const controller = new AbortController();
|
|
1943
|
-
const timeoutId = setTimeout(() => controller.abort(), RECORD_TIMEOUT);
|
|
1944
|
-
await fetch(`${baseUrl2}/sessions`, {
|
|
1945
|
-
method: "POST",
|
|
1946
|
-
headers: {
|
|
1947
|
-
Authorization: `Bearer ${apiKey2}`,
|
|
1948
|
-
"Content-Type": "application/json"
|
|
1949
|
-
},
|
|
1950
|
-
body: JSON.stringify({
|
|
1951
|
-
config_key: configKey,
|
|
1952
|
-
config_version: version,
|
|
1953
|
-
session_id: sessionId,
|
|
1954
|
-
assigned_model: model
|
|
1955
|
-
}),
|
|
1956
|
-
signal: controller.signal
|
|
1957
|
-
});
|
|
1958
|
-
clearTimeout(timeoutId);
|
|
1959
|
-
} catch {
|
|
2058
|
+
const currentVersion = abData.current;
|
|
2059
|
+
const versionData = abData.versions.get(currentVersion);
|
|
2060
|
+
if (!versionData) {
|
|
2061
|
+
throw new Error(`Prompt A/B test '${abTestKey}' has no current version.`);
|
|
1960
2062
|
}
|
|
2063
|
+
const { variants } = versionData;
|
|
2064
|
+
log4(`A/B test '${abTestKey}' has ${variants?.length ?? 0} variants`);
|
|
2065
|
+
log4(`Version data: ${JSON.stringify(versionData, null, 2)}`);
|
|
2066
|
+
if (!variants || variants.length === 0) {
|
|
2067
|
+
throw new Error(
|
|
2068
|
+
`Prompt A/B test '${abTestKey}' has no variants configured.`
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
const hashBytes = createHash("md5").update(sessionId).digest();
|
|
2072
|
+
const hashVal = hashBytes.readUInt32BE(0) % 1e6;
|
|
2073
|
+
let cumulative = 0;
|
|
2074
|
+
let selectedVariant = variants[variants.length - 1];
|
|
2075
|
+
let selectedIndex = variants.length - 1;
|
|
2076
|
+
for (let i = 0; i < variants.length; i++) {
|
|
2077
|
+
cumulative += variants[i].weight * 1e4;
|
|
2078
|
+
if (hashVal < cumulative) {
|
|
2079
|
+
selectedVariant = variants[i];
|
|
2080
|
+
selectedIndex = i;
|
|
2081
|
+
break;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
const promptKey = selectedVariant.prompt_key;
|
|
2085
|
+
const promptVersion = selectedVariant.prompt_version;
|
|
2086
|
+
let promptData = promptCache.get(promptKey);
|
|
2087
|
+
if (!promptData) {
|
|
2088
|
+
await fetchPrompts(SYNC_TIMEOUT);
|
|
2089
|
+
promptData = promptCache.get(promptKey);
|
|
2090
|
+
}
|
|
2091
|
+
if (!promptData) {
|
|
2092
|
+
throw new Error(
|
|
2093
|
+
`Prompt '${promptKey}' (from A/B test '${abTestKey}') not found.`
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
const targetVersion = promptVersion ?? promptData.current;
|
|
2097
|
+
const content = promptData.versions.get(targetVersion);
|
|
2098
|
+
if (!content) {
|
|
2099
|
+
throw new Error(
|
|
2100
|
+
`Prompt '${promptKey}' version ${targetVersion} not found.`
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
const system = replaceVariables(content.systemPrompt, variables);
|
|
2104
|
+
const user = replaceVariables(content.userTemplate, variables);
|
|
2105
|
+
setPromptContext({
|
|
2106
|
+
promptKey,
|
|
2107
|
+
promptVersion: targetVersion,
|
|
2108
|
+
abTestKey,
|
|
2109
|
+
variantIndex: selectedIndex
|
|
2110
|
+
});
|
|
2111
|
+
log4(
|
|
2112
|
+
`\u2705 Got prompt from A/B: ${promptKey} v${targetVersion} (variant ${selectedIndex})`
|
|
2113
|
+
);
|
|
2114
|
+
return {
|
|
2115
|
+
key: promptKey,
|
|
2116
|
+
version: targetVersion,
|
|
2117
|
+
system,
|
|
2118
|
+
user,
|
|
2119
|
+
abTestKey,
|
|
2120
|
+
variantIndex: selectedIndex
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
function clearPromptContext() {
|
|
2124
|
+
promptContext = null;
|
|
1961
2125
|
}
|
|
1962
2126
|
|
|
1963
2127
|
// src/init.ts
|
|
@@ -1971,11 +2135,11 @@ async function init4(options = {}) {
|
|
|
1971
2135
|
captureContent: options.captureContent,
|
|
1972
2136
|
debug: options.debug
|
|
1973
2137
|
});
|
|
1974
|
-
|
|
2138
|
+
init({
|
|
1975
2139
|
apiKey: options.apiKey,
|
|
1976
2140
|
baseUrl: configsUrl
|
|
1977
2141
|
});
|
|
1978
|
-
|
|
2142
|
+
init3({
|
|
1979
2143
|
apiKey: options.apiKey,
|
|
1980
2144
|
baseUrl: promptsUrl
|
|
1981
2145
|
});
|
|
@@ -1983,9 +2147,9 @@ async function init4(options = {}) {
|
|
|
1983
2147
|
|
|
1984
2148
|
// src/mastra.ts
|
|
1985
2149
|
import { ExportResultCode } from "@opentelemetry/core";
|
|
1986
|
-
var
|
|
2150
|
+
var promptContext2 = {};
|
|
1987
2151
|
function setMastraPrompt(promptKey, version) {
|
|
1988
|
-
|
|
2152
|
+
promptContext2 = {
|
|
1989
2153
|
promptKey,
|
|
1990
2154
|
promptVersion: version,
|
|
1991
2155
|
promptAbTestKey: void 0,
|
|
@@ -1993,7 +2157,7 @@ function setMastraPrompt(promptKey, version) {
|
|
|
1993
2157
|
};
|
|
1994
2158
|
}
|
|
1995
2159
|
function setMastraPromptAB(abTestKey, variantIndex) {
|
|
1996
|
-
|
|
2160
|
+
promptContext2 = {
|
|
1997
2161
|
promptKey: void 0,
|
|
1998
2162
|
promptVersion: void 0,
|
|
1999
2163
|
promptAbTestKey: abTestKey,
|
|
@@ -2001,7 +2165,7 @@ function setMastraPromptAB(abTestKey, variantIndex) {
|
|
|
2001
2165
|
};
|
|
2002
2166
|
}
|
|
2003
2167
|
function clearMastraPrompt() {
|
|
2004
|
-
|
|
2168
|
+
promptContext2 = {};
|
|
2005
2169
|
}
|
|
2006
2170
|
var FallomExporter = class {
|
|
2007
2171
|
constructor(options = {}) {
|
|
@@ -2009,9 +2173,13 @@ var FallomExporter = class {
|
|
|
2009
2173
|
this.apiKey = options.apiKey ?? process.env.FALLOM_API_KEY ?? "";
|
|
2010
2174
|
this.baseUrl = options.baseUrl ?? "https://traces.fallom.com";
|
|
2011
2175
|
this.debug = options.debug ?? false;
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2176
|
+
this.session = options.session;
|
|
2177
|
+
if (this.debug) {
|
|
2178
|
+
console.log("[FallomExporter] Constructor called");
|
|
2179
|
+
console.log("[FallomExporter] API key present:", !!this.apiKey);
|
|
2180
|
+
console.log("[FallomExporter] Base URL:", this.baseUrl);
|
|
2181
|
+
console.log("[FallomExporter] Session:", this.session);
|
|
2182
|
+
}
|
|
2015
2183
|
if (!this.apiKey) {
|
|
2016
2184
|
console.warn(
|
|
2017
2185
|
"[FallomExporter] No API key provided. Set FALLOM_API_KEY env var or pass apiKey option."
|
|
@@ -2033,10 +2201,10 @@ var FallomExporter = class {
|
|
|
2033
2201
|
}
|
|
2034
2202
|
this.log(`Exporting ${spans.length} spans...`);
|
|
2035
2203
|
if (this.debug) {
|
|
2036
|
-
for (const
|
|
2037
|
-
this.log(` - ${
|
|
2204
|
+
for (const span of spans) {
|
|
2205
|
+
this.log(` - ${span.name}`, {
|
|
2038
2206
|
attributes: Object.fromEntries(
|
|
2039
|
-
Object.entries(
|
|
2207
|
+
Object.entries(span.attributes).filter(
|
|
2040
2208
|
([k]) => k.startsWith("gen_ai") || k.startsWith("llm")
|
|
2041
2209
|
)
|
|
2042
2210
|
)
|
|
@@ -2072,33 +2240,32 @@ var FallomExporter = class {
|
|
|
2072
2240
|
* Send spans to Fallom's OTLP endpoint.
|
|
2073
2241
|
*/
|
|
2074
2242
|
async sendSpans(spans) {
|
|
2075
|
-
const session = getSession();
|
|
2076
2243
|
const resourceSpans = this.spansToOtlpJson(spans);
|
|
2077
2244
|
const headers = {
|
|
2078
2245
|
"Content-Type": "application/json",
|
|
2079
2246
|
Authorization: `Bearer ${this.apiKey}`
|
|
2080
2247
|
};
|
|
2081
|
-
if (session?.configKey) {
|
|
2082
|
-
headers["X-Fallom-Config-Key"] = session.configKey;
|
|
2248
|
+
if (this.session?.configKey) {
|
|
2249
|
+
headers["X-Fallom-Config-Key"] = this.session.configKey;
|
|
2083
2250
|
}
|
|
2084
|
-
if (session?.sessionId) {
|
|
2085
|
-
headers["X-Fallom-Session-Id"] = session.sessionId;
|
|
2251
|
+
if (this.session?.sessionId) {
|
|
2252
|
+
headers["X-Fallom-Session-Id"] = this.session.sessionId;
|
|
2086
2253
|
}
|
|
2087
|
-
if (session?.customerId) {
|
|
2088
|
-
headers["X-Fallom-Customer-Id"] = session.customerId;
|
|
2254
|
+
if (this.session?.customerId) {
|
|
2255
|
+
headers["X-Fallom-Customer-Id"] = this.session.customerId;
|
|
2089
2256
|
}
|
|
2090
|
-
if (
|
|
2091
|
-
headers["X-Fallom-Prompt-Key"] =
|
|
2257
|
+
if (promptContext2.promptKey) {
|
|
2258
|
+
headers["X-Fallom-Prompt-Key"] = promptContext2.promptKey;
|
|
2092
2259
|
}
|
|
2093
|
-
if (
|
|
2094
|
-
headers["X-Fallom-Prompt-Version"] = String(
|
|
2260
|
+
if (promptContext2.promptVersion !== void 0) {
|
|
2261
|
+
headers["X-Fallom-Prompt-Version"] = String(promptContext2.promptVersion);
|
|
2095
2262
|
}
|
|
2096
|
-
if (
|
|
2097
|
-
headers["X-Fallom-Prompt-AB-Test"] =
|
|
2263
|
+
if (promptContext2.promptAbTestKey) {
|
|
2264
|
+
headers["X-Fallom-Prompt-AB-Test"] = promptContext2.promptAbTestKey;
|
|
2098
2265
|
}
|
|
2099
|
-
if (
|
|
2266
|
+
if (promptContext2.promptVariantIndex !== void 0) {
|
|
2100
2267
|
headers["X-Fallom-Prompt-Variant"] = String(
|
|
2101
|
-
|
|
2268
|
+
promptContext2.promptVariantIndex
|
|
2102
2269
|
);
|
|
2103
2270
|
}
|
|
2104
2271
|
const endpoint = `${this.baseUrl}/v1/traces`;
|
|
@@ -2122,12 +2289,12 @@ var FallomExporter = class {
|
|
|
2122
2289
|
*/
|
|
2123
2290
|
spansToOtlpJson(spans) {
|
|
2124
2291
|
const resourceMap = /* @__PURE__ */ new Map();
|
|
2125
|
-
for (const
|
|
2126
|
-
const resourceKey = JSON.stringify(
|
|
2292
|
+
for (const span of spans) {
|
|
2293
|
+
const resourceKey = JSON.stringify(span.resource.attributes);
|
|
2127
2294
|
if (!resourceMap.has(resourceKey)) {
|
|
2128
2295
|
resourceMap.set(resourceKey, []);
|
|
2129
2296
|
}
|
|
2130
|
-
resourceMap.get(resourceKey).push(
|
|
2297
|
+
resourceMap.get(resourceKey).push(span);
|
|
2131
2298
|
}
|
|
2132
2299
|
const resourceSpans = [];
|
|
2133
2300
|
for (const [_resourceKey, resourceSpanList] of resourceMap) {
|
|
@@ -2142,7 +2309,7 @@ var FallomExporter = class {
|
|
|
2142
2309
|
name: firstSpan.instrumentationLibrary.name,
|
|
2143
2310
|
version: firstSpan.instrumentationLibrary.version
|
|
2144
2311
|
},
|
|
2145
|
-
spans: resourceSpanList.map((
|
|
2312
|
+
spans: resourceSpanList.map((span) => this.spanToOtlp(span))
|
|
2146
2313
|
}
|
|
2147
2314
|
]
|
|
2148
2315
|
});
|
|
@@ -2152,21 +2319,21 @@ var FallomExporter = class {
|
|
|
2152
2319
|
/**
|
|
2153
2320
|
* Convert a single span to OTLP format.
|
|
2154
2321
|
*/
|
|
2155
|
-
spanToOtlp(
|
|
2322
|
+
spanToOtlp(span) {
|
|
2156
2323
|
return {
|
|
2157
|
-
traceId:
|
|
2158
|
-
spanId:
|
|
2159
|
-
parentSpanId:
|
|
2160
|
-
name:
|
|
2161
|
-
kind:
|
|
2162
|
-
startTimeUnixNano: this.hrTimeToNanos(
|
|
2163
|
-
endTimeUnixNano: this.hrTimeToNanos(
|
|
2164
|
-
attributes: this.attributesToOtlp(
|
|
2324
|
+
traceId: span.spanContext().traceId,
|
|
2325
|
+
spanId: span.spanContext().spanId,
|
|
2326
|
+
parentSpanId: span.parentSpanId,
|
|
2327
|
+
name: span.name,
|
|
2328
|
+
kind: span.kind,
|
|
2329
|
+
startTimeUnixNano: this.hrTimeToNanos(span.startTime),
|
|
2330
|
+
endTimeUnixNano: this.hrTimeToNanos(span.endTime),
|
|
2331
|
+
attributes: this.attributesToOtlp(span.attributes),
|
|
2165
2332
|
status: {
|
|
2166
|
-
code:
|
|
2167
|
-
message:
|
|
2333
|
+
code: span.status.code,
|
|
2334
|
+
message: span.status.message
|
|
2168
2335
|
},
|
|
2169
|
-
events:
|
|
2336
|
+
events: span.events.map((event) => ({
|
|
2170
2337
|
timeUnixNano: this.hrTimeToNanos(event.time),
|
|
2171
2338
|
name: event.name,
|
|
2172
2339
|
attributes: this.attributesToOtlp(event.attributes || {})
|
|
@@ -2221,15 +2388,18 @@ var index_default = {
|
|
|
2221
2388
|
init: init4,
|
|
2222
2389
|
trace: trace_exports,
|
|
2223
2390
|
models: models_exports,
|
|
2224
|
-
prompts: prompts_exports
|
|
2391
|
+
prompts: prompts_exports,
|
|
2392
|
+
session
|
|
2225
2393
|
};
|
|
2226
2394
|
export {
|
|
2227
2395
|
FallomExporter,
|
|
2396
|
+
FallomSession,
|
|
2228
2397
|
clearMastraPrompt,
|
|
2229
2398
|
index_default as default,
|
|
2230
2399
|
init4 as init,
|
|
2231
2400
|
models_exports as models,
|
|
2232
2401
|
prompts_exports as prompts,
|
|
2402
|
+
session,
|
|
2233
2403
|
setMastraPrompt,
|
|
2234
2404
|
setMastraPromptAB,
|
|
2235
2405
|
trace_exports as trace
|