@sentrial/sdk 0.2.0 → 0.3.1
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 +486 -187
- 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 +481 -187
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -47,7 +47,12 @@ __export(index_exports, {
|
|
|
47
47
|
getSessionContext: () => getSessionContext,
|
|
48
48
|
getSystemPrompt: () => getSystemPrompt,
|
|
49
49
|
getVariantName: () => getVariantName,
|
|
50
|
+
hashValue: () => hashValue,
|
|
50
51
|
isExperimentMode: () => isExperimentMode,
|
|
52
|
+
redactPayload: () => redactPayload,
|
|
53
|
+
redactString: () => redactString,
|
|
54
|
+
redactValue: () => redactValue,
|
|
55
|
+
replaceMatch: () => replaceMatch,
|
|
51
56
|
sentrial: () => sentrial,
|
|
52
57
|
setClient: () => setClient,
|
|
53
58
|
setDefaultClient: () => setDefaultClient,
|
|
@@ -132,26 +137,126 @@ var ValidationError = class extends SentrialError {
|
|
|
132
137
|
}
|
|
133
138
|
};
|
|
134
139
|
|
|
140
|
+
// src/redact.ts
|
|
141
|
+
var import_crypto = require("crypto");
|
|
142
|
+
var DEFAULT_FIELDS = [
|
|
143
|
+
"userInput",
|
|
144
|
+
"assistantOutput",
|
|
145
|
+
"toolInput",
|
|
146
|
+
"toolOutput"
|
|
147
|
+
];
|
|
148
|
+
var DEFAULT_BUILTIN = {
|
|
149
|
+
emails: true,
|
|
150
|
+
phones: true,
|
|
151
|
+
ssns: true,
|
|
152
|
+
creditCards: true,
|
|
153
|
+
ipAddresses: true
|
|
154
|
+
};
|
|
155
|
+
var EMAIL_PATTERN = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
|
|
156
|
+
var PHONE_PATTERN = /(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g;
|
|
157
|
+
var SSN_PATTERN = /\b\d{3}-\d{2}-\d{4}\b/g;
|
|
158
|
+
var CREDIT_CARD_PATTERN = /\b(?:\d[-\s]?){13,19}\b/g;
|
|
159
|
+
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;
|
|
160
|
+
var BUILTIN_PATTERNS = {
|
|
161
|
+
emails: { pattern: EMAIL_PATTERN, label: "EMAIL" },
|
|
162
|
+
phones: { pattern: PHONE_PATTERN, label: "PHONE" },
|
|
163
|
+
ssns: { pattern: SSN_PATTERN, label: "SSN" },
|
|
164
|
+
creditCards: { pattern: CREDIT_CARD_PATTERN, label: "CREDIT_CARD" },
|
|
165
|
+
ipAddresses: { pattern: IP_ADDRESS_PATTERN, label: "IP_ADDRESS" }
|
|
166
|
+
};
|
|
167
|
+
function hashValue(value) {
|
|
168
|
+
return (0, import_crypto.createHash)("sha256").update(value).digest("hex").slice(0, 6);
|
|
169
|
+
}
|
|
170
|
+
function replaceMatch(match, label, mode) {
|
|
171
|
+
switch (mode) {
|
|
172
|
+
case "label":
|
|
173
|
+
return `[${label}]`;
|
|
174
|
+
case "hash":
|
|
175
|
+
return `[PII:${hashValue(match)}]`;
|
|
176
|
+
case "remove":
|
|
177
|
+
return "";
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function redactString(value, mode, builtinPatterns, customPatterns) {
|
|
181
|
+
let result = value;
|
|
182
|
+
for (const [key, config] of Object.entries(BUILTIN_PATTERNS)) {
|
|
183
|
+
if (builtinPatterns[key]) {
|
|
184
|
+
const regex = new RegExp(config.pattern.source, config.pattern.flags);
|
|
185
|
+
result = result.replace(regex, (match) => replaceMatch(match, config.label, mode));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const custom of customPatterns) {
|
|
189
|
+
const regex = new RegExp(custom.pattern.source, custom.pattern.flags);
|
|
190
|
+
result = result.replace(regex, (match) => replaceMatch(match, custom.label, mode));
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
function redactValue(value, mode, builtinPatterns, customPatterns) {
|
|
195
|
+
if (typeof value === "string") {
|
|
196
|
+
return redactString(value, mode, builtinPatterns, customPatterns);
|
|
197
|
+
}
|
|
198
|
+
if (Array.isArray(value)) {
|
|
199
|
+
return value.map((item) => redactValue(item, mode, builtinPatterns, customPatterns));
|
|
200
|
+
}
|
|
201
|
+
if (value !== null && typeof value === "object") {
|
|
202
|
+
const result = {};
|
|
203
|
+
for (const [key, val] of Object.entries(value)) {
|
|
204
|
+
result[key] = redactValue(val, mode, builtinPatterns, customPatterns);
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
210
|
+
function redactPayload(payload, config) {
|
|
211
|
+
if (!config.enabled) {
|
|
212
|
+
return payload;
|
|
213
|
+
}
|
|
214
|
+
const mode = config.mode ?? "label";
|
|
215
|
+
const fields = config.fields ?? DEFAULT_FIELDS;
|
|
216
|
+
const builtinPatterns = {
|
|
217
|
+
...DEFAULT_BUILTIN,
|
|
218
|
+
...config.builtinPatterns
|
|
219
|
+
};
|
|
220
|
+
const customPatterns = config.customPatterns ?? [];
|
|
221
|
+
const result = { ...payload };
|
|
222
|
+
for (const field of fields) {
|
|
223
|
+
if (field in result && result[field] !== void 0 && result[field] !== null) {
|
|
224
|
+
result[field] = redactValue(result[field], mode, builtinPatterns, customPatterns);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
|
|
135
230
|
// src/cost.ts
|
|
136
231
|
var OPENAI_PRICING = {
|
|
137
232
|
"gpt-5.2": { input: 5, output: 15 },
|
|
138
233
|
"gpt-5": { input: 4, output: 12 },
|
|
234
|
+
"gpt-4.1": { input: 2, output: 8 },
|
|
235
|
+
"gpt-4.1-mini": { input: 0.4, output: 1.6 },
|
|
236
|
+
"gpt-4.1-nano": { input: 0.1, output: 0.4 },
|
|
139
237
|
"gpt-4o": { input: 2.5, output: 10 },
|
|
140
238
|
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
141
239
|
"gpt-4-turbo": { input: 10, output: 30 },
|
|
142
240
|
"gpt-4": { input: 30, output: 60 },
|
|
143
241
|
"gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
242
|
+
"o4-mini": { input: 1.1, output: 4.4 },
|
|
144
243
|
"o3": { input: 10, output: 40 },
|
|
145
|
-
"o3-mini": { input:
|
|
244
|
+
"o3-mini": { input: 1.1, output: 4.4 },
|
|
245
|
+
"o3-pro": { input: 20, output: 80 },
|
|
246
|
+
"o1": { input: 15, output: 60 },
|
|
146
247
|
"o1-preview": { input: 15, output: 60 },
|
|
147
248
|
"o1-mini": { input: 3, output: 12 }
|
|
148
249
|
};
|
|
149
250
|
var ANTHROPIC_PRICING = {
|
|
251
|
+
"claude-opus-4": { input: 15, output: 75 },
|
|
252
|
+
"claude-sonnet-4": { input: 3, output: 15 },
|
|
150
253
|
"claude-4.5-opus": { input: 20, output: 100 },
|
|
151
254
|
"claude-4.5-sonnet": { input: 4, output: 20 },
|
|
152
255
|
"claude-4-opus": { input: 18, output: 90 },
|
|
153
256
|
"claude-4-sonnet": { input: 3.5, output: 17.5 },
|
|
257
|
+
"claude-3-7-sonnet": { input: 3, output: 15 },
|
|
154
258
|
"claude-3-5-sonnet": { input: 3, output: 15 },
|
|
259
|
+
"claude-3-5-haiku": { input: 0.8, output: 4 },
|
|
155
260
|
"claude-3-opus": { input: 15, output: 75 },
|
|
156
261
|
"claude-3-sonnet": { input: 3, output: 15 },
|
|
157
262
|
"claude-3-haiku": { input: 0.25, output: 1.25 }
|
|
@@ -222,11 +327,75 @@ var SentrialClient = class {
|
|
|
222
327
|
apiUrl;
|
|
223
328
|
apiKey;
|
|
224
329
|
failSilently;
|
|
330
|
+
piiConfig;
|
|
331
|
+
piiConfigNeedsHydration = false;
|
|
332
|
+
piiHydrationPromise;
|
|
225
333
|
currentState = {};
|
|
226
334
|
constructor(config = {}) {
|
|
227
335
|
this.apiUrl = (config.apiUrl ?? (typeof process !== "undefined" ? process.env?.SENTRIAL_API_URL : void 0) ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
228
336
|
this.apiKey = config.apiKey ?? (typeof process !== "undefined" ? process.env?.SENTRIAL_API_KEY : void 0);
|
|
229
337
|
this.failSilently = config.failSilently ?? true;
|
|
338
|
+
if (config.pii === true) {
|
|
339
|
+
this.piiConfig = { enabled: true };
|
|
340
|
+
this.piiConfigNeedsHydration = true;
|
|
341
|
+
} else if (config.pii && typeof config.pii === "object") {
|
|
342
|
+
this.piiConfig = config.pii;
|
|
343
|
+
this.piiConfigNeedsHydration = false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Fetch the organization's PII config from the server.
|
|
348
|
+
*
|
|
349
|
+
* Called lazily on the first request when `pii: true` was passed to the constructor.
|
|
350
|
+
* Uses a single shared promise so concurrent requests don't trigger duplicate fetches.
|
|
351
|
+
*/
|
|
352
|
+
async hydratePiiConfig() {
|
|
353
|
+
if (!this.piiConfigNeedsHydration) return;
|
|
354
|
+
if (this.piiHydrationPromise) {
|
|
355
|
+
await this.piiHydrationPromise;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
this.piiHydrationPromise = (async () => {
|
|
359
|
+
try {
|
|
360
|
+
const headers = {};
|
|
361
|
+
if (this.apiKey) {
|
|
362
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
363
|
+
}
|
|
364
|
+
const controller = new AbortController();
|
|
365
|
+
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
366
|
+
let response;
|
|
367
|
+
try {
|
|
368
|
+
response = await fetch(`${this.apiUrl}/api/sdk/pii-config`, {
|
|
369
|
+
method: "GET",
|
|
370
|
+
headers,
|
|
371
|
+
signal: controller.signal
|
|
372
|
+
});
|
|
373
|
+
} finally {
|
|
374
|
+
clearTimeout(timeoutId);
|
|
375
|
+
}
|
|
376
|
+
if (response.ok) {
|
|
377
|
+
const data = await response.json();
|
|
378
|
+
if (data.config) {
|
|
379
|
+
this.piiConfig = {
|
|
380
|
+
enabled: data.config.enabled,
|
|
381
|
+
mode: data.config.mode,
|
|
382
|
+
fields: data.config.fields,
|
|
383
|
+
builtinPatterns: data.config.builtinPatterns,
|
|
384
|
+
customPatterns: (data.config.customPatterns || []).map(
|
|
385
|
+
(cp) => ({
|
|
386
|
+
pattern: new RegExp(cp.pattern, "g"),
|
|
387
|
+
label: cp.label
|
|
388
|
+
})
|
|
389
|
+
),
|
|
390
|
+
enhancedDetection: data.config.enhancedDetection
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
} catch {
|
|
395
|
+
}
|
|
396
|
+
this.piiConfigNeedsHydration = false;
|
|
397
|
+
})();
|
|
398
|
+
await this.piiHydrationPromise;
|
|
230
399
|
}
|
|
231
400
|
/**
|
|
232
401
|
* Make an HTTP request with retry logic and exponential backoff.
|
|
@@ -235,6 +404,9 @@ var SentrialClient = class {
|
|
|
235
404
|
* Up to MAX_RETRIES attempts with exponential backoff.
|
|
236
405
|
*/
|
|
237
406
|
async safeRequest(method, url, body) {
|
|
407
|
+
if (this.piiConfigNeedsHydration) {
|
|
408
|
+
await this.hydratePiiConfig();
|
|
409
|
+
}
|
|
238
410
|
let lastError;
|
|
239
411
|
let backoff = INITIAL_BACKOFF_MS;
|
|
240
412
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
@@ -245,6 +417,7 @@ var SentrialClient = class {
|
|
|
245
417
|
if (this.apiKey) {
|
|
246
418
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
247
419
|
}
|
|
420
|
+
const finalBody = this.piiConfig && body && typeof body === "object" ? redactPayload(body, this.piiConfig) : body;
|
|
248
421
|
const controller = new AbortController();
|
|
249
422
|
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
250
423
|
let response;
|
|
@@ -252,7 +425,7 @@ var SentrialClient = class {
|
|
|
252
425
|
response = await fetch(url, {
|
|
253
426
|
method,
|
|
254
427
|
headers,
|
|
255
|
-
body:
|
|
428
|
+
body: finalBody ? JSON.stringify(finalBody) : void 0,
|
|
256
429
|
signal: controller.signal
|
|
257
430
|
});
|
|
258
431
|
} finally {
|
|
@@ -324,6 +497,9 @@ var SentrialClient = class {
|
|
|
324
497
|
if (params.parentSessionId) {
|
|
325
498
|
payload.parentSessionId = params.parentSessionId;
|
|
326
499
|
}
|
|
500
|
+
if (params.convoId) {
|
|
501
|
+
payload.convoId = params.convoId;
|
|
502
|
+
}
|
|
327
503
|
const response = await this.safeRequest(
|
|
328
504
|
"POST",
|
|
329
505
|
`${this.apiUrl}/api/sdk/sessions`,
|
|
@@ -530,6 +706,7 @@ var SentrialClient = class {
|
|
|
530
706
|
name: `${params.event}:${eventId.slice(0, 8)}`,
|
|
531
707
|
agentName: params.event,
|
|
532
708
|
userId: params.userId,
|
|
709
|
+
convoId: params.convoId,
|
|
533
710
|
metadata: fullMetadata
|
|
534
711
|
});
|
|
535
712
|
if (params.input) {
|
|
@@ -694,7 +871,8 @@ function configureVercel(config) {
|
|
|
694
871
|
});
|
|
695
872
|
_globalConfig = {
|
|
696
873
|
defaultAgent: config.defaultAgent,
|
|
697
|
-
userId: config.userId
|
|
874
|
+
userId: config.userId,
|
|
875
|
+
convoId: config.convoId
|
|
698
876
|
};
|
|
699
877
|
}
|
|
700
878
|
function getClient2() {
|
|
@@ -709,11 +887,15 @@ function extractModelInfo(model) {
|
|
|
709
887
|
return { modelId, provider };
|
|
710
888
|
}
|
|
711
889
|
function guessProvider(modelId) {
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
if (
|
|
715
|
-
if (
|
|
716
|
-
if (
|
|
890
|
+
const id = modelId.toLowerCase();
|
|
891
|
+
if (id.includes("gpt") || id.includes("o1") || id.includes("o3") || id.includes("o4") || id.startsWith("chatgpt")) return "openai";
|
|
892
|
+
if (id.includes("claude")) return "anthropic";
|
|
893
|
+
if (id.includes("gemini")) return "google";
|
|
894
|
+
if (id.includes("mistral") || id.includes("mixtral") || id.includes("codestral") || id.includes("pixtral")) return "mistral";
|
|
895
|
+
if (id.includes("llama")) return "meta";
|
|
896
|
+
if (id.includes("deepseek")) return "deepseek";
|
|
897
|
+
if (id.includes("command")) return "cohere";
|
|
898
|
+
if (id.includes("qwen")) return "alibaba";
|
|
717
899
|
return "unknown";
|
|
718
900
|
}
|
|
719
901
|
function calculateCostForCall(provider, modelId, promptTokens, completionTokens) {
|
|
@@ -725,6 +907,12 @@ function calculateCostForCall(provider, modelId, promptTokens, completionTokens)
|
|
|
725
907
|
return calculateAnthropicCost(params);
|
|
726
908
|
case "google":
|
|
727
909
|
return calculateGoogleCost(params);
|
|
910
|
+
case "deepseek":
|
|
911
|
+
return promptTokens / 1e6 * 0.27 + completionTokens / 1e6 * 1.1;
|
|
912
|
+
case "cohere":
|
|
913
|
+
return promptTokens / 1e6 * 0.5 + completionTokens / 1e6 * 1.5;
|
|
914
|
+
case "mistral":
|
|
915
|
+
return promptTokens / 1e6 * 2 + completionTokens / 1e6 * 6;
|
|
728
916
|
default:
|
|
729
917
|
return promptTokens * 3e-6 + completionTokens * 6e-6;
|
|
730
918
|
}
|
|
@@ -733,7 +921,10 @@ function extractInput(params) {
|
|
|
733
921
|
if (params.prompt) return params.prompt;
|
|
734
922
|
if (params.messages && params.messages.length > 0) {
|
|
735
923
|
const lastUserMessage = [...params.messages].reverse().find((m) => m.role === "user");
|
|
736
|
-
|
|
924
|
+
if (lastUserMessage) {
|
|
925
|
+
return typeof lastUserMessage.content === "string" ? lastUserMessage.content : JSON.stringify(lastUserMessage.content);
|
|
926
|
+
}
|
|
927
|
+
return JSON.stringify(params.messages);
|
|
737
928
|
}
|
|
738
929
|
return "";
|
|
739
930
|
}
|
|
@@ -750,23 +941,25 @@ function wrapTools(tools, sessionId, client) {
|
|
|
750
941
|
try {
|
|
751
942
|
const result = await originalExecute(...args);
|
|
752
943
|
const durationMs = Date.now() - startTime;
|
|
753
|
-
|
|
944
|
+
client.trackToolCall({
|
|
754
945
|
sessionId,
|
|
755
946
|
toolName,
|
|
756
947
|
toolInput: args[0],
|
|
757
948
|
toolOutput: result,
|
|
758
949
|
reasoning: `Tool executed in ${durationMs}ms`
|
|
950
|
+
}).catch(() => {
|
|
759
951
|
});
|
|
760
952
|
return result;
|
|
761
953
|
} catch (error) {
|
|
762
954
|
const durationMs = Date.now() - startTime;
|
|
763
|
-
|
|
955
|
+
client.trackToolCall({
|
|
764
956
|
sessionId,
|
|
765
957
|
toolName,
|
|
766
958
|
toolInput: args[0],
|
|
767
959
|
toolOutput: {},
|
|
768
960
|
toolError: { message: error instanceof Error ? error.message : "Unknown error" },
|
|
769
961
|
reasoning: `Tool failed after ${durationMs}ms`
|
|
962
|
+
}).catch(() => {
|
|
770
963
|
});
|
|
771
964
|
throw error;
|
|
772
965
|
}
|
|
@@ -778,19 +971,68 @@ function wrapTools(tools, sessionId, client) {
|
|
|
778
971
|
}
|
|
779
972
|
return wrappedTools;
|
|
780
973
|
}
|
|
781
|
-
function
|
|
974
|
+
function wrapToolsAsync(tools, sessionPromise, client) {
|
|
975
|
+
const wrappedTools = {};
|
|
976
|
+
for (const [toolName, tool] of Object.entries(tools)) {
|
|
977
|
+
if (typeof tool.execute === "function") {
|
|
978
|
+
const originalExecute = tool.execute;
|
|
979
|
+
wrappedTools[toolName] = {
|
|
980
|
+
...tool,
|
|
981
|
+
execute: async (...args) => {
|
|
982
|
+
const startTime = Date.now();
|
|
983
|
+
const sid = await sessionPromise;
|
|
984
|
+
try {
|
|
985
|
+
const result = await originalExecute(...args);
|
|
986
|
+
const durationMs = Date.now() - startTime;
|
|
987
|
+
if (sid) {
|
|
988
|
+
client.trackToolCall({
|
|
989
|
+
sessionId: sid,
|
|
990
|
+
toolName,
|
|
991
|
+
toolInput: args[0],
|
|
992
|
+
toolOutput: result,
|
|
993
|
+
reasoning: `Tool executed in ${durationMs}ms`
|
|
994
|
+
}).catch(() => {
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
return result;
|
|
998
|
+
} catch (error) {
|
|
999
|
+
const durationMs = Date.now() - startTime;
|
|
1000
|
+
if (sid) {
|
|
1001
|
+
client.trackToolCall({
|
|
1002
|
+
sessionId: sid,
|
|
1003
|
+
toolName,
|
|
1004
|
+
toolInput: args[0],
|
|
1005
|
+
toolOutput: {},
|
|
1006
|
+
toolError: { message: error instanceof Error ? error.message : "Unknown error" },
|
|
1007
|
+
reasoning: `Tool failed after ${durationMs}ms`
|
|
1008
|
+
}).catch(() => {
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
throw error;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
} else {
|
|
1016
|
+
wrappedTools[toolName] = tool;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return wrappedTools;
|
|
1020
|
+
}
|
|
1021
|
+
function wrapGenerateText(originalFn, client, config) {
|
|
782
1022
|
return async (params) => {
|
|
783
1023
|
const startTime = Date.now();
|
|
784
1024
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
785
1025
|
const input = extractInput(params);
|
|
786
1026
|
const sessionId = await client.createSession({
|
|
787
1027
|
name: `generateText: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
788
|
-
agentName:
|
|
789
|
-
userId:
|
|
1028
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
1029
|
+
userId: config.userId ?? "anonymous",
|
|
1030
|
+
convoId: config.convoId,
|
|
790
1031
|
metadata: {
|
|
791
1032
|
model: modelId,
|
|
792
1033
|
provider,
|
|
793
|
-
function: "generateText"
|
|
1034
|
+
function: "generateText",
|
|
1035
|
+
...params.maxSteps ? { maxSteps: params.maxSteps } : {}
|
|
794
1036
|
}
|
|
795
1037
|
});
|
|
796
1038
|
if (!sessionId) {
|
|
@@ -804,23 +1046,46 @@ function wrapGenerateText(originalFn, client) {
|
|
|
804
1046
|
try {
|
|
805
1047
|
const result = await originalFn(wrappedParams);
|
|
806
1048
|
const durationMs = Date.now() - startTime;
|
|
1049
|
+
const resolvedModelId = result.response?.modelId || modelId;
|
|
807
1050
|
const promptTokens = result.usage?.promptTokens || 0;
|
|
808
1051
|
const completionTokens = result.usage?.completionTokens || 0;
|
|
809
1052
|
const totalTokens = result.usage?.totalTokens || promptTokens + completionTokens;
|
|
810
|
-
const cost = calculateCostForCall(provider,
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1053
|
+
const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);
|
|
1054
|
+
const steps = result.steps;
|
|
1055
|
+
if (steps && steps.length >= 1) {
|
|
1056
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1057
|
+
const step = steps[i];
|
|
1058
|
+
await client.trackEvent({
|
|
1059
|
+
sessionId,
|
|
1060
|
+
eventType: "llm_call",
|
|
1061
|
+
eventData: {
|
|
1062
|
+
model: resolvedModelId,
|
|
1063
|
+
provider,
|
|
1064
|
+
step: i + 1,
|
|
1065
|
+
total_steps: steps.length,
|
|
1066
|
+
prompt_tokens: step.usage?.promptTokens || 0,
|
|
1067
|
+
completion_tokens: step.usage?.completionTokens || 0,
|
|
1068
|
+
total_tokens: step.usage?.totalTokens || 0,
|
|
1069
|
+
finish_reason: step.finishReason,
|
|
1070
|
+
tool_calls: step.toolCalls?.map((tc) => tc.toolName)
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
822
1073
|
}
|
|
823
|
-
}
|
|
1074
|
+
} else {
|
|
1075
|
+
await client.trackEvent({
|
|
1076
|
+
sessionId,
|
|
1077
|
+
eventType: "llm_call",
|
|
1078
|
+
eventData: {
|
|
1079
|
+
model: resolvedModelId,
|
|
1080
|
+
provider,
|
|
1081
|
+
prompt_tokens: promptTokens,
|
|
1082
|
+
completion_tokens: completionTokens,
|
|
1083
|
+
total_tokens: totalTokens,
|
|
1084
|
+
finish_reason: result.finishReason,
|
|
1085
|
+
tool_calls: result.toolCalls?.map((tc) => tc.toolName)
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
824
1089
|
await client.completeSession({
|
|
825
1090
|
sessionId,
|
|
826
1091
|
success: true,
|
|
@@ -849,134 +1114,140 @@ function wrapGenerateText(originalFn, client) {
|
|
|
849
1114
|
}
|
|
850
1115
|
};
|
|
851
1116
|
}
|
|
852
|
-
function wrapStreamText(originalFn, client) {
|
|
1117
|
+
function wrapStreamText(originalFn, client, config) {
|
|
853
1118
|
return (params) => {
|
|
854
1119
|
const startTime = Date.now();
|
|
855
1120
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
856
1121
|
const input = extractInput(params);
|
|
857
1122
|
let sessionId = null;
|
|
858
|
-
const sessionPromise =
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1123
|
+
const sessionPromise = (async () => {
|
|
1124
|
+
try {
|
|
1125
|
+
const id = await client.createSession({
|
|
1126
|
+
name: `streamText: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
1127
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
1128
|
+
userId: config.userId ?? "anonymous",
|
|
1129
|
+
convoId: config.convoId,
|
|
1130
|
+
metadata: {
|
|
1131
|
+
model: modelId,
|
|
1132
|
+
provider,
|
|
1133
|
+
function: "streamText"
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
sessionId = id;
|
|
1137
|
+
if (id) {
|
|
1138
|
+
client.setInput(id, input).catch(() => {
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
return id;
|
|
1142
|
+
} catch {
|
|
1143
|
+
return null;
|
|
866
1144
|
}
|
|
867
|
-
})
|
|
868
|
-
sessionId = id;
|
|
869
|
-
if (id) client.setInput(id, input);
|
|
870
|
-
return id;
|
|
871
|
-
});
|
|
1145
|
+
})();
|
|
872
1146
|
const wrappedParams = {
|
|
873
1147
|
...params,
|
|
874
1148
|
tools: params.tools ? wrapToolsAsync(params.tools, sessionPromise, client) : void 0
|
|
875
1149
|
};
|
|
876
1150
|
const result = originalFn(wrappedParams);
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1151
|
+
let tracked = false;
|
|
1152
|
+
async function trackCompletion(fullText, error) {
|
|
1153
|
+
if (tracked) return;
|
|
1154
|
+
tracked = true;
|
|
1155
|
+
const durationMs = Date.now() - startTime;
|
|
1156
|
+
const sid = sessionId || await sessionPromise;
|
|
1157
|
+
if (!sid) return;
|
|
1158
|
+
if (error) {
|
|
1159
|
+
await client.trackError({
|
|
1160
|
+
sessionId: sid,
|
|
1161
|
+
errorType: error.name || "Error",
|
|
1162
|
+
errorMessage: error.message || "Unknown error"
|
|
1163
|
+
});
|
|
1164
|
+
await client.completeSession({
|
|
1165
|
+
sessionId: sid,
|
|
1166
|
+
success: false,
|
|
1167
|
+
failureReason: error.message || "Unknown error",
|
|
1168
|
+
durationMs
|
|
1169
|
+
});
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
let usage;
|
|
880
1173
|
try {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
yield chunk;
|
|
884
|
-
}
|
|
885
|
-
const durationMs = Date.now() - startTime;
|
|
886
|
-
const sid = sessionId || await sessionPromise;
|
|
887
|
-
if (sid) {
|
|
888
|
-
const usage = result.usage ? await result.usage : void 0;
|
|
889
|
-
const promptTokens = usage?.promptTokens || 0;
|
|
890
|
-
const completionTokens = usage?.completionTokens || 0;
|
|
891
|
-
const totalTokens = usage?.totalTokens || promptTokens + completionTokens;
|
|
892
|
-
const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);
|
|
893
|
-
await client.completeSession({
|
|
894
|
-
sessionId: sid,
|
|
895
|
-
success: true,
|
|
896
|
-
output: fullText,
|
|
897
|
-
durationMs,
|
|
898
|
-
estimatedCost: cost,
|
|
899
|
-
promptTokens,
|
|
900
|
-
completionTokens,
|
|
901
|
-
totalTokens
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
} catch (error) {
|
|
905
|
-
const durationMs = Date.now() - startTime;
|
|
906
|
-
const sid = sessionId || await sessionPromise;
|
|
907
|
-
if (sid) {
|
|
908
|
-
await client.trackError({
|
|
909
|
-
sessionId: sid,
|
|
910
|
-
errorType: error instanceof Error ? error.name : "Error",
|
|
911
|
-
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
912
|
-
});
|
|
913
|
-
await client.completeSession({
|
|
914
|
-
sessionId: sid,
|
|
915
|
-
success: false,
|
|
916
|
-
failureReason: error instanceof Error ? error.message : "Unknown error",
|
|
917
|
-
durationMs
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
throw error;
|
|
1174
|
+
usage = result.usage ? await result.usage : void 0;
|
|
1175
|
+
} catch {
|
|
921
1176
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1177
|
+
const promptTokens = usage?.promptTokens || 0;
|
|
1178
|
+
const completionTokens = usage?.completionTokens || 0;
|
|
1179
|
+
const totalTokens = usage?.totalTokens || promptTokens + completionTokens;
|
|
1180
|
+
const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);
|
|
1181
|
+
await client.trackEvent({
|
|
1182
|
+
sessionId: sid,
|
|
1183
|
+
eventType: "llm_call",
|
|
1184
|
+
eventData: {
|
|
1185
|
+
model: modelId,
|
|
1186
|
+
provider,
|
|
1187
|
+
prompt_tokens: promptTokens,
|
|
1188
|
+
completion_tokens: completionTokens,
|
|
1189
|
+
total_tokens: totalTokens
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
await client.completeSession({
|
|
1193
|
+
sessionId: sid,
|
|
1194
|
+
success: true,
|
|
1195
|
+
output: fullText,
|
|
1196
|
+
durationMs,
|
|
1197
|
+
estimatedCost: cost,
|
|
1198
|
+
promptTokens,
|
|
1199
|
+
completionTokens,
|
|
1200
|
+
totalTokens
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
const textProp = result.text;
|
|
1204
|
+
if (typeof textProp === "string") {
|
|
1205
|
+
trackCompletion(textProp).catch(() => {
|
|
1206
|
+
});
|
|
1207
|
+
} else if (textProp != null && typeof textProp.then === "function") {
|
|
1208
|
+
textProp.then((text) => {
|
|
1209
|
+
trackCompletion(text).catch(() => {
|
|
1210
|
+
});
|
|
1211
|
+
}).catch((err) => {
|
|
1212
|
+
trackCompletion("", err instanceof Error ? err : new Error(String(err))).catch(() => {
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1215
|
+
} else {
|
|
1216
|
+
const desc = Object.getOwnPropertyDescriptor(result, "textStream") ?? Object.getOwnPropertyDescriptor(Object.getPrototypeOf(result), "textStream");
|
|
1217
|
+
const isWritable = !desc || desc.writable || desc.set;
|
|
1218
|
+
if (isWritable) {
|
|
1219
|
+
const originalTextStream = result.textStream;
|
|
1220
|
+
let fullText = "";
|
|
1221
|
+
result.textStream = (async function* () {
|
|
936
1222
|
try {
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
await client.trackToolCall({
|
|
941
|
-
sessionId,
|
|
942
|
-
toolName,
|
|
943
|
-
toolInput: args[0],
|
|
944
|
-
toolOutput: result,
|
|
945
|
-
reasoning: `Tool executed in ${durationMs}ms`
|
|
946
|
-
});
|
|
1223
|
+
for await (const chunk of originalTextStream) {
|
|
1224
|
+
fullText += chunk;
|
|
1225
|
+
yield chunk;
|
|
947
1226
|
}
|
|
948
|
-
|
|
1227
|
+
await trackCompletion(fullText);
|
|
949
1228
|
} catch (error) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
toolName,
|
|
955
|
-
toolInput: args[0],
|
|
956
|
-
toolOutput: {},
|
|
957
|
-
toolError: { message: error instanceof Error ? error.message : "Unknown error" },
|
|
958
|
-
reasoning: `Tool failed after ${durationMs}ms`
|
|
959
|
-
});
|
|
960
|
-
}
|
|
1229
|
+
await trackCompletion(
|
|
1230
|
+
"",
|
|
1231
|
+
error instanceof Error ? error : new Error(String(error))
|
|
1232
|
+
);
|
|
961
1233
|
throw error;
|
|
962
1234
|
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
} else {
|
|
966
|
-
wrappedTools[toolName] = tool;
|
|
1235
|
+
})();
|
|
1236
|
+
}
|
|
967
1237
|
}
|
|
968
|
-
|
|
969
|
-
|
|
1238
|
+
return result;
|
|
1239
|
+
};
|
|
970
1240
|
}
|
|
971
|
-
function wrapGenerateObject(originalFn, client) {
|
|
1241
|
+
function wrapGenerateObject(originalFn, client, config) {
|
|
972
1242
|
return async (params) => {
|
|
973
1243
|
const startTime = Date.now();
|
|
974
1244
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
975
1245
|
const input = extractInput(params);
|
|
976
1246
|
const sessionId = await client.createSession({
|
|
977
1247
|
name: `generateObject: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
978
|
-
agentName:
|
|
979
|
-
userId:
|
|
1248
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
1249
|
+
userId: config.userId ?? "anonymous",
|
|
1250
|
+
convoId: config.convoId,
|
|
980
1251
|
metadata: {
|
|
981
1252
|
model: modelId,
|
|
982
1253
|
provider,
|
|
@@ -990,10 +1261,11 @@ function wrapGenerateObject(originalFn, client) {
|
|
|
990
1261
|
try {
|
|
991
1262
|
const result = await originalFn(params);
|
|
992
1263
|
const durationMs = Date.now() - startTime;
|
|
1264
|
+
const resolvedModelId = result.response?.modelId || modelId;
|
|
993
1265
|
const promptTokens = result.usage?.promptTokens || 0;
|
|
994
1266
|
const completionTokens = result.usage?.completionTokens || 0;
|
|
995
1267
|
const totalTokens = result.usage?.totalTokens || promptTokens + completionTokens;
|
|
996
|
-
const cost = calculateCostForCall(provider,
|
|
1268
|
+
const cost = calculateCostForCall(provider, resolvedModelId, promptTokens, completionTokens);
|
|
997
1269
|
await client.completeSession({
|
|
998
1270
|
sessionId,
|
|
999
1271
|
success: true,
|
|
@@ -1022,78 +1294,100 @@ function wrapGenerateObject(originalFn, client) {
|
|
|
1022
1294
|
}
|
|
1023
1295
|
};
|
|
1024
1296
|
}
|
|
1025
|
-
function wrapStreamObject(originalFn, client) {
|
|
1297
|
+
function wrapStreamObject(originalFn, client, config) {
|
|
1026
1298
|
return (params) => {
|
|
1027
1299
|
const startTime = Date.now();
|
|
1028
1300
|
const { modelId, provider } = extractModelInfo(params.model);
|
|
1029
1301
|
const input = extractInput(params);
|
|
1030
|
-
const sessionPromise =
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1302
|
+
const sessionPromise = (async () => {
|
|
1303
|
+
try {
|
|
1304
|
+
const id = await client.createSession({
|
|
1305
|
+
name: `streamObject: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`,
|
|
1306
|
+
agentName: config.defaultAgent ?? "vercel-ai-sdk",
|
|
1307
|
+
userId: config.userId ?? "anonymous",
|
|
1308
|
+
convoId: config.convoId,
|
|
1309
|
+
metadata: {
|
|
1310
|
+
model: modelId,
|
|
1311
|
+
provider,
|
|
1312
|
+
function: "streamObject"
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
if (id) {
|
|
1316
|
+
client.setInput(id, input).catch(() => {
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
return id;
|
|
1320
|
+
} catch {
|
|
1321
|
+
return null;
|
|
1038
1322
|
}
|
|
1039
|
-
})
|
|
1040
|
-
if (id) client.setInput(id, input);
|
|
1041
|
-
return id;
|
|
1042
|
-
});
|
|
1323
|
+
})();
|
|
1043
1324
|
const result = originalFn(params);
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1325
|
+
const objectProp = result.object;
|
|
1326
|
+
if (objectProp && typeof objectProp.then === "function") {
|
|
1327
|
+
objectProp.then(async (obj) => {
|
|
1047
1328
|
const durationMs = Date.now() - startTime;
|
|
1048
|
-
const
|
|
1049
|
-
if (
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);
|
|
1055
|
-
await client.completeSession({
|
|
1056
|
-
sessionId,
|
|
1057
|
-
success: true,
|
|
1058
|
-
output: JSON.stringify(obj),
|
|
1059
|
-
durationMs,
|
|
1060
|
-
estimatedCost: cost,
|
|
1061
|
-
promptTokens,
|
|
1062
|
-
completionTokens,
|
|
1063
|
-
totalTokens
|
|
1064
|
-
});
|
|
1329
|
+
const sid = await sessionPromise;
|
|
1330
|
+
if (!sid) return;
|
|
1331
|
+
let usage;
|
|
1332
|
+
try {
|
|
1333
|
+
usage = result.usage ? await result.usage : void 0;
|
|
1334
|
+
} catch {
|
|
1065
1335
|
}
|
|
1066
|
-
|
|
1336
|
+
const promptTokens = usage?.promptTokens || 0;
|
|
1337
|
+
const completionTokens = usage?.completionTokens || 0;
|
|
1338
|
+
const totalTokens = usage?.totalTokens || promptTokens + completionTokens;
|
|
1339
|
+
const cost = calculateCostForCall(provider, modelId, promptTokens, completionTokens);
|
|
1340
|
+
await client.completeSession({
|
|
1341
|
+
sessionId: sid,
|
|
1342
|
+
success: true,
|
|
1343
|
+
output: JSON.stringify(obj),
|
|
1344
|
+
durationMs,
|
|
1345
|
+
estimatedCost: cost,
|
|
1346
|
+
promptTokens,
|
|
1347
|
+
completionTokens,
|
|
1348
|
+
totalTokens
|
|
1349
|
+
});
|
|
1067
1350
|
}).catch(async (error) => {
|
|
1068
1351
|
const durationMs = Date.now() - startTime;
|
|
1069
|
-
const
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
}
|
|
1083
|
-
throw error;
|
|
1352
|
+
const sid = await sessionPromise;
|
|
1353
|
+
if (!sid) return;
|
|
1354
|
+
await client.trackError({
|
|
1355
|
+
sessionId: sid,
|
|
1356
|
+
errorType: error instanceof Error ? error.name : "Error",
|
|
1357
|
+
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
1358
|
+
});
|
|
1359
|
+
await client.completeSession({
|
|
1360
|
+
sessionId: sid,
|
|
1361
|
+
success: false,
|
|
1362
|
+
failureReason: error instanceof Error ? error.message : "Unknown error",
|
|
1363
|
+
durationMs
|
|
1364
|
+
});
|
|
1084
1365
|
});
|
|
1085
1366
|
}
|
|
1086
1367
|
return result;
|
|
1087
1368
|
};
|
|
1088
1369
|
}
|
|
1089
|
-
function wrapAISDK(ai) {
|
|
1090
|
-
const client = getClient2();
|
|
1370
|
+
function wrapAISDK(ai, options) {
|
|
1371
|
+
const client = options?.client ?? getClient2();
|
|
1372
|
+
const config = {
|
|
1373
|
+
defaultAgent: options?.defaultAgent ?? _globalConfig.defaultAgent,
|
|
1374
|
+
userId: options?.userId ?? _globalConfig.userId,
|
|
1375
|
+
convoId: options?.convoId ?? _globalConfig.convoId
|
|
1376
|
+
};
|
|
1091
1377
|
return {
|
|
1092
|
-
generateText: ai.generateText ? wrapGenerateText(ai.generateText, client) : wrapGenerateText(
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1378
|
+
generateText: ai.generateText ? wrapGenerateText(ai.generateText, client, config) : wrapGenerateText(
|
|
1379
|
+
() => Promise.reject(new Error("generateText not available")),
|
|
1380
|
+
client,
|
|
1381
|
+
config
|
|
1382
|
+
),
|
|
1383
|
+
streamText: ai.streamText ? wrapStreamText(ai.streamText, client, config) : wrapStreamText(() => ({ textStream: (async function* () {
|
|
1384
|
+
})() }), client, config),
|
|
1385
|
+
generateObject: ai.generateObject ? wrapGenerateObject(ai.generateObject, client, config) : wrapGenerateObject(
|
|
1386
|
+
() => Promise.reject(new Error("generateObject not available")),
|
|
1387
|
+
client,
|
|
1388
|
+
config
|
|
1389
|
+
),
|
|
1390
|
+
streamObject: ai.streamObject ? wrapStreamObject(ai.streamObject, client, config) : wrapStreamObject(() => ({}), client, config)
|
|
1097
1391
|
};
|
|
1098
1392
|
}
|
|
1099
1393
|
|
|
@@ -2138,7 +2432,12 @@ var Experiment = class {
|
|
|
2138
2432
|
getSessionContext,
|
|
2139
2433
|
getSystemPrompt,
|
|
2140
2434
|
getVariantName,
|
|
2435
|
+
hashValue,
|
|
2141
2436
|
isExperimentMode,
|
|
2437
|
+
redactPayload,
|
|
2438
|
+
redactString,
|
|
2439
|
+
redactValue,
|
|
2440
|
+
replaceMatch,
|
|
2142
2441
|
sentrial,
|
|
2143
2442
|
setClient,
|
|
2144
2443
|
setDefaultClient,
|