@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.js
CHANGED
|
@@ -3,6 +3,7 @@ import { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_
|
|
|
3
3
|
import { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
4
4
|
import * as otel from '@opentelemetry/api';
|
|
5
5
|
import { trace, propagation, context } from '@opentelemetry/api';
|
|
6
|
+
import { Translator, Provider } from 'rosetta-ai';
|
|
6
7
|
import { v4 } from 'uuid';
|
|
7
8
|
import { BaggageSpanProcessor, ALLOW_ALL_BAGGAGE_KEYS } from '@opentelemetry/baggage-span-processor';
|
|
8
9
|
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
|
|
@@ -118,6 +119,23 @@ function GET_GATEWAY_BASE_URL() {
|
|
|
118
119
|
}
|
|
119
120
|
const env = { GATEWAY_BASE_URL: GET_GATEWAY_BASE_URL() };
|
|
120
121
|
|
|
122
|
+
function toSnakeCase(str) {
|
|
123
|
+
return str
|
|
124
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
125
|
+
.replace(/[^A-Za-z0-9]+/g, '_')
|
|
126
|
+
.replace(/_+/g, '_')
|
|
127
|
+
.replace(/^_+|_+$/g, '')
|
|
128
|
+
.toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
function toKebabCase(input) {
|
|
131
|
+
return input
|
|
132
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
133
|
+
.replace(/[^A-Za-z0-9]+/g, '-')
|
|
134
|
+
.replace(/-+/g, '-')
|
|
135
|
+
.replace(/^-+|-+$/g, '')
|
|
136
|
+
.toLowerCase();
|
|
137
|
+
}
|
|
138
|
+
|
|
121
139
|
var StreamEventTypes;
|
|
122
140
|
(function (StreamEventTypes) {
|
|
123
141
|
StreamEventTypes["Latitude"] = "latitude-event";
|
|
@@ -179,10 +197,24 @@ const expectedOutputConfiguration = z.object({
|
|
|
179
197
|
parsingFormat: z.enum(['string', 'json']),
|
|
180
198
|
fieldAccessor: z.string().optional(), // Field accessor to get the output from if it's a key-value format
|
|
181
199
|
});
|
|
200
|
+
const EVALUATION_TRIGGER_TARGETS = ['first', 'every', 'last'];
|
|
201
|
+
const DEFAULT_LAST_INTERACTION_DEBOUNCE_SECONDS = 120;
|
|
202
|
+
const LAST_INTERACTION_DEBOUNCE_MIN_SECONDS = 30;
|
|
203
|
+
const LAST_INTERACTION_DEBOUNCE_MAX_SECONDS = 60 * 60 * 24; // 1 day
|
|
204
|
+
const triggerConfiguration = z.object({
|
|
205
|
+
target: z.enum(EVALUATION_TRIGGER_TARGETS),
|
|
206
|
+
lastInteractionDebounce: z
|
|
207
|
+
.number()
|
|
208
|
+
.min(LAST_INTERACTION_DEBOUNCE_MIN_SECONDS)
|
|
209
|
+
.max(LAST_INTERACTION_DEBOUNCE_MAX_SECONDS)
|
|
210
|
+
.optional()
|
|
211
|
+
.default(DEFAULT_LAST_INTERACTION_DEBOUNCE_SECONDS),
|
|
212
|
+
});
|
|
182
213
|
const baseEvaluationConfiguration = z.object({
|
|
183
214
|
reverseScale: z.boolean(), // If true, lower is better, otherwise, higher is better
|
|
184
215
|
actualOutput: actualOutputConfiguration,
|
|
185
216
|
expectedOutput: expectedOutputConfiguration.optional(),
|
|
217
|
+
trigger: triggerConfiguration.optional(),
|
|
186
218
|
});
|
|
187
219
|
const baseEvaluationResultMetadata = z.object({
|
|
188
220
|
// configuration: Configuration snapshot is defined in every metric specification
|
|
@@ -571,14 +603,6 @@ z.object({
|
|
|
571
603
|
evaluateLiveLogs: z.boolean().nullable().optional(),
|
|
572
604
|
});
|
|
573
605
|
|
|
574
|
-
var LegacyChainEventTypes;
|
|
575
|
-
(function (LegacyChainEventTypes) {
|
|
576
|
-
LegacyChainEventTypes["Error"] = "chain-error";
|
|
577
|
-
LegacyChainEventTypes["Step"] = "chain-step";
|
|
578
|
-
LegacyChainEventTypes["Complete"] = "chain-complete";
|
|
579
|
-
LegacyChainEventTypes["StepComplete"] = "chain-step-complete";
|
|
580
|
-
})(LegacyChainEventTypes || (LegacyChainEventTypes = {}));
|
|
581
|
-
|
|
582
606
|
var ChainEventTypes;
|
|
583
607
|
(function (ChainEventTypes) {
|
|
584
608
|
ChainEventTypes["ChainCompleted"] = "chain-completed";
|
|
@@ -788,14 +812,6 @@ var RunSourceGroup;
|
|
|
788
812
|
[RunSourceGroup.Playground]: [LogSources.Playground, LogSources.Experiment],
|
|
789
813
|
});
|
|
790
814
|
|
|
791
|
-
var MessageRole;
|
|
792
|
-
(function (MessageRole) {
|
|
793
|
-
MessageRole["system"] = "system";
|
|
794
|
-
MessageRole["user"] = "user";
|
|
795
|
-
MessageRole["assistant"] = "assistant";
|
|
796
|
-
MessageRole["tool"] = "tool";
|
|
797
|
-
})(MessageRole || (MessageRole = {}));
|
|
798
|
-
|
|
799
815
|
var SpanKind;
|
|
800
816
|
(function (SpanKind) {
|
|
801
817
|
SpanKind["Internal"] = "internal";
|
|
@@ -920,12 +936,9 @@ const ATTRIBUTES = {
|
|
|
920
936
|
_root: 'gen_ai.request',
|
|
921
937
|
configuration: 'gen_ai.request.configuration',
|
|
922
938
|
template: 'gen_ai.request.template',
|
|
923
|
-
parameters: 'gen_ai.request.parameters',
|
|
924
|
-
messages: 'gen_ai.request.messages'},
|
|
939
|
+
parameters: 'gen_ai.request.parameters'},
|
|
925
940
|
response: {
|
|
926
|
-
_root: 'gen_ai.response',
|
|
927
|
-
messages: 'gen_ai.response.messages',
|
|
928
|
-
},
|
|
941
|
+
_root: 'gen_ai.response'},
|
|
929
942
|
usage: {
|
|
930
943
|
promptTokens: 'gen_ai.usage.prompt_tokens',
|
|
931
944
|
cachedTokens: 'gen_ai.usage.cached_tokens',
|
|
@@ -960,10 +973,17 @@ const ATTRIBUTES = {
|
|
|
960
973
|
inputTokens: ATTR_GEN_AI_USAGE_INPUT_TOKENS,
|
|
961
974
|
outputTokens: ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
962
975
|
},
|
|
976
|
+
systemInstructions: 'gen_ai.system.instructions', // Contains the PARTS of the "system message"
|
|
963
977
|
tool: {
|
|
964
978
|
call: {
|
|
965
979
|
id: ATTR_GEN_AI_TOOL_CALL_ID,
|
|
966
980
|
arguments: 'gen_ai.tool.call.arguments'}},
|
|
981
|
+
input: {
|
|
982
|
+
messages: 'gen_ai.input.messages',
|
|
983
|
+
},
|
|
984
|
+
output: {
|
|
985
|
+
messages: 'gen_ai.output.messages',
|
|
986
|
+
},
|
|
967
987
|
_deprecated: {
|
|
968
988
|
system: ATTR_GEN_AI_SYSTEM,
|
|
969
989
|
tool: {
|
|
@@ -973,40 +993,6 @@ const ATTRIBUTES = {
|
|
|
973
993
|
value: 'gen_ai.tool.result.value',
|
|
974
994
|
isError: 'gen_ai.tool.result.is_error',
|
|
975
995
|
},
|
|
976
|
-
},
|
|
977
|
-
prompt: {
|
|
978
|
-
_root: 'gen_ai.prompt',
|
|
979
|
-
index: (promptIndex) => ({
|
|
980
|
-
role: `gen_ai.prompt.${promptIndex}.role`,
|
|
981
|
-
content: `gen_ai.prompt.${promptIndex}.content`, // string or object
|
|
982
|
-
toolCalls: (toolCallIndex) => ({
|
|
983
|
-
id: `gen_ai.prompt.${promptIndex}.tool_calls.${toolCallIndex}.id`,
|
|
984
|
-
name: `gen_ai.prompt.${promptIndex}.tool_calls.${toolCallIndex}.name`,
|
|
985
|
-
arguments: `gen_ai.prompt.${promptIndex}.tool_calls.${toolCallIndex}.arguments`,
|
|
986
|
-
}),
|
|
987
|
-
tool: {
|
|
988
|
-
callId: `gen_ai.prompt.${promptIndex}.tool_call_id`,
|
|
989
|
-
toolName: `gen_ai.prompt.${promptIndex}.tool_name`,
|
|
990
|
-
isError: `gen_ai.prompt.${promptIndex}.is_error`,
|
|
991
|
-
},
|
|
992
|
-
}),
|
|
993
|
-
},
|
|
994
|
-
completion: {
|
|
995
|
-
_root: 'gen_ai.completion',
|
|
996
|
-
index: (completionIndex) => ({
|
|
997
|
-
role: `gen_ai.completion.${completionIndex}.role`,
|
|
998
|
-
content: `gen_ai.completion.${completionIndex}.content`, // string or object
|
|
999
|
-
toolCalls: (toolCallIndex) => ({
|
|
1000
|
-
id: `gen_ai.completion.${completionIndex}.tool_calls.${toolCallIndex}.id`,
|
|
1001
|
-
name: `gen_ai.completion.${completionIndex}.tool_calls.${toolCallIndex}.name`,
|
|
1002
|
-
arguments: `gen_ai.completion.${completionIndex}.tool_calls.${toolCallIndex}.arguments`,
|
|
1003
|
-
}),
|
|
1004
|
-
tool: {
|
|
1005
|
-
callId: `gen_ai.prompt.${completionIndex}.tool_call_id`,
|
|
1006
|
-
toolName: `gen_ai.prompt.${completionIndex}.tool_name`,
|
|
1007
|
-
isError: `gen_ai.prompt.${completionIndex}.is_error`,
|
|
1008
|
-
},
|
|
1009
|
-
}),
|
|
1010
996
|
}},
|
|
1011
997
|
},
|
|
1012
998
|
}};
|
|
@@ -1134,11 +1120,25 @@ var Otlp;
|
|
|
1134
1120
|
})(Otlp || (Otlp = {}));
|
|
1135
1121
|
|
|
1136
1122
|
const MAX_SIMULATION_TURNS = 10;
|
|
1123
|
+
const globalGoalSourceSchema = z.object({
|
|
1124
|
+
type: z.literal('global'),
|
|
1125
|
+
value: z.string(),
|
|
1126
|
+
});
|
|
1127
|
+
const columnGoalSourceSchema = z.object({
|
|
1128
|
+
type: z.literal('column'),
|
|
1129
|
+
columnIndex: z.number(),
|
|
1130
|
+
});
|
|
1131
|
+
const simulatedUserGoalSourceSchema = z.discriminatedUnion('type', [
|
|
1132
|
+
globalGoalSourceSchema,
|
|
1133
|
+
columnGoalSourceSchema,
|
|
1134
|
+
]);
|
|
1137
1135
|
const SimulationSettingsSchema = z.object({
|
|
1138
1136
|
simulateToolResponses: z.boolean().optional(),
|
|
1139
1137
|
simulatedTools: z.array(z.string()).optional(), // Empty array means all tools are simulated (if simulateToolResponses is true).
|
|
1140
1138
|
toolSimulationInstructions: z.string().optional(), // A prompt used to guide and generate the simulation result
|
|
1141
1139
|
maxTurns: 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.
|
|
1140
|
+
simulatedUserGoal: z.string().optional(), // Deprecated: Use simulatedUserGoalSource instead. Kept for backward compatibility.
|
|
1141
|
+
simulatedUserGoalSource: simulatedUserGoalSourceSchema.optional(), // The source for the simulated user goal (global text or dataset column).
|
|
1142
1142
|
});
|
|
1143
1143
|
|
|
1144
1144
|
var OptimizationEngine;
|
|
@@ -1212,12 +1212,18 @@ var DocumentTriggerParameters;
|
|
|
1212
1212
|
})(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
|
|
1213
1213
|
const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
|
|
1214
1214
|
|
|
1215
|
+
const translator = new Translator({
|
|
1216
|
+
filterEmptyMessages: true,
|
|
1217
|
+
providerMetadata: 'preserve',
|
|
1218
|
+
});
|
|
1215
1219
|
class ManualInstrumentation {
|
|
1216
1220
|
enabled;
|
|
1217
1221
|
tracer;
|
|
1218
|
-
|
|
1222
|
+
options;
|
|
1223
|
+
constructor(tracer, options) {
|
|
1219
1224
|
this.enabled = false;
|
|
1220
1225
|
this.tracer = tracer;
|
|
1226
|
+
this.options = options ?? {};
|
|
1221
1227
|
}
|
|
1222
1228
|
isEnabled() {
|
|
1223
1229
|
return this.enabled;
|
|
@@ -1257,36 +1263,6 @@ class ManualInstrumentation {
|
|
|
1257
1263
|
}
|
|
1258
1264
|
return context;
|
|
1259
1265
|
}
|
|
1260
|
-
capitalize(str) {
|
|
1261
|
-
if (str.length === 0)
|
|
1262
|
-
return str;
|
|
1263
|
-
return str.charAt(0).toUpperCase() + str.toLowerCase().slice(1);
|
|
1264
|
-
}
|
|
1265
|
-
toCamelCase(str) {
|
|
1266
|
-
return str
|
|
1267
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
1268
|
-
.replace(/[^A-Za-z0-9]+/g, ' ')
|
|
1269
|
-
.trim()
|
|
1270
|
-
.split(' ')
|
|
1271
|
-
.map((w, i) => (i ? this.capitalize(w) : w.toLowerCase()))
|
|
1272
|
-
.join('');
|
|
1273
|
-
}
|
|
1274
|
-
toSnakeCase(str) {
|
|
1275
|
-
return str
|
|
1276
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
1277
|
-
.replace(/[^A-Za-z0-9]+/g, '_')
|
|
1278
|
-
.replace(/_+/g, '_')
|
|
1279
|
-
.replace(/^_+|_+$/g, '')
|
|
1280
|
-
.toLowerCase();
|
|
1281
|
-
}
|
|
1282
|
-
toKebabCase(input) {
|
|
1283
|
-
return input
|
|
1284
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
1285
|
-
.replace(/[^A-Za-z0-9]+/g, '-')
|
|
1286
|
-
.replace(/-+/g, '-')
|
|
1287
|
-
.replace(/^-+|-+$/g, '')
|
|
1288
|
-
.toLowerCase();
|
|
1289
|
-
}
|
|
1290
1266
|
error(span, error, options) {
|
|
1291
1267
|
options = options || {};
|
|
1292
1268
|
span.recordException(error);
|
|
@@ -1334,6 +1310,9 @@ class ManualInstrumentation {
|
|
|
1334
1310
|
},
|
|
1335
1311
|
};
|
|
1336
1312
|
}
|
|
1313
|
+
unknown(ctx, options) {
|
|
1314
|
+
return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Unknown].name, SpanType.Unknown, options);
|
|
1315
|
+
}
|
|
1337
1316
|
tool(ctx, options) {
|
|
1338
1317
|
const start = options;
|
|
1339
1318
|
let jsonArguments = '';
|
|
@@ -1353,7 +1332,7 @@ class ManualInstrumentation {
|
|
|
1353
1332
|
},
|
|
1354
1333
|
});
|
|
1355
1334
|
return {
|
|
1356
|
-
|
|
1335
|
+
...span,
|
|
1357
1336
|
end: (options) => {
|
|
1358
1337
|
const end = options;
|
|
1359
1338
|
let stringResult = '';
|
|
@@ -1376,176 +1355,15 @@ class ManualInstrumentation {
|
|
|
1376
1355
|
},
|
|
1377
1356
|
});
|
|
1378
1357
|
},
|
|
1379
|
-
fail: span.fail,
|
|
1380
1358
|
};
|
|
1381
1359
|
}
|
|
1382
|
-
attribifyMessageToolCalls(otelMessageField, toolCalls) {
|
|
1383
|
-
const attributes = {};
|
|
1384
|
-
for (let i = 0; i < toolCalls.length; i++) {
|
|
1385
|
-
for (const key in toolCalls[i]) {
|
|
1386
|
-
const field = this.toCamelCase(key);
|
|
1387
|
-
const value = toolCalls[i][key];
|
|
1388
|
-
if (value === null || value === undefined)
|
|
1389
|
-
continue;
|
|
1390
|
-
switch (field) {
|
|
1391
|
-
case 'id':
|
|
1392
|
-
case 'toolCallId':
|
|
1393
|
-
case 'toolUseId': {
|
|
1394
|
-
if (typeof value !== 'string')
|
|
1395
|
-
continue;
|
|
1396
|
-
attributes[otelMessageField.toolCalls(i).id] = value;
|
|
1397
|
-
break;
|
|
1398
|
-
}
|
|
1399
|
-
case 'name':
|
|
1400
|
-
case 'toolName': {
|
|
1401
|
-
if (typeof value !== 'string')
|
|
1402
|
-
continue;
|
|
1403
|
-
attributes[otelMessageField.toolCalls(i).name] = value;
|
|
1404
|
-
break;
|
|
1405
|
-
}
|
|
1406
|
-
case 'arguments':
|
|
1407
|
-
case 'toolArguments':
|
|
1408
|
-
case 'input': {
|
|
1409
|
-
if (typeof value === 'string') {
|
|
1410
|
-
attributes[otelMessageField.toolCalls(i).arguments] = value;
|
|
1411
|
-
}
|
|
1412
|
-
else {
|
|
1413
|
-
try {
|
|
1414
|
-
attributes[otelMessageField.toolCalls(i).arguments] =
|
|
1415
|
-
JSON.stringify(value);
|
|
1416
|
-
}
|
|
1417
|
-
catch (_error) {
|
|
1418
|
-
attributes[otelMessageField.toolCalls(i).arguments] = '{}';
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
break;
|
|
1422
|
-
}
|
|
1423
|
-
/* OpenAI function calls */
|
|
1424
|
-
case 'function': {
|
|
1425
|
-
if (typeof value !== 'object')
|
|
1426
|
-
continue;
|
|
1427
|
-
if (!('name' in value))
|
|
1428
|
-
continue;
|
|
1429
|
-
if (typeof value.name !== 'string')
|
|
1430
|
-
continue;
|
|
1431
|
-
if (!('arguments' in value))
|
|
1432
|
-
continue;
|
|
1433
|
-
if (typeof value.arguments !== 'string')
|
|
1434
|
-
continue;
|
|
1435
|
-
attributes[otelMessageField.toolCalls(i).name] = value.name;
|
|
1436
|
-
attributes[otelMessageField.toolCalls(i).arguments] =
|
|
1437
|
-
value.arguments;
|
|
1438
|
-
break;
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
return attributes;
|
|
1444
|
-
}
|
|
1445
|
-
attribifyMessageContent(otelMessageField, content) {
|
|
1446
|
-
let attributes = {};
|
|
1447
|
-
if (typeof content === 'string') {
|
|
1448
|
-
return attributes;
|
|
1449
|
-
}
|
|
1450
|
-
try {
|
|
1451
|
-
attributes[otelMessageField.content] = JSON.stringify(content);
|
|
1452
|
-
}
|
|
1453
|
-
catch (_error) {
|
|
1454
|
-
attributes[otelMessageField.content] = '[]';
|
|
1455
|
-
}
|
|
1456
|
-
if (!Array.isArray(content))
|
|
1457
|
-
return attributes;
|
|
1458
|
-
/* Tool calls for Anthropic and PromptL are in the content */
|
|
1459
|
-
const toolCalls = [];
|
|
1460
|
-
for (const item of content) {
|
|
1461
|
-
for (const key in item) {
|
|
1462
|
-
if (this.toCamelCase(key) !== 'type')
|
|
1463
|
-
continue;
|
|
1464
|
-
if (typeof item[key] !== 'string')
|
|
1465
|
-
continue;
|
|
1466
|
-
if (item[key] !== 'tool-call' && item[key] !== 'tool_use')
|
|
1467
|
-
continue;
|
|
1468
|
-
toolCalls.push(item);
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
if (toolCalls.length > 0) {
|
|
1472
|
-
attributes = {
|
|
1473
|
-
...attributes,
|
|
1474
|
-
...this.attribifyMessageToolCalls(otelMessageField, toolCalls),
|
|
1475
|
-
};
|
|
1476
|
-
}
|
|
1477
|
-
return attributes;
|
|
1478
|
-
}
|
|
1479
|
-
attribifyMessages(direction, messages) {
|
|
1480
|
-
const otelField = direction === 'input'
|
|
1481
|
-
? ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.prompt
|
|
1482
|
-
: ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.completion;
|
|
1483
|
-
let attributes = {};
|
|
1484
|
-
for (let i = 0; i < messages.length; i++) {
|
|
1485
|
-
for (const key in messages[i]) {
|
|
1486
|
-
const field = this.toCamelCase(key);
|
|
1487
|
-
const value = messages[i][key];
|
|
1488
|
-
if (value === null || value === undefined)
|
|
1489
|
-
continue;
|
|
1490
|
-
switch (field) {
|
|
1491
|
-
case 'role': {
|
|
1492
|
-
if (typeof value !== 'string')
|
|
1493
|
-
continue;
|
|
1494
|
-
attributes[otelField.index(i).role] = value;
|
|
1495
|
-
break;
|
|
1496
|
-
}
|
|
1497
|
-
/* Tool calls for Anthropic and PromptL are in the content */
|
|
1498
|
-
case 'content': {
|
|
1499
|
-
attributes = {
|
|
1500
|
-
...attributes,
|
|
1501
|
-
...this.attribifyMessageContent(otelField.index(i), value),
|
|
1502
|
-
};
|
|
1503
|
-
break;
|
|
1504
|
-
}
|
|
1505
|
-
/* Tool calls for OpenAI */
|
|
1506
|
-
case 'toolCalls': {
|
|
1507
|
-
if (!Array.isArray(value))
|
|
1508
|
-
continue;
|
|
1509
|
-
attributes = {
|
|
1510
|
-
...attributes,
|
|
1511
|
-
...this.attribifyMessageToolCalls(otelField.index(i), value),
|
|
1512
|
-
};
|
|
1513
|
-
break;
|
|
1514
|
-
}
|
|
1515
|
-
/* Tool result for OpenAI / Anthropic / PromptL */
|
|
1516
|
-
case 'toolCallId':
|
|
1517
|
-
case 'toolId':
|
|
1518
|
-
case 'toolUseId': {
|
|
1519
|
-
if (typeof value !== 'string')
|
|
1520
|
-
continue;
|
|
1521
|
-
attributes[otelField.index(i).tool.callId] = value;
|
|
1522
|
-
break;
|
|
1523
|
-
}
|
|
1524
|
-
case 'toolName': {
|
|
1525
|
-
if (typeof value !== 'string')
|
|
1526
|
-
continue;
|
|
1527
|
-
attributes[otelField.index(i).tool.toolName] = value;
|
|
1528
|
-
break;
|
|
1529
|
-
}
|
|
1530
|
-
// Note: 'toolResult' is 'content' itself
|
|
1531
|
-
case 'isError': {
|
|
1532
|
-
if (typeof value !== 'boolean')
|
|
1533
|
-
continue;
|
|
1534
|
-
attributes[otelField.index(i).tool.isError] = value;
|
|
1535
|
-
break;
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
return attributes;
|
|
1541
|
-
}
|
|
1542
1360
|
attribifyConfiguration(direction, configuration) {
|
|
1543
1361
|
const prefix = direction === 'input'
|
|
1544
1362
|
? ATTRIBUTES.LATITUDE.request._root
|
|
1545
1363
|
: ATTRIBUTES.LATITUDE.response._root;
|
|
1546
1364
|
const attributes = {};
|
|
1547
1365
|
for (const key in configuration) {
|
|
1548
|
-
const field =
|
|
1366
|
+
const field = toSnakeCase(key);
|
|
1549
1367
|
let value = configuration[key];
|
|
1550
1368
|
if (value === null || value === undefined)
|
|
1551
1369
|
continue;
|
|
@@ -1576,21 +1394,28 @@ class ManualInstrumentation {
|
|
|
1576
1394
|
}
|
|
1577
1395
|
const attrConfiguration = this.attribifyConfiguration('input', configuration);
|
|
1578
1396
|
const input = start.input ?? [];
|
|
1397
|
+
let jsonSystem = '';
|
|
1579
1398
|
let jsonInput = '';
|
|
1580
1399
|
try {
|
|
1581
|
-
|
|
1400
|
+
const translated = translator.translate(input, {
|
|
1401
|
+
from: this.options.provider,
|
|
1402
|
+
to: Provider.GenAI,
|
|
1403
|
+
direction: 'input',
|
|
1404
|
+
});
|
|
1405
|
+
jsonSystem = JSON.stringify(translated.system ?? []);
|
|
1406
|
+
jsonInput = JSON.stringify(translated.messages ?? []);
|
|
1582
1407
|
}
|
|
1583
1408
|
catch (_error) {
|
|
1409
|
+
jsonSystem = '[]';
|
|
1584
1410
|
jsonInput = '[]';
|
|
1585
1411
|
}
|
|
1586
|
-
const attrInput = this.attribifyMessages('input', input);
|
|
1587
1412
|
const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
|
|
1588
1413
|
attributes: {
|
|
1589
1414
|
[ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.system]: start.provider,
|
|
1590
1415
|
[ATTRIBUTES.LATITUDE.request.configuration]: jsonConfiguration,
|
|
1591
1416
|
...attrConfiguration,
|
|
1592
|
-
[ATTRIBUTES.
|
|
1593
|
-
|
|
1417
|
+
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.systemInstructions]: jsonSystem,
|
|
1418
|
+
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.input.messages]: jsonInput,
|
|
1594
1419
|
...(start.attributes || {}),
|
|
1595
1420
|
[ATTRIBUTES.LATITUDE.commitUuid]: start.versionUuid,
|
|
1596
1421
|
[ATTRIBUTES.LATITUDE.documentUuid]: start.promptUuid,
|
|
@@ -1598,18 +1423,22 @@ class ManualInstrumentation {
|
|
|
1598
1423
|
},
|
|
1599
1424
|
});
|
|
1600
1425
|
return {
|
|
1601
|
-
|
|
1426
|
+
...span,
|
|
1602
1427
|
end: (options) => {
|
|
1603
1428
|
const end = options ?? {};
|
|
1604
1429
|
const output = end.output ?? [];
|
|
1605
1430
|
let jsonOutput = '';
|
|
1606
1431
|
try {
|
|
1607
|
-
|
|
1432
|
+
const translated = translator.translate(output, {
|
|
1433
|
+
from: this.options.provider,
|
|
1434
|
+
to: Provider.GenAI,
|
|
1435
|
+
direction: 'output',
|
|
1436
|
+
});
|
|
1437
|
+
jsonOutput = JSON.stringify(translated.messages ?? []);
|
|
1608
1438
|
}
|
|
1609
1439
|
catch (_error) {
|
|
1610
1440
|
jsonOutput = '[]';
|
|
1611
1441
|
}
|
|
1612
|
-
const attrOutput = this.attribifyMessages('output', output);
|
|
1613
1442
|
const tokens = {
|
|
1614
1443
|
prompt: end.tokens?.prompt ?? 0,
|
|
1615
1444
|
cached: end.tokens?.cached ?? 0,
|
|
@@ -1621,8 +1450,7 @@ class ManualInstrumentation {
|
|
|
1621
1450
|
const finishReason = end.finishReason ?? '';
|
|
1622
1451
|
span.end({
|
|
1623
1452
|
attributes: {
|
|
1624
|
-
[ATTRIBUTES.
|
|
1625
|
-
...attrOutput,
|
|
1453
|
+
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.output.messages]: jsonOutput,
|
|
1626
1454
|
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.inputTokens]: inputTokens,
|
|
1627
1455
|
[ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.outputTokens]: outputTokens,
|
|
1628
1456
|
[ATTRIBUTES.LATITUDE.usage.promptTokens]: tokens.prompt,
|
|
@@ -1637,7 +1465,6 @@ class ManualInstrumentation {
|
|
|
1637
1465
|
},
|
|
1638
1466
|
});
|
|
1639
1467
|
},
|
|
1640
|
-
fail: span.fail,
|
|
1641
1468
|
};
|
|
1642
1469
|
}
|
|
1643
1470
|
embedding(ctx, options) {
|
|
@@ -1649,7 +1476,7 @@ class ManualInstrumentation {
|
|
|
1649
1476
|
: ATTRIBUTES.OPENTELEMETRY.HTTP.response.header;
|
|
1650
1477
|
const attributes = {};
|
|
1651
1478
|
for (const key in headers) {
|
|
1652
|
-
const field =
|
|
1479
|
+
const field = toKebabCase(key);
|
|
1653
1480
|
const value = headers[key];
|
|
1654
1481
|
if (value === null || value === undefined)
|
|
1655
1482
|
continue;
|
|
@@ -1684,7 +1511,7 @@ class ManualInstrumentation {
|
|
|
1684
1511
|
},
|
|
1685
1512
|
});
|
|
1686
1513
|
return {
|
|
1687
|
-
|
|
1514
|
+
...span,
|
|
1688
1515
|
end: (options) => {
|
|
1689
1516
|
const end = options;
|
|
1690
1517
|
// Note: do not serialize headers as a single attribute because fields won't be redacted
|
|
@@ -1710,7 +1537,6 @@ class ManualInstrumentation {
|
|
|
1710
1537
|
},
|
|
1711
1538
|
});
|
|
1712
1539
|
},
|
|
1713
|
-
fail: span.fail,
|
|
1714
1540
|
};
|
|
1715
1541
|
}
|
|
1716
1542
|
prompt(ctx, { documentLogUuid, versionUuid, promptUuid, projectId, experimentUuid, testDeploymentId, externalId, template, parameters, name, source, ...rest }) {
|
|
@@ -1751,7 +1577,9 @@ class ManualInstrumentation {
|
|
|
1751
1577
|
...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
|
|
1752
1578
|
...(rest.attributes || {}),
|
|
1753
1579
|
};
|
|
1754
|
-
return this.span(ctx, name ||
|
|
1580
|
+
return this.span(ctx, name || `chat-${documentLogUuid}`, SpanType.Chat, {
|
|
1581
|
+
attributes,
|
|
1582
|
+
});
|
|
1755
1583
|
}
|
|
1756
1584
|
external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
|
|
1757
1585
|
const attributes = {
|
|
@@ -1791,8 +1619,8 @@ class LatitudeInstrumentation {
|
|
|
1791
1619
|
return this.manualTelemetry.isEnabled();
|
|
1792
1620
|
}
|
|
1793
1621
|
enable() {
|
|
1794
|
-
this.options.module.instrument(this);
|
|
1795
1622
|
this.manualTelemetry.enable();
|
|
1623
|
+
this.options.module.instrument(this);
|
|
1796
1624
|
}
|
|
1797
1625
|
disable() {
|
|
1798
1626
|
this.manualTelemetry.disable();
|
|
@@ -1987,6 +1815,9 @@ class SpanFactory {
|
|
|
1987
1815
|
constructor(telemetry) {
|
|
1988
1816
|
this.telemetry = telemetry;
|
|
1989
1817
|
}
|
|
1818
|
+
span(options, ctx) {
|
|
1819
|
+
return this.telemetry.unknown(ctx ?? context.active(), options);
|
|
1820
|
+
}
|
|
1990
1821
|
tool(options, ctx) {
|
|
1991
1822
|
return this.telemetry.tool(ctx ?? context.active(), options);
|
|
1992
1823
|
}
|
|
@@ -2020,6 +1851,9 @@ class ContextManager {
|
|
|
2020
1851
|
active() {
|
|
2021
1852
|
return context.active();
|
|
2022
1853
|
}
|
|
1854
|
+
with(ctx, fn, thisArg, ...args) {
|
|
1855
|
+
return context.with(ctx, fn, thisArg, ...args);
|
|
1856
|
+
}
|
|
2023
1857
|
}
|
|
2024
1858
|
class InstrumentationManager {
|
|
2025
1859
|
instrumentations;
|
|
@@ -2066,6 +1900,23 @@ class ScopedTracerProvider {
|
|
|
2066
1900
|
return this.provider.getTracer(this.scope, this.version, options);
|
|
2067
1901
|
}
|
|
2068
1902
|
}
|
|
1903
|
+
class LifecycleManager {
|
|
1904
|
+
nodeProvider;
|
|
1905
|
+
exporter;
|
|
1906
|
+
constructor(nodeProvider, exporter) {
|
|
1907
|
+
this.nodeProvider = nodeProvider;
|
|
1908
|
+
this.exporter = exporter;
|
|
1909
|
+
}
|
|
1910
|
+
async flush() {
|
|
1911
|
+
await this.nodeProvider.forceFlush();
|
|
1912
|
+
await this.exporter.forceFlush?.();
|
|
1913
|
+
}
|
|
1914
|
+
async shutdown() {
|
|
1915
|
+
await this.flush();
|
|
1916
|
+
await this.nodeProvider.shutdown();
|
|
1917
|
+
await this.exporter.shutdown?.();
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
2069
1920
|
const DEFAULT_SPAN_EXPORTER = (apiKey) => new OTLPTraceExporter({
|
|
2070
1921
|
url: TRACES_URL,
|
|
2071
1922
|
headers: {
|
|
@@ -2084,6 +1935,7 @@ var Instrumentation;
|
|
|
2084
1935
|
Instrumentation["Langchain"] = "langchain";
|
|
2085
1936
|
Instrumentation["Latitude"] = "latitude";
|
|
2086
1937
|
Instrumentation["LlamaIndex"] = "llamaindex";
|
|
1938
|
+
Instrumentation["Manual"] = "manual";
|
|
2087
1939
|
Instrumentation["OpenAI"] = "openai";
|
|
2088
1940
|
Instrumentation["TogetherAI"] = "togetherai";
|
|
2089
1941
|
Instrumentation["VertexAI"] = "vertexai";
|
|
@@ -2097,6 +1949,7 @@ class LatitudeTelemetry {
|
|
|
2097
1949
|
context;
|
|
2098
1950
|
instrumentation;
|
|
2099
1951
|
tracer;
|
|
1952
|
+
lifecycle;
|
|
2100
1953
|
constructor(apiKey, options) {
|
|
2101
1954
|
this.options = options || {};
|
|
2102
1955
|
if (!this.options.exporter) {
|
|
@@ -2113,6 +1966,7 @@ class LatitudeTelemetry {
|
|
|
2113
1966
|
this.nodeProvider = new NodeTracerProvider({
|
|
2114
1967
|
resource: new Resource({ [ATTR_SERVICE_NAME]: SERVICE_NAME }),
|
|
2115
1968
|
});
|
|
1969
|
+
this.lifecycle = new LifecycleManager(this.nodeProvider, this.options.exporter);
|
|
2116
1970
|
// Note: important, must run before the exporter span processors
|
|
2117
1971
|
this.nodeProvider.addSpanProcessor(new BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS));
|
|
2118
1972
|
if (this.options.processors) {
|
|
@@ -2142,19 +1996,16 @@ class LatitudeTelemetry {
|
|
|
2142
1996
|
this.context = new ContextManager(this.manualInstrumentation);
|
|
2143
1997
|
}
|
|
2144
1998
|
async flush() {
|
|
2145
|
-
await this.
|
|
2146
|
-
await this.options.exporter.forceFlush?.();
|
|
1999
|
+
await this.lifecycle.flush();
|
|
2147
2000
|
}
|
|
2148
2001
|
async shutdown() {
|
|
2149
|
-
await this.
|
|
2150
|
-
await this.nodeProvider.shutdown();
|
|
2151
|
-
await this.options.exporter.shutdown?.();
|
|
2002
|
+
await this.lifecycle.shutdown();
|
|
2152
2003
|
}
|
|
2153
2004
|
// TODO(tracing): auto instrument outgoing HTTP requests
|
|
2154
2005
|
initInstrumentations() {
|
|
2155
2006
|
this.instrumentationsList = [];
|
|
2156
|
-
const tracer = this.tracer.get(
|
|
2157
|
-
this.manualInstrumentation = new ManualInstrumentation(tracer);
|
|
2007
|
+
const tracer = this.tracer.get(Instrumentation.Manual);
|
|
2008
|
+
this.manualInstrumentation = new ManualInstrumentation(tracer, this.options.instrumentations?.manual);
|
|
2158
2009
|
this.instrumentationsList.push(this.manualInstrumentation);
|
|
2159
2010
|
const latitude = this.options.instrumentations?.latitude;
|
|
2160
2011
|
if (latitude) {
|
|
@@ -2164,12 +2015,12 @@ class LatitudeTelemetry {
|
|
|
2164
2015
|
}
|
|
2165
2016
|
const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
|
|
2166
2017
|
const providerPkg = this.options.instrumentations?.[instrumentationType];
|
|
2018
|
+
if (!providerPkg)
|
|
2019
|
+
return;
|
|
2167
2020
|
const provider = this.tracer.provider(instrumentationType);
|
|
2168
2021
|
const instrumentation = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
|
|
2169
2022
|
instrumentation.setTracerProvider(provider);
|
|
2170
|
-
|
|
2171
|
-
instrumentation.manuallyInstrument(providerPkg);
|
|
2172
|
-
}
|
|
2023
|
+
instrumentation.manuallyInstrument(providerPkg);
|
|
2173
2024
|
registerInstrumentations({
|
|
2174
2025
|
instrumentations: [instrumentation],
|
|
2175
2026
|
tracerProvider: provider,
|
|
@@ -2191,19 +2042,17 @@ class LatitudeTelemetry {
|
|
|
2191
2042
|
if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
|
|
2192
2043
|
throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
|
|
2193
2044
|
}
|
|
2194
|
-
const span = this.manualInstrumentation.unresolvedExternal(
|
|
2045
|
+
const span = this.manualInstrumentation.unresolvedExternal(BACKGROUND(), options);
|
|
2046
|
+
let result;
|
|
2195
2047
|
try {
|
|
2196
|
-
|
|
2197
|
-
span.end();
|
|
2198
|
-
return result;
|
|
2048
|
+
result = await context.with(span.context, async () => await fn(span.context));
|
|
2199
2049
|
}
|
|
2200
2050
|
catch (error) {
|
|
2201
2051
|
span.fail(error);
|
|
2202
2052
|
throw error;
|
|
2203
2053
|
}
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
}
|
|
2054
|
+
span.end();
|
|
2055
|
+
return result;
|
|
2207
2056
|
}
|
|
2208
2057
|
}
|
|
2209
2058
|
|