@sentrial/sdk 0.2.0 → 0.3.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/dist/index.cjs +464 -162
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +168 -65
- package/dist/index.d.ts +168 -65
- package/dist/index.js +459 -162
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -67,26 +67,126 @@ var ValidationError = class extends SentrialError {
|
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
// src/redact.ts
|
|
71
|
+
import { createHash } from "crypto";
|
|
72
|
+
var DEFAULT_FIELDS = [
|
|
73
|
+
"userInput",
|
|
74
|
+
"assistantOutput",
|
|
75
|
+
"toolInput",
|
|
76
|
+
"toolOutput"
|
|
77
|
+
];
|
|
78
|
+
var DEFAULT_BUILTIN = {
|
|
79
|
+
emails: true,
|
|
80
|
+
phones: true,
|
|
81
|
+
ssns: true,
|
|
82
|
+
creditCards: true,
|
|
83
|
+
ipAddresses: true
|
|
84
|
+
};
|
|
85
|
+
var EMAIL_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
|
|
86
|
+
var PHONE_PATTERN = /(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g;
|
|
87
|
+
var SSN_PATTERN = /\b\d{3}-\d{2}-\d{4}\b/g;
|
|
88
|
+
var CREDIT_CARD_PATTERN = /\b(?:\d[-\s]?){13,19}\b/g;
|
|
89
|
+
var IP_ADDRESS_PATTERN = /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g;
|
|
90
|
+
var BUILTIN_PATTERNS = {
|
|
91
|
+
emails: { pattern: EMAIL_PATTERN, label: "EMAIL" },
|
|
92
|
+
phones: { pattern: PHONE_PATTERN, label: "PHONE" },
|
|
93
|
+
ssns: { pattern: SSN_PATTERN, label: "SSN" },
|
|
94
|
+
creditCards: { pattern: CREDIT_CARD_PATTERN, label: "CREDIT_CARD" },
|
|
95
|
+
ipAddresses: { pattern: IP_ADDRESS_PATTERN, label: "IP_ADDRESS" }
|
|
96
|
+
};
|
|
97
|
+
function hashValue(value) {
|
|
98
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 6);
|
|
99
|
+
}
|
|
100
|
+
function replaceMatch(match, label, mode) {
|
|
101
|
+
switch (mode) {
|
|
102
|
+
case "label":
|
|
103
|
+
return `[${label}]`;
|
|
104
|
+
case "hash":
|
|
105
|
+
return `[PII:${hashValue(match)}]`;
|
|
106
|
+
case "remove":
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function redactString(value, mode, builtinPatterns, customPatterns) {
|
|
111
|
+
let result = value;
|
|
112
|
+
for (const [key, config] of Object.entries(BUILTIN_PATTERNS)) {
|
|
113
|
+
if (builtinPatterns[key]) {
|
|
114
|
+
const regex = new RegExp(config.pattern.source, config.pattern.flags);
|
|
115
|
+
result = result.replace(regex, (match) => replaceMatch(match, config.label, mode));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const custom of customPatterns) {
|
|
119
|
+
const regex = new RegExp(custom.pattern.source, custom.pattern.flags);
|
|
120
|
+
result = result.replace(regex, (match) => replaceMatch(match, custom.label, mode));
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
function redactValue(value, mode, builtinPatterns, customPatterns) {
|
|
125
|
+
if (typeof value === "string") {
|
|
126
|
+
return redactString(value, mode, builtinPatterns, customPatterns);
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(value)) {
|
|
129
|
+
return value.map((item) => redactValue(item, mode, builtinPatterns, customPatterns));
|
|
130
|
+
}
|
|
131
|
+
if (value !== null && typeof value === "object") {
|
|
132
|
+
const result = {};
|
|
133
|
+
for (const [key, val] of Object.entries(value)) {
|
|
134
|
+
result[key] = redactValue(val, mode, builtinPatterns, customPatterns);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
function redactPayload(payload, config) {
|
|
141
|
+
if (!config.enabled) {
|
|
142
|
+
return payload;
|
|
143
|
+
}
|
|
144
|
+
const mode = config.mode ?? "label";
|
|
145
|
+
const fields = config.fields ?? DEFAULT_FIELDS;
|
|
146
|
+
const builtinPatterns = {
|
|
147
|
+
...DEFAULT_BUILTIN,
|
|
148
|
+
...config.builtinPatterns
|
|
149
|
+
};
|
|
150
|
+
const customPatterns = config.customPatterns ?? [];
|
|
151
|
+
const result = { ...payload };
|
|
152
|
+
for (const field of fields) {
|
|
153
|
+
if (field in result && result[field] !== void 0 && result[field] !== null) {
|
|
154
|
+
result[field] = redactValue(result[field], mode, builtinPatterns, customPatterns);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
|
|
70
160
|
// src/cost.ts
|
|
71
161
|
var OPENAI_PRICING = {
|
|
72
162
|
"gpt-5.2": { input: 5, output: 15 },
|
|
73
163
|
"gpt-5": { input: 4, output: 12 },
|
|
164
|
+
"gpt-4.1": { input: 2, output: 8 },
|
|
165
|
+
"gpt-4.1-mini": { input: 0.4, output: 1.6 },
|
|
166
|
+
"gpt-4.1-nano": { input: 0.1, output: 0.4 },
|
|
74
167
|
"gpt-4o": { input: 2.5, output: 10 },
|
|
75
168
|
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
76
169
|
"gpt-4-turbo": { input: 10, output: 30 },
|
|
77
170
|
"gpt-4": { input: 30, output: 60 },
|
|
78
171
|
"gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
172
|
+
"o4-mini": { input: 1.1, output: 4.4 },
|
|
79
173
|
"o3": { input: 10, output: 40 },
|
|
80
|
-
"o3-mini": { input:
|
|
174
|
+
"o3-mini": { input: 1.1, output: 4.4 },
|
|
175
|
+
"o3-pro": { input: 20, output: 80 },
|
|
176
|
+
"o1": { input: 15, output: 60 },
|
|
81
177
|
"o1-preview": { input: 15, output: 60 },
|
|
82
178
|
"o1-mini": { input: 3, output: 12 }
|
|
83
179
|
};
|
|
84
180
|
var ANTHROPIC_PRICING = {
|
|
181
|
+
"claude-opus-4": { input: 15, output: 75 },
|
|
182
|
+
"claude-sonnet-4": { input: 3, output: 15 },
|
|
85
183
|
"claude-4.5-opus": { input: 20, output: 100 },
|
|
86
184
|
"claude-4.5-sonnet": { input: 4, output: 20 },
|
|
87
185
|
"claude-4-opus": { input: 18, output: 90 },
|
|
88
186
|
"claude-4-sonnet": { input: 3.5, output: 17.5 },
|
|
187
|
+
"claude-3-7-sonnet": { input: 3, output: 15 },
|
|
89
188
|
"claude-3-5-sonnet": { input: 3, output: 15 },
|
|
189
|
+
"claude-3-5-haiku": { input: 0.8, output: 4 },
|
|
90
190
|
"claude-3-opus": { input: 15, output: 75 },
|
|
91
191
|
"claude-3-sonnet": { input: 3, output: 15 },
|
|
92
192
|
"claude-3-haiku": { input: 0.25, output: 1.25 }
|
|
@@ -157,11 +257,75 @@ var SentrialClient = class {
|
|
|
157
257
|
apiUrl;
|
|
158
258
|
apiKey;
|
|
159
259
|
failSilently;
|
|
260
|
+
piiConfig;
|
|
261
|
+
piiConfigNeedsHydration = false;
|
|
262
|
+
piiHydrationPromise;
|
|
160
263
|
currentState = {};
|
|
161
264
|
constructor(config = {}) {
|
|
162
265
|
this.apiUrl = (config.apiUrl ?? (typeof process !== "undefined" ? process.env?.SENTRIAL_API_URL : void 0) ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
163
266
|
this.apiKey = config.apiKey ?? (typeof process !== "undefined" ? process.env?.SENTRIAL_API_KEY : void 0);
|
|
164
267
|
this.failSilently = config.failSilently ?? true;
|
|
268
|
+
if (config.pii === true) {
|
|
269
|
+
this.piiConfig = { enabled: true };
|
|
270
|
+
this.piiConfigNeedsHydration = true;
|
|
271
|
+
} else if (config.pii && typeof config.pii === "object") {
|
|
272
|
+
this.piiConfig = config.pii;
|
|
273
|
+
this.piiConfigNeedsHydration = false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Fetch the organization's PII config from the server.
|
|
278
|
+
*
|
|
279
|
+
* Called lazily on the first request when `pii: true` was passed to the constructor.
|
|
280
|
+
* Uses a single shared promise so concurrent requests don't trigger duplicate fetches.
|
|
281
|
+
*/
|
|
282
|
+
async hydratePiiConfig() {
|
|
283
|
+
if (!this.piiConfigNeedsHydration) return;
|
|
284
|
+
if (this.piiHydrationPromise) {
|
|
285
|
+
await this.piiHydrationPromise;
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
this.piiHydrationPromise = (async () => {
|
|
289
|
+
try {
|
|
290
|
+
const headers = {};
|
|
291
|
+
if (this.apiKey) {
|
|
292
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
293
|
+
}
|
|
294
|
+
const controller = new AbortController();
|
|
295
|
+
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
296
|
+
let response;
|
|
297
|
+
try {
|
|
298
|
+
response = await fetch(`${this.apiUrl}/api/sdk/pii-config`, {
|
|
299
|
+
method: "GET",
|
|
300
|
+
headers,
|
|
301
|
+
signal: controller.signal
|
|
302
|
+
});
|
|
303
|
+
} finally {
|
|
304
|
+
clearTimeout(timeoutId);
|
|
305
|
+
}
|
|
306
|
+
if (response.ok) {
|
|
307
|
+
const data = await response.json();
|
|
308
|
+
if (data.config) {
|
|
309
|
+
this.piiConfig = {
|
|
310
|
+
enabled: data.config.enabled,
|
|
311
|
+
mode: data.config.mode,
|
|
312
|
+
fields: data.config.fields,
|
|
313
|
+
builtinPatterns: data.config.builtinPatterns,
|
|
314
|
+
customPatterns: (data.config.customPatterns || []).map(
|
|
315
|
+
(cp) => ({
|
|
316
|
+
pattern: new RegExp(cp.pattern, "g"),
|
|
317
|
+
label: cp.label
|
|
318
|
+
})
|
|
319
|
+
),
|
|
320
|
+
enhancedDetection: data.config.enhancedDetection
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
this.piiConfigNeedsHydration = false;
|
|
327
|
+
})();
|
|
328
|
+
await this.piiHydrationPromise;
|
|
165
329
|
}
|
|
166
330
|
/**
|
|
167
331
|
* Make an HTTP request with retry logic and exponential backoff.
|
|
@@ -170,6 +334,9 @@ var SentrialClient = class {
|
|
|
170
334
|
* Up to MAX_RETRIES attempts with exponential backoff.
|
|
171
335
|
*/
|
|
172
336
|
async safeRequest(method, url, body) {
|
|
337
|
+
if (this.piiConfigNeedsHydration) {
|
|
338
|
+
await this.hydratePiiConfig();
|
|
339
|
+
}
|
|
173
340
|
let lastError;
|
|
174
341
|
let backoff = INITIAL_BACKOFF_MS;
|
|
175
342
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
@@ -180,6 +347,7 @@ var SentrialClient = class {
|
|
|
180
347
|
if (this.apiKey) {
|
|
181
348
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
182
349
|
}
|
|
350
|
+
const finalBody = this.piiConfig && body && typeof body === "object" ? redactPayload(body, this.piiConfig) : body;
|
|
183
351
|
const controller = new AbortController();
|
|
184
352
|
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
185
353
|
let response;
|
|
@@ -187,7 +355,7 @@ var SentrialClient = class {
|
|
|
187
355
|
response = await fetch(url, {
|
|
188
356
|
method,
|
|
189
357
|
headers,
|
|
190
|
-
body:
|
|
358
|
+
body: finalBody ? JSON.stringify(finalBody) : void 0,
|
|
191
359
|
signal: controller.signal
|
|
192
360
|
});
|
|
193
361
|
} finally {
|
|
@@ -259,6 +427,9 @@ var SentrialClient = class {
|
|
|
259
427
|
if (params.parentSessionId) {
|
|
260
428
|
payload.parentSessionId = params.parentSessionId;
|
|
261
429
|
}
|
|
430
|
+
if (params.convoId) {
|
|
431
|
+
payload.convoId = params.convoId;
|
|
432
|
+
}
|
|
262
433
|
const response = await this.safeRequest(
|
|
263
434
|
"POST",
|
|
264
435
|
`${this.apiUrl}/api/sdk/sessions`,
|
|
@@ -465,6 +636,7 @@ var SentrialClient = class {
|
|
|
465
636
|
name: `${params.event}:${eventId.slice(0, 8)}`,
|
|
466
637
|
agentName: params.event,
|
|
467
638
|
userId: params.userId,
|
|
639
|
+
convoId: params.convoId,
|
|
468
640
|
metadata: fullMetadata
|
|
469
641
|
});
|
|
470
642
|
if (params.input) {
|
|
@@ -629,7 +801,8 @@ function configureVercel(config) {
|
|
|
629
801
|
});
|
|
630
802
|
_globalConfig = {
|
|
631
803
|
defaultAgent: config.defaultAgent,
|
|
632
|
-
userId: config.userId
|
|
804
|
+
userId: config.userId,
|
|
805
|
+
convoId: config.convoId
|
|
633
806
|
};
|
|
634
807
|
}
|
|
635
808
|
function getClient2() {
|
|
@@ -644,11 +817,15 @@ function extractModelInfo(model) {
|
|
|
644
817
|
return { modelId, provider };
|
|
645
818
|
}
|
|
646
819
|
function guessProvider(modelId) {
|
|
647
|
-
|
|
648
|
-
if (
|
|
649
|
-
if (
|
|
650
|
-
if (
|
|
651
|
-
if (
|
|
820
|
+
const id = modelId.toLowerCase();
|
|
821
|
+
if (id.includes("gpt") || id.includes("o1") || id.includes("o3") || id.includes("o4") || id.startsWith("chatgpt")) return "openai";
|
|
822
|
+
if (id.includes("claude")) return "anthropic";
|
|
823
|
+
if (id.includes("gemini")) return "google";
|
|
824
|
+
if (id.includes("mistral") || id.includes("mixtral") || id.includes("codestral") || id.includes("pixtral")) return "mistral";
|
|
825
|
+
if (id.includes("llama")) return "meta";
|
|
826
|
+
if (id.includes("deepseek")) return "deepseek";
|
|
827
|
+
if (id.includes("command")) return "cohere";
|
|
828
|
+
if (id.includes("qwen")) return "alibaba";
|
|
652
829
|
return "unknown";
|
|
653
830
|
}
|
|
654
831
|
function calculateCostForCall(provider, modelId, promptTokens, completionTokens) {
|
|
@@ -660,6 +837,12 @@ function calculateCostForCall(provider, modelId, promptTokens, completionTokens)
|
|
|
660
837
|
return calculateAnthropicCost(params);
|
|
661
838
|
case "google":
|
|
662
839
|
return calculateGoogleCost(params);
|
|
840
|
+
case "deepseek":
|
|
841
|
+
return promptTokens / 1e6 * 0.27 + completionTokens / 1e6 * 1.1;
|
|
842
|
+
case "cohere":
|
|
843
|
+
return promptTokens / 1e6 * 0.5 + completionTokens / 1e6 * 1.5;
|
|
844
|
+
case "mistral":
|
|
845
|
+
return promptTokens / 1e6 * 2 + completionTokens / 1e6 * 6;
|
|
663
846
|
default:
|
|
664
847
|
return promptTokens * 3e-6 + completionTokens * 6e-6;
|
|
665
848
|
}
|
|
@@ -668,7 +851,10 @@ function extractInput(params) {
|
|
|
668
851
|
if (params.prompt) return params.prompt;
|
|
669
852
|
if (params.messages && params.messages.length > 0) {
|
|
670
853
|
const lastUserMessage = [...params.messages].reverse().find((m) => m.role === "user");
|
|
671
|
-
|
|
854
|
+
if (lastUserMessage) {
|
|
855
|
+
return typeof lastUserMessage.content === "string" ? lastUserMessage.content : JSON.stringify(lastUserMessage.content);
|
|
856
|
+
}
|
|
857
|
+
return JSON.stringify(params.messages);
|
|
672
858
|
}
|
|
673
859
|
return "";
|
|
674
860
|
}
|
|
@@ -685,23 +871,25 @@ function wrapTools(tools, sessionId, client) {
|
|
|
685
871
|
try {
|
|
686
872
|
const result = await originalExecute(...args);
|
|
687
873
|
const durationMs = Date.now() - startTime;
|
|
688
|
-
|
|
874
|
+
client.trackToolCall({
|
|
689
875
|
sessionId,
|
|
690
876
|
toolName,
|
|
691
877
|
toolInput: args[0],
|
|
692
878
|
toolOutput: result,
|
|
693
879
|
reasoning: `Tool executed in ${durationMs}ms`
|
|
880
|
+
}).catch(() => {
|
|
694
881
|
});
|
|
695
882
|
return result;
|
|
696
883
|
} catch (error) {
|
|
697
884
|
const durationMs = Date.now() - startTime;
|
|
698
|
-
|
|
885
|
+
client.trackToolCall({
|
|
699
886
|
sessionId,
|
|
700
887
|
toolName,
|
|
701
888
|
toolInput: args[0],
|
|
702
889
|
toolOutput: {},
|
|
703
890
|
toolError: { message: error instanceof Error ? error.message : "Unknown error" },
|
|
704
891
|
reasoning: `Tool failed after ${durationMs}ms`
|
|
892
|
+
}).catch(() => {
|
|
705
893
|
});
|
|
706
894
|
throw error;
|
|
707
895
|
}
|
|
@@ -713,19 +901,68 @@ function wrapTools(tools, sessionId, client) {
|
|
|
713
901
|
}
|
|
714
902
|
return wrappedTools;
|
|
715
903
|
}
|
|
716
|
-
function
|
|
904
|
+
function wrapToolsAsync(tools, sessionPromise, client) {
|
|
905
|
+
const wrappedTools = {};
|
|
906
|
+
for (const [toolName, tool] of Object.entries(tools)) {
|
|
907
|
+
if (typeof tool.execute === "function") {
|
|
908
|
+
const originalExecute = tool.execute;
|
|
909
|
+
wrappedTools[toolName] = {
|
|
910
|
+
...tool,
|
|
911
|
+
execute: async (...args) => {
|
|
912
|
+
const startTime = Date.now();
|
|
913
|
+
const sid = await sessionPromise;
|
|
914
|
+
try {
|
|
915
|
+
const result = await originalExecute(...args);
|
|
916
|
+
const durationMs = Date.now() - startTime;
|
|
917
|
+
if (sid) {
|
|
918
|
+
client.trackToolCall({
|
|
919
|
+
sessionId: sid,
|
|
920
|
+
toolName,
|
|
921
|
+
toolInput: args[0],
|
|
922
|
+
toolOutput: result,
|
|
923
|
+
reasoning: `Tool executed in ${durationMs}ms`
|
|
924
|
+
}).catch(() => {
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
return result;
|
|
928
|
+
} catch (error) {
|
|
929
|
+
const durationMs = Date.now() - startTime;
|
|
930
|
+
if (sid) {
|
|
931
|
+
client.trackToolCall({
|
|
932
|
+
sessionId: sid,
|
|
933
|
+
toolName,
|
|
934
|
+
toolInput: args[0],
|
|
935
|
+
toolOutput: {},
|
|
936
|
+
toolError: { message: error instanceof Error ? error.message : "Unknown error" },
|
|
937
|
+
reasoning: `Tool failed after ${durationMs}ms`
|
|
938
|
+
}).catch(() => {
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
throw error;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
} else {
|
|
946
|
+
wrappedTools[toolName] = tool;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return wrappedTools;
|
|
950
|
+
}
|
|
951
|
+
function wrapGenerateText(originalFn, client, config) {
|
|
717
952
|
return async (params) => {
|
|
718
953
|
const startTime = Date.now();
|
|
719
954
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
720
955
|
const input = extractInput(params);
|
|
721
956
|
const sessionId = await client.createSession({
|
|
722
957
|
name: `generateText: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
723
|
-
agentName:
|
|
724
|
-
userId:
|
|
958
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
959
|
+
userId: config.userId ?? "anonymous",
|
|
960
|
+
convoId: config.convoId,
|
|
725
961
|
metadata: {
|
|
726
962
|
model: modelId,
|
|
727
963
|
provider,
|
|
728
|
-
function: "generateText"
|
|
964
|
+
function: "generateText",
|
|
965
|
+
...params.maxSteps ? { maxSteps: params.maxSteps } : {}
|
|
729
966
|
}
|
|
730
967
|
});
|
|
731
968
|
if (!sessionId) {
|
|
@@ -739,23 +976,46 @@ function wrapGenerateText(originalFn, client) {
|
|
|
739
976
|
try {
|
|
740
977
|
const result = await originalFn(wrappedParams);
|
|
741
978
|
const durationMs = Date.now() - startTime;
|
|
979
|
+
const resolvedModelId = result.response?.modelId || modelId;
|
|
742
980
|
const promptTokens = result.usage?.promptTokens || 0;
|
|
743
981
|
const completionTokens = result.usage?.completionTokens || 0;
|
|
744
982
|
const totalTokens = result.usage?.totalTokens || promptTokens + completionTokens;
|
|
745
|
-
const cost = calculateCostForCall(provider,
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
983
|
+
const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);
|
|
984
|
+
const steps = result.steps;
|
|
985
|
+
if (steps && steps.length >= 1) {
|
|
986
|
+
for (let i = 0; i < steps.length; i++) {
|
|
987
|
+
const step = steps[i];
|
|
988
|
+
await client.trackEvent({
|
|
989
|
+
sessionId,
|
|
990
|
+
eventType: "llm_call",
|
|
991
|
+
eventData: {
|
|
992
|
+
model: resolvedModelId,
|
|
993
|
+
provider,
|
|
994
|
+
step: i + 1,
|
|
995
|
+
total_steps: steps.length,
|
|
996
|
+
prompt_tokens: step.usage?.promptTokens || 0,
|
|
997
|
+
completion_tokens: step.usage?.completionTokens || 0,
|
|
998
|
+
total_tokens: step.usage?.totalTokens || 0,
|
|
999
|
+
finish_reason: step.finishReason,
|
|
1000
|
+
tool_calls: step.toolCalls?.map((tc) => tc.toolName)
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
757
1003
|
}
|
|
758
|
-
}
|
|
1004
|
+
} else {
|
|
1005
|
+
await client.trackEvent({
|
|
1006
|
+
sessionId,
|
|
1007
|
+
eventType: "llm_call",
|
|
1008
|
+
eventData: {
|
|
1009
|
+
model: resolvedModelId,
|
|
1010
|
+
provider,
|
|
1011
|
+
prompt_tokens: promptTokens,
|
|
1012
|
+
completion_tokens: completionTokens,
|
|
1013
|
+
total_tokens: totalTokens,
|
|
1014
|
+
finish_reason: result.finishReason,
|
|
1015
|
+
tool_calls: result.toolCalls?.map((tc) => tc.toolName)
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
759
1019
|
await client.completeSession({
|
|
760
1020
|
sessionId,
|
|
761
1021
|
success: true,
|
|
@@ -784,134 +1044,139 @@ function wrapGenerateText(originalFn, client) {
|
|
|
784
1044
|
}
|
|
785
1045
|
};
|
|
786
1046
|
}
|
|
787
|
-
function wrapStreamText(originalFn, client) {
|
|
1047
|
+
function wrapStreamText(originalFn, client, config) {
|
|
788
1048
|
return (params) => {
|
|
789
1049
|
const startTime = Date.now();
|
|
790
1050
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
791
1051
|
const input = extractInput(params);
|
|
792
1052
|
let sessionId = null;
|
|
793
|
-
const sessionPromise =
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
1053
|
+
const sessionPromise = (async () => {
|
|
1054
|
+
try {
|
|
1055
|
+
const id = await client.createSession({
|
|
1056
|
+
name: `streamText: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
1057
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
1058
|
+
userId: config.userId ?? "anonymous",
|
|
1059
|
+
convoId: config.convoId,
|
|
1060
|
+
metadata: {
|
|
1061
|
+
model: modelId,
|
|
1062
|
+
provider,
|
|
1063
|
+
function: "streamText"
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
sessionId = id;
|
|
1067
|
+
if (id) {
|
|
1068
|
+
client.setInput(id, input).catch(() => {
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
return id;
|
|
1072
|
+
} catch {
|
|
1073
|
+
return null;
|
|
801
1074
|
}
|
|
802
|
-
})
|
|
803
|
-
sessionId = id;
|
|
804
|
-
if (id) client.setInput(id, input);
|
|
805
|
-
return id;
|
|
806
|
-
});
|
|
1075
|
+
})();
|
|
807
1076
|
const wrappedParams = {
|
|
808
1077
|
...params,
|
|
809
1078
|
tools: params.tools ? wrapToolsAsync(params.tools, sessionPromise, client) : void 0
|
|
810
1079
|
};
|
|
811
1080
|
const result = originalFn(wrappedParams);
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1081
|
+
let tracked = false;
|
|
1082
|
+
async function trackCompletion(fullText, error) {
|
|
1083
|
+
if (tracked) return;
|
|
1084
|
+
tracked = true;
|
|
1085
|
+
const durationMs = Date.now() - startTime;
|
|
1086
|
+
const sid = sessionId || await sessionPromise;
|
|
1087
|
+
if (!sid) return;
|
|
1088
|
+
if (error) {
|
|
1089
|
+
await client.trackError({
|
|
1090
|
+
sessionId: sid,
|
|
1091
|
+
errorType: error.name || "Error",
|
|
1092
|
+
errorMessage: error.message || "Unknown error"
|
|
1093
|
+
});
|
|
1094
|
+
await client.completeSession({
|
|
1095
|
+
sessionId: sid,
|
|
1096
|
+
success: false,
|
|
1097
|
+
failureReason: error.message || "Unknown error",
|
|
1098
|
+
durationMs
|
|
1099
|
+
});
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
let usage;
|
|
815
1103
|
try {
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
yield chunk;
|
|
819
|
-
}
|
|
820
|
-
const durationMs = Date.now() - startTime;
|
|
821
|
-
const sid = sessionId || await sessionPromise;
|
|
822
|
-
if (sid) {
|
|
823
|
-
const usage = result.usage ? await result.usage : void 0;
|
|
824
|
-
const promptTokens = usage?.promptTokens || 0;
|
|
825
|
-
const completionTokens = usage?.completionTokens || 0;
|
|
826
|
-
const totalTokens = usage?.totalTokens || promptTokens + completionTokens;
|
|
827
|
-
const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);
|
|
828
|
-
await client.completeSession({
|
|
829
|
-
sessionId: sid,
|
|
830
|
-
success: true,
|
|
831
|
-
output: fullText,
|
|
832
|
-
durationMs,
|
|
833
|
-
estimatedCost: cost,
|
|
834
|
-
promptTokens,
|
|
835
|
-
completionTokens,
|
|
836
|
-
totalTokens
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
} catch (error) {
|
|
840
|
-
const durationMs = Date.now() - startTime;
|
|
841
|
-
const sid = sessionId || await sessionPromise;
|
|
842
|
-
if (sid) {
|
|
843
|
-
await client.trackError({
|
|
844
|
-
sessionId: sid,
|
|
845
|
-
errorType: error instanceof Error ? error.name : "Error",
|
|
846
|
-
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
847
|
-
});
|
|
848
|
-
await client.completeSession({
|
|
849
|
-
sessionId: sid,
|
|
850
|
-
success: false,
|
|
851
|
-
failureReason: error instanceof Error ? error.message : "Unknown error",
|
|
852
|
-
durationMs
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
throw error;
|
|
1104
|
+
usage = result.usage ? await result.usage : void 0;
|
|
1105
|
+
} catch {
|
|
856
1106
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
const sessionId = await sessionPromise;
|
|
871
|
-
try {
|
|
872
|
-
const result = await originalExecute(...args);
|
|
873
|
-
const durationMs = Date.now() - startTime;
|
|
874
|
-
if (sessionId) {
|
|
875
|
-
await client.trackToolCall({
|
|
876
|
-
sessionId,
|
|
877
|
-
toolName,
|
|
878
|
-
toolInput: args[0],
|
|
879
|
-
toolOutput: result,
|
|
880
|
-
reasoning: `Tool executed in ${durationMs}ms`
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
return result;
|
|
884
|
-
} catch (error) {
|
|
885
|
-
const durationMs = Date.now() - startTime;
|
|
886
|
-
if (sessionId) {
|
|
887
|
-
await client.trackToolCall({
|
|
888
|
-
sessionId,
|
|
889
|
-
toolName,
|
|
890
|
-
toolInput: args[0],
|
|
891
|
-
toolOutput: {},
|
|
892
|
-
toolError: { message: error instanceof Error ? error.message : "Unknown error" },
|
|
893
|
-
reasoning: `Tool failed after ${durationMs}ms`
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
throw error;
|
|
897
|
-
}
|
|
1107
|
+
const promptTokens = usage?.promptTokens || 0;
|
|
1108
|
+
const completionTokens = usage?.completionTokens || 0;
|
|
1109
|
+
const totalTokens = usage?.totalTokens || promptTokens + completionTokens;
|
|
1110
|
+
const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);
|
|
1111
|
+
await client.trackEvent({
|
|
1112
|
+
sessionId: sid,
|
|
1113
|
+
eventType: "llm_call",
|
|
1114
|
+
eventData: {
|
|
1115
|
+
model: modelId,
|
|
1116
|
+
provider,
|
|
1117
|
+
prompt_tokens: promptTokens,
|
|
1118
|
+
completion_tokens: completionTokens,
|
|
1119
|
+
total_tokens: totalTokens
|
|
898
1120
|
}
|
|
899
|
-
};
|
|
1121
|
+
});
|
|
1122
|
+
await client.completeSession({
|
|
1123
|
+
sessionId: sid,
|
|
1124
|
+
success: true,
|
|
1125
|
+
output: fullText,
|
|
1126
|
+
durationMs,
|
|
1127
|
+
estimatedCost: cost,
|
|
1128
|
+
promptTokens,
|
|
1129
|
+
completionTokens,
|
|
1130
|
+
totalTokens
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
const textProp = result.text;
|
|
1134
|
+
if (typeof textProp === "string") {
|
|
1135
|
+
trackCompletion(textProp).catch(() => {
|
|
1136
|
+
});
|
|
1137
|
+
} else if (textProp != null && typeof textProp.then === "function") {
|
|
1138
|
+
const originalTextPromise = textProp;
|
|
1139
|
+
result.text = originalTextPromise.then((text) => {
|
|
1140
|
+
trackCompletion(text).catch(() => {
|
|
1141
|
+
});
|
|
1142
|
+
return text;
|
|
1143
|
+
}).catch((err) => {
|
|
1144
|
+
trackCompletion("", err instanceof Error ? err : new Error(String(err))).catch(() => {
|
|
1145
|
+
});
|
|
1146
|
+
throw err;
|
|
1147
|
+
});
|
|
900
1148
|
} else {
|
|
901
|
-
|
|
1149
|
+
const originalTextStream = result.textStream;
|
|
1150
|
+
let fullText = "";
|
|
1151
|
+
result.textStream = (async function* () {
|
|
1152
|
+
try {
|
|
1153
|
+
for await (const chunk of originalTextStream) {
|
|
1154
|
+
fullText += chunk;
|
|
1155
|
+
yield chunk;
|
|
1156
|
+
}
|
|
1157
|
+
await trackCompletion(fullText);
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
await trackCompletion(
|
|
1160
|
+
"",
|
|
1161
|
+
error instanceof Error ? error : new Error(String(error))
|
|
1162
|
+
);
|
|
1163
|
+
throw error;
|
|
1164
|
+
}
|
|
1165
|
+
})();
|
|
902
1166
|
}
|
|
903
|
-
|
|
904
|
-
|
|
1167
|
+
return result;
|
|
1168
|
+
};
|
|
905
1169
|
}
|
|
906
|
-
function wrapGenerateObject(originalFn, client) {
|
|
1170
|
+
function wrapGenerateObject(originalFn, client, config) {
|
|
907
1171
|
return async (params) => {
|
|
908
1172
|
const startTime = Date.now();
|
|
909
1173
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
910
1174
|
const input = extractInput(params);
|
|
911
1175
|
const sessionId = await client.createSession({
|
|
912
1176
|
name: `generateObject: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
913
|
-
agentName:
|
|
914
|
-
userId:
|
|
1177
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
1178
|
+
userId: config.userId ?? "anonymous",
|
|
1179
|
+
convoId: config.convoId,
|
|
915
1180
|
metadata: {
|
|
916
1181
|
model: modelId,
|
|
917
1182
|
provider,
|
|
@@ -925,10 +1190,11 @@ function wrapGenerateObject(originalFn, client) {
|
|
|
925
1190
|
try {
|
|
926
1191
|
const result = await originalFn(params);
|
|
927
1192
|
const durationMs = Date.now() - startTime;
|
|
1193
|
+
const resolvedModelId = result.response?.modelId || modelId;
|
|
928
1194
|
const promptTokens = result.usage?.promptTokens || 0;
|
|
929
1195
|
const completionTokens = result.usage?.completionTokens || 0;
|
|
930
1196
|
const totalTokens = result.usage?.totalTokens || promptTokens + completionTokens;
|
|
931
|
-
const cost = calculateCostForCall(provider,
|
|
1197
|
+
const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);
|
|
932
1198
|
await client.completeSession({
|
|
933
1199
|
sessionId,
|
|
934
1200
|
success: true,
|
|
@@ -957,38 +1223,51 @@ function wrapGenerateObject(originalFn, client) {
|
|
|
957
1223
|
}
|
|
958
1224
|
};
|
|
959
1225
|
}
|
|
960
|
-
function wrapStreamObject(originalFn, client) {
|
|
1226
|
+
function wrapStreamObject(originalFn, client, config) {
|
|
961
1227
|
return (params) => {
|
|
962
1228
|
const startTime = Date.now();
|
|
963
1229
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
964
1230
|
const input = extractInput(params);
|
|
965
|
-
const sessionPromise =
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1231
|
+
const sessionPromise = (async () => {
|
|
1232
|
+
try {
|
|
1233
|
+
const id = await client.createSession({
|
|
1234
|
+
name: `streamObject: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
1235
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
1236
|
+
userId: config.userId ?? "anonymous",
|
|
1237
|
+
convoId: config.convoId,
|
|
1238
|
+
metadata: {
|
|
1239
|
+
model: modelId,
|
|
1240
|
+
provider,
|
|
1241
|
+
function: "streamObject"
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
if (id) {
|
|
1245
|
+
client.setInput(id, input).catch(() => {
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
return id;
|
|
1249
|
+
} catch {
|
|
1250
|
+
return null;
|
|
973
1251
|
}
|
|
974
|
-
})
|
|
975
|
-
if (id) client.setInput(id, input);
|
|
976
|
-
return id;
|
|
977
|
-
});
|
|
1252
|
+
})();
|
|
978
1253
|
const result = originalFn(params);
|
|
979
1254
|
if (result.object) {
|
|
980
1255
|
const originalObjectPromise = result.object;
|
|
981
1256
|
result.object = originalObjectPromise.then(async (obj) => {
|
|
982
1257
|
const durationMs = Date.now() - startTime;
|
|
983
|
-
const
|
|
984
|
-
if (
|
|
985
|
-
|
|
1258
|
+
const sid = await sessionPromise;
|
|
1259
|
+
if (sid) {
|
|
1260
|
+
let usage;
|
|
1261
|
+
try {
|
|
1262
|
+
usage = result.usage ? await result.usage : void 0;
|
|
1263
|
+
} catch {
|
|
1264
|
+
}
|
|
986
1265
|
const promptTokens = usage?.promptTokens || 0;
|
|
987
1266
|
const completionTokens = usage?.completionTokens || 0;
|
|
988
1267
|
const totalTokens = usage?.totalTokens || promptTokens + completionTokens;
|
|
989
1268
|
const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);
|
|
990
1269
|
await client.completeSession({
|
|
991
|
-
sessionId,
|
|
1270
|
+
sessionId: sid,
|
|
992
1271
|
success: true,
|
|
993
1272
|
output: JSON.stringify(obj),
|
|
994
1273
|
durationMs,
|
|
@@ -1001,15 +1280,15 @@ function wrapStreamObject(originalFn, client) {
|
|
|
1001
1280
|
return obj;
|
|
1002
1281
|
}).catch(async (error) => {
|
|
1003
1282
|
const durationMs = Date.now() - startTime;
|
|
1004
|
-
const
|
|
1005
|
-
if (
|
|
1283
|
+
const sid = await sessionPromise;
|
|
1284
|
+
if (sid) {
|
|
1006
1285
|
await client.trackError({
|
|
1007
|
-
sessionId,
|
|
1286
|
+
sessionId: sid,
|
|
1008
1287
|
errorType: error instanceof Error ? error.name : "Error",
|
|
1009
1288
|
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
1010
1289
|
});
|
|
1011
1290
|
await client.completeSession({
|
|
1012
|
-
sessionId,
|
|
1291
|
+
sessionId: sid,
|
|
1013
1292
|
success: false,
|
|
1014
1293
|
failureReason: error instanceof Error ? error.message : "Unknown error",
|
|
1015
1294
|
durationMs
|
|
@@ -1021,14 +1300,27 @@ function wrapStreamObject(originalFn, client) {
|
|
|
1021
1300
|
return result;
|
|
1022
1301
|
};
|
|
1023
1302
|
}
|
|
1024
|
-
function wrapAISDK(ai) {
|
|
1025
|
-
const client = getClient2();
|
|
1303
|
+
function wrapAISDK(ai, options) {
|
|
1304
|
+
const client = options?.client ?? getClient2();
|
|
1305
|
+
const config = {
|
|
1306
|
+
defaultAgent: options?.defaultAgent ?? _globalConfig.defaultAgent,
|
|
1307
|
+
userId: options?.userId ?? _globalConfig.userId,
|
|
1308
|
+
convoId: options?.convoId ?? _globalConfig.convoId
|
|
1309
|
+
};
|
|
1026
1310
|
return {
|
|
1027
|
-
generateText: ai.generateText ? wrapGenerateText(ai.generateText, client) : wrapGenerateText(
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1311
|
+
generateText: ai.generateText ? wrapGenerateText(ai.generateText, client, config) : wrapGenerateText(
|
|
1312
|
+
() => Promise.reject(new Error("generateText not available")),
|
|
1313
|
+
client,
|
|
1314
|
+
config
|
|
1315
|
+
),
|
|
1316
|
+
streamText: ai.streamText ? wrapStreamText(ai.streamText, client, config) : wrapStreamText(() => ({ textStream: (async function* () {
|
|
1317
|
+
})() }), client, config),
|
|
1318
|
+
generateObject: ai.generateObject ? wrapGenerateObject(ai.generateObject, client, config) : wrapGenerateObject(
|
|
1319
|
+
() => Promise.reject(new Error("generateObject not available")),
|
|
1320
|
+
client,
|
|
1321
|
+
config
|
|
1322
|
+
),
|
|
1323
|
+
streamObject: ai.streamObject ? wrapStreamObject(ai.streamObject, client, config) : wrapStreamObject(() => ({}), client, config)
|
|
1032
1324
|
};
|
|
1033
1325
|
}
|
|
1034
1326
|
|
|
@@ -2072,7 +2364,12 @@ export {
|
|
|
2072
2364
|
getSessionContext,
|
|
2073
2365
|
getSystemPrompt,
|
|
2074
2366
|
getVariantName,
|
|
2367
|
+
hashValue,
|
|
2075
2368
|
isExperimentMode,
|
|
2369
|
+
redactPayload,
|
|
2370
|
+
redactString,
|
|
2371
|
+
redactValue,
|
|
2372
|
+
replaceMatch,
|
|
2076
2373
|
sentrial,
|
|
2077
2374
|
setClient,
|
|
2078
2375
|
setDefaultClient,
|