@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.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 rosettaAi = require('rosetta-ai');
7
8
  var uuid = require('uuid');
8
9
  var baggageSpanProcessor = require('@opentelemetry/baggage-span-processor');
9
10
  var contextAsyncHooks = require('@opentelemetry/context-async-hooks');
@@ -138,6 +139,23 @@ function GET_GATEWAY_BASE_URL() {
138
139
  }
139
140
  const env = { GATEWAY_BASE_URL: GET_GATEWAY_BASE_URL() };
140
141
 
142
+ function toSnakeCase(str) {
143
+ return str
144
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
145
+ .replace(/[^A-Za-z0-9]+/g, '_')
146
+ .replace(/_+/g, '_')
147
+ .replace(/^_+|_+$/g, '')
148
+ .toLowerCase();
149
+ }
150
+ function toKebabCase(input) {
151
+ return input
152
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
153
+ .replace(/[^A-Za-z0-9]+/g, '-')
154
+ .replace(/-+/g, '-')
155
+ .replace(/^-+|-+$/g, '')
156
+ .toLowerCase();
157
+ }
158
+
141
159
  var StreamEventTypes;
142
160
  (function (StreamEventTypes) {
143
161
  StreamEventTypes["Latitude"] = "latitude-event";
@@ -150,6 +168,15 @@ zod.z.object({
150
168
  isError: zod.z.boolean().optional(),
151
169
  text: zod.z.string().optional(),
152
170
  });
171
+ zod.z.object({
172
+ inputTokens: zod.z.number(),
173
+ outputTokens: zod.z.number(),
174
+ promptTokens: zod.z.number(),
175
+ completionTokens: zod.z.number(),
176
+ totalTokens: zod.z.number(),
177
+ reasoningTokens: zod.z.number(),
178
+ cachedInputTokens: zod.z.number(),
179
+ });
153
180
 
154
181
  var ParameterType;
155
182
  (function (ParameterType) {
@@ -190,10 +217,24 @@ const expectedOutputConfiguration = zod.z.object({
190
217
  parsingFormat: zod.z.enum(['string', 'json']),
191
218
  fieldAccessor: zod.z.string().optional(), // Field accessor to get the output from if it's a key-value format
192
219
  });
220
+ const EVALUATION_TRIGGER_TARGETS = ['first', 'every', 'last'];
221
+ const DEFAULT_LAST_INTERACTION_DEBOUNCE_SECONDS = 120;
222
+ const LAST_INTERACTION_DEBOUNCE_MIN_SECONDS = 30;
223
+ const LAST_INTERACTION_DEBOUNCE_MAX_SECONDS = 60 * 60 * 24; // 1 day
224
+ const triggerConfiguration = zod.z.object({
225
+ target: zod.z.enum(EVALUATION_TRIGGER_TARGETS),
226
+ lastInteractionDebounce: zod.z
227
+ .number()
228
+ .min(LAST_INTERACTION_DEBOUNCE_MIN_SECONDS)
229
+ .max(LAST_INTERACTION_DEBOUNCE_MAX_SECONDS)
230
+ .optional()
231
+ .default(DEFAULT_LAST_INTERACTION_DEBOUNCE_SECONDS),
232
+ });
193
233
  const baseEvaluationConfiguration = zod.z.object({
194
234
  reverseScale: zod.z.boolean(), // If true, lower is better, otherwise, higher is better
195
235
  actualOutput: actualOutputConfiguration,
196
236
  expectedOutput: expectedOutputConfiguration.optional(),
237
+ trigger: triggerConfiguration.optional(),
197
238
  });
198
239
  const baseEvaluationResultMetadata = zod.z.object({
199
240
  // configuration: Configuration snapshot is defined in every metric specification
@@ -209,7 +250,6 @@ const compositeEvaluationConfiguration = baseEvaluationConfiguration.extend({
209
250
  evaluationUuids: zod.z.array(zod.z.string()),
210
251
  minThreshold: zod.z.number().optional(), // Threshold percentage
211
252
  maxThreshold: zod.z.number().optional(), // Threshold percentage
212
- defaultTarget: zod.z.boolean().optional(), // Default for optimizations and distillations
213
253
  });
214
254
  const compositeEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
215
255
  results: zod.z.record(zod.z.string(), // Evaluation uuid
@@ -276,12 +316,34 @@ const CompositeEvaluationSpecification = {
276
316
  },
277
317
  };
278
318
 
319
+ const selectedContextSchema = zod.z.object({
320
+ messageIndex: zod.z.number().int().nonnegative(),
321
+ contentBlockIndex: zod.z.number().int().nonnegative(),
322
+ contentType: zod.z.enum([
323
+ 'text',
324
+ 'reasoning',
325
+ 'image',
326
+ 'file',
327
+ 'tool-call',
328
+ 'tool-result',
329
+ ]),
330
+ textRange: zod.z
331
+ .object({
332
+ start: zod.z.number().int().nonnegative(),
333
+ end: zod.z.number().int().nonnegative(),
334
+ })
335
+ .optional(),
336
+ selectedText: zod.z.string().optional(),
337
+ toolCallId: zod.z.string().optional(),
338
+ });
279
339
  const humanEvaluationConfiguration = baseEvaluationConfiguration.extend({
280
340
  enableControls: zod.z.boolean().optional(), // UI annotation controls
281
341
  criteria: zod.z.string().optional(),
282
342
  });
283
343
  const humanEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
284
344
  reason: zod.z.string().optional(),
345
+ enrichedReason: zod.z.string().optional(),
346
+ selectedContexts: zod.z.array(selectedContextSchema).optional(),
285
347
  });
286
348
  const humanEvaluationResultError = baseEvaluationResultError.extend({});
287
349
  // BINARY
@@ -559,18 +621,8 @@ zod.z.object({
559
621
  });
560
622
  zod.z.object({
561
623
  evaluateLiveLogs: zod.z.boolean().nullable().optional(),
562
- enableSuggestions: zod.z.boolean().nullable().optional(),
563
- autoApplySuggestions: zod.z.boolean().nullable().optional(),
564
624
  });
565
625
 
566
- var LegacyChainEventTypes;
567
- (function (LegacyChainEventTypes) {
568
- LegacyChainEventTypes["Error"] = "chain-error";
569
- LegacyChainEventTypes["Step"] = "chain-step";
570
- LegacyChainEventTypes["Complete"] = "chain-complete";
571
- LegacyChainEventTypes["StepComplete"] = "chain-step-complete";
572
- })(LegacyChainEventTypes || (LegacyChainEventTypes = {}));
573
-
574
626
  var ChainEventTypes;
575
627
  (function (ChainEventTypes) {
576
628
  ChainEventTypes["ChainCompleted"] = "chain-completed";
@@ -758,6 +810,7 @@ var LogSources;
758
810
  LogSources["ShadowTest"] = "shadow_test";
759
811
  LogSources["ABTestChallenger"] = "ab_test_challenger";
760
812
  LogSources["User"] = "user";
813
+ LogSources["Optimization"] = "optimization";
761
814
  })(LogSources || (LogSources = {}));
762
815
 
763
816
  var RunSourceGroup;
@@ -768,7 +821,8 @@ var RunSourceGroup;
768
821
  ({
769
822
  [RunSourceGroup.Production]: [
770
823
  LogSources.API,
771
- LogSources.Copilot,
824
+ LogSources.ShadowTest,
825
+ LogSources.ABTestChallenger,
772
826
  LogSources.EmailTrigger,
773
827
  LogSources.IntegrationTrigger,
774
828
  LogSources.ScheduledTrigger,
@@ -778,14 +832,6 @@ var RunSourceGroup;
778
832
  [RunSourceGroup.Playground]: [LogSources.Playground, LogSources.Experiment],
779
833
  });
780
834
 
781
- var MessageRole;
782
- (function (MessageRole) {
783
- MessageRole["system"] = "system";
784
- MessageRole["user"] = "user";
785
- MessageRole["assistant"] = "assistant";
786
- MessageRole["tool"] = "tool";
787
- })(MessageRole || (MessageRole = {}));
788
-
789
835
  var SpanKind;
790
836
  (function (SpanKind) {
791
837
  SpanKind["Internal"] = "internal";
@@ -797,26 +843,49 @@ var SpanKind;
797
843
  // Note: loosely based on OpenTelemetry GenAI semantic conventions
798
844
  var SpanType;
799
845
  (function (SpanType) {
800
- SpanType["Tool"] = "tool";
801
- SpanType["Completion"] = "completion";
802
- SpanType["Embedding"] = "embedding";
803
- SpanType["Retrieval"] = "retrieval";
804
- SpanType["Reranking"] = "reranking";
805
- SpanType["Http"] = "http";
806
- SpanType["Unknown"] = "unknown";
846
+ // Latitude wrappers
807
847
  SpanType["Prompt"] = "prompt";
808
848
  SpanType["Chat"] = "chat";
809
849
  SpanType["External"] = "external";
810
850
  SpanType["UnresolvedExternal"] = "unresolved_external";
811
- SpanType["Step"] = "step";
851
+ // Added a HTTP span to capture raw HTTP requests and responses when running from Latitude
852
+ SpanType["Http"] = "http";
853
+ // Any known span from supported specifications will be grouped into one of these types
854
+ SpanType["Completion"] = "completion";
855
+ SpanType["Tool"] = "tool";
856
+ SpanType["Embedding"] = "embedding";
857
+ SpanType["Unknown"] = "unknown";
812
858
  })(SpanType || (SpanType = {}));
859
+ [
860
+ SpanType.Prompt,
861
+ SpanType.External,
862
+ SpanType.Chat,
863
+ ];
813
864
  const SPAN_SPECIFICATIONS = {
814
- [SpanType.Tool]: {
815
- name: 'Tool',
816
- description: 'A tool call',
817
- isGenAI: true,
865
+ [SpanType.Prompt]: {
866
+ name: 'Prompt',
867
+ description: 'A prompt span',
868
+ isGenAI: false,
869
+ isHidden: false,
870
+ },
871
+ [SpanType.Chat]: {
872
+ name: 'Chat',
873
+ description: 'A chat continuation span',
874
+ isGenAI: false,
875
+ isHidden: false,
876
+ },
877
+ [SpanType.External]: {
878
+ name: 'External',
879
+ description: 'An external capture span',
880
+ isGenAI: false,
818
881
  isHidden: false,
819
882
  },
883
+ [SpanType.UnresolvedExternal]: {
884
+ name: 'Unresolved External',
885
+ description: 'An external span that needs path resolution before storage',
886
+ isGenAI: false,
887
+ isHidden: true,
888
+ },
820
889
  [SpanType.Completion]: {
821
890
  name: 'Completion',
822
891
  description: 'A completion call',
@@ -829,15 +898,9 @@ const SPAN_SPECIFICATIONS = {
829
898
  isGenAI: true,
830
899
  isHidden: false,
831
900
  },
832
- [SpanType.Retrieval]: {
833
- name: 'Retrieval',
834
- description: 'A retrieval call',
835
- isGenAI: true,
836
- isHidden: false,
837
- },
838
- [SpanType.Reranking]: {
839
- name: 'Reranking',
840
- description: 'A reranking call',
901
+ [SpanType.Tool]: {
902
+ name: 'Tool',
903
+ description: 'A tool call',
841
904
  isGenAI: true,
842
905
  isHidden: false,
843
906
  },
@@ -853,36 +916,6 @@ const SPAN_SPECIFICATIONS = {
853
916
  isGenAI: false,
854
917
  isHidden: true,
855
918
  },
856
- [SpanType.Prompt]: {
857
- name: 'Prompt',
858
- description: 'A prompt span',
859
- isGenAI: false,
860
- isHidden: false,
861
- },
862
- [SpanType.Chat]: {
863
- name: 'Chat',
864
- description: 'A chat continuation span',
865
- isGenAI: false,
866
- isHidden: false,
867
- },
868
- [SpanType.External]: {
869
- name: 'External',
870
- description: 'An external capture span',
871
- isGenAI: false,
872
- isHidden: false,
873
- },
874
- [SpanType.UnresolvedExternal]: {
875
- name: 'Unresolved External',
876
- description: 'An external span that needs path resolution before storage',
877
- isGenAI: false,
878
- isHidden: true,
879
- },
880
- [SpanType.Step]: {
881
- name: 'Step',
882
- description: 'A step span',
883
- isGenAI: false,
884
- isHidden: false,
885
- },
886
919
  };
887
920
  var SpanStatus;
888
921
  (function (SpanStatus) {
@@ -890,6 +923,11 @@ var SpanStatus;
890
923
  SpanStatus["Ok"] = "ok";
891
924
  SpanStatus["Error"] = "error";
892
925
  })(SpanStatus || (SpanStatus = {}));
926
+ new Set([
927
+ SpanType.Prompt,
928
+ SpanType.Chat,
929
+ SpanType.External,
930
+ ]);
893
931
 
894
932
  // Note: Traces are unmaterialized but this context is used to propagate the trace
895
933
  // See www.w3.org/TR/trace-context and w3c.github.io/baggage
@@ -899,6 +937,100 @@ zod.z.object({
899
937
  baggage: zod.z.string().optional(), // <key>=urlencoded(<value>)[,<key>=urlencoded(<value>)]*
900
938
  });
901
939
 
940
+ const ATTRIBUTES = {
941
+ // Custom attributes added and used by Latitude spans (Prompt / External / Chat)
942
+ LATITUDE: {
943
+ type: 'latitude.type',
944
+ documentUuid: 'latitude.document_uuid',
945
+ promptPath: 'latitude.prompt_path',
946
+ commitUuid: 'latitude.commit_uuid',
947
+ documentLogUuid: 'latitude.document_log_uuid',
948
+ projectId: 'latitude.project_id',
949
+ experimentUuid: 'latitude.experiment_uuid',
950
+ source: 'latitude.source',
951
+ externalId: 'latitude.external_id',
952
+ testDeploymentId: 'latitude.test_deployment_id',
953
+ previousTraceId: 'latitude.previous_trace_id',
954
+ // Custom additions to the GenAI semantic conventions (deprecated)
955
+ request: {
956
+ _root: 'gen_ai.request',
957
+ configuration: 'gen_ai.request.configuration',
958
+ template: 'gen_ai.request.template',
959
+ parameters: 'gen_ai.request.parameters'},
960
+ response: {
961
+ _root: 'gen_ai.response'},
962
+ usage: {
963
+ promptTokens: 'gen_ai.usage.prompt_tokens',
964
+ cachedTokens: 'gen_ai.usage.cached_tokens',
965
+ reasoningTokens: 'gen_ai.usage.reasoning_tokens',
966
+ completionTokens: 'gen_ai.usage.completion_tokens',
967
+ },
968
+ },
969
+ // Official OpenTelemetry semantic conventions
970
+ OPENTELEMETRY: {
971
+ HTTP: {
972
+ request: {
973
+ url: 'http.request.url',
974
+ body: 'http.request.body',
975
+ header: 'http.request.header',
976
+ method: semanticConventions.ATTR_HTTP_REQUEST_METHOD,
977
+ },
978
+ response: {
979
+ body: 'http.response.body',
980
+ header: 'http.response.header',
981
+ statusCode: semanticConventions.ATTR_HTTP_RESPONSE_STATUS_CODE,
982
+ },
983
+ },
984
+ // GenAI semantic conventions
985
+ // https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/
986
+ GEN_AI: {
987
+ operation: incubating.ATTR_GEN_AI_OPERATION_NAME,
988
+ response: {
989
+ model: incubating.ATTR_GEN_AI_RESPONSE_MODEL,
990
+ finishReasons: incubating.ATTR_GEN_AI_RESPONSE_FINISH_REASONS,
991
+ },
992
+ usage: {
993
+ inputTokens: incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS,
994
+ outputTokens: incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
995
+ },
996
+ systemInstructions: 'gen_ai.system.instructions', // Contains the PARTS of the "system message"
997
+ tool: {
998
+ call: {
999
+ id: incubating.ATTR_GEN_AI_TOOL_CALL_ID,
1000
+ arguments: 'gen_ai.tool.call.arguments'}},
1001
+ input: {
1002
+ messages: 'gen_ai.input.messages',
1003
+ },
1004
+ output: {
1005
+ messages: 'gen_ai.output.messages',
1006
+ },
1007
+ _deprecated: {
1008
+ system: incubating.ATTR_GEN_AI_SYSTEM,
1009
+ tool: {
1010
+ name: incubating.ATTR_GEN_AI_TOOL_NAME,
1011
+ type: incubating.ATTR_GEN_AI_TOOL_TYPE,
1012
+ result: {
1013
+ value: 'gen_ai.tool.result.value',
1014
+ isError: 'gen_ai.tool.result.is_error',
1015
+ },
1016
+ }},
1017
+ },
1018
+ }};
1019
+ const VALUES = {
1020
+ OPENTELEMETRY: {
1021
+ GEN_AI: {
1022
+ response: {
1023
+ finishReasons: {
1024
+ stop: 'stop',
1025
+ toolCalls: 'tool_calls'},
1026
+ },
1027
+ tool: {
1028
+ type: {
1029
+ function: 'function',
1030
+ },
1031
+ }},
1032
+ }};
1033
+
902
1034
  /* Note: Instrumentation scopes from all language SDKs */
903
1035
  const SCOPE_LATITUDE = 'so.latitude.instrumentation';
904
1036
  var InstrumentationScope;
@@ -927,52 +1059,6 @@ var InstrumentationScope;
927
1059
  InstrumentationScope["Transformers"] = "transformers";
928
1060
  InstrumentationScope["AlephAlpha"] = "alephalpha";
929
1061
  })(InstrumentationScope || (InstrumentationScope = {}));
930
- /* Note: non-standard OpenTelemetry semantic conventions used in Latitude */
931
- const ATTR_LATITUDE = 'latitude';
932
- const ATTR_LATITUDE_TYPE = `${ATTR_LATITUDE}.type`;
933
- const ATTR_LATITUDE_DOCUMENT_UUID = `${ATTR_LATITUDE}.document_uuid`;
934
- const ATTR_LATITUDE_PROMPT_PATH = `${ATTR_LATITUDE}.prompt_path`;
935
- const ATTR_LATITUDE_COMMIT_UUID = `${ATTR_LATITUDE}.commit_uuid`;
936
- const ATTR_LATITUDE_DOCUMENT_LOG_UUID = `${ATTR_LATITUDE}.document_log_uuid`;
937
- const ATTR_LATITUDE_PROJECT_ID = `${ATTR_LATITUDE}.project_id`;
938
- const ATTR_LATITUDE_EXPERIMENT_UUID = `${ATTR_LATITUDE}.experiment_uuid`;
939
- const ATTR_LATITUDE_SOURCE = `${ATTR_LATITUDE}.source`;
940
- const ATTR_LATITUDE_EXTERNAL_ID = `${ATTR_LATITUDE}.external_id`;
941
- const ATTR_LATITUDE_TEST_DEPLOYMENT_ID = `${ATTR_LATITUDE}.test_deployment_id`;
942
- const ATTR_LATITUDE_PREVIOUS_TRACE_ID = `${ATTR_LATITUDE}.previous_trace_id`;
943
- const GEN_AI_TOOL_TYPE_VALUE_FUNCTION = 'function';
944
- const ATTR_GEN_AI_TOOL_CALL_ARGUMENTS = 'gen_ai.tool.call.arguments';
945
- const ATTR_GEN_AI_TOOL_RESULT_VALUE = 'gen_ai.tool.result.value';
946
- const ATTR_GEN_AI_TOOL_RESULT_IS_ERROR = 'gen_ai.tool.result.is_error';
947
- const ATTR_GEN_AI_REQUEST = 'gen_ai.request';
948
- const ATTR_GEN_AI_REQUEST_CONFIGURATION = 'gen_ai.request.configuration';
949
- const ATTR_GEN_AI_REQUEST_TEMPLATE = 'gen_ai.request.template';
950
- const ATTR_GEN_AI_REQUEST_PARAMETERS = 'gen_ai.request.parameters';
951
- const ATTR_GEN_AI_REQUEST_MESSAGES = 'gen_ai.request.messages';
952
- const ATTR_GEN_AI_RESPONSE = 'gen_ai.response';
953
- const ATTR_GEN_AI_RESPONSE_MESSAGES = 'gen_ai.response.messages';
954
- const ATTR_GEN_AI_USAGE_PROMPT_TOKENS = 'gen_ai.usage.prompt_tokens';
955
- const ATTR_GEN_AI_USAGE_CACHED_TOKENS = 'gen_ai.usage.cached_tokens';
956
- const ATTR_GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens'; // prettier-ignore
957
- const ATTR_GEN_AI_USAGE_COMPLETION_TOKENS = 'gen_ai.usage.completion_tokens'; // prettier-ignore
958
- const ATTR_GEN_AI_PROMPTS = 'gen_ai.prompt'; // gen_ai.prompt.{index}.{role/content/...}
959
- const ATTR_GEN_AI_COMPLETIONS = 'gen_ai.completion'; // gen_ai.completion.{index}.{role/content/...}
960
- const ATTR_GEN_AI_MESSAGE_ROLE = 'role';
961
- const ATTR_GEN_AI_MESSAGE_CONTENT = 'content'; // string or object
962
- const ATTR_GEN_AI_MESSAGE_TOOL_NAME = 'tool_name';
963
- const ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID = 'tool_call_id';
964
- const ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR = 'is_error';
965
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS = 'tool_calls'; // gen_ai.completion.{index}.tool_calls.{index}.{id/name/arguments}
966
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID = 'id';
967
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME = 'name';
968
- const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS = 'arguments';
969
- const GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP = 'stop';
970
- const GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS = 'tool_calls';
971
- const ATTR_HTTP_REQUEST_URL = 'http.request.url';
972
- const ATTR_HTTP_REQUEST_BODY = 'http.request.body';
973
- const ATTR_HTTP_REQUEST_HEADER = 'http.request.header';
974
- const ATTR_HTTP_RESPONSE_BODY = 'http.response.body';
975
- const ATTR_HTTP_RESPONSE_HEADER = 'http.response.header';
976
1062
  /* Note: Schemas for span ingestion following OpenTelemetry service request specification */
977
1063
  var Otlp;
978
1064
  (function (Otlp) {
@@ -1053,6 +1139,54 @@ var Otlp;
1053
1139
  });
1054
1140
  })(Otlp || (Otlp = {}));
1055
1141
 
1142
+ const MAX_SIMULATION_TURNS = 10;
1143
+ const globalGoalSourceSchema = zod.z.object({
1144
+ type: zod.z.literal('global'),
1145
+ value: zod.z.string(),
1146
+ });
1147
+ const columnGoalSourceSchema = zod.z.object({
1148
+ type: zod.z.literal('column'),
1149
+ columnIndex: zod.z.number(),
1150
+ });
1151
+ const simulatedUserGoalSourceSchema = zod.z.discriminatedUnion('type', [
1152
+ globalGoalSourceSchema,
1153
+ columnGoalSourceSchema,
1154
+ ]);
1155
+ const SimulationSettingsSchema = zod.z.object({
1156
+ simulateToolResponses: zod.z.boolean().optional(),
1157
+ simulatedTools: zod.z.array(zod.z.string()).optional(), // Empty array means all tools are simulated (if simulateToolResponses is true).
1158
+ toolSimulationInstructions: zod.z.string().optional(), // A prompt used to guide and generate the simulation result
1159
+ maxTurns: zod.z.number().min(1).max(MAX_SIMULATION_TURNS).optional(), // The maximum number of turns to simulate. Default is 1, and any greater value will add a new user message to the simulated conversation.
1160
+ simulatedUserGoal: zod.z.string().optional(), // Deprecated: Use simulatedUserGoalSource instead. Kept for backward compatibility.
1161
+ simulatedUserGoalSource: simulatedUserGoalSourceSchema.optional(), // The source for the simulated user goal (global text or dataset column).
1162
+ });
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
+
1056
1190
  // TODO(tracing): deprecated
1057
1191
  const HEAD_COMMIT = 'live';
1058
1192
  var Providers;
@@ -1098,12 +1232,18 @@ var DocumentTriggerParameters;
1098
1232
  })(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
1099
1233
  const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
1100
1234
 
1235
+ const translator = new rosettaAi.Translator({
1236
+ filterEmptyMessages: true,
1237
+ providerMetadata: 'preserve',
1238
+ });
1101
1239
  class ManualInstrumentation {
1102
1240
  enabled;
1103
1241
  tracer;
1104
- constructor(tracer) {
1242
+ options;
1243
+ constructor(tracer, options) {
1105
1244
  this.enabled = false;
1106
1245
  this.tracer = tracer;
1246
+ this.options = options ?? {};
1107
1247
  }
1108
1248
  isEnabled() {
1109
1249
  return this.enabled;
@@ -1143,36 +1283,6 @@ class ManualInstrumentation {
1143
1283
  }
1144
1284
  return context;
1145
1285
  }
1146
- capitalize(str) {
1147
- if (str.length === 0)
1148
- return str;
1149
- return str.charAt(0).toUpperCase() + str.toLowerCase().slice(1);
1150
- }
1151
- toCamelCase(str) {
1152
- return str
1153
- .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
1154
- .replace(/[^A-Za-z0-9]+/g, ' ')
1155
- .trim()
1156
- .split(' ')
1157
- .map((w, i) => (i ? this.capitalize(w) : w.toLowerCase()))
1158
- .join('');
1159
- }
1160
- toSnakeCase(str) {
1161
- return str
1162
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
1163
- .replace(/[^A-Za-z0-9]+/g, '_')
1164
- .replace(/_+/g, '_')
1165
- .replace(/^_+|_+$/g, '')
1166
- .toLowerCase();
1167
- }
1168
- toKebabCase(input) {
1169
- return input
1170
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
1171
- .replace(/[^A-Za-z0-9]+/g, '-')
1172
- .replace(/-+/g, '-')
1173
- .replace(/^-+|-+$/g, '')
1174
- .toLowerCase();
1175
- }
1176
1286
  error(span, error, options) {
1177
1287
  options = options || {};
1178
1288
  span.recordException(error);
@@ -1198,9 +1308,9 @@ class ManualInstrumentation {
1198
1308
  }
1199
1309
  const span = this.tracer.startSpan(name, {
1200
1310
  attributes: {
1201
- [ATTR_LATITUDE_TYPE]: type,
1311
+ [ATTRIBUTES.LATITUDE.type]: type,
1202
1312
  ...(operation && {
1203
- [incubating.ATTR_GEN_AI_OPERATION_NAME]: operation,
1313
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.operation]: operation,
1204
1314
  }),
1205
1315
  ...(start.attributes || {}),
1206
1316
  },
@@ -1220,26 +1330,29 @@ class ManualInstrumentation {
1220
1330
  },
1221
1331
  };
1222
1332
  }
1333
+ unknown(ctx, options) {
1334
+ return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Unknown].name, SpanType.Unknown, options);
1335
+ }
1223
1336
  tool(ctx, options) {
1224
1337
  const start = options;
1225
1338
  let jsonArguments = '';
1226
1339
  try {
1227
1340
  jsonArguments = JSON.stringify(start.call.arguments);
1228
1341
  }
1229
- catch (error) {
1342
+ catch (_error) {
1230
1343
  jsonArguments = '{}';
1231
1344
  }
1232
1345
  const span = this.span(ctx, start.name, SpanType.Tool, {
1233
1346
  attributes: {
1234
- [incubating.ATTR_GEN_AI_TOOL_NAME]: start.name,
1235
- [incubating.ATTR_GEN_AI_TOOL_TYPE]: GEN_AI_TOOL_TYPE_VALUE_FUNCTION,
1236
- [incubating.ATTR_GEN_AI_TOOL_CALL_ID]: start.call.id,
1237
- [ATTR_GEN_AI_TOOL_CALL_ARGUMENTS]: jsonArguments,
1347
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.name]: start.name,
1348
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.type]: VALUES.OPENTELEMETRY.GEN_AI.tool.type.function,
1349
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.id]: start.call.id,
1350
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.arguments]: jsonArguments,
1238
1351
  ...(start.attributes || {}),
1239
1352
  },
1240
1353
  });
1241
1354
  return {
1242
- context: span.context,
1355
+ ...span,
1243
1356
  end: (options) => {
1244
1357
  const end = options;
1245
1358
  let stringResult = '';
@@ -1247,7 +1360,7 @@ class ManualInstrumentation {
1247
1360
  try {
1248
1361
  stringResult = JSON.stringify(end.result.value);
1249
1362
  }
1250
- catch (error) {
1363
+ catch (_error) {
1251
1364
  stringResult = '{}';
1252
1365
  }
1253
1366
  }
@@ -1256,180 +1369,21 @@ class ManualInstrumentation {
1256
1369
  }
1257
1370
  span.end({
1258
1371
  attributes: {
1259
- [ATTR_GEN_AI_TOOL_RESULT_VALUE]: stringResult,
1260
- [ATTR_GEN_AI_TOOL_RESULT_IS_ERROR]: end.result.isError,
1372
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.value]: stringResult,
1373
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.isError]: end.result.isError,
1261
1374
  ...(end.attributes || {}),
1262
1375
  },
1263
1376
  });
1264
1377
  },
1265
- fail: span.fail,
1266
1378
  };
1267
1379
  }
1268
- attribifyMessageToolCalls(prefix, toolCalls) {
1269
- const attributes = {};
1270
- for (let i = 0; i < toolCalls.length; i++) {
1271
- for (const key in toolCalls[i]) {
1272
- const field = this.toCamelCase(key);
1273
- let value = toolCalls[i][key];
1274
- if (value === null || value === undefined)
1275
- continue;
1276
- switch (field) {
1277
- case 'id':
1278
- case 'toolCallId':
1279
- case 'toolUseId': {
1280
- if (typeof value !== 'string')
1281
- continue;
1282
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID}`] = value;
1283
- break;
1284
- }
1285
- case 'name':
1286
- case 'toolName': {
1287
- if (typeof value !== 'string')
1288
- continue;
1289
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value;
1290
- break;
1291
- }
1292
- case 'arguments':
1293
- case 'toolArguments':
1294
- case 'input': {
1295
- if (typeof value === 'string') {
1296
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value;
1297
- }
1298
- else {
1299
- try {
1300
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = JSON.stringify(value);
1301
- }
1302
- catch (error) {
1303
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = '{}';
1304
- }
1305
- }
1306
- break;
1307
- }
1308
- /* OpenAI function calls */
1309
- case 'function': {
1310
- if (typeof value !== 'object')
1311
- continue;
1312
- if (!('name' in value))
1313
- continue;
1314
- if (typeof value.name !== 'string')
1315
- continue;
1316
- if (!('arguments' in value))
1317
- continue;
1318
- if (typeof value.arguments !== 'string')
1319
- continue;
1320
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value.name;
1321
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value.arguments;
1322
- break;
1323
- }
1324
- }
1325
- }
1326
- }
1327
- return attributes;
1328
- }
1329
- attribifyMessageContent(prefix, content) {
1330
- let attributes = {};
1331
- if (typeof content === 'string') {
1332
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = content;
1333
- return attributes;
1334
- }
1335
- try {
1336
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] =
1337
- JSON.stringify(content);
1338
- }
1339
- catch (error) {
1340
- attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = '[]';
1341
- }
1342
- if (!Array.isArray(content))
1343
- return attributes;
1344
- /* Tool calls for Anthropic and PromptL are in the content */
1345
- const toolCalls = [];
1346
- for (const item of content) {
1347
- for (const key in item) {
1348
- if (this.toCamelCase(key) !== 'type')
1349
- continue;
1350
- if (typeof item[key] !== 'string')
1351
- continue;
1352
- if (item[key] !== 'tool-call' && item[key] !== 'tool_use')
1353
- continue;
1354
- toolCalls.push(item);
1355
- }
1356
- }
1357
- if (toolCalls.length > 0) {
1358
- attributes = {
1359
- ...attributes,
1360
- ...this.attribifyMessageToolCalls(prefix, toolCalls),
1361
- };
1362
- }
1363
- return attributes;
1364
- }
1365
- attribifyMessages(direction, messages) {
1366
- const prefix = direction === 'input' ? ATTR_GEN_AI_PROMPTS : ATTR_GEN_AI_COMPLETIONS;
1367
- let attributes = {};
1368
- for (let i = 0; i < messages.length; i++) {
1369
- for (const key in messages[i]) {
1370
- const field = this.toCamelCase(key);
1371
- let value = messages[i][key];
1372
- if (value === null || value === undefined)
1373
- continue;
1374
- switch (field) {
1375
- case 'role': {
1376
- if (typeof value !== 'string')
1377
- continue;
1378
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_ROLE}`] = value;
1379
- break;
1380
- }
1381
- /* Tool calls for Anthropic and PromptL are in the content */
1382
- case 'content': {
1383
- attributes = {
1384
- ...attributes,
1385
- ...this.attribifyMessageContent(`${prefix}.${i}`, value),
1386
- };
1387
- break;
1388
- }
1389
- /* Tool calls for OpenAI */
1390
- case 'toolCalls': {
1391
- if (!Array.isArray(value))
1392
- continue;
1393
- attributes = {
1394
- ...attributes,
1395
- ...this.attribifyMessageToolCalls(`${prefix}.${i}`, value),
1396
- };
1397
- break;
1398
- }
1399
- /* Tool result for OpenAI / Anthropic / PromptL */
1400
- case 'toolCallId':
1401
- case 'toolId':
1402
- case 'toolUseId': {
1403
- if (typeof value !== 'string')
1404
- continue;
1405
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID}`] =
1406
- value;
1407
- break;
1408
- }
1409
- case 'toolName': {
1410
- if (typeof value !== 'string')
1411
- continue;
1412
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_NAME}`] =
1413
- value;
1414
- break;
1415
- }
1416
- // Note: 'toolResult' is 'content' itself
1417
- case 'isError': {
1418
- if (typeof value !== 'boolean')
1419
- continue;
1420
- attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR}`] = value;
1421
- break;
1422
- }
1423
- }
1424
- }
1425
- }
1426
- return attributes;
1427
- }
1428
1380
  attribifyConfiguration(direction, configuration) {
1429
- const prefix = direction === 'input' ? ATTR_GEN_AI_REQUEST : ATTR_GEN_AI_RESPONSE;
1381
+ const prefix = direction === 'input'
1382
+ ? ATTRIBUTES.LATITUDE.request._root
1383
+ : ATTRIBUTES.LATITUDE.response._root;
1430
1384
  const attributes = {};
1431
1385
  for (const key in configuration) {
1432
- const field = this.toSnakeCase(key);
1386
+ const field = toSnakeCase(key);
1433
1387
  let value = configuration[key];
1434
1388
  if (value === null || value === undefined)
1435
1389
  continue;
@@ -1437,7 +1391,7 @@ class ManualInstrumentation {
1437
1391
  try {
1438
1392
  value = JSON.stringify(value);
1439
1393
  }
1440
- catch (error) {
1394
+ catch (_error) {
1441
1395
  value = '{}';
1442
1396
  }
1443
1397
  }
@@ -1455,45 +1409,56 @@ class ManualInstrumentation {
1455
1409
  try {
1456
1410
  jsonConfiguration = JSON.stringify(configuration);
1457
1411
  }
1458
- catch (error) {
1412
+ catch (_error) {
1459
1413
  jsonConfiguration = '{}';
1460
1414
  }
1461
1415
  const attrConfiguration = this.attribifyConfiguration('input', configuration);
1462
1416
  const input = start.input ?? [];
1417
+ let jsonSystem = '';
1463
1418
  let jsonInput = '';
1464
1419
  try {
1465
- jsonInput = JSON.stringify(input);
1420
+ const translated = translator.translate(input, {
1421
+ from: this.options.provider,
1422
+ to: rosettaAi.Provider.GenAI,
1423
+ direction: 'input',
1424
+ });
1425
+ jsonSystem = JSON.stringify(translated.system ?? []);
1426
+ jsonInput = JSON.stringify(translated.messages ?? []);
1466
1427
  }
1467
- catch (error) {
1428
+ catch (_error) {
1429
+ jsonSystem = '[]';
1468
1430
  jsonInput = '[]';
1469
1431
  }
1470
- const attrInput = this.attribifyMessages('input', input);
1471
1432
  const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
1472
1433
  attributes: {
1473
- [incubating.ATTR_GEN_AI_SYSTEM]: start.provider,
1474
- [ATTR_GEN_AI_REQUEST_CONFIGURATION]: jsonConfiguration,
1434
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.system]: start.provider,
1435
+ [ATTRIBUTES.LATITUDE.request.configuration]: jsonConfiguration,
1475
1436
  ...attrConfiguration,
1476
- [ATTR_GEN_AI_REQUEST_MESSAGES]: jsonInput,
1477
- ...attrInput,
1437
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.systemInstructions]: jsonSystem,
1438
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.input.messages]: jsonInput,
1478
1439
  ...(start.attributes || {}),
1479
- [ATTR_LATITUDE_COMMIT_UUID]: start.versionUuid,
1480
- [ATTR_LATITUDE_DOCUMENT_UUID]: start.promptUuid,
1481
- [ATTR_LATITUDE_EXPERIMENT_UUID]: start.experimentUuid,
1440
+ [ATTRIBUTES.LATITUDE.commitUuid]: start.versionUuid,
1441
+ [ATTRIBUTES.LATITUDE.documentUuid]: start.promptUuid,
1442
+ [ATTRIBUTES.LATITUDE.experimentUuid]: start.experimentUuid,
1482
1443
  },
1483
1444
  });
1484
1445
  return {
1485
- context: span.context,
1446
+ ...span,
1486
1447
  end: (options) => {
1487
1448
  const end = options ?? {};
1488
1449
  const output = end.output ?? [];
1489
1450
  let jsonOutput = '';
1490
1451
  try {
1491
- jsonOutput = JSON.stringify(output);
1452
+ const translated = translator.translate(output, {
1453
+ from: this.options.provider,
1454
+ to: rosettaAi.Provider.GenAI,
1455
+ direction: 'output',
1456
+ });
1457
+ jsonOutput = JSON.stringify(translated.messages ?? []);
1492
1458
  }
1493
- catch (error) {
1459
+ catch (_error) {
1494
1460
  jsonOutput = '[]';
1495
1461
  }
1496
- const attrOutput = this.attribifyMessages('output', output);
1497
1462
  const tokens = {
1498
1463
  prompt: end.tokens?.prompt ?? 0,
1499
1464
  cached: end.tokens?.cached ?? 0,
@@ -1505,39 +1470,33 @@ class ManualInstrumentation {
1505
1470
  const finishReason = end.finishReason ?? '';
1506
1471
  span.end({
1507
1472
  attributes: {
1508
- [ATTR_GEN_AI_RESPONSE_MESSAGES]: jsonOutput,
1509
- ...attrOutput,
1510
- [incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
1511
- [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: tokens.prompt,
1512
- [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: tokens.cached,
1513
- [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: tokens.reasoning,
1514
- [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: tokens.completion,
1515
- [incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
1516
- [incubating.ATTR_GEN_AI_RESPONSE_MODEL]: start.model,
1517
- [incubating.ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [finishReason],
1473
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.output.messages]: jsonOutput,
1474
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.inputTokens]: inputTokens,
1475
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.outputTokens]: outputTokens,
1476
+ [ATTRIBUTES.LATITUDE.usage.promptTokens]: tokens.prompt,
1477
+ [ATTRIBUTES.LATITUDE.usage.cachedTokens]: tokens.cached,
1478
+ [ATTRIBUTES.LATITUDE.usage.reasoningTokens]: tokens.reasoning,
1479
+ [ATTRIBUTES.LATITUDE.usage.completionTokens]: tokens.completion,
1480
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.model]: start.model,
1481
+ [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.finishReasons]: [
1482
+ finishReason,
1483
+ ],
1518
1484
  ...(end.attributes || {}),
1519
1485
  },
1520
1486
  });
1521
1487
  },
1522
- fail: span.fail,
1523
1488
  };
1524
1489
  }
1525
1490
  embedding(ctx, options) {
1526
1491
  return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Embedding].name, SpanType.Embedding, options);
1527
1492
  }
1528
- retrieval(ctx, options) {
1529
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Retrieval].name, SpanType.Retrieval, options);
1530
- }
1531
- reranking(ctx, options) {
1532
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Reranking].name, SpanType.Reranking, options);
1533
- }
1534
1493
  attribifyHeaders(direction, headers) {
1535
1494
  const prefix = direction === 'request'
1536
- ? ATTR_HTTP_REQUEST_HEADER
1537
- : ATTR_HTTP_RESPONSE_HEADER;
1495
+ ? ATTRIBUTES.OPENTELEMETRY.HTTP.request.header
1496
+ : ATTRIBUTES.OPENTELEMETRY.HTTP.response.header;
1538
1497
  const attributes = {};
1539
1498
  for (const key in headers) {
1540
- const field = this.toKebabCase(key);
1499
+ const field = toKebabCase(key);
1541
1500
  const value = headers[key];
1542
1501
  if (value === null || value === undefined)
1543
1502
  continue;
@@ -1558,21 +1517,21 @@ class ManualInstrumentation {
1558
1517
  try {
1559
1518
  finalBody = JSON.stringify(start.request.body);
1560
1519
  }
1561
- catch (error) {
1520
+ catch (_error) {
1562
1521
  finalBody = '{}';
1563
1522
  }
1564
1523
  }
1565
1524
  const span = this.span(ctx, start.name || `${method} ${start.request.url}`, SpanType.Http, {
1566
1525
  attributes: {
1567
- [semanticConventions.ATTR_HTTP_REQUEST_METHOD]: method,
1568
- [ATTR_HTTP_REQUEST_URL]: start.request.url,
1526
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.method]: method,
1527
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.url]: start.request.url,
1569
1528
  ...attrHeaders,
1570
- [ATTR_HTTP_REQUEST_BODY]: finalBody,
1529
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.request.body]: finalBody,
1571
1530
  ...(start.attributes || {}),
1572
1531
  },
1573
1532
  });
1574
1533
  return {
1575
- context: span.context,
1534
+ ...span,
1576
1535
  end: (options) => {
1577
1536
  const end = options;
1578
1537
  // Note: do not serialize headers as a single attribute because fields won't be redacted
@@ -1585,20 +1544,19 @@ class ManualInstrumentation {
1585
1544
  try {
1586
1545
  finalBody = JSON.stringify(end.response.body);
1587
1546
  }
1588
- catch (error) {
1547
+ catch (_error) {
1589
1548
  finalBody = '{}';
1590
1549
  }
1591
1550
  }
1592
1551
  span.end({
1593
1552
  attributes: {
1594
- [semanticConventions.ATTR_HTTP_RESPONSE_STATUS_CODE]: end.response.status,
1553
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.response.statusCode]: end.response.status,
1595
1554
  ...attrHeaders,
1596
- [ATTR_HTTP_RESPONSE_BODY]: finalBody,
1555
+ [ATTRIBUTES.OPENTELEMETRY.HTTP.response.body]: finalBody,
1597
1556
  ...(end.attributes || {}),
1598
1557
  },
1599
1558
  });
1600
1559
  },
1601
- fail: span.fail,
1602
1560
  };
1603
1561
  }
1604
1562
  prompt(ctx, { documentLogUuid, versionUuid, promptUuid, projectId, experimentUuid, testDeploymentId, externalId, template, parameters, name, source, ...rest }) {
@@ -1606,49 +1564,50 @@ class ManualInstrumentation {
1606
1564
  try {
1607
1565
  jsonParameters = JSON.stringify(parameters || {});
1608
1566
  }
1609
- catch (error) {
1567
+ catch (_error) {
1610
1568
  jsonParameters = '{}';
1611
1569
  }
1612
1570
  const attributes = {
1613
- [ATTR_GEN_AI_REQUEST_TEMPLATE]: template,
1614
- [ATTR_GEN_AI_REQUEST_PARAMETERS]: jsonParameters,
1615
- [ATTR_LATITUDE_COMMIT_UUID]: versionUuid || HEAD_COMMIT,
1616
- [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1617
- [ATTR_LATITUDE_PROJECT_ID]: projectId,
1618
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1571
+ [ATTRIBUTES.LATITUDE.request.template]: template,
1572
+ [ATTRIBUTES.LATITUDE.request.parameters]: jsonParameters,
1573
+ [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid || HEAD_COMMIT,
1574
+ [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1575
+ [ATTRIBUTES.LATITUDE.projectId]: projectId,
1576
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1619
1577
  ...(experimentUuid && {
1620
- [ATTR_LATITUDE_EXPERIMENT_UUID]: experimentUuid,
1578
+ [ATTRIBUTES.LATITUDE.experimentUuid]: experimentUuid,
1621
1579
  }),
1622
1580
  ...(testDeploymentId && {
1623
- [ATTR_LATITUDE_TEST_DEPLOYMENT_ID]: testDeploymentId,
1581
+ [ATTRIBUTES.LATITUDE.testDeploymentId]: testDeploymentId,
1624
1582
  }),
1625
- ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1626
- ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1583
+ ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1584
+ ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1627
1585
  ...(rest.attributes || {}),
1628
1586
  };
1629
1587
  return this.span(ctx, name || `prompt-${promptUuid}`, SpanType.Prompt, {
1630
1588
  attributes,
1631
1589
  });
1632
1590
  }
1633
- step(ctx, options) {
1634
- return this.span(ctx, 'step', SpanType.Step, options);
1635
- }
1636
- chat(ctx, { documentLogUuid, previousTraceId, source, name, ...rest }) {
1591
+ chat(ctx, { documentLogUuid, previousTraceId, source, name, versionUuid, promptUuid, ...rest }) {
1637
1592
  const attributes = {
1638
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1639
- [ATTR_LATITUDE_PREVIOUS_TRACE_ID]: previousTraceId,
1640
- ...(source && { [ATTR_LATITUDE_SOURCE]: source }),
1593
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1594
+ [ATTRIBUTES.LATITUDE.previousTraceId]: previousTraceId,
1595
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1596
+ ...(promptUuid && { [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid }),
1597
+ ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1641
1598
  ...(rest.attributes || {}),
1642
1599
  };
1643
- return this.span(ctx, name || 'chat', SpanType.Chat, { attributes });
1600
+ return this.span(ctx, name || `chat-${documentLogUuid}`, SpanType.Chat, {
1601
+ attributes,
1602
+ });
1644
1603
  }
1645
1604
  external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
1646
1605
  const attributes = {
1647
- [ATTR_LATITUDE_DOCUMENT_UUID]: promptUuid,
1648
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: documentLogUuid,
1649
- [ATTR_LATITUDE_SOURCE]: source ?? LogSources.API,
1650
- ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1651
- ...(externalId && { [ATTR_LATITUDE_EXTERNAL_ID]: externalId }),
1606
+ [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1607
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1608
+ [ATTRIBUTES.LATITUDE.source]: source ?? LogSources.API,
1609
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1610
+ ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1652
1611
  ...(rest.attributes || {}),
1653
1612
  };
1654
1613
  return this.span(ctx, name || `external-${promptUuid}`, SpanType.External, {
@@ -1657,11 +1616,11 @@ class ManualInstrumentation {
1657
1616
  }
1658
1617
  unresolvedExternal(ctx, { path, projectId, versionUuid, conversationUuid, name, ...rest }) {
1659
1618
  const attributes = {
1660
- [ATTR_LATITUDE_PROMPT_PATH]: path,
1661
- [ATTR_LATITUDE_PROJECT_ID]: projectId,
1662
- ...(versionUuid && { [ATTR_LATITUDE_COMMIT_UUID]: versionUuid }),
1619
+ [ATTRIBUTES.LATITUDE.promptPath]: path,
1620
+ [ATTRIBUTES.LATITUDE.projectId]: projectId,
1621
+ ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1663
1622
  ...(conversationUuid && {
1664
- [ATTR_LATITUDE_DOCUMENT_LOG_UUID]: conversationUuid,
1623
+ [ATTRIBUTES.LATITUDE.documentLogUuid]: conversationUuid,
1665
1624
  }),
1666
1625
  ...(rest.attributes || {}),
1667
1626
  };
@@ -1680,8 +1639,8 @@ class LatitudeInstrumentation {
1680
1639
  return this.manualTelemetry.isEnabled();
1681
1640
  }
1682
1641
  enable() {
1683
- this.options.module.instrument(this);
1684
1642
  this.manualTelemetry.enable();
1643
+ this.options.module.instrument(this);
1685
1644
  }
1686
1645
  disable() {
1687
1646
  this.manualTelemetry.disable();
@@ -1726,19 +1685,6 @@ class LatitudeInstrumentation {
1726
1685
  $prompt.end();
1727
1686
  return result;
1728
1687
  }
1729
- async wrapRenderStep(fn, ...args) {
1730
- const $step = this.manualTelemetry.step(otel.context.active());
1731
- let result;
1732
- try {
1733
- result = await otel.context.with($step.context, async () => await fn(...args));
1734
- }
1735
- catch (error) {
1736
- $step.fail(error);
1737
- throw error;
1738
- }
1739
- $step.end();
1740
- return result;
1741
- }
1742
1688
  async wrapRenderCompletion(fn, ...args) {
1743
1689
  if (!this.options.completions) {
1744
1690
  return await fn(...args);
@@ -1772,8 +1718,8 @@ class LatitudeInstrumentation {
1772
1718
  completion: completionTokens,
1773
1719
  },
1774
1720
  finishReason: result.toolRequests.length > 0
1775
- ? GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS
1776
- : GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP,
1721
+ ? VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.toolCalls
1722
+ : VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.stop,
1777
1723
  });
1778
1724
  return result;
1779
1725
  }
@@ -1818,6 +1764,7 @@ var LatitudeErrorCodes;
1818
1764
  LatitudeErrorCodes["NotImplementedError"] = "NotImplementedError";
1819
1765
  LatitudeErrorCodes["PaymentRequiredError"] = "PaymentRequiredError";
1820
1766
  LatitudeErrorCodes["AbortedError"] = "AbortedError";
1767
+ LatitudeErrorCodes["BillingError"] = "BillingError";
1821
1768
  })(LatitudeErrorCodes || (LatitudeErrorCodes = {}));
1822
1769
  // NOTE: If you add a new error code, please add it to the pg enum in models/runErrors.ts
1823
1770
  var RunErrorCodes;
@@ -1888,6 +1835,9 @@ class SpanFactory {
1888
1835
  constructor(telemetry) {
1889
1836
  this.telemetry = telemetry;
1890
1837
  }
1838
+ span(options, ctx) {
1839
+ return this.telemetry.unknown(ctx ?? otel.context.active(), options);
1840
+ }
1891
1841
  tool(options, ctx) {
1892
1842
  return this.telemetry.tool(ctx ?? otel.context.active(), options);
1893
1843
  }
@@ -1897,21 +1847,12 @@ class SpanFactory {
1897
1847
  embedding(options, ctx) {
1898
1848
  return this.telemetry.embedding(ctx ?? otel.context.active(), options);
1899
1849
  }
1900
- retrieval(options, ctx) {
1901
- return this.telemetry.retrieval(ctx ?? otel.context.active(), options);
1902
- }
1903
- reranking(options, ctx) {
1904
- return this.telemetry.reranking(ctx ?? otel.context.active(), options);
1905
- }
1906
1850
  http(options, ctx) {
1907
1851
  return this.telemetry.http(ctx ?? otel.context.active(), options);
1908
1852
  }
1909
1853
  prompt(options, ctx) {
1910
1854
  return this.telemetry.prompt(ctx ?? otel.context.active(), options);
1911
1855
  }
1912
- step(options, ctx) {
1913
- return this.telemetry.step(ctx ?? otel.context.active(), options);
1914
- }
1915
1856
  chat(options, ctx) {
1916
1857
  return this.telemetry.chat(ctx ?? otel.context.active(), options);
1917
1858
  }
@@ -1930,6 +1871,9 @@ class ContextManager {
1930
1871
  active() {
1931
1872
  return otel.context.active();
1932
1873
  }
1874
+ with(ctx, fn, thisArg, ...args) {
1875
+ return otel.context.with(ctx, fn, thisArg, ...args);
1876
+ }
1933
1877
  }
1934
1878
  class InstrumentationManager {
1935
1879
  instrumentations;
@@ -1976,6 +1920,23 @@ class ScopedTracerProvider {
1976
1920
  return this.provider.getTracer(this.scope, this.version, options);
1977
1921
  }
1978
1922
  }
1923
+ class LifecycleManager {
1924
+ nodeProvider;
1925
+ exporter;
1926
+ constructor(nodeProvider, exporter) {
1927
+ this.nodeProvider = nodeProvider;
1928
+ this.exporter = exporter;
1929
+ }
1930
+ async flush() {
1931
+ await this.nodeProvider.forceFlush();
1932
+ await this.exporter.forceFlush?.();
1933
+ }
1934
+ async shutdown() {
1935
+ await this.flush();
1936
+ await this.nodeProvider.shutdown();
1937
+ await this.exporter.shutdown?.();
1938
+ }
1939
+ }
1979
1940
  const DEFAULT_SPAN_EXPORTER = (apiKey) => new exporterTraceOtlpHttp.OTLPTraceExporter({
1980
1941
  url: TRACES_URL,
1981
1942
  headers: {
@@ -1994,6 +1955,7 @@ exports.Instrumentation = void 0;
1994
1955
  Instrumentation["Langchain"] = "langchain";
1995
1956
  Instrumentation["Latitude"] = "latitude";
1996
1957
  Instrumentation["LlamaIndex"] = "llamaindex";
1958
+ Instrumentation["Manual"] = "manual";
1997
1959
  Instrumentation["OpenAI"] = "openai";
1998
1960
  Instrumentation["TogetherAI"] = "togetherai";
1999
1961
  Instrumentation["VertexAI"] = "vertexai";
@@ -2007,6 +1969,7 @@ class LatitudeTelemetry {
2007
1969
  context;
2008
1970
  instrumentation;
2009
1971
  tracer;
1972
+ lifecycle;
2010
1973
  constructor(apiKey, options) {
2011
1974
  this.options = options || {};
2012
1975
  if (!this.options.exporter) {
@@ -2023,6 +1986,7 @@ class LatitudeTelemetry {
2023
1986
  this.nodeProvider = new sdkTraceNode.NodeTracerProvider({
2024
1987
  resource: new resources.Resource({ [semanticConventions.ATTR_SERVICE_NAME]: SERVICE_NAME }),
2025
1988
  });
1989
+ this.lifecycle = new LifecycleManager(this.nodeProvider, this.options.exporter);
2026
1990
  // Note: important, must run before the exporter span processors
2027
1991
  this.nodeProvider.addSpanProcessor(new baggageSpanProcessor.BaggageSpanProcessor(baggageSpanProcessor.ALLOW_ALL_BAGGAGE_KEYS));
2028
1992
  if (this.options.processors) {
@@ -2052,19 +2016,16 @@ class LatitudeTelemetry {
2052
2016
  this.context = new ContextManager(this.manualInstrumentation);
2053
2017
  }
2054
2018
  async flush() {
2055
- await this.nodeProvider.forceFlush();
2056
- await this.options.exporter.forceFlush?.();
2019
+ await this.lifecycle.flush();
2057
2020
  }
2058
2021
  async shutdown() {
2059
- await this.flush();
2060
- await this.nodeProvider.shutdown();
2061
- await this.options.exporter.shutdown?.();
2022
+ await this.lifecycle.shutdown();
2062
2023
  }
2063
2024
  // TODO(tracing): auto instrument outgoing HTTP requests
2064
2025
  initInstrumentations() {
2065
2026
  this.instrumentationsList = [];
2066
- const tracer = this.tracer.get(InstrumentationScope.Manual);
2067
- this.manualInstrumentation = new ManualInstrumentation(tracer);
2027
+ const tracer = this.tracer.get(exports.Instrumentation.Manual);
2028
+ this.manualInstrumentation = new ManualInstrumentation(tracer, this.options.instrumentations?.manual);
2068
2029
  this.instrumentationsList.push(this.manualInstrumentation);
2069
2030
  const latitude = this.options.instrumentations?.latitude;
2070
2031
  if (latitude) {
@@ -2074,12 +2035,12 @@ class LatitudeTelemetry {
2074
2035
  }
2075
2036
  const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
2076
2037
  const providerPkg = this.options.instrumentations?.[instrumentationType];
2038
+ if (!providerPkg)
2039
+ return;
2077
2040
  const provider = this.tracer.provider(instrumentationType);
2078
2041
  const instrumentation$1 = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
2079
2042
  instrumentation$1.setTracerProvider(provider);
2080
- if (providerPkg) {
2081
- instrumentation$1.manuallyInstrument(providerPkg);
2082
- }
2043
+ instrumentation$1.manuallyInstrument(providerPkg);
2083
2044
  instrumentation.registerInstrumentations({
2084
2045
  instrumentations: [instrumentation$1],
2085
2046
  tracerProvider: provider,
@@ -2092,27 +2053,26 @@ class LatitudeTelemetry {
2092
2053
  configureInstrumentation(exports.Instrumentation.Cohere, instrumentationCohere.CohereInstrumentation); // prettier-ignore
2093
2054
  configureInstrumentation(exports.Instrumentation.Langchain, instrumentationLangchain.LangChainInstrumentation); // prettier-ignore
2094
2055
  configureInstrumentation(exports.Instrumentation.LlamaIndex, instrumentationLlamaindex.LlamaIndexInstrumentation); // prettier-ignore
2095
- configureInstrumentation(exports.Instrumentation.OpenAI, instrumentationOpenai.OpenAIInstrumentation, { enrichTokens: true }); // prettier-ignore
2096
- configureInstrumentation(exports.Instrumentation.TogetherAI, instrumentationTogether.TogetherInstrumentation, { enrichTokens: true }); // prettier-ignore
2056
+ // NOTE: `stream: true` in OpenAI make enrichTokens fail, so disabling.
2057
+ configureInstrumentation(exports.Instrumentation.OpenAI, instrumentationOpenai.OpenAIInstrumentation, { enrichTokens: false }); // prettier-ignore
2058
+ configureInstrumentation(exports.Instrumentation.TogetherAI, instrumentationTogether.TogetherInstrumentation, { enrichTokens: false }); // prettier-ignore
2097
2059
  configureInstrumentation(exports.Instrumentation.VertexAI, instrumentationVertexai.VertexAIInstrumentation); // prettier-ignore
2098
2060
  }
2099
2061
  async capture(options, fn) {
2100
2062
  if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
2101
2063
  throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
2102
2064
  }
2103
- const span = this.manualInstrumentation.unresolvedExternal(otel__namespace.ROOT_CONTEXT, options);
2065
+ const span = this.manualInstrumentation.unresolvedExternal(BACKGROUND(), options);
2066
+ let result;
2104
2067
  try {
2105
- const result = await otel.context.with(span.context, () => fn(span.context));
2106
- span.end();
2107
- return result;
2068
+ result = await otel.context.with(span.context, async () => await fn(span.context));
2108
2069
  }
2109
2070
  catch (error) {
2110
2071
  span.fail(error);
2111
2072
  throw error;
2112
2073
  }
2113
- finally {
2114
- await this.flush();
2115
- }
2074
+ span.end();
2075
+ return result;
2116
2076
  }
2117
2077
  }
2118
2078