@latitude-data/telemetry 1.0.4 → 1.1.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 CHANGED
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var zod = require('zod');
4
- var otel = require('@opentelemetry/api');
5
- var semanticConventions = require('@opentelemetry/semantic-conventions');
6
4
  var incubating = require('@opentelemetry/semantic-conventions/incubating');
5
+ var semanticConventions = require('@opentelemetry/semantic-conventions');
6
+ var otel = require('@opentelemetry/api');
7
+ var uuid = require('uuid');
7
8
  var baggageSpanProcessor = require('@opentelemetry/baggage-span-processor');
8
9
  var contextAsyncHooks = require('@opentelemetry/context-async-hooks');
9
10
  var core = require('@opentelemetry/core');
@@ -94,7 +95,7 @@ const DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
94
95
  attributes: [
95
96
  /^.*auth.*$/i,
96
97
  /^.*authorization.*$/i,
97
- /^(?!ai\.).*usage.*$/i,
98
+ /^(?!gen_ai\.).*usage.*$/i,
98
99
  /^(?!gen_ai\.).*token.*$/i,
99
100
  /^.*secret.*$/i,
100
101
  /^.*key.*$/i,
@@ -149,6 +150,15 @@ zod.z.object({
149
150
  isError: zod.z.boolean().optional(),
150
151
  text: zod.z.string().optional(),
151
152
  });
153
+ zod.z.object({
154
+ inputTokens: zod.z.number(),
155
+ outputTokens: zod.z.number(),
156
+ promptTokens: zod.z.number(),
157
+ completionTokens: zod.z.number(),
158
+ totalTokens: zod.z.number(),
159
+ reasoningTokens: zod.z.number(),
160
+ cachedInputTokens: zod.z.number(),
161
+ });
152
162
 
153
163
  var ParameterType;
154
164
  (function (ParameterType) {
@@ -274,12 +284,34 @@ const CompositeEvaluationSpecification = {
274
284
  },
275
285
  };
276
286
 
287
+ const selectedContextSchema = zod.z.object({
288
+ messageIndex: zod.z.number().int().nonnegative(),
289
+ contentBlockIndex: zod.z.number().int().nonnegative(),
290
+ contentType: zod.z.enum([
291
+ 'text',
292
+ 'reasoning',
293
+ 'image',
294
+ 'file',
295
+ 'tool-call',
296
+ 'tool-result',
297
+ ]),
298
+ textRange: zod.z
299
+ .object({
300
+ start: zod.z.number().int().nonnegative(),
301
+ end: zod.z.number().int().nonnegative(),
302
+ })
303
+ .optional(),
304
+ selectedText: zod.z.string().optional(),
305
+ toolCallId: zod.z.string().optional(),
306
+ });
277
307
  const humanEvaluationConfiguration = baseEvaluationConfiguration.extend({
278
- enableControls: zod.z.boolean().optional(),
308
+ enableControls: zod.z.boolean().optional(), // UI annotation controls
279
309
  criteria: zod.z.string().optional(),
280
310
  });
281
311
  const humanEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
282
312
  reason: zod.z.string().optional(),
313
+ enrichedReason: zod.z.string().optional(),
314
+ selectedContexts: zod.z.array(selectedContextSchema).optional(),
283
315
  });
284
316
  const humanEvaluationResultError = baseEvaluationResultError.extend({});
285
317
  // BINARY
@@ -557,8 +589,6 @@ zod.z.object({
557
589
  });
558
590
  zod.z.object({
559
591
  evaluateLiveLogs: zod.z.boolean().nullable().optional(),
560
- enableSuggestions: zod.z.boolean().nullable().optional(),
561
- autoApplySuggestions: zod.z.boolean().nullable().optional(),
562
592
  });
563
593
 
564
594
  var LegacyChainEventTypes;
@@ -756,6 +786,7 @@ var LogSources;
756
786
  LogSources["ShadowTest"] = "shadow_test";
757
787
  LogSources["ABTestChallenger"] = "ab_test_challenger";
758
788
  LogSources["User"] = "user";
789
+ LogSources["Optimization"] = "optimization";
759
790
  })(LogSources || (LogSources = {}));
760
791
 
761
792
  var RunSourceGroup;
@@ -766,7 +797,8 @@ var RunSourceGroup;
766
797
  ({
767
798
  [RunSourceGroup.Production]: [
768
799
  LogSources.API,
769
- LogSources.Copilot,
800
+ LogSources.ShadowTest,
801
+ LogSources.ABTestChallenger,
770
802
  LogSources.EmailTrigger,
771
803
  LogSources.IntegrationTrigger,
772
804
  LogSources.ScheduledTrigger,
@@ -795,23 +827,49 @@ var SpanKind;
795
827
  // Note: loosely based on OpenTelemetry GenAI semantic conventions
796
828
  var SpanType;
797
829
  (function (SpanType) {
798
- SpanType["Tool"] = "tool";
830
+ // Latitude wrappers
831
+ SpanType["Prompt"] = "prompt";
832
+ SpanType["Chat"] = "chat";
833
+ SpanType["External"] = "external";
834
+ SpanType["UnresolvedExternal"] = "unresolved_external";
835
+ // Added a HTTP span to capture raw HTTP requests and responses when running from Latitude
836
+ SpanType["Http"] = "http";
837
+ // Any known span from supported specifications will be grouped into one of these types
799
838
  SpanType["Completion"] = "completion";
839
+ SpanType["Tool"] = "tool";
800
840
  SpanType["Embedding"] = "embedding";
801
- SpanType["Retrieval"] = "retrieval";
802
- SpanType["Reranking"] = "reranking";
803
- SpanType["Http"] = "http";
804
841
  SpanType["Unknown"] = "unknown";
805
- SpanType["Prompt"] = "prompt";
806
- SpanType["Step"] = "step";
807
842
  })(SpanType || (SpanType = {}));
843
+ [
844
+ SpanType.Prompt,
845
+ SpanType.External,
846
+ SpanType.Chat,
847
+ ];
808
848
  const SPAN_SPECIFICATIONS = {
809
- [SpanType.Tool]: {
810
- name: 'Tool',
811
- description: 'A tool call',
812
- isGenAI: true,
849
+ [SpanType.Prompt]: {
850
+ name: 'Prompt',
851
+ description: 'A prompt span',
852
+ isGenAI: false,
813
853
  isHidden: false,
814
854
  },
855
+ [SpanType.Chat]: {
856
+ name: 'Chat',
857
+ description: 'A chat continuation span',
858
+ isGenAI: false,
859
+ isHidden: false,
860
+ },
861
+ [SpanType.External]: {
862
+ name: 'External',
863
+ description: 'An external capture span',
864
+ isGenAI: false,
865
+ isHidden: false,
866
+ },
867
+ [SpanType.UnresolvedExternal]: {
868
+ name: 'Unresolved External',
869
+ description: 'An external span that needs path resolution before storage',
870
+ isGenAI: false,
871
+ isHidden: true,
872
+ },
815
873
  [SpanType.Completion]: {
816
874
  name: 'Completion',
817
875
  description: 'A completion call',
@@ -824,15 +882,9 @@ const SPAN_SPECIFICATIONS = {
824
882
  isGenAI: true,
825
883
  isHidden: false,
826
884
  },
827
- [SpanType.Retrieval]: {
828
- name: 'Retrieval',
829
- description: 'A retrieval call',
830
- isGenAI: true,
831
- isHidden: false,
832
- },
833
- [SpanType.Reranking]: {
834
- name: 'Reranking',
835
- description: 'A reranking call',
885
+ [SpanType.Tool]: {
886
+ name: 'Tool',
887
+ description: 'A tool call',
836
888
  isGenAI: true,
837
889
  isHidden: false,
838
890
  },
@@ -848,18 +900,6 @@ const SPAN_SPECIFICATIONS = {
848
900
  isGenAI: false,
849
901
  isHidden: true,
850
902
  },
851
- [SpanType.Prompt]: {
852
- name: 'Prompt',
853
- description: 'A prompt span',
854
- isGenAI: false,
855
- isHidden: false,
856
- },
857
- [SpanType.Step]: {
858
- name: 'Step',
859
- description: 'A step span',
860
- isGenAI: false,
861
- isHidden: false,
862
- },
863
903
  };
864
904
  var SpanStatus;
865
905
  (function (SpanStatus) {
@@ -867,6 +907,11 @@ var SpanStatus;
867
907
  SpanStatus["Ok"] = "ok";
868
908
  SpanStatus["Error"] = "error";
869
909
  })(SpanStatus || (SpanStatus = {}));
910
+ new Set([
911
+ SpanType.Prompt,
912
+ SpanType.Chat,
913
+ SpanType.External,
914
+ ]);
870
915
 
871
916
  // Note: Traces are unmaterialized but this context is used to propagate the trace
872
917
  // See www.w3.org/TR/trace-context and w3c.github.io/baggage
@@ -876,6 +921,130 @@ zod.z.object({
876
921
  baggage: zod.z.string().optional(), // <key>=urlencoded(<value>)[,<key>=urlencoded(<value>)]*
877
922
  });
878
923
 
924
+ const ATTRIBUTES = {
925
+ // Custom attributes added and used by Latitude spans (Prompt / External / Chat)
926
+ LATITUDE: {
927
+ type: 'latitude.type',
928
+ documentUuid: 'latitude.document_uuid',
929
+ promptPath: 'latitude.prompt_path',
930
+ commitUuid: 'latitude.commit_uuid',
931
+ documentLogUuid: 'latitude.document_log_uuid',
932
+ projectId: 'latitude.project_id',
933
+ experimentUuid: 'latitude.experiment_uuid',
934
+ source: 'latitude.source',
935
+ externalId: 'latitude.external_id',
936
+ testDeploymentId: 'latitude.test_deployment_id',
937
+ previousTraceId: 'latitude.previous_trace_id',
938
+ // Custom additions to the GenAI semantic conventions (deprecated)
939
+ request: {
940
+ _root: 'gen_ai.request',
941
+ configuration: 'gen_ai.request.configuration',
942
+ template: 'gen_ai.request.template',
943
+ parameters: 'gen_ai.request.parameters',
944
+ messages: 'gen_ai.request.messages'},
945
+ response: {
946
+ _root: 'gen_ai.response',
947
+ messages: 'gen_ai.response.messages',
948
+ },
949
+ usage: {
950
+ promptTokens: 'gen_ai.usage.prompt_tokens',
951
+ cachedTokens: 'gen_ai.usage.cached_tokens',
952
+ reasoningTokens: 'gen_ai.usage.reasoning_tokens',
953
+ completionTokens: 'gen_ai.usage.completion_tokens',
954
+ },
955
+ },
956
+ // Official OpenTelemetry semantic conventions
957
+ OPENTELEMETRY: {
958
+ HTTP: {
959
+ request: {
960
+ url: 'http.request.url',
961
+ body: 'http.request.body',
962
+ header: 'http.request.header',
963
+ method: semanticConventions.ATTR_HTTP_REQUEST_METHOD,
964
+ },
965
+ response: {
966
+ body: 'http.response.body',
967
+ header: 'http.response.header',
968
+ statusCode: semanticConventions.ATTR_HTTP_RESPONSE_STATUS_CODE,
969
+ },
970
+ },
971
+ // GenAI semantic conventions
972
+ // https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/
973
+ GEN_AI: {
974
+ operation: incubating.ATTR_GEN_AI_OPERATION_NAME,
975
+ response: {
976
+ model: incubating.ATTR_GEN_AI_RESPONSE_MODEL,
977
+ finishReasons: incubating.ATTR_GEN_AI_RESPONSE_FINISH_REASONS,
978
+ },
979
+ usage: {
980
+ inputTokens: incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS,
981
+ outputTokens: incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
982
+ },
983
+ tool: {
984
+ call: {
985
+ id: incubating.ATTR_GEN_AI_TOOL_CALL_ID,
986
+ arguments: 'gen_ai.tool.call.arguments'}},
987
+ _deprecated: {
988
+ system: incubating.ATTR_GEN_AI_SYSTEM,
989
+ tool: {
990
+ name: incubating.ATTR_GEN_AI_TOOL_NAME,
991
+ type: incubating.ATTR_GEN_AI_TOOL_TYPE,
992
+ result: {
993
+ value: 'gen_ai.tool.result.value',
994
+ isError: 'gen_ai.tool.result.is_error',
995
+ },
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
+ }},
1031
+ },
1032
+ }};
1033
+ const VALUES = {
1034
+ OPENTELEMETRY: {
1035
+ GEN_AI: {
1036
+ response: {
1037
+ finishReasons: {
1038
+ stop: 'stop',
1039
+ toolCalls: 'tool_calls'},
1040
+ },
1041
+ tool: {
1042
+ type: {
1043
+ function: 'function',
1044
+ },
1045
+ }},
1046
+ }};
1047
+
879
1048
  /* Note: Instrumentation scopes from all language SDKs */
880
1049
  const SCOPE_LATITUDE = 'so.latitude.instrumentation';
881
1050
  var InstrumentationScope;
@@ -904,43 +1073,6 @@ var InstrumentationScope;
904
1073
  InstrumentationScope["Transformers"] = "transformers";
905
1074
  InstrumentationScope["AlephAlpha"] = "alephalpha";
906
1075
  })(InstrumentationScope || (InstrumentationScope = {}));
907
- /* Note: non-standard OpenTelemetry semantic conventions used in Latitude */
908
- const ATTR_LATITUDE = 'latitude';
909
- const ATTR_LATITUDE_TYPE = `${ATTR_LATITUDE}.type`;
910
- const ATTR_LATITUDE_TEST_DEPLOYMENT_ID = `${ATTR_LATITUDE}.test_deployment_id`;
911
- const GEN_AI_TOOL_TYPE_VALUE_FUNCTION = 'function';
912
- const ATTR_GEN_AI_TOOL_CALL_ARGUMENTS = 'gen_ai.tool.call.arguments';
913
- const ATTR_GEN_AI_TOOL_RESULT_VALUE = 'gen_ai.tool.result.value';
914
- const ATTR_GEN_AI_TOOL_RESULT_IS_ERROR = 'gen_ai.tool.result.is_error';
915
- const ATTR_GEN_AI_REQUEST = 'gen_ai.request';
916
- const ATTR_GEN_AI_REQUEST_CONFIGURATION = 'gen_ai.request.configuration';
917
- const ATTR_GEN_AI_REQUEST_TEMPLATE = 'gen_ai.request.template';
918
- const ATTR_GEN_AI_REQUEST_PARAMETERS = 'gen_ai.request.parameters';
919
- const ATTR_GEN_AI_REQUEST_MESSAGES = 'gen_ai.request.messages';
920
- const ATTR_GEN_AI_RESPONSE = 'gen_ai.response';
921
- const ATTR_GEN_AI_RESPONSE_MESSAGES = 'gen_ai.response.messages';
922
- const ATTR_GEN_AI_USAGE_PROMPT_TOKENS = 'gen_ai.usage.prompt_tokens';
923
- const ATTR_GEN_AI_USAGE_CACHED_TOKENS = 'gen_ai.usage.cached_tokens';
924
- const ATTR_GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens'; // prettier-ignore
925
- const ATTR_GEN_AI_USAGE_COMPLETION_TOKENS = 'gen_ai.usage.completion_tokens'; // prettier-ignore
926
- const ATTR_GEN_AI_PROMPTS = 'gen_ai.prompt'; // gen_ai.prompt.{index}.{role/content/...}
927
- const ATTR_GEN_AI_COMPLETIONS = 'gen_ai.completion'; // gen_ai.completion.{index}.{role/content/...}
928
- const ATTR_GEN_AI_MESSAGE_ROLE = 'role';
929
- const ATTR_GEN_AI_MESSAGE_CONTENT = 'content'; // string or object
930
- const ATTR_GEN_AI_MESSAGE_TOOL_NAME = 'tool_name';
931
- const ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID = 'tool_call_id';
932
- const ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR = 'is_error';
933
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS = 'tool_calls'; // gen_ai.completion.{index}.tool_calls.{index}.{id/name/arguments}
934
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID = 'id';
935
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME = 'name';
936
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS = 'arguments';
937
- const GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP = 'stop';
938
- const GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS = 'tool_calls';
939
- const ATTR_HTTP_REQUEST_URL = 'http.request.url';
940
- const ATTR_HTTP_REQUEST_BODY = 'http.request.body';
941
- const ATTR_HTTP_REQUEST_HEADER = 'http.request.header';
942
- const ATTR_HTTP_RESPONSE_BODY = 'http.response.body';
943
- const ATTR_HTTP_RESPONSE_HEADER = 'http.response.header';
944
1076
  /* Note: Schemas for span ingestion following OpenTelemetry service request specification */
945
1077
  var Otlp;
946
1078
  (function (Otlp) {
@@ -1021,6 +1153,40 @@ var Otlp;
1021
1153
  });
1022
1154
  })(Otlp || (Otlp = {}));
1023
1155
 
1156
+ const MAX_SIMULATION_TURNS = 10;
1157
+ const SimulationSettingsSchema = zod.z.object({
1158
+ simulateToolResponses: zod.z.boolean().optional(),
1159
+ simulatedTools: zod.z.array(zod.z.string()).optional(), // Empty array means all tools are simulated (if simulateToolResponses is true).
1160
+ toolSimulationInstructions: zod.z.string().optional(), // A prompt used to guide and generate the simulation result
1161
+ 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.
1162
+ });
1163
+
1164
+ var OptimizationEngine;
1165
+ (function (OptimizationEngine) {
1166
+ OptimizationEngine["Identity"] = "identity";
1167
+ OptimizationEngine["Gepa"] = "gepa";
1168
+ })(OptimizationEngine || (OptimizationEngine = {}));
1169
+ const OptimizationBudgetSchema = zod.z.object({
1170
+ time: zod.z.number().min(0).optional(),
1171
+ tokens: zod.z.number().min(0).optional(),
1172
+ });
1173
+ zod.z.object({
1174
+ parameters: zod.z
1175
+ .record(zod.z.string(), zod.z.object({
1176
+ column: zod.z.string().optional(), // Note: corresponding column in the user-provided trainset and testset
1177
+ isPii: zod.z.boolean().optional(),
1178
+ }))
1179
+ .optional(),
1180
+ simulation: SimulationSettingsSchema.optional(),
1181
+ scope: zod.z
1182
+ .object({
1183
+ configuration: zod.z.boolean().optional(),
1184
+ instructions: zod.z.boolean().optional(),
1185
+ })
1186
+ .optional(),
1187
+ budget: OptimizationBudgetSchema.optional(),
1188
+ });
1189
+
1024
1190
  // TODO(tracing): deprecated
1025
1191
  const HEAD_COMMIT = 'live';
1026
1192
  var Providers;
@@ -1064,6 +1230,7 @@ var DocumentTriggerParameters;
1064
1230
  DocumentTriggerParameters["Body"] = "body";
1065
1231
  DocumentTriggerParameters["Attachments"] = "attachments";
1066
1232
  })(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
1233
+ const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
1067
1234
 
1068
1235
  class ManualInstrumentation {
1069
1236
  enabled;
@@ -1082,7 +1249,33 @@ class ManualInstrumentation {
1082
1249
  this.enabled = false;
1083
1250
  }
1084
1251
  resume(ctx) {
1085
- return otel.propagation.extract(otel__namespace.ROOT_CONTEXT, ctx);
1252
+ const parts = ctx.traceparent.split('-');
1253
+ if (parts.length !== 4) {
1254
+ return otel__namespace.ROOT_CONTEXT;
1255
+ }
1256
+ const [, traceId, spanId, flags] = parts;
1257
+ if (!traceId || !spanId) {
1258
+ return otel__namespace.ROOT_CONTEXT;
1259
+ }
1260
+ const spanContext = {
1261
+ traceId,
1262
+ spanId,
1263
+ traceFlags: parseInt(flags ?? '01', 16),
1264
+ isRemote: true,
1265
+ };
1266
+ let context = otel.trace.setSpanContext(otel__namespace.ROOT_CONTEXT, spanContext);
1267
+ if (ctx.baggage) {
1268
+ const baggageEntries = {};
1269
+ for (const pair of ctx.baggage.split(',')) {
1270
+ const [key, value] = pair.split('=');
1271
+ if (key && value) {
1272
+ baggageEntries[key] = { value: decodeURIComponent(value) };
1273
+ }
1274
+ }
1275
+ const baggage = otel.propagation.createBaggage(baggageEntries);
1276
+ context = otel.propagation.setBaggage(context, baggage);
1277
+ }
1278
+ return context;
1086
1279
  }
1087
1280
  capitalize(str) {
1088
1281
  if (str.length === 0)
@@ -1139,9 +1332,9 @@ class ManualInstrumentation {
1139
1332
  }
1140
1333
  const span = this.tracer.startSpan(name, {
1141
1334
  attributes: {
1142
- [ATTR_LATITUDE_TYPE]: type,
1335
+ [ATTRIBUTES.LATITUDE.type]: type,
1143
1336
  ...(operation && {
1144
- [incubating.ATTR_GEN_AI_OPERATION_NAME]: operation,
1337
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.operation]: operation,
1145
1338
  }),
1146
1339
  ...(start.attributes || {}),
1147
1340
  },
@@ -1167,15 +1360,15 @@ class ManualInstrumentation {
1167
1360
  try {
1168
1361
  jsonArguments = JSON.stringify(start.call.arguments);
1169
1362
  }
1170
- catch (error) {
1363
+ catch (_error) {
1171
1364
  jsonArguments = '{}';
1172
1365
  }
1173
1366
  const span = this.span(ctx, start.name, SpanType.Tool, {
1174
1367
  attributes: {
1175
- [incubating.ATTR_GEN_AI_TOOL_NAME]: start.name,
1176
- [incubating.ATTR_GEN_AI_TOOL_TYPE]: GEN_AI_TOOL_TYPE_VALUE_FUNCTION,
1177
- [incubating.ATTR_GEN_AI_TOOL_CALL_ID]: start.call.id,
1178
- [ATTR_GEN_AI_TOOL_CALL_ARGUMENTS]: jsonArguments,
1368
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.name]: start.name,
1369
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.type]: VALUES.OPENTELEMETRY.GEN_AI.tool.type.function,
1370
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.id]: start.call.id,
1371
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.arguments]: jsonArguments,
1179
1372
  ...(start.attributes || {}),
1180
1373
  },
1181
1374
  });
@@ -1188,7 +1381,7 @@ class ManualInstrumentation {
1188
1381
  try {
1189
1382
  stringResult = JSON.stringify(end.result.value);
1190
1383
  }
1191
- catch (error) {
1384
+ catch (_error) {
1192
1385
  stringResult = '{}';
1193
1386
  }
1194
1387
  }
@@ -1197,8 +1390,8 @@ class ManualInstrumentation {
1197
1390
  }
1198
1391
  span.end({
1199
1392
  attributes: {
1200
- [ATTR_GEN_AI_TOOL_RESULT_VALUE]: stringResult,
1201
- [ATTR_GEN_AI_TOOL_RESULT_IS_ERROR]: end.result.isError,
1393
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.value]: stringResult,
1394
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.isError]: end.result.isError,
1202
1395
  ...(end.attributes || {}),
1203
1396
  },
1204
1397
  });
@@ -1206,12 +1399,12 @@ class ManualInstrumentation {
1206
1399
  fail: span.fail,
1207
1400
  };
1208
1401
  }
1209
- attribifyMessageToolCalls(prefix, toolCalls) {
1402
+ attribifyMessageToolCalls(otelMessageField, toolCalls) {
1210
1403
  const attributes = {};
1211
1404
  for (let i = 0; i < toolCalls.length; i++) {
1212
1405
  for (const key in toolCalls[i]) {
1213
1406
  const field = this.toCamelCase(key);
1214
- let value = toolCalls[i][key];
1407
+ const value = toolCalls[i][key];
1215
1408
  if (value === null || value === undefined)
1216
1409
  continue;
1217
1410
  switch (field) {
@@ -1220,28 +1413,29 @@ class ManualInstrumentation {
1220
1413
  case 'toolUseId': {
1221
1414
  if (typeof value !== 'string')
1222
1415
  continue;
1223
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID}`] = value;
1416
+ attributes[otelMessageField.toolCalls(i).id] = value;
1224
1417
  break;
1225
1418
  }
1226
1419
  case 'name':
1227
1420
  case 'toolName': {
1228
1421
  if (typeof value !== 'string')
1229
1422
  continue;
1230
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value;
1423
+ attributes[otelMessageField.toolCalls(i).name] = value;
1231
1424
  break;
1232
1425
  }
1233
1426
  case 'arguments':
1234
1427
  case 'toolArguments':
1235
1428
  case 'input': {
1236
1429
  if (typeof value === 'string') {
1237
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value;
1430
+ attributes[otelMessageField.toolCalls(i).arguments] = value;
1238
1431
  }
1239
1432
  else {
1240
1433
  try {
1241
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = JSON.stringify(value);
1434
+ attributes[otelMessageField.toolCalls(i).arguments] =
1435
+ JSON.stringify(value);
1242
1436
  }
1243
- catch (error) {
1244
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = '{}';
1437
+ catch (_error) {
1438
+ attributes[otelMessageField.toolCalls(i).arguments] = '{}';
1245
1439
  }
1246
1440
  }
1247
1441
  break;
@@ -1258,8 +1452,9 @@ class ManualInstrumentation {
1258
1452
  continue;
1259
1453
  if (typeof value.arguments !== 'string')
1260
1454
  continue;
1261
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value.name;
1262
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value.arguments;
1455
+ attributes[otelMessageField.toolCalls(i).name] = value.name;
1456
+ attributes[otelMessageField.toolCalls(i).arguments] =
1457
+ value.arguments;
1263
1458
  break;
1264
1459
  }
1265
1460
  }
@@ -1267,18 +1462,16 @@ class ManualInstrumentation {
1267
1462
  }
1268
1463
  return attributes;
1269
1464
  }
1270
- attribifyMessageContent(prefix, content) {
1465
+ attribifyMessageContent(otelMessageField, content) {
1271
1466
  let attributes = {};
1272
1467
  if (typeof content === 'string') {
1273
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = content;
1274
1468
  return attributes;
1275
1469
  }
1276
1470
  try {
1277
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] =
1278
- JSON.stringify(content);
1471
+ attributes[otelMessageField.content] = JSON.stringify(content);
1279
1472
  }
1280
- catch (error) {
1281
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = '[]';
1473
+ catch (_error) {
1474
+ attributes[otelMessageField.content] = '[]';
1282
1475
  }
1283
1476
  if (!Array.isArray(content))
1284
1477
  return attributes;
@@ -1298,32 +1491,34 @@ class ManualInstrumentation {
1298
1491
  if (toolCalls.length > 0) {
1299
1492
  attributes = {
1300
1493
  ...attributes,
1301
- ...this.attribifyMessageToolCalls(prefix, toolCalls),
1494
+ ...this.attribifyMessageToolCalls(otelMessageField, toolCalls),
1302
1495
  };
1303
1496
  }
1304
1497
  return attributes;
1305
1498
  }
1306
1499
  attribifyMessages(direction, messages) {
1307
- const prefix = direction === 'input' ? ATTR_GEN_AI_PROMPTS : ATTR_GEN_AI_COMPLETIONS;
1500
+ const otelField = direction === 'input'
1501
+ ? ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.prompt
1502
+ : ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.completion;
1308
1503
  let attributes = {};
1309
1504
  for (let i = 0; i < messages.length; i++) {
1310
1505
  for (const key in messages[i]) {
1311
1506
  const field = this.toCamelCase(key);
1312
- let value = messages[i][key];
1507
+ const value = messages[i][key];
1313
1508
  if (value === null || value === undefined)
1314
1509
  continue;
1315
1510
  switch (field) {
1316
1511
  case 'role': {
1317
1512
  if (typeof value !== 'string')
1318
1513
  continue;
1319
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_ROLE}`] = value;
1514
+ attributes[otelField.index(i).role] = value;
1320
1515
  break;
1321
1516
  }
1322
1517
  /* Tool calls for Anthropic and PromptL are in the content */
1323
1518
  case 'content': {
1324
1519
  attributes = {
1325
1520
  ...attributes,
1326
- ...this.attribifyMessageContent(`${prefix}.${i}`, value),
1521
+ ...this.attribifyMessageContent(otelField.index(i), value),
1327
1522
  };
1328
1523
  break;
1329
1524
  }
@@ -1333,7 +1528,7 @@ class ManualInstrumentation {
1333
1528
  continue;
1334
1529
  attributes = {
1335
1530
  ...attributes,
1336
- ...this.attribifyMessageToolCalls(`${prefix}.${i}`, value),
1531
+ ...this.attribifyMessageToolCalls(otelField.index(i), value),
1337
1532
  };
1338
1533
  break;
1339
1534
  }
@@ -1343,22 +1538,20 @@ class ManualInstrumentation {
1343
1538
  case 'toolUseId': {
1344
1539
  if (typeof value !== 'string')
1345
1540
  continue;
1346
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID}`] =
1347
- value;
1541
+ attributes[otelField.index(i).tool.callId] = value;
1348
1542
  break;
1349
1543
  }
1350
1544
  case 'toolName': {
1351
1545
  if (typeof value !== 'string')
1352
1546
  continue;
1353
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_NAME}`] =
1354
- value;
1547
+ attributes[otelField.index(i).tool.toolName] = value;
1355
1548
  break;
1356
1549
  }
1357
1550
  // Note: 'toolResult' is 'content' itself
1358
1551
  case 'isError': {
1359
1552
  if (typeof value !== 'boolean')
1360
1553
  continue;
1361
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR}`] = value;
1554
+ attributes[otelField.index(i).tool.isError] = value;
1362
1555
  break;
1363
1556
  }
1364
1557
  }
@@ -1367,7 +1560,9 @@ class ManualInstrumentation {
1367
1560
  return attributes;
1368
1561
  }
1369
1562
  attribifyConfiguration(direction, configuration) {
1370
- const prefix = direction === 'input' ? ATTR_GEN_AI_REQUEST : ATTR_GEN_AI_RESPONSE;
1563
+ const prefix = direction === 'input'
1564
+ ? ATTRIBUTES.LATITUDE.request._root
1565
+ : ATTRIBUTES.LATITUDE.response._root;
1371
1566
  const attributes = {};
1372
1567
  for (const key in configuration) {
1373
1568
  const field = this.toSnakeCase(key);
@@ -1378,7 +1573,7 @@ class ManualInstrumentation {
1378
1573
  try {
1379
1574
  value = JSON.stringify(value);
1380
1575
  }
1381
- catch (error) {
1576
+ catch (_error) {
1382
1577
  value = '{}';
1383
1578
  }
1384
1579
  }
@@ -1389,64 +1584,75 @@ class ManualInstrumentation {
1389
1584
  completion(ctx, options) {
1390
1585
  const start = options;
1391
1586
  const configuration = {
1392
- ...start.configuration,
1587
+ ...(start.configuration ?? {}),
1393
1588
  model: start.model,
1394
1589
  };
1395
1590
  let jsonConfiguration = '';
1396
1591
  try {
1397
1592
  jsonConfiguration = JSON.stringify(configuration);
1398
1593
  }
1399
- catch (error) {
1594
+ catch (_error) {
1400
1595
  jsonConfiguration = '{}';
1401
1596
  }
1402
1597
  const attrConfiguration = this.attribifyConfiguration('input', configuration);
1598
+ const input = start.input ?? [];
1403
1599
  let jsonInput = '';
1404
1600
  try {
1405
- jsonInput = JSON.stringify(start.input);
1601
+ jsonInput = JSON.stringify(input);
1406
1602
  }
1407
- catch (error) {
1603
+ catch (_error) {
1408
1604
  jsonInput = '[]';
1409
1605
  }
1410
- const attrInput = this.attribifyMessages('input', start.input);
1606
+ const attrInput = this.attribifyMessages('input', input);
1411
1607
  const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
1412
1608
  attributes: {
1413
- [incubating.ATTR_GEN_AI_SYSTEM]: start.provider,
1414
- [ATTR_GEN_AI_REQUEST_CONFIGURATION]: jsonConfiguration,
1609
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.system]: start.provider,
1610
+ [ATTRIBUTES.LATITUDE.request.configuration]: jsonConfiguration,
1415
1611
  ...attrConfiguration,
1416
- [ATTR_GEN_AI_REQUEST_MESSAGES]: jsonInput,
1612
+ [ATTRIBUTES.LATITUDE.request.messages]: jsonInput,
1417
1613
  ...attrInput,
1418
1614
  ...(start.attributes || {}),
1419
- ['latitude.commitUuid']: start.versionUuid,
1420
- ['latitude.documentUuid']: start.promptUuid,
1421
- ['latitude.experimentUuid']: start.experimentUuid,
1615
+ [ATTRIBUTES.LATITUDE.commitUuid]: start.versionUuid,
1616
+ [ATTRIBUTES.LATITUDE.documentUuid]: start.promptUuid,
1617
+ [ATTRIBUTES.LATITUDE.experimentUuid]: start.experimentUuid,
1422
1618
  },
1423
1619
  });
1424
1620
  return {
1425
1621
  context: span.context,
1426
1622
  end: (options) => {
1427
- const end = options;
1623
+ const end = options ?? {};
1624
+ const output = end.output ?? [];
1428
1625
  let jsonOutput = '';
1429
1626
  try {
1430
- jsonOutput = JSON.stringify(end.output);
1627
+ jsonOutput = JSON.stringify(output);
1431
1628
  }
1432
- catch (error) {
1629
+ catch (_error) {
1433
1630
  jsonOutput = '[]';
1434
1631
  }
1435
- const attrOutput = this.attribifyMessages('output', end.output);
1436
- const inputTokens = end.tokens.prompt + end.tokens.cached;
1437
- const outputTokens = end.tokens.reasoning + end.tokens.completion;
1632
+ const attrOutput = this.attribifyMessages('output', output);
1633
+ const tokens = {
1634
+ prompt: end.tokens?.prompt ?? 0,
1635
+ cached: end.tokens?.cached ?? 0,
1636
+ reasoning: end.tokens?.reasoning ?? 0,
1637
+ completion: end.tokens?.completion ?? 0,
1638
+ };
1639
+ const inputTokens = tokens.prompt + tokens.cached;
1640
+ const outputTokens = tokens.reasoning + tokens.completion;
1641
+ const finishReason = end.finishReason ?? '';
1438
1642
  span.end({
1439
1643
  attributes: {
1440
- [ATTR_GEN_AI_RESPONSE_MESSAGES]: jsonOutput,
1644
+ [ATTRIBUTES.LATITUDE.response.messages]: jsonOutput,
1441
1645
  ...attrOutput,
1442
- [incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
1443
- [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: end.tokens.prompt,
1444
- [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: end.tokens.cached,
1445
- [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: end.tokens.reasoning,
1446
- [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: end.tokens.completion,
1447
- [incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
1448
- [incubating.ATTR_GEN_AI_RESPONSE_MODEL]: start.model,
1449
- [incubating.ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [end.finishReason],
1646
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.inputTokens]: inputTokens,
1647
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.outputTokens]: outputTokens,
1648
+ [ATTRIBUTES.LATITUDE.usage.promptTokens]: tokens.prompt,
1649
+ [ATTRIBUTES.LATITUDE.usage.cachedTokens]: tokens.cached,
1650
+ [ATTRIBUTES.LATITUDE.usage.reasoningTokens]: tokens.reasoning,
1651
+ [ATTRIBUTES.LATITUDE.usage.completionTokens]: tokens.completion,
1652
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.model]: start.model,
1653
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.finishReasons]: [
1654
+ finishReason,
1655
+ ],
1450
1656
  ...(end.attributes || {}),
1451
1657
  },
1452
1658
  });
@@ -1457,16 +1663,10 @@ class ManualInstrumentation {
1457
1663
  embedding(ctx, options) {
1458
1664
  return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Embedding].name, SpanType.Embedding, options);
1459
1665
  }
1460
- retrieval(ctx, options) {
1461
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Retrieval].name, SpanType.Retrieval, options);
1462
- }
1463
- reranking(ctx, options) {
1464
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Reranking].name, SpanType.Reranking, options);
1465
- }
1466
1666
  attribifyHeaders(direction, headers) {
1467
1667
  const prefix = direction === 'request'
1468
- ? ATTR_HTTP_REQUEST_HEADER
1469
- : ATTR_HTTP_RESPONSE_HEADER;
1668
+ ? ATTRIBUTES.OPENTELEMETRY.HTTP.request.header
1669
+ : ATTRIBUTES.OPENTELEMETRY.HTTP.response.header;
1470
1670
  const attributes = {};
1471
1671
  for (const key in headers) {
1472
1672
  const field = this.toKebabCase(key);
@@ -1490,16 +1690,16 @@ class ManualInstrumentation {
1490
1690
  try {
1491
1691
  finalBody = JSON.stringify(start.request.body);
1492
1692
  }
1493
- catch (error) {
1693
+ catch (_error) {
1494
1694
  finalBody = '{}';
1495
1695
  }
1496
1696
  }
1497
1697
  const span = this.span(ctx, start.name || `${method} ${start.request.url}`, SpanType.Http, {
1498
1698
  attributes: {
1499
- [semanticConventions.ATTR_HTTP_REQUEST_METHOD]: method,
1500
- [ATTR_HTTP_REQUEST_URL]: start.request.url,
1699
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.method]: method,
1700
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.url]: start.request.url,
1501
1701
  ...attrHeaders,
1502
- [ATTR_HTTP_REQUEST_BODY]: finalBody,
1702
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.body]: finalBody,
1503
1703
  ...(start.attributes || {}),
1504
1704
  },
1505
1705
  });
@@ -1517,15 +1717,15 @@ class ManualInstrumentation {
1517
1717
  try {
1518
1718
  finalBody = JSON.stringify(end.response.body);
1519
1719
  }
1520
- catch (error) {
1720
+ catch (_error) {
1521
1721
  finalBody = '{}';
1522
1722
  }
1523
1723
  }
1524
1724
  span.end({
1525
1725
  attributes: {
1526
- [semanticConventions.ATTR_HTTP_RESPONSE_STATUS_CODE]: end.response.status,
1726
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.response.statusCode]: end.response.status,
1527
1727
  ...attrHeaders,
1528
- [ATTR_HTTP_RESPONSE_BODY]: finalBody,
1728
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.response.body]: finalBody,
1529
1729
  ...(end.attributes || {}),
1530
1730
  },
1531
1731
  });
@@ -1538,49 +1738,84 @@ class ManualInstrumentation {
1538
1738
  try {
1539
1739
  jsonParameters = JSON.stringify(parameters || {});
1540
1740
  }
1541
- catch (error) {
1741
+ catch (_error) {
1542
1742
  jsonParameters = '{}';
1543
1743
  }
1544
1744
  const attributes = {
1545
- [ATTR_GEN_AI_REQUEST_TEMPLATE]: template,
1546
- [ATTR_GEN_AI_REQUEST_PARAMETERS]: jsonParameters,
1547
- ['latitude.commitUuid']: versionUuid || HEAD_COMMIT,
1548
- ['latitude.documentUuid']: promptUuid,
1549
- ['latitude.projectId']: projectId,
1550
- ...(documentLogUuid && { ['latitude.documentLogUuid']: documentLogUuid }),
1551
- ...(experimentUuid && { ['latitude.experimentUuid']: experimentUuid }),
1745
+ [ATTRIBUTES.LATITUDE.request.template]: template,
1746
+ [ATTRIBUTES.LATITUDE.request.parameters]: jsonParameters,
1747
+ [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid || HEAD_COMMIT,
1748
+ [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1749
+ [ATTRIBUTES.LATITUDE.projectId]: projectId,
1750
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1751
+ ...(experimentUuid && {
1752
+ [ATTRIBUTES.LATITUDE.experimentUuid]: experimentUuid,
1753
+ }),
1552
1754
  ...(testDeploymentId && {
1553
- [ATTR_LATITUDE_TEST_DEPLOYMENT_ID]: testDeploymentId,
1755
+ [ATTRIBUTES.LATITUDE.testDeploymentId]: testDeploymentId,
1554
1756
  }),
1555
- ...(externalId && { ['latitude.externalId']: externalId }),
1556
- ...(source && { ['latitude.source']: source }),
1757
+ ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1758
+ ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1557
1759
  ...(rest.attributes || {}),
1558
1760
  };
1559
1761
  return this.span(ctx, name || `prompt-${promptUuid}`, SpanType.Prompt, {
1560
1762
  attributes,
1561
1763
  });
1562
1764
  }
1563
- step(ctx, options) {
1564
- return this.span(ctx, 'step', SpanType.Step, options);
1765
+ chat(ctx, { documentLogUuid, previousTraceId, source, name, versionUuid, promptUuid, ...rest }) {
1766
+ const attributes = {
1767
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1768
+ [ATTRIBUTES.LATITUDE.previousTraceId]: previousTraceId,
1769
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1770
+ ...(promptUuid && { [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid }),
1771
+ ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1772
+ ...(rest.attributes || {}),
1773
+ };
1774
+ return this.span(ctx, name || 'chat', SpanType.Chat, { attributes });
1775
+ }
1776
+ external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
1777
+ const attributes = {
1778
+ [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1779
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1780
+ [ATTRIBUTES.LATITUDE.source]: source ?? LogSources.API,
1781
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1782
+ ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1783
+ ...(rest.attributes || {}),
1784
+ };
1785
+ return this.span(ctx, name || `external-${promptUuid}`, SpanType.External, {
1786
+ attributes,
1787
+ });
1788
+ }
1789
+ unresolvedExternal(ctx, { path, projectId, versionUuid, conversationUuid, name, ...rest }) {
1790
+ const attributes = {
1791
+ [ATTRIBUTES.LATITUDE.promptPath]: path,
1792
+ [ATTRIBUTES.LATITUDE.projectId]: projectId,
1793
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1794
+ ...(conversationUuid && {
1795
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: conversationUuid,
1796
+ }),
1797
+ ...(rest.attributes || {}),
1798
+ };
1799
+ return this.span(ctx, name || `capture-${path}`, SpanType.UnresolvedExternal, { attributes });
1565
1800
  }
1566
1801
  }
1567
1802
 
1568
1803
  class LatitudeInstrumentation {
1569
1804
  options;
1570
- telemetry;
1805
+ manualTelemetry;
1571
1806
  constructor(tracer, options) {
1572
- this.telemetry = new ManualInstrumentation(tracer);
1807
+ this.manualTelemetry = new ManualInstrumentation(tracer);
1573
1808
  this.options = options;
1574
1809
  }
1575
1810
  isEnabled() {
1576
- return this.telemetry.isEnabled();
1811
+ return this.manualTelemetry.isEnabled();
1577
1812
  }
1578
1813
  enable() {
1579
1814
  this.options.module.instrument(this);
1580
- this.telemetry.enable();
1815
+ this.manualTelemetry.enable();
1581
1816
  }
1582
1817
  disable() {
1583
- this.telemetry.disable();
1818
+ this.manualTelemetry.disable();
1584
1819
  this.options.module.uninstrument();
1585
1820
  }
1586
1821
  countTokens(messages) {
@@ -1604,7 +1839,8 @@ class LatitudeInstrumentation {
1604
1839
  }
1605
1840
  async wrapRenderChain(fn, ...args) {
1606
1841
  const { prompt, parameters } = args[0];
1607
- const $prompt = this.telemetry.prompt(otel.context.active(), {
1842
+ const $prompt = this.manualTelemetry.prompt(otel.context.active(), {
1843
+ documentLogUuid: uuid.v4(),
1608
1844
  versionUuid: prompt.versionUuid,
1609
1845
  promptUuid: prompt.uuid,
1610
1846
  template: prompt.content,
@@ -1621,26 +1857,13 @@ class LatitudeInstrumentation {
1621
1857
  $prompt.end();
1622
1858
  return result;
1623
1859
  }
1624
- async wrapRenderStep(fn, ...args) {
1625
- const $step = this.telemetry.step(otel.context.active());
1626
- let result;
1627
- try {
1628
- result = await otel.context.with($step.context, async () => await fn(...args));
1629
- }
1630
- catch (error) {
1631
- $step.fail(error);
1632
- throw error;
1633
- }
1634
- $step.end();
1635
- return result;
1636
- }
1637
1860
  async wrapRenderCompletion(fn, ...args) {
1638
1861
  if (!this.options.completions) {
1639
1862
  return await fn(...args);
1640
1863
  }
1641
1864
  const { provider, config, messages } = args[0];
1642
1865
  const model = config.model || 'unknown';
1643
- const $completion = this.telemetry.completion(otel.context.active(), {
1866
+ const $completion = this.manualTelemetry.completion(otel.context.active(), {
1644
1867
  name: `${provider} / ${model}`,
1645
1868
  provider: provider,
1646
1869
  model: model,
@@ -1667,14 +1890,14 @@ class LatitudeInstrumentation {
1667
1890
  completion: completionTokens,
1668
1891
  },
1669
1892
  finishReason: result.toolRequests.length > 0
1670
- ? GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS
1671
- : GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP,
1893
+ ? VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.toolCalls
1894
+ : VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.stop,
1672
1895
  });
1673
1896
  return result;
1674
1897
  }
1675
1898
  async wrapRenderTool(fn, ...args) {
1676
1899
  const { toolRequest } = args[0];
1677
- const $tool = this.telemetry.tool(otel.context.active(), {
1900
+ const $tool = this.manualTelemetry.tool(otel.context.active(), {
1678
1901
  name: toolRequest.toolName,
1679
1902
  call: {
1680
1903
  id: toolRequest.toolCallId,
@@ -1699,10 +1922,157 @@ class LatitudeInstrumentation {
1699
1922
  }
1700
1923
  }
1701
1924
 
1925
+ var LatitudeErrorCodes;
1926
+ (function (LatitudeErrorCodes) {
1927
+ LatitudeErrorCodes["UnexpectedError"] = "UnexpectedError";
1928
+ LatitudeErrorCodes["OverloadedError"] = "OverloadedError";
1929
+ LatitudeErrorCodes["RateLimitError"] = "RateLimitError";
1930
+ LatitudeErrorCodes["UnauthorizedError"] = "UnauthorizedError";
1931
+ LatitudeErrorCodes["ForbiddenError"] = "ForbiddenError";
1932
+ LatitudeErrorCodes["BadRequestError"] = "BadRequestError";
1933
+ LatitudeErrorCodes["NotFoundError"] = "NotFoundError";
1934
+ LatitudeErrorCodes["ConflictError"] = "ConflictError";
1935
+ LatitudeErrorCodes["UnprocessableEntityError"] = "UnprocessableEntityError";
1936
+ LatitudeErrorCodes["NotImplementedError"] = "NotImplementedError";
1937
+ LatitudeErrorCodes["PaymentRequiredError"] = "PaymentRequiredError";
1938
+ LatitudeErrorCodes["AbortedError"] = "AbortedError";
1939
+ LatitudeErrorCodes["BillingError"] = "BillingError";
1940
+ })(LatitudeErrorCodes || (LatitudeErrorCodes = {}));
1941
+ // NOTE: If you add a new error code, please add it to the pg enum in models/runErrors.ts
1942
+ var RunErrorCodes;
1943
+ (function (RunErrorCodes) {
1944
+ RunErrorCodes["AIProviderConfigError"] = "ai_provider_config_error";
1945
+ RunErrorCodes["AIRunError"] = "ai_run_error";
1946
+ RunErrorCodes["ChainCompileError"] = "chain_compile_error";
1947
+ RunErrorCodes["DefaultProviderExceededQuota"] = "default_provider_exceeded_quota_error";
1948
+ RunErrorCodes["DefaultProviderInvalidModel"] = "default_provider_invalid_model_error";
1949
+ RunErrorCodes["DocumentConfigError"] = "document_config_error";
1950
+ RunErrorCodes["ErrorGeneratingMockToolResult"] = "error_generating_mock_tool_result";
1951
+ RunErrorCodes["FailedToWakeUpIntegrationError"] = "failed_to_wake_up_integration_error";
1952
+ RunErrorCodes["InvalidResponseFormatError"] = "invalid_response_format_error";
1953
+ RunErrorCodes["MaxStepCountExceededError"] = "max_step_count_exceeded_error";
1954
+ RunErrorCodes["MissingProvider"] = "missing_provider_error";
1955
+ RunErrorCodes["RateLimit"] = "rate_limit_error";
1956
+ RunErrorCodes["Unknown"] = "unknown_error";
1957
+ RunErrorCodes["UnsupportedProviderResponseTypeError"] = "unsupported_provider_response_type_error";
1958
+ RunErrorCodes["PaymentRequiredError"] = "payment_required_error";
1959
+ RunErrorCodes["AbortError"] = "abort_error";
1960
+ // DEPRECATED, but do not delete
1961
+ RunErrorCodes["EvaluationRunMissingProviderLogError"] = "ev_run_missing_provider_log_error";
1962
+ RunErrorCodes["EvaluationRunMissingWorkspaceError"] = "ev_run_missing_workspace_error";
1963
+ RunErrorCodes["EvaluationRunResponseJsonFormatError"] = "ev_run_response_json_format_error";
1964
+ RunErrorCodes["EvaluationRunUnsupportedResultTypeError"] = "ev_run_unsupported_result_type_error";
1965
+ })(RunErrorCodes || (RunErrorCodes = {}));
1966
+ var ApiErrorCodes;
1967
+ (function (ApiErrorCodes) {
1968
+ ApiErrorCodes["HTTPException"] = "http_exception";
1969
+ ApiErrorCodes["InternalServerError"] = "internal_server_error";
1970
+ })(ApiErrorCodes || (ApiErrorCodes = {}));
1971
+
1972
+ class LatitudeError extends Error {
1973
+ statusCode = 500;
1974
+ name = LatitudeErrorCodes.UnexpectedError;
1975
+ headers = {};
1976
+ details;
1977
+ constructor(message, details, status, name) {
1978
+ super(message);
1979
+ this.details = details ?? {};
1980
+ this.statusCode = status ?? this.statusCode;
1981
+ this.name = name ?? this.constructor.name;
1982
+ }
1983
+ serialize() {
1984
+ return {
1985
+ name: this.name,
1986
+ code: this.name,
1987
+ status: this.statusCode,
1988
+ message: this.message,
1989
+ details: this.details,
1990
+ };
1991
+ }
1992
+ static deserialize(json) {
1993
+ return new LatitudeError(json.message, json.details, json.status, json.name);
1994
+ }
1995
+ }
1996
+ class BadRequestError extends LatitudeError {
1997
+ statusCode = 400;
1998
+ name = LatitudeErrorCodes.BadRequestError;
1999
+ }
2000
+
1702
2001
  const TRACES_URL = `${env.GATEWAY_BASE_URL}/api/v3/traces`;
1703
2002
  const SERVICE_NAME = process.env.npm_package_name || 'unknown';
1704
2003
  const SCOPE_VERSION = process.env.npm_package_version || 'unknown';
1705
2004
  const BACKGROUND = () => otel__namespace.ROOT_CONTEXT;
2005
+ class SpanFactory {
2006
+ telemetry;
2007
+ constructor(telemetry) {
2008
+ this.telemetry = telemetry;
2009
+ }
2010
+ tool(options, ctx) {
2011
+ return this.telemetry.tool(ctx ?? otel.context.active(), options);
2012
+ }
2013
+ completion(options, ctx) {
2014
+ return this.telemetry.completion(ctx ?? otel.context.active(), options);
2015
+ }
2016
+ embedding(options, ctx) {
2017
+ return this.telemetry.embedding(ctx ?? otel.context.active(), options);
2018
+ }
2019
+ http(options, ctx) {
2020
+ return this.telemetry.http(ctx ?? otel.context.active(), options);
2021
+ }
2022
+ prompt(options, ctx) {
2023
+ return this.telemetry.prompt(ctx ?? otel.context.active(), options);
2024
+ }
2025
+ chat(options, ctx) {
2026
+ return this.telemetry.chat(ctx ?? otel.context.active(), options);
2027
+ }
2028
+ external(options, ctx) {
2029
+ return this.telemetry.external(ctx ?? otel.context.active(), options);
2030
+ }
2031
+ }
2032
+ class ContextManager {
2033
+ telemetry;
2034
+ constructor(telemetry) {
2035
+ this.telemetry = telemetry;
2036
+ }
2037
+ resume(ctx) {
2038
+ return this.telemetry.resume(ctx);
2039
+ }
2040
+ active() {
2041
+ return otel.context.active();
2042
+ }
2043
+ }
2044
+ class InstrumentationManager {
2045
+ instrumentations;
2046
+ constructor(instrumentations) {
2047
+ this.instrumentations = instrumentations;
2048
+ }
2049
+ enable() {
2050
+ this.instrumentations.forEach((instrumentation) => {
2051
+ if (!instrumentation.isEnabled())
2052
+ instrumentation.enable();
2053
+ });
2054
+ }
2055
+ disable() {
2056
+ this.instrumentations.forEach((instrumentation) => {
2057
+ if (instrumentation.isEnabled())
2058
+ instrumentation.disable();
2059
+ });
2060
+ }
2061
+ }
2062
+ class TracerManager {
2063
+ nodeProvider;
2064
+ scopeVersion;
2065
+ constructor(nodeProvider, scopeVersion) {
2066
+ this.nodeProvider = nodeProvider;
2067
+ this.scopeVersion = scopeVersion;
2068
+ }
2069
+ get(scope) {
2070
+ return this.provider(scope).getTracer('');
2071
+ }
2072
+ provider(scope) {
2073
+ return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${scope}`, this.scopeVersion, this.nodeProvider);
2074
+ }
2075
+ }
1706
2076
  class ScopedTracerProvider {
1707
2077
  scope;
1708
2078
  version;
@@ -1740,9 +2110,13 @@ exports.Instrumentation = void 0;
1740
2110
  })(exports.Instrumentation || (exports.Instrumentation = {}));
1741
2111
  class LatitudeTelemetry {
1742
2112
  options;
1743
- provider;
1744
- telemetry;
1745
- instrumentations;
2113
+ nodeProvider;
2114
+ manualInstrumentation;
2115
+ instrumentationsList;
2116
+ span;
2117
+ context;
2118
+ instrumentation;
2119
+ tracer;
1746
2120
  constructor(apiKey, options) {
1747
2121
  this.options = options || {};
1748
2122
  if (!this.options.exporter) {
@@ -1756,63 +2130,61 @@ class LatitudeTelemetry {
1756
2130
  new core.W3CBaggagePropagator(),
1757
2131
  ],
1758
2132
  }));
1759
- this.provider = new sdkTraceNode.NodeTracerProvider({
2133
+ this.nodeProvider = new sdkTraceNode.NodeTracerProvider({
1760
2134
  resource: new resources.Resource({ [semanticConventions.ATTR_SERVICE_NAME]: SERVICE_NAME }),
1761
2135
  });
1762
2136
  // Note: important, must run before the exporter span processors
1763
- this.provider.addSpanProcessor(new baggageSpanProcessor.BaggageSpanProcessor(baggageSpanProcessor.ALLOW_ALL_BAGGAGE_KEYS));
2137
+ this.nodeProvider.addSpanProcessor(new baggageSpanProcessor.BaggageSpanProcessor(baggageSpanProcessor.ALLOW_ALL_BAGGAGE_KEYS));
1764
2138
  if (this.options.processors) {
1765
2139
  this.options.processors.forEach((processor) => {
1766
- this.provider.addSpanProcessor(processor);
2140
+ this.nodeProvider.addSpanProcessor(processor);
1767
2141
  });
1768
2142
  }
1769
2143
  else {
1770
- this.provider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
2144
+ this.nodeProvider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
1771
2145
  }
1772
2146
  if (this.options.disableBatch) {
1773
- this.provider.addSpanProcessor(new sdkTraceNode.SimpleSpanProcessor(this.options.exporter));
2147
+ this.nodeProvider.addSpanProcessor(new sdkTraceNode.SimpleSpanProcessor(this.options.exporter));
1774
2148
  }
1775
2149
  else {
1776
- this.provider.addSpanProcessor(new sdkTraceNode.BatchSpanProcessor(this.options.exporter));
2150
+ this.nodeProvider.addSpanProcessor(new sdkTraceNode.BatchSpanProcessor(this.options.exporter));
1777
2151
  }
1778
- this.provider.register();
2152
+ this.nodeProvider.register();
1779
2153
  process.on('SIGTERM', async () => this.shutdown);
1780
2154
  process.on('SIGINT', async () => this.shutdown);
1781
- this.telemetry = null;
1782
- this.instrumentations = [];
2155
+ this.manualInstrumentation = null;
2156
+ this.instrumentationsList = [];
2157
+ this.tracer = new TracerManager(this.nodeProvider, SCOPE_VERSION);
1783
2158
  this.initInstrumentations();
1784
- this.instrument();
2159
+ this.instrumentation = new InstrumentationManager(this.instrumentationsList);
2160
+ this.instrumentation.enable();
2161
+ this.span = new SpanFactory(this.manualInstrumentation);
2162
+ this.context = new ContextManager(this.manualInstrumentation);
1785
2163
  }
1786
2164
  async flush() {
1787
- await this.provider.forceFlush();
2165
+ await this.nodeProvider.forceFlush();
1788
2166
  await this.options.exporter.forceFlush?.();
1789
2167
  }
1790
2168
  async shutdown() {
1791
2169
  await this.flush();
1792
- await this.provider.shutdown();
2170
+ await this.nodeProvider.shutdown();
1793
2171
  await this.options.exporter.shutdown?.();
1794
2172
  }
1795
- tracerProvider(instrumentation) {
1796
- return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${instrumentation}`, SCOPE_VERSION, this.provider);
1797
- }
1798
- tracer(instrumentation) {
1799
- return this.tracerProvider(instrumentation).getTracer('');
1800
- }
1801
2173
  // TODO(tracing): auto instrument outgoing HTTP requests
1802
2174
  initInstrumentations() {
1803
- this.instrumentations = [];
1804
- const tracer = this.tracer(InstrumentationScope.Manual);
1805
- this.telemetry = new ManualInstrumentation(tracer);
1806
- this.instrumentations.push(this.telemetry);
2175
+ this.instrumentationsList = [];
2176
+ const tracer = this.tracer.get(InstrumentationScope.Manual);
2177
+ this.manualInstrumentation = new ManualInstrumentation(tracer);
2178
+ this.instrumentationsList.push(this.manualInstrumentation);
1807
2179
  const latitude = this.options.instrumentations?.latitude;
1808
2180
  if (latitude) {
1809
- const tracer = this.tracer(exports.Instrumentation.Latitude);
2181
+ const tracer = this.tracer.get(exports.Instrumentation.Latitude);
1810
2182
  const instrumentation = new LatitudeInstrumentation(tracer, typeof latitude === 'object' ? latitude : { module: latitude });
1811
- this.instrumentations.push(instrumentation);
2183
+ this.instrumentationsList.push(instrumentation);
1812
2184
  }
1813
2185
  const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
1814
2186
  const providerPkg = this.options.instrumentations?.[instrumentationType];
1815
- const provider = this.tracerProvider(instrumentationType);
2187
+ const provider = this.tracer.provider(instrumentationType);
1816
2188
  const instrumentation$1 = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
1817
2189
  instrumentation$1.setTracerProvider(provider);
1818
2190
  if (providerPkg) {
@@ -1822,7 +2194,7 @@ class LatitudeTelemetry {
1822
2194
  instrumentations: [instrumentation$1],
1823
2195
  tracerProvider: provider,
1824
2196
  });
1825
- this.instrumentations.push(instrumentation$1);
2197
+ this.instrumentationsList.push(instrumentation$1);
1826
2198
  };
1827
2199
  configureInstrumentation(exports.Instrumentation.Anthropic, instrumentationAnthropic.AnthropicInstrumentation); // prettier-ignore
1828
2200
  configureInstrumentation(exports.Instrumentation.AIPlatform, instrumentationVertexai.AIPlatformInstrumentation); // prettier-ignore
@@ -1830,48 +2202,28 @@ class LatitudeTelemetry {
1830
2202
  configureInstrumentation(exports.Instrumentation.Cohere, instrumentationCohere.CohereInstrumentation); // prettier-ignore
1831
2203
  configureInstrumentation(exports.Instrumentation.Langchain, instrumentationLangchain.LangChainInstrumentation); // prettier-ignore
1832
2204
  configureInstrumentation(exports.Instrumentation.LlamaIndex, instrumentationLlamaindex.LlamaIndexInstrumentation); // prettier-ignore
1833
- configureInstrumentation(exports.Instrumentation.OpenAI, instrumentationOpenai.OpenAIInstrumentation, { enrichTokens: true }); // prettier-ignore
1834
- configureInstrumentation(exports.Instrumentation.TogetherAI, instrumentationTogether.TogetherInstrumentation, { enrichTokens: true }); // prettier-ignore
2205
+ // NOTE: `stream: true` in OpenAI make enrichTokens fail, so disabling.
2206
+ configureInstrumentation(exports.Instrumentation.OpenAI, instrumentationOpenai.OpenAIInstrumentation, { enrichTokens: false }); // prettier-ignore
2207
+ configureInstrumentation(exports.Instrumentation.TogetherAI, instrumentationTogether.TogetherInstrumentation, { enrichTokens: false }); // prettier-ignore
1835
2208
  configureInstrumentation(exports.Instrumentation.VertexAI, instrumentationVertexai.VertexAIInstrumentation); // prettier-ignore
1836
2209
  }
1837
- instrument() {
1838
- this.instrumentations.forEach((instrumentation) => {
1839
- if (!instrumentation.isEnabled())
1840
- instrumentation.enable();
1841
- });
1842
- }
1843
- uninstrument() {
1844
- this.instrumentations.forEach((instrumentation) => {
1845
- if (instrumentation.isEnabled())
1846
- instrumentation.disable();
1847
- });
1848
- }
1849
- resume(ctx) {
1850
- return this.telemetry.resume(ctx);
1851
- }
1852
- tool(ctx, options) {
1853
- return this.telemetry.tool(ctx, options);
1854
- }
1855
- completion(ctx, options) {
1856
- return this.telemetry.completion(ctx, options);
1857
- }
1858
- embedding(ctx, options) {
1859
- return this.telemetry.embedding(ctx, options);
1860
- }
1861
- retrieval(ctx, options) {
1862
- return this.telemetry.retrieval(ctx, options);
1863
- }
1864
- reranking(ctx, options) {
1865
- return this.telemetry.reranking(ctx, options);
1866
- }
1867
- http(ctx, options) {
1868
- return this.telemetry.http(ctx, options);
1869
- }
1870
- prompt(ctx, options) {
1871
- return this.telemetry.prompt(ctx, options);
1872
- }
1873
- step(ctx, options) {
1874
- return this.telemetry.step(ctx, options);
2210
+ async capture(options, fn) {
2211
+ if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
2212
+ throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
2213
+ }
2214
+ const span = this.manualInstrumentation.unresolvedExternal(otel__namespace.ROOT_CONTEXT, options);
2215
+ try {
2216
+ const result = await otel.context.with(span.context, () => fn(span.context));
2217
+ span.end();
2218
+ return result;
2219
+ }
2220
+ catch (error) {
2221
+ span.fail(error);
2222
+ throw error;
2223
+ }
2224
+ finally {
2225
+ await this.flush();
2226
+ }
1875
2227
  }
1876
2228
  }
1877
2229