@latitude-data/telemetry 1.1.1 → 2.0.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 +130 -281
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +50 -38
- package/dist/index.js +130 -281
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ var zod = require('zod');
|
|
|
4
4
|
var incubating = require('@opentelemetry/semantic-conventions/incubating');
|
|
5
5
|
var semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
6
6
|
var otel = require('@opentelemetry/api');
|
|
7
|
+
var rosettaAi = require('rosetta-ai');
|
|
7
8
|
var uuid = require('uuid');
|
|
8
9
|
var baggageSpanProcessor = require('@opentelemetry/baggage-span-processor');
|
|
9
10
|
var contextAsyncHooks = require('@opentelemetry/context-async-hooks');
|
|
@@ -138,6 +139,23 @@ function GET_GATEWAY_BASE_URL() {
|
|
|
138
139
|
}
|
|
139
140
|
const env = { GATEWAY_BASE_URL: GET_GATEWAY_BASE_URL() };
|
|
140
141
|
|
|
142
|
+
function toSnakeCase(str) {
|
|
143
|
+
return str
|
|
144
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
145
|
+
.replace(/[^A-Za-z0-9]+/g, '_')
|
|
146
|
+
.replace(/_+/g, '_')
|
|
147
|
+
.replace(/^_+|_+$/g, '')
|
|
148
|
+
.toLowerCase();
|
|
149
|
+
}
|
|
150
|
+
function toKebabCase(input) {
|
|
151
|
+
return input
|
|
152
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
153
|
+
.replace(/[^A-Za-z0-9]+/g, '-')
|
|
154
|
+
.replace(/-+/g, '-')
|
|
155
|
+
.replace(/^-+|-+$/g, '')
|
|
156
|
+
.toLowerCase();
|
|
157
|
+
}
|
|
158
|
+
|
|
141
159
|
var StreamEventTypes;
|
|
142
160
|
(function (StreamEventTypes) {
|
|
143
161
|
StreamEventTypes["Latitude"] = "latitude-event";
|
|
@@ -199,10 +217,24 @@ const expectedOutputConfiguration = zod.z.object({
|
|
|
199
217
|
parsingFormat: zod.z.enum(['string', 'json']),
|
|
200
218
|
fieldAccessor: zod.z.string().optional(), // Field accessor to get the output from if it's a key-value format
|
|
201
219
|
});
|
|
220
|
+
const EVALUATION_TRIGGER_TARGETS = ['first', 'every', 'last'];
|
|
221
|
+
const DEFAULT_LAST_INTERACTION_DEBOUNCE_SECONDS = 120;
|
|
222
|
+
const LAST_INTERACTION_DEBOUNCE_MIN_SECONDS = 30;
|
|
223
|
+
const LAST_INTERACTION_DEBOUNCE_MAX_SECONDS = 60 * 60 * 24; // 1 day
|
|
224
|
+
const triggerConfiguration = zod.z.object({
|
|
225
|
+
target: zod.z.enum(EVALUATION_TRIGGER_TARGETS),
|
|
226
|
+
lastInteractionDebounce: zod.z
|
|
227
|
+
.number()
|
|
228
|
+
.min(LAST_INTERACTION_DEBOUNCE_MIN_SECONDS)
|
|
229
|
+
.max(LAST_INTERACTION_DEBOUNCE_MAX_SECONDS)
|
|
230
|
+
.optional()
|
|
231
|
+
.default(DEFAULT_LAST_INTERACTION_DEBOUNCE_SECONDS),
|
|
232
|
+
});
|
|
202
233
|
const baseEvaluationConfiguration = zod.z.object({
|
|
203
234
|
reverseScale: zod.z.boolean(), // If true, lower is better, otherwise, higher is better
|
|
204
235
|
actualOutput: actualOutputConfiguration,
|
|
205
236
|
expectedOutput: expectedOutputConfiguration.optional(),
|
|
237
|
+
trigger: triggerConfiguration.optional(),
|
|
206
238
|
});
|
|
207
239
|
const baseEvaluationResultMetadata = zod.z.object({
|
|
208
240
|
// configuration: Configuration snapshot is defined in every metric specification
|
|
@@ -591,14 +623,6 @@ zod.z.object({
|
|
|
591
623
|
evaluateLiveLogs: zod.z.boolean().nullable().optional(),
|
|
592
624
|
});
|
|
593
625
|
|
|
594
|
-
var LegacyChainEventTypes;
|
|
595
|
-
(function (LegacyChainEventTypes) {
|
|
596
|
-
LegacyChainEventTypes["Error"] = "chain-error";
|
|
597
|
-
LegacyChainEventTypes["Step"] = "chain-step";
|
|
598
|
-
LegacyChainEventTypes["Complete"] = "chain-complete";
|
|
599
|
-
LegacyChainEventTypes["StepComplete"] = "chain-step-complete";
|
|
600
|
-
})(LegacyChainEventTypes || (LegacyChainEventTypes = {}));
|
|
601
|
-
|
|
602
626
|
var ChainEventTypes;
|
|
603
627
|
(function (ChainEventTypes) {
|
|
604
628
|
ChainEventTypes["ChainCompleted"] = "chain-completed";
|
|
@@ -808,14 +832,6 @@ var RunSourceGroup;
|
|
|
808
832
|
[RunSourceGroup.Playground]: [LogSources.Playground, LogSources.Experiment],
|
|
809
833
|
});
|
|
810
834
|
|
|
811
|
-
var MessageRole;
|
|
812
|
-
(function (MessageRole) {
|
|
813
|
-
MessageRole["system"] = "system";
|
|
814
|
-
MessageRole["user"] = "user";
|
|
815
|
-
MessageRole["assistant"] = "assistant";
|
|
816
|
-
MessageRole["tool"] = "tool";
|
|
817
|
-
})(MessageRole || (MessageRole = {}));
|
|
818
|
-
|
|
819
835
|
var SpanKind;
|
|
820
836
|
(function (SpanKind) {
|
|
821
837
|
SpanKind["Internal"] = "internal";
|
|
@@ -940,12 +956,9 @@ const ATTRIBUTES = {
|
|
|
940
956
|
_root: 'gen_ai.request',
|
|
941
957
|
configuration: 'gen_ai.request.configuration',
|
|
942
958
|
template: 'gen_ai.request.template',
|
|
943
|
-
parameters: 'gen_ai.request.parameters',
|
|
944
|
-
messages: 'gen_ai.request.messages'},
|
|
959
|
+
parameters: 'gen_ai.request.parameters'},
|
|
945
960
|
response: {
|
|
946
|
-
_root: 'gen_ai.response',
|
|
947
|
-
messages: 'gen_ai.response.messages',
|
|
948
|
-
},
|
|
961
|
+
_root: 'gen_ai.response'},
|
|
949
962
|
usage: {
|
|
950
963
|
promptTokens: 'gen_ai.usage.prompt_tokens',
|
|
951
964
|
cachedTokens: 'gen_ai.usage.cached_tokens',
|
|
@@ -980,10 +993,17 @@ const ATTRIBUTES = {
|
|
|
980
993
|
inputTokens: incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS,
|
|
981
994
|
outputTokens: incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
982
995
|
},
|
|
996
|
+
systemInstructions: 'gen_ai.system.instructions', // Contains the PARTS of the "system message"
|
|
983
997
|
tool: {
|
|
984
998
|
call: {
|
|
985
999
|
id: incubating.ATTR_GEN_AI_TOOL_CALL_ID,
|
|
986
1000
|
arguments: 'gen_ai.tool.call.arguments'}},
|
|
1001
|
+
input: {
|
|
1002
|
+
messages: 'gen_ai.input.messages',
|
|
1003
|
+
},
|
|
1004
|
+
output: {
|
|
1005
|
+
messages: 'gen_ai.output.messages',
|
|
1006
|
+
},
|
|
987
1007
|
_deprecated: {
|
|
988
1008
|
system: incubating.ATTR_GEN_AI_SYSTEM,
|
|
989
1009
|
tool: {
|
|
@@ -993,40 +1013,6 @@ const ATTRIBUTES = {
|
|
|
993
1013
|
value: 'gen_ai.tool.result.value',
|
|
994
1014
|
isError: 'gen_ai.tool.result.is_error',
|
|
995
1015
|
},
|
|
996
|
-
},
|
|
997
|
-
prompt: {
|
|
998
|
-
_root: 'gen_ai.prompt',
|
|
999
|
-
index: (promptIndex) => ({
|
|
1000
|
-
role: `gen_ai.prompt.${promptIndex}.role`,
|
|
1001
|
-
content: `gen_ai.prompt.${promptIndex}.content`, // string or object
|
|
1002
|
-
toolCalls: (toolCallIndex) => ({
|
|
1003
|
-
id: `gen_ai.prompt.${promptIndex}.tool_calls.${toolCallIndex}.id`,
|
|
1004
|
-
name: `gen_ai.prompt.${promptIndex}.tool_calls.${toolCallIndex}.name`,
|
|
1005
|
-
arguments: `gen_ai.prompt.${promptIndex}.tool_calls.${toolCallIndex}.arguments`,
|
|
1006
|
-
}),
|
|
1007
|
-
tool: {
|
|
1008
|
-
callId: `gen_ai.prompt.${promptIndex}.tool_call_id`,
|
|
1009
|
-
toolName: `gen_ai.prompt.${promptIndex}.tool_name`,
|
|
1010
|
-
isError: `gen_ai.prompt.${promptIndex}.is_error`,
|
|
1011
|
-
},
|
|
1012
|
-
}),
|
|
1013
|
-
},
|
|
1014
|
-
completion: {
|
|
1015
|
-
_root: 'gen_ai.completion',
|
|
1016
|
-
index: (completionIndex) => ({
|
|
1017
|
-
role: `gen_ai.completion.${completionIndex}.role`,
|
|
1018
|
-
content: `gen_ai.completion.${completionIndex}.content`, // string or object
|
|
1019
|
-
toolCalls: (toolCallIndex) => ({
|
|
1020
|
-
id: `gen_ai.completion.${completionIndex}.tool_calls.${toolCallIndex}.id`,
|
|
1021
|
-
name: `gen_ai.completion.${completionIndex}.tool_calls.${toolCallIndex}.name`,
|
|
1022
|
-
arguments: `gen_ai.completion.${completionIndex}.tool_calls.${toolCallIndex}.arguments`,
|
|
1023
|
-
}),
|
|
1024
|
-
tool: {
|
|
1025
|
-
callId: `gen_ai.prompt.${completionIndex}.tool_call_id`,
|
|
1026
|
-
toolName: `gen_ai.prompt.${completionIndex}.tool_name`,
|
|
1027
|
-
isError: `gen_ai.prompt.${completionIndex}.is_error`,
|
|
1028
|
-
},
|
|
1029
|
-
}),
|
|
1030
1016
|
}},
|
|
1031
1017
|
},
|
|
1032
1018
|
}};
|
|
@@ -1154,11 +1140,25 @@ var Otlp;
|
|
|
1154
1140
|
})(Otlp || (Otlp = {}));
|
|
1155
1141
|
|
|
1156
1142
|
const MAX_SIMULATION_TURNS = 10;
|
|
1143
|
+
const globalGoalSourceSchema = zod.z.object({
|
|
1144
|
+
type: zod.z.literal('global'),
|
|
1145
|
+
value: zod.z.string(),
|
|
1146
|
+
});
|
|
1147
|
+
const columnGoalSourceSchema = zod.z.object({
|
|
1148
|
+
type: zod.z.literal('column'),
|
|
1149
|
+
columnIndex: zod.z.number(),
|
|
1150
|
+
});
|
|
1151
|
+
const simulatedUserGoalSourceSchema = zod.z.discriminatedUnion('type', [
|
|
1152
|
+
globalGoalSourceSchema,
|
|
1153
|
+
columnGoalSourceSchema,
|
|
1154
|
+
]);
|
|
1157
1155
|
const SimulationSettingsSchema = zod.z.object({
|
|
1158
1156
|
simulateToolResponses: zod.z.boolean().optional(),
|
|
1159
1157
|
simulatedTools: zod.z.array(zod.z.string()).optional(), // Empty array means all tools are simulated (if simulateToolResponses is true).
|
|
1160
1158
|
toolSimulationInstructions: zod.z.string().optional(), // A prompt used to guide and generate the simulation result
|
|
1161
1159
|
maxTurns: zod.z.number().min(1).max(MAX_SIMULATION_TURNS).optional(), // The maximum number of turns to simulate. Default is 1, and any greater value will add a new user message to the simulated conversation.
|
|
1160
|
+
simulatedUserGoal: zod.z.string().optional(), // Deprecated: Use simulatedUserGoalSource instead. Kept for backward compatibility.
|
|
1161
|
+
simulatedUserGoalSource: simulatedUserGoalSourceSchema.optional(), // The source for the simulated user goal (global text or dataset column).
|
|
1162
1162
|
});
|
|
1163
1163
|
|
|
1164
1164
|
var OptimizationEngine;
|
|
@@ -1232,12 +1232,18 @@ var DocumentTriggerParameters;
|
|
|
1232
1232
|
})(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
|
|
1233
1233
|
const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
|
|
1234
1234
|
|
|
1235
|
+
const translator = new rosettaAi.Translator({
|
|
1236
|
+
filterEmptyMessages: true,
|
|
1237
|
+
providerMetadata: 'preserve',
|
|
1238
|
+
});
|
|
1235
1239
|
class ManualInstrumentation {
|
|
1236
1240
|
enabled;
|
|
1237
1241
|
tracer;
|
|
1238
|
-
|
|
1242
|
+
options;
|
|
1243
|
+
constructor(tracer, options) {
|
|
1239
1244
|
this.enabled = false;
|
|
1240
1245
|
this.tracer = tracer;
|
|
1246
|
+
this.options = options ?? {};
|
|
1241
1247
|
}
|
|
1242
1248
|
isEnabled() {
|
|
1243
1249
|
return this.enabled;
|
|
@@ -1277,36 +1283,6 @@ class ManualInstrumentation {
|
|
|
1277
1283
|
}
|
|
1278
1284
|
return context;
|
|
1279
1285
|
}
|
|
1280
|
-
capitalize(str) {
|
|
1281
|
-
if (str.length === 0)
|
|
1282
|
-
return str;
|
|
1283
|
-
return str.charAt(0).toUpperCase() + str.toLowerCase().slice(1);
|
|
1284
|
-
}
|
|
1285
|
-
toCamelCase(str) {
|
|
1286
|
-
return str
|
|
1287
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
1288
|
-
.replace(/[^A-Za-z0-9]+/g, ' ')
|
|
1289
|
-
.trim()
|
|
1290
|
-
.split(' ')
|
|
1291
|
-
.map((w, i) => (i ? this.capitalize(w) : w.toLowerCase()))
|
|
1292
|
-
.join('');
|
|
1293
|
-
}
|
|
1294
|
-
toSnakeCase(str) {
|
|
1295
|
-
return str
|
|
1296
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
1297
|
-
.replace(/[^A-Za-z0-9]+/g, '_')
|
|
1298
|
-
.replace(/_+/g, '_')
|
|
1299
|
-
.replace(/^_+|_+$/g, '')
|
|
1300
|
-
.toLowerCase();
|
|
1301
|
-
}
|
|
1302
|
-
toKebabCase(input) {
|
|
1303
|
-
return input
|
|
1304
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
1305
|
-
.replace(/[^A-Za-z0-9]+/g, '-')
|
|
1306
|
-
.replace(/-+/g, '-')
|
|
1307
|
-
.replace(/^-+|-+$/g, '')
|
|
1308
|
-
.toLowerCase();
|
|
1309
|
-
}
|
|
1310
1286
|
error(span, error, options) {
|
|
1311
1287
|
options = options || {};
|
|
1312
1288
|
span.recordException(error);
|
|
@@ -1354,6 +1330,9 @@ class ManualInstrumentation {
|
|
|
1354
1330
|
},
|
|
1355
1331
|
};
|
|
1356
1332
|
}
|
|
1333
|
+
unknown(ctx, options) {
|
|
1334
|
+
return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Unknown].name, SpanType.Unknown, options);
|
|
1335
|
+
}
|
|
1357
1336
|
tool(ctx, options) {
|
|
1358
1337
|
const start = options;
|
|
1359
1338
|
let jsonArguments = '';
|
|
@@ -1373,7 +1352,7 @@ class ManualInstrumentation {
|
|
|
1373
1352
|
},
|
|
1374
1353
|
});
|
|
1375
1354
|
return {
|
|
1376
|
-
|
|
1355
|
+
...span,
|
|
1377
1356
|
end: (options) => {
|
|
1378
1357
|
const end = options;
|
|
1379
1358
|
let stringResult = '';
|
|
@@ -1396,176 +1375,15 @@ class ManualInstrumentation {
|
|
|
1396
1375
|
},
|
|
1397
1376
|
});
|
|
1398
1377
|
},
|
|
1399
|
-
fail: span.fail,
|
|
1400
1378
|
};
|
|
1401
1379
|
}
|
|
1402
|
-
attribifyMessageToolCalls(otelMessageField, toolCalls) {
|
|
1403
|
-
const attributes = {};
|
|
1404
|
-
for (let i = 0; i < toolCalls.length; i++) {
|
|
1405
|
-
for (const key in toolCalls[i]) {
|
|
1406
|
-
const field = this.toCamelCase(key);
|
|
1407
|
-
const value = toolCalls[i][key];
|
|
1408
|
-
if (value === null || value === undefined)
|
|
1409
|
-
continue;
|
|
1410
|
-
switch (field) {
|
|
1411
|
-
case 'id':
|
|
1412
|
-
case 'toolCallId':
|
|
1413
|
-
case 'toolUseId': {
|
|
1414
|
-
if (typeof value !== 'string')
|
|
1415
|
-
continue;
|
|
1416
|
-
attributes[otelMessageField.toolCalls(i).id] = value;
|
|
1417
|
-
break;
|
|
1418
|
-
}
|
|
1419
|
-
case 'name':
|
|
1420
|
-
case 'toolName': {
|
|
1421
|
-
if (typeof value !== 'string')
|
|
1422
|
-
continue;
|
|
1423
|
-
attributes[otelMessageField.toolCalls(i).name] = value;
|
|
1424
|
-
break;
|
|
1425
|
-
}
|
|
1426
|
-
case 'arguments':
|
|
1427
|
-
case 'toolArguments':
|
|
1428
|
-
case 'input': {
|
|
1429
|
-
if (typeof value === 'string') {
|
|
1430
|
-
attributes[otelMessageField.toolCalls(i).arguments] = value;
|
|
1431
|
-
}
|
|
1432
|
-
else {
|
|
1433
|
-
try {
|
|
1434
|
-
attributes[otelMessageField.toolCalls(i).arguments] =
|
|
1435
|
-
JSON.stringify(value);
|
|
1436
|
-
}
|
|
1437
|
-
catch (_error) {
|
|
1438
|
-
attributes[otelMessageField.toolCalls(i).arguments] = '{}';
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
break;
|
|
1442
|
-
}
|
|
1443
|
-
/* OpenAI function calls */
|
|
1444
|
-
case 'function': {
|
|
1445
|
-
if (typeof value !== 'object')
|
|
1446
|
-
continue;
|
|
1447
|
-
if (!('name' in value))
|
|
1448
|
-
continue;
|
|
1449
|
-
if (typeof value.name !== 'string')
|
|
1450
|
-
continue;
|
|
1451
|
-
if (!('arguments' in value))
|
|
1452
|
-
continue;
|
|
1453
|
-
if (typeof value.arguments !== 'string')
|
|
1454
|
-
continue;
|
|
1455
|
-
attributes[otelMessageField.toolCalls(i).name] = value.name;
|
|
1456
|
-
attributes[otelMessageField.toolCalls(i).arguments] =
|
|
1457
|
-
value.arguments;
|
|
1458
|
-
break;
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
return attributes;
|
|
1464
|
-
}
|
|
1465
|
-
attribifyMessageContent(otelMessageField, content) {
|
|
1466
|
-
let attributes = {};
|
|
1467
|
-
if (typeof content === 'string') {
|
|
1468
|
-
return attributes;
|
|
1469
|
-
}
|
|
1470
|
-
try {
|
|
1471
|
-
attributes[otelMessageField.content] = JSON.stringify(content);
|
|
1472
|
-
}
|
|
1473
|
-
catch (_error) {
|
|
1474
|
-
attributes[otelMessageField.content] = '[]';
|
|
1475
|
-
}
|
|
1476
|
-
if (!Array.isArray(content))
|
|
1477
|
-
return attributes;
|
|
1478
|
-
/* Tool calls for Anthropic and PromptL are in the content */
|
|
1479
|
-
const toolCalls = [];
|
|
1480
|
-
for (const item of content) {
|
|
1481
|
-
for (const key in item) {
|
|
1482
|
-
if (this.toCamelCase(key) !== 'type')
|
|
1483
|
-
continue;
|
|
1484
|
-
if (typeof item[key] !== 'string')
|
|
1485
|
-
continue;
|
|
1486
|
-
if (item[key] !== 'tool-call' && item[key] !== 'tool_use')
|
|
1487
|
-
continue;
|
|
1488
|
-
toolCalls.push(item);
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
if (toolCalls.length > 0) {
|
|
1492
|
-
attributes = {
|
|
1493
|
-
...attributes,
|
|
1494
|
-
...this.attribifyMessageToolCalls(otelMessageField, toolCalls),
|
|
1495
|
-
};
|
|
1496
|
-
}
|
|
1497
|
-
return attributes;
|
|
1498
|
-
}
|
|
1499
|
-
attribifyMessages(direction, messages) {
|
|
1500
|
-
const otelField = direction === 'input'
|
|
1501
|
-
? ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.prompt
|
|
1502
|
-
: ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.completion;
|
|
1503
|
-
let attributes = {};
|
|
1504
|
-
for (let i = 0; i < messages.length; i++) {
|
|
1505
|
-
for (const key in messages[i]) {
|
|
1506
|
-
const field = this.toCamelCase(key);
|
|
1507
|
-
const value = messages[i][key];
|
|
1508
|
-
if (value === null || value === undefined)
|
|
1509
|
-
continue;
|
|
1510
|
-
switch (field) {
|
|
1511
|
-
case 'role': {
|
|
1512
|
-
if (typeof value !== 'string')
|
|
1513
|
-
continue;
|
|
1514
|
-
attributes[otelField.index(i).role] = value;
|
|
1515
|
-
break;
|
|
1516
|
-
}
|
|
1517
|
-
/* Tool calls for Anthropic and PromptL are in the content */
|
|
1518
|
-
case 'content': {
|
|
1519
|
-
attributes = {
|
|
1520
|
-
...attributes,
|
|
1521
|
-
...this.attribifyMessageContent(otelField.index(i), value),
|
|
1522
|
-
};
|
|
1523
|
-
break;
|
|
1524
|
-
}
|
|
1525
|
-
/* Tool calls for OpenAI */
|
|
1526
|
-
case 'toolCalls': {
|
|
1527
|
-
if (!Array.isArray(value))
|
|
1528
|
-
continue;
|
|
1529
|
-
attributes = {
|
|
1530
|
-
...attributes,
|
|
1531
|
-
...this.attribifyMessageToolCalls(otelField.index(i), value),
|
|
1532
|
-
};
|
|
1533
|
-
break;
|
|
1534
|
-
}
|
|
1535
|
-
/* Tool result for OpenAI / Anthropic / PromptL */
|
|
1536
|
-
case 'toolCallId':
|
|
1537
|
-
case 'toolId':
|
|
1538
|
-
case 'toolUseId': {
|
|
1539
|
-
if (typeof value !== 'string')
|
|
1540
|
-
continue;
|
|
1541
|
-
attributes[otelField.index(i).tool.callId] = value;
|
|
1542
|
-
break;
|
|
1543
|
-
}
|
|
1544
|
-
case 'toolName': {
|
|
1545
|
-
if (typeof value !== 'string')
|
|
1546
|
-
continue;
|
|
1547
|
-
attributes[otelField.index(i).tool.toolName] = value;
|
|
1548
|
-
break;
|
|
1549
|
-
}
|
|
1550
|
-
// Note: 'toolResult' is 'content' itself
|
|
1551
|
-
case 'isError': {
|
|
1552
|
-
if (typeof value !== 'boolean')
|
|
1553
|
-
continue;
|
|
1554
|
-
attributes[otelField.index(i).tool.isError] = value;
|
|
1555
|
-
break;
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
return attributes;
|
|
1561
|
-
}
|
|
1562
1380
|
attribifyConfiguration(direction, configuration) {
|
|
1563
1381
|
const prefix = direction === 'input'
|
|
1564
1382
|
? ATTRIBUTES.LATITUDE.request._root
|
|
1565
1383
|
: ATTRIBUTES.LATITUDE.response._root;
|
|
1566
1384
|
const attributes = {};
|
|
1567
1385
|
for (const key in configuration) {
|
|
1568
|
-
const field =
|
|
1386
|
+
const field = toSnakeCase(key);
|
|
1569
1387
|
let value = configuration[key];
|
|
1570
1388
|
if (value === null || value === undefined)
|
|
1571
1389
|
continue;
|
|
@@ -1596,21 +1414,28 @@ class ManualInstrumentation {
|
|
|
1596
1414
|
}
|
|
1597
1415
|
const attrConfiguration = this.attribifyConfiguration('input', configuration);
|
|
1598
1416
|
const input = start.input ?? [];
|
|
1417
|
+
let jsonSystem = '';
|
|
1599
1418
|
let jsonInput = '';
|
|
1600
1419
|
try {
|
|
1601
|
-
|
|
1420
|
+
const translated = translator.translate(input, {
|
|
1421
|
+
from: this.options.provider,
|
|
1422
|
+
to: rosettaAi.Provider.GenAI,
|
|
1423
|
+
direction: 'input',
|
|
1424
|
+
});
|
|
1425
|
+
jsonSystem = JSON.stringify(translated.system ?? []);
|
|
1426
|
+
jsonInput = JSON.stringify(translated.messages ?? []);
|
|
1602
1427
|
}
|
|
1603
1428
|
catch (_error) {
|
|
1429
|
+
jsonSystem = '[]';
|
|
1604
1430
|
jsonInput = '[]';
|
|
1605
1431
|
}
|
|
1606
|
-
const attrInput = this.attribifyMessages('input', input);
|
|
1607
1432
|
const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
|
|
1608
1433
|
attributes: {
|
|
1609
1434
|
[ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.system]: start.provider,
|
|
1610
1435
|
[ATTRIBUTES.LATITUDE.request.configuration]: jsonConfiguration,
|
|
1611
1436
|
...attrConfiguration,
|
|
1612
|
-
[ATTRIBUTES.
|
|
1613
|
-
|
|
1437
|
+
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.systemInstructions]: jsonSystem,
|
|
1438
|
+
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.input.messages]: jsonInput,
|
|
1614
1439
|
...(start.attributes || {}),
|
|
1615
1440
|
[ATTRIBUTES.LATITUDE.commitUuid]: start.versionUuid,
|
|
1616
1441
|
[ATTRIBUTES.LATITUDE.documentUuid]: start.promptUuid,
|
|
@@ -1618,18 +1443,22 @@ class ManualInstrumentation {
|
|
|
1618
1443
|
},
|
|
1619
1444
|
});
|
|
1620
1445
|
return {
|
|
1621
|
-
|
|
1446
|
+
...span,
|
|
1622
1447
|
end: (options) => {
|
|
1623
1448
|
const end = options ?? {};
|
|
1624
1449
|
const output = end.output ?? [];
|
|
1625
1450
|
let jsonOutput = '';
|
|
1626
1451
|
try {
|
|
1627
|
-
|
|
1452
|
+
const translated = translator.translate(output, {
|
|
1453
|
+
from: this.options.provider,
|
|
1454
|
+
to: rosettaAi.Provider.GenAI,
|
|
1455
|
+
direction: 'output',
|
|
1456
|
+
});
|
|
1457
|
+
jsonOutput = JSON.stringify(translated.messages ?? []);
|
|
1628
1458
|
}
|
|
1629
1459
|
catch (_error) {
|
|
1630
1460
|
jsonOutput = '[]';
|
|
1631
1461
|
}
|
|
1632
|
-
const attrOutput = this.attribifyMessages('output', output);
|
|
1633
1462
|
const tokens = {
|
|
1634
1463
|
prompt: end.tokens?.prompt ?? 0,
|
|
1635
1464
|
cached: end.tokens?.cached ?? 0,
|
|
@@ -1641,8 +1470,7 @@ class ManualInstrumentation {
|
|
|
1641
1470
|
const finishReason = end.finishReason ?? '';
|
|
1642
1471
|
span.end({
|
|
1643
1472
|
attributes: {
|
|
1644
|
-
[ATTRIBUTES.
|
|
1645
|
-
...attrOutput,
|
|
1473
|
+
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.output.messages]: jsonOutput,
|
|
1646
1474
|
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.inputTokens]: inputTokens,
|
|
1647
1475
|
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.outputTokens]: outputTokens,
|
|
1648
1476
|
[ATTRIBUTES.LATITUDE.usage.promptTokens]: tokens.prompt,
|
|
@@ -1657,7 +1485,6 @@ class ManualInstrumentation {
|
|
|
1657
1485
|
},
|
|
1658
1486
|
});
|
|
1659
1487
|
},
|
|
1660
|
-
fail: span.fail,
|
|
1661
1488
|
};
|
|
1662
1489
|
}
|
|
1663
1490
|
embedding(ctx, options) {
|
|
@@ -1669,7 +1496,7 @@ class ManualInstrumentation {
|
|
|
1669
1496
|
: ATTRIBUTES.OPENTELEMETRY.HTTP.response.header;
|
|
1670
1497
|
const attributes = {};
|
|
1671
1498
|
for (const key in headers) {
|
|
1672
|
-
const field =
|
|
1499
|
+
const field = toKebabCase(key);
|
|
1673
1500
|
const value = headers[key];
|
|
1674
1501
|
if (value === null || value === undefined)
|
|
1675
1502
|
continue;
|
|
@@ -1704,7 +1531,7 @@ class ManualInstrumentation {
|
|
|
1704
1531
|
},
|
|
1705
1532
|
});
|
|
1706
1533
|
return {
|
|
1707
|
-
|
|
1534
|
+
...span,
|
|
1708
1535
|
end: (options) => {
|
|
1709
1536
|
const end = options;
|
|
1710
1537
|
// Note: do not serialize headers as a single attribute because fields won't be redacted
|
|
@@ -1730,7 +1557,6 @@ class ManualInstrumentation {
|
|
|
1730
1557
|
},
|
|
1731
1558
|
});
|
|
1732
1559
|
},
|
|
1733
|
-
fail: span.fail,
|
|
1734
1560
|
};
|
|
1735
1561
|
}
|
|
1736
1562
|
prompt(ctx, { documentLogUuid, versionUuid, promptUuid, projectId, experimentUuid, testDeploymentId, externalId, template, parameters, name, source, ...rest }) {
|
|
@@ -1771,7 +1597,9 @@ class ManualInstrumentation {
|
|
|
1771
1597
|
...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
|
|
1772
1598
|
...(rest.attributes || {}),
|
|
1773
1599
|
};
|
|
1774
|
-
return this.span(ctx, name ||
|
|
1600
|
+
return this.span(ctx, name || `chat-${documentLogUuid}`, SpanType.Chat, {
|
|
1601
|
+
attributes,
|
|
1602
|
+
});
|
|
1775
1603
|
}
|
|
1776
1604
|
external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
|
|
1777
1605
|
const attributes = {
|
|
@@ -1811,8 +1639,8 @@ class LatitudeInstrumentation {
|
|
|
1811
1639
|
return this.manualTelemetry.isEnabled();
|
|
1812
1640
|
}
|
|
1813
1641
|
enable() {
|
|
1814
|
-
this.options.module.instrument(this);
|
|
1815
1642
|
this.manualTelemetry.enable();
|
|
1643
|
+
this.options.module.instrument(this);
|
|
1816
1644
|
}
|
|
1817
1645
|
disable() {
|
|
1818
1646
|
this.manualTelemetry.disable();
|
|
@@ -2007,6 +1835,9 @@ class SpanFactory {
|
|
|
2007
1835
|
constructor(telemetry) {
|
|
2008
1836
|
this.telemetry = telemetry;
|
|
2009
1837
|
}
|
|
1838
|
+
span(options, ctx) {
|
|
1839
|
+
return this.telemetry.unknown(ctx ?? otel.context.active(), options);
|
|
1840
|
+
}
|
|
2010
1841
|
tool(options, ctx) {
|
|
2011
1842
|
return this.telemetry.tool(ctx ?? otel.context.active(), options);
|
|
2012
1843
|
}
|
|
@@ -2040,6 +1871,9 @@ class ContextManager {
|
|
|
2040
1871
|
active() {
|
|
2041
1872
|
return otel.context.active();
|
|
2042
1873
|
}
|
|
1874
|
+
with(ctx, fn, thisArg, ...args) {
|
|
1875
|
+
return otel.context.with(ctx, fn, thisArg, ...args);
|
|
1876
|
+
}
|
|
2043
1877
|
}
|
|
2044
1878
|
class InstrumentationManager {
|
|
2045
1879
|
instrumentations;
|
|
@@ -2086,6 +1920,23 @@ class ScopedTracerProvider {
|
|
|
2086
1920
|
return this.provider.getTracer(this.scope, this.version, options);
|
|
2087
1921
|
}
|
|
2088
1922
|
}
|
|
1923
|
+
class LifecycleManager {
|
|
1924
|
+
nodeProvider;
|
|
1925
|
+
exporter;
|
|
1926
|
+
constructor(nodeProvider, exporter) {
|
|
1927
|
+
this.nodeProvider = nodeProvider;
|
|
1928
|
+
this.exporter = exporter;
|
|
1929
|
+
}
|
|
1930
|
+
async flush() {
|
|
1931
|
+
await this.nodeProvider.forceFlush();
|
|
1932
|
+
await this.exporter.forceFlush?.();
|
|
1933
|
+
}
|
|
1934
|
+
async shutdown() {
|
|
1935
|
+
await this.flush();
|
|
1936
|
+
await this.nodeProvider.shutdown();
|
|
1937
|
+
await this.exporter.shutdown?.();
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
2089
1940
|
const DEFAULT_SPAN_EXPORTER = (apiKey) => new exporterTraceOtlpHttp.OTLPTraceExporter({
|
|
2090
1941
|
url: TRACES_URL,
|
|
2091
1942
|
headers: {
|
|
@@ -2104,6 +1955,7 @@ exports.Instrumentation = void 0;
|
|
|
2104
1955
|
Instrumentation["Langchain"] = "langchain";
|
|
2105
1956
|
Instrumentation["Latitude"] = "latitude";
|
|
2106
1957
|
Instrumentation["LlamaIndex"] = "llamaindex";
|
|
1958
|
+
Instrumentation["Manual"] = "manual";
|
|
2107
1959
|
Instrumentation["OpenAI"] = "openai";
|
|
2108
1960
|
Instrumentation["TogetherAI"] = "togetherai";
|
|
2109
1961
|
Instrumentation["VertexAI"] = "vertexai";
|
|
@@ -2117,6 +1969,7 @@ class LatitudeTelemetry {
|
|
|
2117
1969
|
context;
|
|
2118
1970
|
instrumentation;
|
|
2119
1971
|
tracer;
|
|
1972
|
+
lifecycle;
|
|
2120
1973
|
constructor(apiKey, options) {
|
|
2121
1974
|
this.options = options || {};
|
|
2122
1975
|
if (!this.options.exporter) {
|
|
@@ -2133,6 +1986,7 @@ class LatitudeTelemetry {
|
|
|
2133
1986
|
this.nodeProvider = new sdkTraceNode.NodeTracerProvider({
|
|
2134
1987
|
resource: new resources.Resource({ [semanticConventions.ATTR_SERVICE_NAME]: SERVICE_NAME }),
|
|
2135
1988
|
});
|
|
1989
|
+
this.lifecycle = new LifecycleManager(this.nodeProvider, this.options.exporter);
|
|
2136
1990
|
// Note: important, must run before the exporter span processors
|
|
2137
1991
|
this.nodeProvider.addSpanProcessor(new baggageSpanProcessor.BaggageSpanProcessor(baggageSpanProcessor.ALLOW_ALL_BAGGAGE_KEYS));
|
|
2138
1992
|
if (this.options.processors) {
|
|
@@ -2162,19 +2016,16 @@ class LatitudeTelemetry {
|
|
|
2162
2016
|
this.context = new ContextManager(this.manualInstrumentation);
|
|
2163
2017
|
}
|
|
2164
2018
|
async flush() {
|
|
2165
|
-
await this.
|
|
2166
|
-
await this.options.exporter.forceFlush?.();
|
|
2019
|
+
await this.lifecycle.flush();
|
|
2167
2020
|
}
|
|
2168
2021
|
async shutdown() {
|
|
2169
|
-
await this.
|
|
2170
|
-
await this.nodeProvider.shutdown();
|
|
2171
|
-
await this.options.exporter.shutdown?.();
|
|
2022
|
+
await this.lifecycle.shutdown();
|
|
2172
2023
|
}
|
|
2173
2024
|
// TODO(tracing): auto instrument outgoing HTTP requests
|
|
2174
2025
|
initInstrumentations() {
|
|
2175
2026
|
this.instrumentationsList = [];
|
|
2176
|
-
const tracer = this.tracer.get(
|
|
2177
|
-
this.manualInstrumentation = new ManualInstrumentation(tracer);
|
|
2027
|
+
const tracer = this.tracer.get(exports.Instrumentation.Manual);
|
|
2028
|
+
this.manualInstrumentation = new ManualInstrumentation(tracer, this.options.instrumentations?.manual);
|
|
2178
2029
|
this.instrumentationsList.push(this.manualInstrumentation);
|
|
2179
2030
|
const latitude = this.options.instrumentations?.latitude;
|
|
2180
2031
|
if (latitude) {
|
|
@@ -2184,12 +2035,12 @@ class LatitudeTelemetry {
|
|
|
2184
2035
|
}
|
|
2185
2036
|
const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
|
|
2186
2037
|
const providerPkg = this.options.instrumentations?.[instrumentationType];
|
|
2038
|
+
if (!providerPkg)
|
|
2039
|
+
return;
|
|
2187
2040
|
const provider = this.tracer.provider(instrumentationType);
|
|
2188
2041
|
const instrumentation$1 = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
|
|
2189
2042
|
instrumentation$1.setTracerProvider(provider);
|
|
2190
|
-
|
|
2191
|
-
instrumentation$1.manuallyInstrument(providerPkg);
|
|
2192
|
-
}
|
|
2043
|
+
instrumentation$1.manuallyInstrument(providerPkg);
|
|
2193
2044
|
instrumentation.registerInstrumentations({
|
|
2194
2045
|
instrumentations: [instrumentation$1],
|
|
2195
2046
|
tracerProvider: provider,
|
|
@@ -2211,19 +2062,17 @@ class LatitudeTelemetry {
|
|
|
2211
2062
|
if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
|
|
2212
2063
|
throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
|
|
2213
2064
|
}
|
|
2214
|
-
const span = this.manualInstrumentation.unresolvedExternal(
|
|
2065
|
+
const span = this.manualInstrumentation.unresolvedExternal(BACKGROUND(), options);
|
|
2066
|
+
let result;
|
|
2215
2067
|
try {
|
|
2216
|
-
|
|
2217
|
-
span.end();
|
|
2218
|
-
return result;
|
|
2068
|
+
result = await otel.context.with(span.context, async () => await fn(span.context));
|
|
2219
2069
|
}
|
|
2220
2070
|
catch (error) {
|
|
2221
2071
|
span.fail(error);
|
|
2222
2072
|
throw error;
|
|
2223
2073
|
}
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
}
|
|
2074
|
+
span.end();
|
|
2075
|
+
return result;
|
|
2227
2076
|
}
|
|
2228
2077
|
}
|
|
2229
2078
|
|