@latitude-data/telemetry 1.1.0 → 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.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { z } from 'zod';
2
+ import { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_TYPE, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_RESPONSE_FINISH_REASONS, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_GEN_AI_USAGE_INPUT_TOKENS } from '@opentelemetry/semantic-conventions/incubating';
3
+ import { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
2
4
  import * as otel from '@opentelemetry/api';
3
5
  import { trace, propagation, context } from '@opentelemetry/api';
4
- import { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
5
- import { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_TYPE, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_RESPONSE_FINISH_REASONS, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_GEN_AI_USAGE_INPUT_TOKENS } from '@opentelemetry/semantic-conventions/incubating';
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";
@@ -130,6 +148,15 @@ z.object({
130
148
  isError: z.boolean().optional(),
131
149
  text: z.string().optional(),
132
150
  });
151
+ z.object({
152
+ inputTokens: z.number(),
153
+ outputTokens: z.number(),
154
+ promptTokens: z.number(),
155
+ completionTokens: z.number(),
156
+ totalTokens: z.number(),
157
+ reasoningTokens: z.number(),
158
+ cachedInputTokens: z.number(),
159
+ });
133
160
 
134
161
  var ParameterType;
135
162
  (function (ParameterType) {
@@ -170,10 +197,24 @@ const expectedOutputConfiguration = z.object({
170
197
  parsingFormat: z.enum(['string', 'json']),
171
198
  fieldAccessor: z.string().optional(), // Field accessor to get the output from if it's a key-value format
172
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
+ });
173
213
  const baseEvaluationConfiguration = z.object({
174
214
  reverseScale: z.boolean(), // If true, lower is better, otherwise, higher is better
175
215
  actualOutput: actualOutputConfiguration,
176
216
  expectedOutput: expectedOutputConfiguration.optional(),
217
+ trigger: triggerConfiguration.optional(),
177
218
  });
178
219
  const baseEvaluationResultMetadata = z.object({
179
220
  // configuration: Configuration snapshot is defined in every metric specification
@@ -189,7 +230,6 @@ const compositeEvaluationConfiguration = baseEvaluationConfiguration.extend({
189
230
  evaluationUuids: z.array(z.string()),
190
231
  minThreshold: z.number().optional(), // Threshold percentage
191
232
  maxThreshold: z.number().optional(), // Threshold percentage
192
- defaultTarget: z.boolean().optional(), // Default for optimizations and distillations
193
233
  });
194
234
  const compositeEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
195
235
  results: z.record(z.string(), // Evaluation uuid
@@ -256,12 +296,34 @@ const CompositeEvaluationSpecification = {
256
296
  },
257
297
  };
258
298
 
299
+ const selectedContextSchema = z.object({
300
+ messageIndex: z.number().int().nonnegative(),
301
+ contentBlockIndex: z.number().int().nonnegative(),
302
+ contentType: z.enum([
303
+ 'text',
304
+ 'reasoning',
305
+ 'image',
306
+ 'file',
307
+ 'tool-call',
308
+ 'tool-result',
309
+ ]),
310
+ textRange: z
311
+ .object({
312
+ start: z.number().int().nonnegative(),
313
+ end: z.number().int().nonnegative(),
314
+ })
315
+ .optional(),
316
+ selectedText: z.string().optional(),
317
+ toolCallId: z.string().optional(),
318
+ });
259
319
  const humanEvaluationConfiguration = baseEvaluationConfiguration.extend({
260
320
  enableControls: z.boolean().optional(), // UI annotation controls
261
321
  criteria: z.string().optional(),
262
322
  });
263
323
  const humanEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
264
324
  reason: z.string().optional(),
325
+ enrichedReason: z.string().optional(),
326
+ selectedContexts: z.array(selectedContextSchema).optional(),
265
327
  });
266
328
  const humanEvaluationResultError = baseEvaluationResultError.extend({});
267
329
  // BINARY
@@ -539,18 +601,8 @@ z.object({
539
601
  });
540
602
  z.object({
541
603
  evaluateLiveLogs: z.boolean().nullable().optional(),
542
- enableSuggestions: z.boolean().nullable().optional(),
543
- autoApplySuggestions: z.boolean().nullable().optional(),
544
604
  });
545
605
 
546
- var LegacyChainEventTypes;
547
- (function (LegacyChainEventTypes) {
548
- LegacyChainEventTypes["Error"] = "chain-error";
549
- LegacyChainEventTypes["Step"] = "chain-step";
550
- LegacyChainEventTypes["Complete"] = "chain-complete";
551
- LegacyChainEventTypes["StepComplete"] = "chain-step-complete";
552
- })(LegacyChainEventTypes || (LegacyChainEventTypes = {}));
553
-
554
606
  var ChainEventTypes;
555
607
  (function (ChainEventTypes) {
556
608
  ChainEventTypes["ChainCompleted"] = "chain-completed";
@@ -738,6 +790,7 @@ var LogSources;
738
790
  LogSources["ShadowTest"] = "shadow_test";
739
791
  LogSources["ABTestChallenger"] = "ab_test_challenger";
740
792
  LogSources["User"] = "user";
793
+ LogSources["Optimization"] = "optimization";
741
794
  })(LogSources || (LogSources = {}));
742
795
 
743
796
  var RunSourceGroup;
@@ -748,7 +801,8 @@ var RunSourceGroup;
748
801
  ({
749
802
  [RunSourceGroup.Production]: [
750
803
  LogSources.API,
751
- LogSources.Copilot,
804
+ LogSources.ShadowTest,
805
+ LogSources.ABTestChallenger,
752
806
  LogSources.EmailTrigger,
753
807
  LogSources.IntegrationTrigger,
754
808
  LogSources.ScheduledTrigger,
@@ -758,14 +812,6 @@ var RunSourceGroup;
758
812
  [RunSourceGroup.Playground]: [LogSources.Playground, LogSources.Experiment],
759
813
  });
760
814
 
761
- var MessageRole;
762
- (function (MessageRole) {
763
- MessageRole["system"] = "system";
764
- MessageRole["user"] = "user";
765
- MessageRole["assistant"] = "assistant";
766
- MessageRole["tool"] = "tool";
767
- })(MessageRole || (MessageRole = {}));
768
-
769
815
  var SpanKind;
770
816
  (function (SpanKind) {
771
817
  SpanKind["Internal"] = "internal";
@@ -777,26 +823,49 @@ var SpanKind;
777
823
  // Note: loosely based on OpenTelemetry GenAI semantic conventions
778
824
  var SpanType;
779
825
  (function (SpanType) {
780
- SpanType["Tool"] = "tool";
781
- SpanType["Completion"] = "completion";
782
- SpanType["Embedding"] = "embedding";
783
- SpanType["Retrieval"] = "retrieval";
784
- SpanType["Reranking"] = "reranking";
785
- SpanType["Http"] = "http";
786
- SpanType["Unknown"] = "unknown";
826
+ // Latitude wrappers
787
827
  SpanType["Prompt"] = "prompt";
788
828
  SpanType["Chat"] = "chat";
789
829
  SpanType["External"] = "external";
790
830
  SpanType["UnresolvedExternal"] = "unresolved_external";
791
- SpanType["Step"] = "step";
831
+ // Added a HTTP span to capture raw HTTP requests and responses when running from Latitude
832
+ SpanType["Http"] = "http";
833
+ // Any known span from supported specifications will be grouped into one of these types
834
+ SpanType["Completion"] = "completion";
835
+ SpanType["Tool"] = "tool";
836
+ SpanType["Embedding"] = "embedding";
837
+ SpanType["Unknown"] = "unknown";
792
838
  })(SpanType || (SpanType = {}));
839
+ [
840
+ SpanType.Prompt,
841
+ SpanType.External,
842
+ SpanType.Chat,
843
+ ];
793
844
  const SPAN_SPECIFICATIONS = {
794
- [SpanType.Tool]: {
795
- name: 'Tool',
796
- description: 'A tool call',
797
- isGenAI: true,
845
+ [SpanType.Prompt]: {
846
+ name: 'Prompt',
847
+ description: 'A prompt span',
848
+ isGenAI: false,
849
+ isHidden: false,
850
+ },
851
+ [SpanType.Chat]: {
852
+ name: 'Chat',
853
+ description: 'A chat continuation span',
854
+ isGenAI: false,
855
+ isHidden: false,
856
+ },
857
+ [SpanType.External]: {
858
+ name: 'External',
859
+ description: 'An external capture span',
860
+ isGenAI: false,
798
861
  isHidden: false,
799
862
  },
863
+ [SpanType.UnresolvedExternal]: {
864
+ name: 'Unresolved External',
865
+ description: 'An external span that needs path resolution before storage',
866
+ isGenAI: false,
867
+ isHidden: true,
868
+ },
800
869
  [SpanType.Completion]: {
801
870
  name: 'Completion',
802
871
  description: 'A completion call',
@@ -809,15 +878,9 @@ const SPAN_SPECIFICATIONS = {
809
878
  isGenAI: true,
810
879
  isHidden: false,
811
880
  },
812
- [SpanType.Retrieval]: {
813
- name: 'Retrieval',
814
- description: 'A retrieval call',
815
- isGenAI: true,
816
- isHidden: false,
817
- },
818
- [SpanType.Reranking]: {
819
- name: 'Reranking',
820
- description: 'A reranking call',
881
+ [SpanType.Tool]: {
882
+ name: 'Tool',
883
+ description: 'A tool call',
821
884
  isGenAI: true,
822
885
  isHidden: false,
823
886
  },
@@ -833,36 +896,6 @@ const SPAN_SPECIFICATIONS = {
833
896
  isGenAI: false,
834
897
  isHidden: true,
835
898
  },
836
- [SpanType.Prompt]: {
837
- name: 'Prompt',
838
- description: 'A prompt span',
839
- isGenAI: false,
840
- isHidden: false,
841
- },
842
- [SpanType.Chat]: {
843
- name: 'Chat',
844
- description: 'A chat continuation span',
845
- isGenAI: false,
846
- isHidden: false,
847
- },
848
- [SpanType.External]: {
849
- name: 'External',
850
- description: 'An external capture span',
851
- isGenAI: false,
852
- isHidden: false,
853
- },
854
- [SpanType.UnresolvedExternal]: {
855
- name: 'Unresolved External',
856
- description: 'An external span that needs path resolution before storage',
857
- isGenAI: false,
858
- isHidden: true,
859
- },
860
- [SpanType.Step]: {
861
- name: 'Step',
862
- description: 'A step span',
863
- isGenAI: false,
864
- isHidden: false,
865
- },
866
899
  };
867
900
  var SpanStatus;
868
901
  (function (SpanStatus) {
@@ -870,6 +903,11 @@ var SpanStatus;
870
903
  SpanStatus["Ok"] = "ok";
871
904
  SpanStatus["Error"] = "error";
872
905
  })(SpanStatus || (SpanStatus = {}));
906
+ new Set([
907
+ SpanType.Prompt,
908
+ SpanType.Chat,
909
+ SpanType.External,
910
+ ]);
873
911
 
874
912
  // Note: Traces are unmaterialized but this context is used to propagate the trace
875
913
  // See www.w3.org/TR/trace-context and w3c.github.io/baggage
@@ -879,6 +917,100 @@ z.object({
879
917
  baggage: z.string().optional(), // <key>=urlencoded(<value>)[,<key>=urlencoded(<value>)]*
880
918
  });
881
919
 
920
+ const ATTRIBUTES = {
921
+ // Custom attributes added and used by Latitude spans (Prompt / External / Chat)
922
+ LATITUDE: {
923
+ type: 'latitude.type',
924
+ documentUuid: 'latitude.document_uuid',
925
+ promptPath: 'latitude.prompt_path',
926
+ commitUuid: 'latitude.commit_uuid',
927
+ documentLogUuid: 'latitude.document_log_uuid',
928
+ projectId: 'latitude.project_id',
929
+ experimentUuid: 'latitude.experiment_uuid',
930
+ source: 'latitude.source',
931
+ externalId: 'latitude.external_id',
932
+ testDeploymentId: 'latitude.test_deployment_id',
933
+ previousTraceId: 'latitude.previous_trace_id',
934
+ // Custom additions to the GenAI semantic conventions (deprecated)
935
+ request: {
936
+ _root: 'gen_ai.request',
937
+ configuration: 'gen_ai.request.configuration',
938
+ template: 'gen_ai.request.template',
939
+ parameters: 'gen_ai.request.parameters'},
940
+ response: {
941
+ _root: 'gen_ai.response'},
942
+ usage: {
943
+ promptTokens: 'gen_ai.usage.prompt_tokens',
944
+ cachedTokens: 'gen_ai.usage.cached_tokens',
945
+ reasoningTokens: 'gen_ai.usage.reasoning_tokens',
946
+ completionTokens: 'gen_ai.usage.completion_tokens',
947
+ },
948
+ },
949
+ // Official OpenTelemetry semantic conventions
950
+ OPENTELEMETRY: {
951
+ HTTP: {
952
+ request: {
953
+ url: 'http.request.url',
954
+ body: 'http.request.body',
955
+ header: 'http.request.header',
956
+ method: ATTR_HTTP_REQUEST_METHOD,
957
+ },
958
+ response: {
959
+ body: 'http.response.body',
960
+ header: 'http.response.header',
961
+ statusCode: ATTR_HTTP_RESPONSE_STATUS_CODE,
962
+ },
963
+ },
964
+ // GenAI semantic conventions
965
+ // https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/
966
+ GEN_AI: {
967
+ operation: ATTR_GEN_AI_OPERATION_NAME,
968
+ response: {
969
+ model: ATTR_GEN_AI_RESPONSE_MODEL,
970
+ finishReasons: ATTR_GEN_AI_RESPONSE_FINISH_REASONS,
971
+ },
972
+ usage: {
973
+ inputTokens: ATTR_GEN_AI_USAGE_INPUT_TOKENS,
974
+ outputTokens: ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
975
+ },
976
+ systemInstructions: 'gen_ai.system.instructions', // Contains the PARTS of the "system message"
977
+ tool: {
978
+ call: {
979
+ id: ATTR_GEN_AI_TOOL_CALL_ID,
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
+ },
987
+ _deprecated: {
988
+ system: ATTR_GEN_AI_SYSTEM,
989
+ tool: {
990
+ name: ATTR_GEN_AI_TOOL_NAME,
991
+ type: 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
+ },
998
+ }};
999
+ const VALUES = {
1000
+ OPENTELEMETRY: {
1001
+ GEN_AI: {
1002
+ response: {
1003
+ finishReasons: {
1004
+ stop: 'stop',
1005
+ toolCalls: 'tool_calls'},
1006
+ },
1007
+ tool: {
1008
+ type: {
1009
+ function: 'function',
1010
+ },
1011
+ }},
1012
+ }};
1013
+
882
1014
  /* Note: Instrumentation scopes from all language SDKs */
883
1015
  const SCOPE_LATITUDE = 'so.latitude.instrumentation';
884
1016
  var InstrumentationScope;
@@ -907,52 +1039,6 @@ var InstrumentationScope;
907
1039
  InstrumentationScope["Transformers"] = "transformers";
908
1040
  InstrumentationScope["AlephAlpha"] = "alephalpha";
909
1041
  })(InstrumentationScope || (InstrumentationScope = {}));
910
- /* Note: non-standard OpenTelemetry semantic conventions used in Latitude */
911
- const ATTR_LATITUDE = 'latitude';
912
- const ATTR_LATITUDE_TYPE = `${ATTR_LATITUDE}.type`;
913
- const ATTR_LATITUDE_DOCUMENT_UUID = `${ATTR_LATITUDE}.document_uuid`;
914
- const ATTR_LATITUDE_PROMPT_PATH = `${ATTR_LATITUDE}.prompt_path`;
915
- const ATTR_LATITUDE_COMMIT_UUID = `${ATTR_LATITUDE}.commit_uuid`;
916
- const ATTR_LATITUDE_DOCUMENT_LOG_UUID = `${ATTR_LATITUDE}.document_log_uuid`;
917
- const ATTR_LATITUDE_PROJECT_ID = `${ATTR_LATITUDE}.project_id`;
918
- const ATTR_LATITUDE_EXPERIMENT_UUID = `${ATTR_LATITUDE}.experiment_uuid`;
919
- const ATTR_LATITUDE_SOURCE = `${ATTR_LATITUDE}.source`;
920
- const ATTR_LATITUDE_EXTERNAL_ID = `${ATTR_LATITUDE}.external_id`;
921
- const ATTR_LATITUDE_TEST_DEPLOYMENT_ID = `${ATTR_LATITUDE}.test_deployment_id`;
922
- const ATTR_LATITUDE_PREVIOUS_TRACE_ID = `${ATTR_LATITUDE}.previous_trace_id`;
923
- const GEN_AI_TOOL_TYPE_VALUE_FUNCTION = 'function';
924
- const ATTR_GEN_AI_TOOL_CALL_ARGUMENTS = 'gen_ai.tool.call.arguments';
925
- const ATTR_GEN_AI_TOOL_RESULT_VALUE = 'gen_ai.tool.result.value';
926
- const ATTR_GEN_AI_TOOL_RESULT_IS_ERROR = 'gen_ai.tool.result.is_error';
927
- const ATTR_GEN_AI_REQUEST = 'gen_ai.request';
928
- const ATTR_GEN_AI_REQUEST_CONFIGURATION = 'gen_ai.request.configuration';
929
- const ATTR_GEN_AI_REQUEST_TEMPLATE = 'gen_ai.request.template';
930
- const ATTR_GEN_AI_REQUEST_PARAMETERS = 'gen_ai.request.parameters';
931
- const ATTR_GEN_AI_REQUEST_MESSAGES = 'gen_ai.request.messages';
932
- const ATTR_GEN_AI_RESPONSE = 'gen_ai.response';
933
- const ATTR_GEN_AI_RESPONSE_MESSAGES = 'gen_ai.response.messages';
934
- const ATTR_GEN_AI_USAGE_PROMPT_TOKENS = 'gen_ai.usage.prompt_tokens';
935
- const ATTR_GEN_AI_USAGE_CACHED_TOKENS = 'gen_ai.usage.cached_tokens';
936
- const ATTR_GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens'; // prettier-ignore
937
- const ATTR_GEN_AI_USAGE_COMPLETION_TOKENS = 'gen_ai.usage.completion_tokens'; // prettier-ignore
938
- const ATTR_GEN_AI_PROMPTS = 'gen_ai.prompt'; // gen_ai.prompt.{index}.{role/content/...}
939
- const ATTR_GEN_AI_COMPLETIONS = 'gen_ai.completion'; // gen_ai.completion.{index}.{role/content/...}
940
- const ATTR_GEN_AI_MESSAGE_ROLE = 'role';
941
- const ATTR_GEN_AI_MESSAGE_CONTENT = 'content'; // string or object
942
- const ATTR_GEN_AI_MESSAGE_TOOL_NAME = 'tool_name';
943
- const ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID = 'tool_call_id';
944
- const ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR = 'is_error';
945
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS = 'tool_calls'; // gen_ai.completion.{index}.tool_calls.{index}.{id/name/arguments}
946
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID = 'id';
947
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME = 'name';
948
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS = 'arguments';
949
- const GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP = 'stop';
950
- const GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS = 'tool_calls';
951
- const ATTR_HTTP_REQUEST_URL = 'http.request.url';
952
- const ATTR_HTTP_REQUEST_BODY = 'http.request.body';
953
- const ATTR_HTTP_REQUEST_HEADER = 'http.request.header';
954
- const ATTR_HTTP_RESPONSE_BODY = 'http.response.body';
955
- const ATTR_HTTP_RESPONSE_HEADER = 'http.response.header';
956
1042
  /* Note: Schemas for span ingestion following OpenTelemetry service request specification */
957
1043
  var Otlp;
958
1044
  (function (Otlp) {
@@ -1033,6 +1119,54 @@ var Otlp;
1033
1119
  });
1034
1120
  })(Otlp || (Otlp = {}));
1035
1121
 
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
+ ]);
1135
+ const SimulationSettingsSchema = z.object({
1136
+ simulateToolResponses: z.boolean().optional(),
1137
+ simulatedTools: z.array(z.string()).optional(), // Empty array means all tools are simulated (if simulateToolResponses is true).
1138
+ toolSimulationInstructions: z.string().optional(), // A prompt used to guide and generate the simulation result
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
+ });
1143
+
1144
+ var OptimizationEngine;
1145
+ (function (OptimizationEngine) {
1146
+ OptimizationEngine["Identity"] = "identity";
1147
+ OptimizationEngine["Gepa"] = "gepa";
1148
+ })(OptimizationEngine || (OptimizationEngine = {}));
1149
+ const OptimizationBudgetSchema = z.object({
1150
+ time: z.number().min(0).optional(),
1151
+ tokens: z.number().min(0).optional(),
1152
+ });
1153
+ z.object({
1154
+ parameters: z
1155
+ .record(z.string(), z.object({
1156
+ column: z.string().optional(), // Note: corresponding column in the user-provided trainset and testset
1157
+ isPii: z.boolean().optional(),
1158
+ }))
1159
+ .optional(),
1160
+ simulation: SimulationSettingsSchema.optional(),
1161
+ scope: z
1162
+ .object({
1163
+ configuration: z.boolean().optional(),
1164
+ instructions: z.boolean().optional(),
1165
+ })
1166
+ .optional(),
1167
+ budget: OptimizationBudgetSchema.optional(),
1168
+ });
1169
+
1036
1170
  // TODO(tracing): deprecated
1037
1171
  const HEAD_COMMIT = 'live';
1038
1172
  var Providers;
@@ -1078,12 +1212,18 @@ var DocumentTriggerParameters;
1078
1212
  })(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
1079
1213
  const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
1080
1214
 
1215
+ const translator = new Translator({
1216
+ filterEmptyMessages: true,
1217
+ providerMetadata: 'preserve',
1218
+ });
1081
1219
  class ManualInstrumentation {
1082
1220
  enabled;
1083
1221
  tracer;
1084
- constructor(tracer) {
1222
+ options;
1223
+ constructor(tracer, options) {
1085
1224
  this.enabled = false;
1086
1225
  this.tracer = tracer;
1226
+ this.options = options ?? {};
1087
1227
  }
1088
1228
  isEnabled() {
1089
1229
  return this.enabled;
@@ -1123,36 +1263,6 @@ class ManualInstrumentation {
1123
1263
  }
1124
1264
  return context;
1125
1265
  }
1126
- capitalize(str) {
1127
- if (str.length === 0)
1128
- return str;
1129
- return str.charAt(0).toUpperCase() + str.toLowerCase().slice(1);
1130
- }
1131
- toCamelCase(str) {
1132
- return str
1133
- .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
1134
- .replace(/[^A-Za-z0-9]+/g, ' ')
1135
- .trim()
1136
- .split(' ')
1137
- .map((w, i) => (i ? this.capitalize(w) : w.toLowerCase()))
1138
- .join('');
1139
- }
1140
- toSnakeCase(str) {
1141
- return str
1142
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
1143
- .replace(/[^A-Za-z0-9]+/g, '_')
1144
- .replace(/_+/g, '_')
1145
- .replace(/^_+|_+$/g, '')
1146
- .toLowerCase();
1147
- }
1148
- toKebabCase(input) {
1149
- return input
1150
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
1151
- .replace(/[^A-Za-z0-9]+/g, '-')
1152
- .replace(/-+/g, '-')
1153
- .replace(/^-+|-+$/g, '')
1154
- .toLowerCase();
1155
- }
1156
1266
  error(span, error, options) {
1157
1267
  options = options || {};
1158
1268
  span.recordException(error);
@@ -1178,9 +1288,9 @@ class ManualInstrumentation {
1178
1288
  }
1179
1289
  const span = this.tracer.startSpan(name, {
1180
1290
  attributes: {
1181
- [ATTR_LATITUDE_TYPE]: type,
1291
+ [ATTRIBUTES.LATITUDE.type]: type,
1182
1292
  ...(operation && {
1183
- [ATTR_GEN_AI_OPERATION_NAME]: operation,
1293
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.operation]: operation,
1184
1294
  }),
1185
1295
  ...(start.attributes || {}),
1186
1296
  },
@@ -1200,26 +1310,29 @@ class ManualInstrumentation {
1200
1310
  },
1201
1311
  };
1202
1312
  }
1313
+ unknown(ctx, options) {
1314
+ return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Unknown].name, SpanType.Unknown, options);
1315
+ }
1203
1316
  tool(ctx, options) {
1204
1317
  const start = options;
1205
1318
  let jsonArguments = '';
1206
1319
  try {
1207
1320
  jsonArguments = JSON.stringify(start.call.arguments);
1208
1321
  }
1209
- catch (error) {
1322
+ catch (_error) {
1210
1323
  jsonArguments = '{}';
1211
1324
  }
1212
1325
  const span = this.span(ctx, start.name, SpanType.Tool, {
1213
1326
  attributes: {
1214
- [ATTR_GEN_AI_TOOL_NAME]: start.name,
1215
- [ATTR_GEN_AI_TOOL_TYPE]: GEN_AI_TOOL_TYPE_VALUE_FUNCTION,
1216
- [ATTR_GEN_AI_TOOL_CALL_ID]: start.call.id,
1217
- [ATTR_GEN_AI_TOOL_CALL_ARGUMENTS]: jsonArguments,
1327
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.name]: start.name,
1328
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.type]: VALUES.OPENTELEMETRY.GEN_AI.tool.type.function,
1329
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.id]: start.call.id,
1330
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.arguments]: jsonArguments,
1218
1331
  ...(start.attributes || {}),
1219
1332
  },
1220
1333
  });
1221
1334
  return {
1222
- context: span.context,
1335
+ ...span,
1223
1336
  end: (options) => {
1224
1337
  const end = options;
1225
1338
  let stringResult = '';
@@ -1227,7 +1340,7 @@ class ManualInstrumentation {
1227
1340
  try {
1228
1341
  stringResult = JSON.stringify(end.result.value);
1229
1342
  }
1230
- catch (error) {
1343
+ catch (_error) {
1231
1344
  stringResult = '{}';
1232
1345
  }
1233
1346
  }
@@ -1236,180 +1349,21 @@ class ManualInstrumentation {
1236
1349
  }
1237
1350
  span.end({
1238
1351
  attributes: {
1239
- [ATTR_GEN_AI_TOOL_RESULT_VALUE]: stringResult,
1240
- [ATTR_GEN_AI_TOOL_RESULT_IS_ERROR]: end.result.isError,
1352
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.value]: stringResult,
1353
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.isError]: end.result.isError,
1241
1354
  ...(end.attributes || {}),
1242
1355
  },
1243
1356
  });
1244
1357
  },
1245
- fail: span.fail,
1246
1358
  };
1247
1359
  }
1248
- attribifyMessageToolCalls(prefix, toolCalls) {
1249
- const attributes = {};
1250
- for (let i = 0; i < toolCalls.length; i++) {
1251
- for (const key in toolCalls[i]) {
1252
- const field = this.toCamelCase(key);
1253
- let value = toolCalls[i][key];
1254
- if (value === null || value === undefined)
1255
- continue;
1256
- switch (field) {
1257
- case 'id':
1258
- case 'toolCallId':
1259
- case 'toolUseId': {
1260
- if (typeof value !== 'string')
1261
- continue;
1262
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID}`] = value;
1263
- break;
1264
- }
1265
- case 'name':
1266
- case 'toolName': {
1267
- if (typeof value !== 'string')
1268
- continue;
1269
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value;
1270
- break;
1271
- }
1272
- case 'arguments':
1273
- case 'toolArguments':
1274
- case 'input': {
1275
- if (typeof value === 'string') {
1276
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value;
1277
- }
1278
- else {
1279
- try {
1280
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = JSON.stringify(value);
1281
- }
1282
- catch (error) {
1283
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = '{}';
1284
- }
1285
- }
1286
- break;
1287
- }
1288
- /* OpenAI function calls */
1289
- case 'function': {
1290
- if (typeof value !== 'object')
1291
- continue;
1292
- if (!('name' in value))
1293
- continue;
1294
- if (typeof value.name !== 'string')
1295
- continue;
1296
- if (!('arguments' in value))
1297
- continue;
1298
- if (typeof value.arguments !== 'string')
1299
- continue;
1300
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value.name;
1301
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value.arguments;
1302
- break;
1303
- }
1304
- }
1305
- }
1306
- }
1307
- return attributes;
1308
- }
1309
- attribifyMessageContent(prefix, content) {
1310
- let attributes = {};
1311
- if (typeof content === 'string') {
1312
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = content;
1313
- return attributes;
1314
- }
1315
- try {
1316
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] =
1317
- JSON.stringify(content);
1318
- }
1319
- catch (error) {
1320
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = '[]';
1321
- }
1322
- if (!Array.isArray(content))
1323
- return attributes;
1324
- /* Tool calls for Anthropic and PromptL are in the content */
1325
- const toolCalls = [];
1326
- for (const item of content) {
1327
- for (const key in item) {
1328
- if (this.toCamelCase(key) !== 'type')
1329
- continue;
1330
- if (typeof item[key] !== 'string')
1331
- continue;
1332
- if (item[key] !== 'tool-call' && item[key] !== 'tool_use')
1333
- continue;
1334
- toolCalls.push(item);
1335
- }
1336
- }
1337
- if (toolCalls.length > 0) {
1338
- attributes = {
1339
- ...attributes,
1340
- ...this.attribifyMessageToolCalls(prefix, toolCalls),
1341
- };
1342
- }
1343
- return attributes;
1344
- }
1345
- attribifyMessages(direction, messages) {
1346
- const prefix = direction === 'input' ? ATTR_GEN_AI_PROMPTS : ATTR_GEN_AI_COMPLETIONS;
1347
- let attributes = {};
1348
- for (let i = 0; i < messages.length; i++) {
1349
- for (const key in messages[i]) {
1350
- const field = this.toCamelCase(key);
1351
- let value = messages[i][key];
1352
- if (value === null || value === undefined)
1353
- continue;
1354
- switch (field) {
1355
- case 'role': {
1356
- if (typeof value !== 'string')
1357
- continue;
1358
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_ROLE}`] = value;
1359
- break;
1360
- }
1361
- /* Tool calls for Anthropic and PromptL are in the content */
1362
- case 'content': {
1363
- attributes = {
1364
- ...attributes,
1365
- ...this.attribifyMessageContent(`${prefix}.${i}`, value),
1366
- };
1367
- break;
1368
- }
1369
- /* Tool calls for OpenAI */
1370
- case 'toolCalls': {
1371
- if (!Array.isArray(value))
1372
- continue;
1373
- attributes = {
1374
- ...attributes,
1375
- ...this.attribifyMessageToolCalls(`${prefix}.${i}`, value),
1376
- };
1377
- break;
1378
- }
1379
- /* Tool result for OpenAI / Anthropic / PromptL */
1380
- case 'toolCallId':
1381
- case 'toolId':
1382
- case 'toolUseId': {
1383
- if (typeof value !== 'string')
1384
- continue;
1385
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID}`] =
1386
- value;
1387
- break;
1388
- }
1389
- case 'toolName': {
1390
- if (typeof value !== 'string')
1391
- continue;
1392
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_NAME}`] =
1393
- value;
1394
- break;
1395
- }
1396
- // Note: 'toolResult' is 'content' itself
1397
- case 'isError': {
1398
- if (typeof value !== 'boolean')
1399
- continue;
1400
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR}`] = value;
1401
- break;
1402
- }
1403
- }
1404
- }
1405
- }
1406
- return attributes;
1407
- }
1408
1360
  attribifyConfiguration(direction, configuration) {
1409
- const prefix = direction === 'input' ? ATTR_GEN_AI_REQUEST : ATTR_GEN_AI_RESPONSE;
1361
+ const prefix = direction === 'input'
1362
+ ? ATTRIBUTES.LATITUDE.request._root
1363
+ : ATTRIBUTES.LATITUDE.response._root;
1410
1364
  const attributes = {};
1411
1365
  for (const key in configuration) {
1412
- const field = this.toSnakeCase(key);
1366
+ const field = toSnakeCase(key);
1413
1367
  let value = configuration[key];
1414
1368
  if (value === null || value === undefined)
1415
1369
  continue;
@@ -1417,7 +1371,7 @@ class ManualInstrumentation {
1417
1371
  try {
1418
1372
  value = JSON.stringify(value);
1419
1373
  }
1420
- catch (error) {
1374
+ catch (_error) {
1421
1375
  value = '{}';
1422
1376
  }
1423
1377
  }
@@ -1435,45 +1389,56 @@ class ManualInstrumentation {
1435
1389
  try {
1436
1390
  jsonConfiguration = JSON.stringify(configuration);
1437
1391
  }
1438
- catch (error) {
1392
+ catch (_error) {
1439
1393
  jsonConfiguration = '{}';
1440
1394
  }
1441
1395
  const attrConfiguration = this.attribifyConfiguration('input', configuration);
1442
1396
  const input = start.input ?? [];
1397
+ let jsonSystem = '';
1443
1398
  let jsonInput = '';
1444
1399
  try {
1445
- jsonInput = JSON.stringify(input);
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 ?? []);
1446
1407
  }
1447
- catch (error) {
1408
+ catch (_error) {
1409
+ jsonSystem = '[]';
1448
1410
  jsonInput = '[]';
1449
1411
  }
1450
- const attrInput = this.attribifyMessages('input', input);
1451
1412
  const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
1452
1413
  attributes: {
1453
- [ATTR_GEN_AI_SYSTEM]: start.provider,
1454
- [ATTR_GEN_AI_REQUEST_CONFIGURATION]: jsonConfiguration,
1414
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.system]: start.provider,
1415
+ [ATTRIBUTES.LATITUDE.request.configuration]: jsonConfiguration,
1455
1416
  ...attrConfiguration,
1456
- [ATTR_GEN_AI_REQUEST_MESSAGES]: jsonInput,
1457
- ...attrInput,
1417
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.systemInstructions]: jsonSystem,
1418
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.input.messages]: jsonInput,
1458
1419
  ...(start.attributes || {}),
1459
- [ATTR_LATITUDE_COMMIT_UUID]: start.versionUuid,
1460
- [ATTR_LATITUDE_DOCUMENT_UUID]: start.promptUuid,
1461
- [ATTR_LATITUDE_EXPERIMENT_UUID]: start.experimentUuid,
1420
+ [ATTRIBUTES.LATITUDE.commitUuid]: start.versionUuid,
1421
+ [ATTRIBUTES.LATITUDE.documentUuid]: start.promptUuid,
1422
+ [ATTRIBUTES.LATITUDE.experimentUuid]: start.experimentUuid,
1462
1423
  },
1463
1424
  });
1464
1425
  return {
1465
- context: span.context,
1426
+ ...span,
1466
1427
  end: (options) => {
1467
1428
  const end = options ?? {};
1468
1429
  const output = end.output ?? [];
1469
1430
  let jsonOutput = '';
1470
1431
  try {
1471
- jsonOutput = JSON.stringify(output);
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 ?? []);
1472
1438
  }
1473
- catch (error) {
1439
+ catch (_error) {
1474
1440
  jsonOutput = '[]';
1475
1441
  }
1476
- const attrOutput = this.attribifyMessages('output', output);
1477
1442
  const tokens = {
1478
1443
  prompt: end.tokens?.prompt ?? 0,
1479
1444
  cached: end.tokens?.cached ?? 0,
@@ -1485,39 +1450,33 @@ class ManualInstrumentation {
1485
1450
  const finishReason = end.finishReason ?? '';
1486
1451
  span.end({
1487
1452
  attributes: {
1488
- [ATTR_GEN_AI_RESPONSE_MESSAGES]: jsonOutput,
1489
- ...attrOutput,
1490
- [ATTR_GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
1491
- [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: tokens.prompt,
1492
- [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: tokens.cached,
1493
- [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: tokens.reasoning,
1494
- [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: tokens.completion,
1495
- [ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
1496
- [ATTR_GEN_AI_RESPONSE_MODEL]: start.model,
1497
- [ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [finishReason],
1453
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.output.messages]: jsonOutput,
1454
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.inputTokens]: inputTokens,
1455
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.outputTokens]: outputTokens,
1456
+ [ATTRIBUTES.LATITUDE.usage.promptTokens]: tokens.prompt,
1457
+ [ATTRIBUTES.LATITUDE.usage.cachedTokens]: tokens.cached,
1458
+ [ATTRIBUTES.LATITUDE.usage.reasoningTokens]: tokens.reasoning,
1459
+ [ATTRIBUTES.LATITUDE.usage.completionTokens]: tokens.completion,
1460
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.model]: start.model,
1461
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.finishReasons]: [
1462
+ finishReason,
1463
+ ],
1498
1464
  ...(end.attributes || {}),
1499
1465
  },
1500
1466
  });
1501
1467
  },
1502
- fail: span.fail,
1503
1468
  };
1504
1469
  }
1505
1470
  embedding(ctx, options) {
1506
1471
  return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Embedding].name, SpanType.Embedding, options);
1507
1472
  }
1508
- retrieval(ctx, options) {
1509
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Retrieval].name, SpanType.Retrieval, options);
1510
- }
1511
- reranking(ctx, options) {
1512
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Reranking].name, SpanType.Reranking, options);
1513
- }
1514
1473
  attribifyHeaders(direction, headers) {
1515
1474
  const prefix = direction === 'request'
1516
- ? ATTR_HTTP_REQUEST_HEADER
1517
- : ATTR_HTTP_RESPONSE_HEADER;
1475
+ ? ATTRIBUTES.OPENTELEMETRY.HTTP.request.header
1476
+ : ATTRIBUTES.OPENTELEMETRY.HTTP.response.header;
1518
1477
  const attributes = {};
1519
1478
  for (const key in headers) {
1520
- const field = this.toKebabCase(key);
1479
+ const field = toKebabCase(key);
1521
1480
  const value = headers[key];
1522
1481
  if (value === null || value === undefined)
1523
1482
  continue;
@@ -1538,21 +1497,21 @@ class ManualInstrumentation {
1538
1497
  try {
1539
1498
  finalBody = JSON.stringify(start.request.body);
1540
1499
  }
1541
- catch (error) {
1500
+ catch (_error) {
1542
1501
  finalBody = '{}';
1543
1502
  }
1544
1503
  }
1545
1504
  const span = this.span(ctx, start.name || `${method} ${start.request.url}`, SpanType.Http, {
1546
1505
  attributes: {
1547
- [ATTR_HTTP_REQUEST_METHOD]: method,
1548
- [ATTR_HTTP_REQUEST_URL]: start.request.url,
1506
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.method]: method,
1507
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.url]: start.request.url,
1549
1508
  ...attrHeaders,
1550
- [ATTR_HTTP_REQUEST_BODY]: finalBody,
1509
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.body]: finalBody,
1551
1510
  ...(start.attributes || {}),
1552
1511
  },
1553
1512
  });
1554
1513
  return {
1555
- context: span.context,
1514
+ ...span,
1556
1515
  end: (options) => {
1557
1516
  const end = options;
1558
1517
  // Note: do not serialize headers as a single attribute because fields won't be redacted
@@ -1565,20 +1524,19 @@ class ManualInstrumentation {
1565
1524
  try {
1566
1525
  finalBody = JSON.stringify(end.response.body);
1567
1526
  }
1568
- catch (error) {
1527
+ catch (_error) {
1569
1528
  finalBody = '{}';
1570
1529
  }
1571
1530
  }
1572
1531
  span.end({
1573
1532
  attributes: {
1574
- [ATTR_HTTP_RESPONSE_STATUS_CODE]: end.response.status,
1533
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.response.statusCode]: end.response.status,
1575
1534
  ...attrHeaders,
1576
- [ATTR_HTTP_RESPONSE_BODY]: finalBody,
1535
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.response.body]: finalBody,
1577
1536
  ...(end.attributes || {}),
1578
1537
  },
1579
1538
  });
1580
1539
  },
1581
- fail: span.fail,
1582
1540
  };
1583
1541
  }
1584
1542
  prompt(ctx, { documentLogUuid, versionUuid, promptUuid, projectId, experimentUuid, testDeploymentId, externalId, template, parameters, name, source, ...rest }) {
@@ -1586,49 +1544,50 @@ class ManualInstrumentation {
1586
1544
  try {
1587
1545
  jsonParameters = JSON.stringify(parameters || {});
1588
1546
  }
1589
- catch (error) {
1547
+ catch (_error) {
1590
1548
  jsonParameters = '{}';
1591
1549
  }
1592
1550
  const attributes = {
1593
- [ATTR_GEN_AI_REQUEST_TEMPLATE]: template,
1594
- [ATTR_GEN_AI_REQUEST_PARAMETERS]: jsonParameters,
1595
- [ATTR_LATITUDE_COMMIT_UUID]: versionUuid || HEAD_COMMIT,
1596
- [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1597
- [ATTR_LATITUDE_PROJECT_ID]: projectId,
1598
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1551
+ [ATTRIBUTES.LATITUDE.request.template]: template,
1552
+ [ATTRIBUTES.LATITUDE.request.parameters]: jsonParameters,
1553
+ [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid || HEAD_COMMIT,
1554
+ [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1555
+ [ATTRIBUTES.LATITUDE.projectId]: projectId,
1556
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1599
1557
  ...(experimentUuid && {
1600
- [ATTR_LATITUDE_EXPERIMENT_UUID]: experimentUuid,
1558
+ [ATTRIBUTES.LATITUDE.experimentUuid]: experimentUuid,
1601
1559
  }),
1602
1560
  ...(testDeploymentId && {
1603
- [ATTR_LATITUDE_TEST_DEPLOYMENT_ID]: testDeploymentId,
1561
+ [ATTRIBUTES.LATITUDE.testDeploymentId]: testDeploymentId,
1604
1562
  }),
1605
- ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1606
- ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1563
+ ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1564
+ ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1607
1565
  ...(rest.attributes || {}),
1608
1566
  };
1609
1567
  return this.span(ctx, name || `prompt-${promptUuid}`, SpanType.Prompt, {
1610
1568
  attributes,
1611
1569
  });
1612
1570
  }
1613
- step(ctx, options) {
1614
- return this.span(ctx, 'step', SpanType.Step, options);
1615
- }
1616
- chat(ctx, { documentLogUuid, previousTraceId, source, name, ...rest }) {
1571
+ chat(ctx, { documentLogUuid, previousTraceId, source, name, versionUuid, promptUuid, ...rest }) {
1617
1572
  const attributes = {
1618
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1619
- [ATTR_LATITUDE_PREVIOUS_TRACE_ID]: previousTraceId,
1620
- ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1573
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1574
+ [ATTRIBUTES.LATITUDE.previousTraceId]: previousTraceId,
1575
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1576
+ ...(promptUuid && { [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid }),
1577
+ ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1621
1578
  ...(rest.attributes || {}),
1622
1579
  };
1623
- return this.span(ctx, name || 'chat', SpanType.Chat, { attributes });
1580
+ return this.span(ctx, name || `chat-${documentLogUuid}`, SpanType.Chat, {
1581
+ attributes,
1582
+ });
1624
1583
  }
1625
1584
  external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
1626
1585
  const attributes = {
1627
- [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1628
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1629
- [ATTR_LATITUDE_SOURCE]: source ?? LogSources.API,
1630
- ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1631
- ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1586
+ [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1587
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1588
+ [ATTRIBUTES.LATITUDE.source]: source ?? LogSources.API,
1589
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1590
+ ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1632
1591
  ...(rest.attributes || {}),
1633
1592
  };
1634
1593
  return this.span(ctx, name || `external-${promptUuid}`, SpanType.External, {
@@ -1637,11 +1596,11 @@ class ManualInstrumentation {
1637
1596
  }
1638
1597
  unresolvedExternal(ctx, { path, projectId, versionUuid, conversationUuid, name, ...rest }) {
1639
1598
  const attributes = {
1640
- [ATTR_LATITUDE_PROMPT_PATH]: path,
1641
- [ATTR_LATITUDE_PROJECT_ID]: projectId,
1642
- ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1599
+ [ATTRIBUTES.LATITUDE.promptPath]: path,
1600
+ [ATTRIBUTES.LATITUDE.projectId]: projectId,
1601
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1643
1602
  ...(conversationUuid && {
1644
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: conversationUuid,
1603
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: conversationUuid,
1645
1604
  }),
1646
1605
  ...(rest.attributes || {}),
1647
1606
  };
@@ -1660,8 +1619,8 @@ class LatitudeInstrumentation {
1660
1619
  return this.manualTelemetry.isEnabled();
1661
1620
  }
1662
1621
  enable() {
1663
- this.options.module.instrument(this);
1664
1622
  this.manualTelemetry.enable();
1623
+ this.options.module.instrument(this);
1665
1624
  }
1666
1625
  disable() {
1667
1626
  this.manualTelemetry.disable();
@@ -1706,19 +1665,6 @@ class LatitudeInstrumentation {
1706
1665
  $prompt.end();
1707
1666
  return result;
1708
1667
  }
1709
- async wrapRenderStep(fn, ...args) {
1710
- const $step = this.manualTelemetry.step(context.active());
1711
- let result;
1712
- try {
1713
- result = await context.with($step.context, async () => await fn(...args));
1714
- }
1715
- catch (error) {
1716
- $step.fail(error);
1717
- throw error;
1718
- }
1719
- $step.end();
1720
- return result;
1721
- }
1722
1668
  async wrapRenderCompletion(fn, ...args) {
1723
1669
  if (!this.options.completions) {
1724
1670
  return await fn(...args);
@@ -1752,8 +1698,8 @@ class LatitudeInstrumentation {
1752
1698
  completion: completionTokens,
1753
1699
  },
1754
1700
  finishReason: result.toolRequests.length > 0
1755
- ? GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS
1756
- : GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP,
1701
+ ? VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.toolCalls
1702
+ : VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.stop,
1757
1703
  });
1758
1704
  return result;
1759
1705
  }
@@ -1798,6 +1744,7 @@ var LatitudeErrorCodes;
1798
1744
  LatitudeErrorCodes["NotImplementedError"] = "NotImplementedError";
1799
1745
  LatitudeErrorCodes["PaymentRequiredError"] = "PaymentRequiredError";
1800
1746
  LatitudeErrorCodes["AbortedError"] = "AbortedError";
1747
+ LatitudeErrorCodes["BillingError"] = "BillingError";
1801
1748
  })(LatitudeErrorCodes || (LatitudeErrorCodes = {}));
1802
1749
  // NOTE: If you add a new error code, please add it to the pg enum in models/runErrors.ts
1803
1750
  var RunErrorCodes;
@@ -1868,6 +1815,9 @@ class SpanFactory {
1868
1815
  constructor(telemetry) {
1869
1816
  this.telemetry = telemetry;
1870
1817
  }
1818
+ span(options, ctx) {
1819
+ return this.telemetry.unknown(ctx ?? context.active(), options);
1820
+ }
1871
1821
  tool(options, ctx) {
1872
1822
  return this.telemetry.tool(ctx ?? context.active(), options);
1873
1823
  }
@@ -1877,21 +1827,12 @@ class SpanFactory {
1877
1827
  embedding(options, ctx) {
1878
1828
  return this.telemetry.embedding(ctx ?? context.active(), options);
1879
1829
  }
1880
- retrieval(options, ctx) {
1881
- return this.telemetry.retrieval(ctx ?? context.active(), options);
1882
- }
1883
- reranking(options, ctx) {
1884
- return this.telemetry.reranking(ctx ?? context.active(), options);
1885
- }
1886
1830
  http(options, ctx) {
1887
1831
  return this.telemetry.http(ctx ?? context.active(), options);
1888
1832
  }
1889
1833
  prompt(options, ctx) {
1890
1834
  return this.telemetry.prompt(ctx ?? context.active(), options);
1891
1835
  }
1892
- step(options, ctx) {
1893
- return this.telemetry.step(ctx ?? context.active(), options);
1894
- }
1895
1836
  chat(options, ctx) {
1896
1837
  return this.telemetry.chat(ctx ?? context.active(), options);
1897
1838
  }
@@ -1910,6 +1851,9 @@ class ContextManager {
1910
1851
  active() {
1911
1852
  return context.active();
1912
1853
  }
1854
+ with(ctx, fn, thisArg, ...args) {
1855
+ return context.with(ctx, fn, thisArg, ...args);
1856
+ }
1913
1857
  }
1914
1858
  class InstrumentationManager {
1915
1859
  instrumentations;
@@ -1956,6 +1900,23 @@ class ScopedTracerProvider {
1956
1900
  return this.provider.getTracer(this.scope, this.version, options);
1957
1901
  }
1958
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
+ }
1959
1920
  const DEFAULT_SPAN_EXPORTER = (apiKey) => new OTLPTraceExporter({
1960
1921
  url: TRACES_URL,
1961
1922
  headers: {
@@ -1974,6 +1935,7 @@ var Instrumentation;
1974
1935
  Instrumentation["Langchain"] = "langchain";
1975
1936
  Instrumentation["Latitude"] = "latitude";
1976
1937
  Instrumentation["LlamaIndex"] = "llamaindex";
1938
+ Instrumentation["Manual"] = "manual";
1977
1939
  Instrumentation["OpenAI"] = "openai";
1978
1940
  Instrumentation["TogetherAI"] = "togetherai";
1979
1941
  Instrumentation["VertexAI"] = "vertexai";
@@ -1987,6 +1949,7 @@ class LatitudeTelemetry {
1987
1949
  context;
1988
1950
  instrumentation;
1989
1951
  tracer;
1952
+ lifecycle;
1990
1953
  constructor(apiKey, options) {
1991
1954
  this.options = options || {};
1992
1955
  if (!this.options.exporter) {
@@ -2003,6 +1966,7 @@ class LatitudeTelemetry {
2003
1966
  this.nodeProvider = new NodeTracerProvider({
2004
1967
  resource: new Resource({ [ATTR_SERVICE_NAME]: SERVICE_NAME }),
2005
1968
  });
1969
+ this.lifecycle = new LifecycleManager(this.nodeProvider, this.options.exporter);
2006
1970
  // Note: important, must run before the exporter span processors
2007
1971
  this.nodeProvider.addSpanProcessor(new BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS));
2008
1972
  if (this.options.processors) {
@@ -2032,19 +1996,16 @@ class LatitudeTelemetry {
2032
1996
  this.context = new ContextManager(this.manualInstrumentation);
2033
1997
  }
2034
1998
  async flush() {
2035
- await this.nodeProvider.forceFlush();
2036
- await this.options.exporter.forceFlush?.();
1999
+ await this.lifecycle.flush();
2037
2000
  }
2038
2001
  async shutdown() {
2039
- await this.flush();
2040
- await this.nodeProvider.shutdown();
2041
- await this.options.exporter.shutdown?.();
2002
+ await this.lifecycle.shutdown();
2042
2003
  }
2043
2004
  // TODO(tracing): auto instrument outgoing HTTP requests
2044
2005
  initInstrumentations() {
2045
2006
  this.instrumentationsList = [];
2046
- const tracer = this.tracer.get(InstrumentationScope.Manual);
2047
- this.manualInstrumentation = new ManualInstrumentation(tracer);
2007
+ const tracer = this.tracer.get(Instrumentation.Manual);
2008
+ this.manualInstrumentation = new ManualInstrumentation(tracer, this.options.instrumentations?.manual);
2048
2009
  this.instrumentationsList.push(this.manualInstrumentation);
2049
2010
  const latitude = this.options.instrumentations?.latitude;
2050
2011
  if (latitude) {
@@ -2054,12 +2015,12 @@ class LatitudeTelemetry {
2054
2015
  }
2055
2016
  const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
2056
2017
  const providerPkg = this.options.instrumentations?.[instrumentationType];
2018
+ if (!providerPkg)
2019
+ return;
2057
2020
  const provider = this.tracer.provider(instrumentationType);
2058
2021
  const instrumentation = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
2059
2022
  instrumentation.setTracerProvider(provider);
2060
- if (providerPkg) {
2061
- instrumentation.manuallyInstrument(providerPkg);
2062
- }
2023
+ instrumentation.manuallyInstrument(providerPkg);
2063
2024
  registerInstrumentations({
2064
2025
  instrumentations: [instrumentation],
2065
2026
  tracerProvider: provider,
@@ -2072,27 +2033,26 @@ class LatitudeTelemetry {
2072
2033
  configureInstrumentation(Instrumentation.Cohere, CohereInstrumentation); // prettier-ignore
2073
2034
  configureInstrumentation(Instrumentation.Langchain, LangChainInstrumentation); // prettier-ignore
2074
2035
  configureInstrumentation(Instrumentation.LlamaIndex, LlamaIndexInstrumentation); // prettier-ignore
2075
- configureInstrumentation(Instrumentation.OpenAI, OpenAIInstrumentation, { enrichTokens: true }); // prettier-ignore
2076
- configureInstrumentation(Instrumentation.TogetherAI, TogetherInstrumentation, { enrichTokens: true }); // prettier-ignore
2036
+ // NOTE: `stream: true` in OpenAI make enrichTokens fail, so disabling.
2037
+ configureInstrumentation(Instrumentation.OpenAI, OpenAIInstrumentation, { enrichTokens: false }); // prettier-ignore
2038
+ configureInstrumentation(Instrumentation.TogetherAI, TogetherInstrumentation, { enrichTokens: false }); // prettier-ignore
2077
2039
  configureInstrumentation(Instrumentation.VertexAI, VertexAIInstrumentation); // prettier-ignore
2078
2040
  }
2079
2041
  async capture(options, fn) {
2080
2042
  if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
2081
2043
  throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
2082
2044
  }
2083
- const span = this.manualInstrumentation.unresolvedExternal(otel.ROOT_CONTEXT, options);
2045
+ const span = this.manualInstrumentation.unresolvedExternal(BACKGROUND(), options);
2046
+ let result;
2084
2047
  try {
2085
- const result = await context.with(span.context, () => fn(span.context));
2086
- span.end();
2087
- return result;
2048
+ result = await context.with(span.context, async () => await fn(span.context));
2088
2049
  }
2089
2050
  catch (error) {
2090
2051
  span.fail(error);
2091
2052
  throw error;
2092
2053
  }
2093
- finally {
2094
- await this.flush();
2095
- }
2054
+ span.end();
2055
+ return result;
2096
2056
  }
2097
2057
  }
2098
2058