@latitude-data/telemetry 0.0.4 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,487 +1,2053 @@
1
1
  'use strict';
2
2
 
3
+ var zod = require('zod');
4
+ var otel = require('@opentelemetry/api');
5
+ var semanticConventions = require('@opentelemetry/semantic-conventions');
6
+ var incubating = require('@opentelemetry/semantic-conventions/incubating');
7
+ var uuid = require('uuid');
8
+ var baggageSpanProcessor = require('@opentelemetry/baggage-span-processor');
9
+ var contextAsyncHooks = require('@opentelemetry/context-async-hooks');
3
10
  var core = require('@opentelemetry/core');
4
- var otlpExporterBase = require('@opentelemetry/otlp-exporter-base');
5
- var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
6
- var sdkNode = require('@opentelemetry/sdk-node');
11
+ var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
12
+ var instrumentation = require('@opentelemetry/instrumentation');
13
+ var resources = require('@opentelemetry/resources');
7
14
  var sdkTraceNode = require('@opentelemetry/sdk-trace-node');
8
15
  var instrumentationAnthropic = require('@traceloop/instrumentation-anthropic');
9
- var instrumentationOpenai = require('@traceloop/instrumentation-openai');
10
16
  var instrumentationAzure = require('@traceloop/instrumentation-azure');
11
- var instrumentationVertexai = require('@traceloop/instrumentation-vertexai');
12
17
  var instrumentationBedrock = require('@traceloop/instrumentation-bedrock');
13
18
  var instrumentationCohere = require('@traceloop/instrumentation-cohere');
14
- var resources = require('@opentelemetry/resources');
15
- var api = require('@opentelemetry/api');
19
+ var instrumentationLangchain = require('@traceloop/instrumentation-langchain');
20
+ var instrumentationLlamaindex = require('@traceloop/instrumentation-llamaindex');
21
+ var instrumentationOpenai = require('@traceloop/instrumentation-openai');
22
+ var instrumentationTogether = require('@traceloop/instrumentation-together');
23
+ var instrumentationVertexai = require('@traceloop/instrumentation-vertexai');
16
24
 
17
- const DOMAIN = 'gateway.latitude.so/api/v2/otlp'
18
- ;
19
- const PROTOCOL = 'https' ;
20
- class LatitudeExporter extends otlpExporterBase.OTLPExporterBase {
21
- url;
22
- headers;
23
- constructor(config) {
24
- super(config);
25
- this.url = config.endpoint || this.__defaultUrl;
26
- this.headers = {
27
- Authorization: `Bearer ${config.apiKey}`,
28
- };
29
- }
30
- async export(spans, resultCallback) {
31
- await this.send(spans, () => resultCallback({ code: core.ExportResultCode.SUCCESS }), (error) => {
32
- resultCallback({ code: core.ExportResultCode.FAILED, error });
25
+ function _interopNamespaceDefault(e) {
26
+ var n = Object.create(null);
27
+ if (e) {
28
+ Object.keys(e).forEach(function (k) {
29
+ if (k !== 'default') {
30
+ var d = Object.getOwnPropertyDescriptor(e, k);
31
+ Object.defineProperty(n, k, d.get ? d : {
32
+ enumerable: true,
33
+ get: function () { return e[k]; }
34
+ });
35
+ }
33
36
  });
34
37
  }
35
- async shutdown() {
36
- // No-op
37
- }
38
- async onShutdown() {
39
- // No-op
38
+ n.default = e;
39
+ return Object.freeze(n);
40
+ }
41
+
42
+ var otel__namespace = /*#__PURE__*/_interopNamespaceDefault(otel);
43
+
44
+ class RedactSpanProcessor {
45
+ options;
46
+ constructor(options) {
47
+ this.options = options;
48
+ if (!options.mask) {
49
+ this.options.mask = (_attribute, _value) => '******';
50
+ }
40
51
  }
41
- async onInit() {
42
- // No-op
52
+ onStart(_span, _context) {
53
+ // Noop
43
54
  }
44
- async send(spans, onSuccess, onError) {
45
- if (spans.length === 0) {
46
- onSuccess();
47
- return;
48
- }
49
- const serviceRequest = this.convert(spans);
50
- const body = JSON.stringify(serviceRequest);
51
- try {
52
- const response = await fetch(this.url, {
53
- method: 'POST',
54
- headers: {
55
- 'Content-Type': 'application/json',
56
- ...this.headers,
57
- },
58
- body,
59
- });
60
- if (!response.ok || response.status >= 400) {
61
- throw new Error(`${response.status} ${response.statusText}`);
62
- }
63
- onSuccess();
55
+ onEnd(span) {
56
+ Object.assign(span.attributes, this.redactAttributes(span.attributes));
57
+ for (const event of span.events) {
58
+ if (!event.attributes)
59
+ continue;
60
+ Object.assign(event.attributes, this.redactAttributes(event.attributes));
64
61
  }
65
- catch (error) {
66
- onError(error);
62
+ for (const link of span.links) {
63
+ if (!link.attributes)
64
+ continue;
65
+ Object.assign(link.attributes, this.redactAttributes(link.attributes));
67
66
  }
68
67
  }
69
- convert(spans) {
70
- return {
71
- resourceSpans: [
72
- {
73
- resource: {
74
- attributes: this.convertAttributes(spans[0]?.resource?.attributes || {}),
75
- },
76
- scopeSpans: [
77
- {
78
- spans: spans.map((span) => ({
79
- traceId: span.spanContext().traceId,
80
- spanId: span.spanContext().spanId,
81
- parentSpanId: span.parentSpanId,
82
- name: span.name,
83
- kind: span.kind,
84
- startTimeUnixNano: core.hrTimeToNanoseconds(span.startTime).toString(),
85
- endTimeUnixNano: span.endTime
86
- ? core.hrTimeToNanoseconds(span.endTime).toString()
87
- : undefined,
88
- attributes: this.convertAttributes(span.attributes),
89
- status: span.status && {
90
- code: span.status.code,
91
- message: span.status.message,
92
- },
93
- events: span.events?.map((event) => ({
94
- timeUnixNano: core.hrTimeToNanoseconds(event.time).toString(),
95
- name: event.name,
96
- attributes: this.convertAttributes(event.attributes),
97
- })),
98
- links: span.links?.map((link) => ({
99
- traceId: link.context.traceId,
100
- spanId: link.context.spanId,
101
- attributes: this.convertAttributes(link.attributes),
102
- })),
103
- })),
104
- },
105
- ],
106
- },
107
- ],
108
- };
68
+ forceFlush() {
69
+ return Promise.resolve();
109
70
  }
110
- getDefaultUrl(config) {
111
- return config.endpoint || this.__defaultUrl;
71
+ shutdown() {
72
+ return Promise.resolve();
112
73
  }
113
- get __defaultUrl() {
114
- return `${PROTOCOL}://${DOMAIN}/v1/traces`;
74
+ shouldRedact(attribute) {
75
+ return this.options.attributes.some((pattern) => {
76
+ if (typeof pattern === 'string') {
77
+ return attribute === pattern;
78
+ }
79
+ else if (pattern instanceof RegExp) {
80
+ return pattern.test(attribute);
81
+ }
82
+ return false;
83
+ });
115
84
  }
116
- convertAttributes(attributes = {}) {
117
- return Object.entries(attributes).map(([key, value]) => ({
118
- key,
119
- value: this.convertAttributeValue(value),
120
- }));
85
+ redactAttributes(attributes) {
86
+ const redacted = {};
87
+ for (const [key, value] of Object.entries(attributes)) {
88
+ if (this.shouldRedact(key)) {
89
+ redacted[key] = this.options.mask(key, value);
90
+ }
91
+ }
92
+ return redacted;
121
93
  }
122
- convertAttributeValue(value) {
123
- if (typeof value === 'string')
124
- return { stringValue: value };
125
- if (typeof value === 'number')
126
- return { intValue: value };
127
- if (typeof value === 'boolean')
128
- return { boolValue: value };
129
- return { stringValue: String(value) };
94
+ }
95
+ const DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
96
+ attributes: [
97
+ /^.*auth.*$/i,
98
+ /^.*authorization.*$/i,
99
+ /^(?!gen_ai\.).*token.*$/i,
100
+ /^.*secret.*$/i,
101
+ /^.*key.*$/i,
102
+ /^.*password.*$/i,
103
+ /^.*cookie.*$/i,
104
+ /^.*session.*$/i,
105
+ /^.*credential.*$/i,
106
+ /^.*signature.*$/i,
107
+ /^.*oauth.*$/i,
108
+ /^.*saml.*$/i,
109
+ /^.*openid.*$/i,
110
+ /^.*refresh.*$/i,
111
+ /^.*jwt.*$/i,
112
+ /^.*otp.*$/i,
113
+ /^.*mfa.*$/i,
114
+ /^.*csrf.*$/i,
115
+ /^.*xsrf.*$/i,
116
+ /^.*refresh.*$/i,
117
+ /^.*x[-_]forwarded[-_]for.*$/i,
118
+ /^.*x[-_]real[-_]ip.*$/i,
119
+ ],
120
+ });
121
+
122
+ const DEFAULT_GATEWAY_BASE_URL = {
123
+ production: 'https://gateway.latitude.so',
124
+ development: 'http://localhost:8787',
125
+ test: 'http://localhost:8787',
126
+ }["production"];
127
+ function GET_GATEWAY_BASE_URL() {
128
+ if (process.env.GATEWAY_BASE_URL) {
129
+ return process.env.GATEWAY_BASE_URL;
130
130
  }
131
+ if (!process.env.GATEWAY_HOSTNAME) {
132
+ return DEFAULT_GATEWAY_BASE_URL;
133
+ }
134
+ const protocol = process.env.GATEWAY_SSL ? 'https' : 'http';
135
+ const port = process.env.GATEWAY_PORT ?? (process.env.GATEWAY_SSL ? 443 : 80);
136
+ const hostname = process.env.GATEWAY_HOSTNAME;
137
+ return `${protocol}://${hostname}:${port}`;
131
138
  }
139
+ const env = { GATEWAY_BASE_URL: GET_GATEWAY_BASE_URL() };
132
140
 
133
- /**
134
- * Below are the semantic conventions for the Vercel AI SDK.
135
- * @see https://sdk.vercel.ai/docs/ai-sdk-core/telemetry#collected-data
136
- */
137
- const AI_PREFIX = 'ai';
138
- const AIPrefixes = {
139
- settings: 'settings',
140
- model: 'model',
141
- usage: 'usage',
142
- telemetry: 'telemetry',
143
- prompt: 'prompt',
144
- toolCall: 'toolCall',
145
- response: 'response',
146
- };
147
- const AIUsagePostfixes = {
148
- completionTokens: 'completionTokens',
149
- promptTokens: 'promptTokens',
141
+ var SegmentSource;
142
+ (function (SegmentSource) {
143
+ SegmentSource["API"] = "api";
144
+ SegmentSource["Playground"] = "playground";
145
+ SegmentSource["Evaluation"] = "evaluation";
146
+ SegmentSource["Experiment"] = "experiment";
147
+ SegmentSource["User"] = "user";
148
+ SegmentSource["SharedPrompt"] = "shared_prompt";
149
+ SegmentSource["AgentAsTool"] = "agent_as_tool";
150
+ SegmentSource["EmailTrigger"] = "email_trigger";
151
+ SegmentSource["ScheduledTrigger"] = "scheduled_trigger";
152
+ })(SegmentSource || (SegmentSource = {}));
153
+ var SegmentType;
154
+ (function (SegmentType) {
155
+ SegmentType["Document"] = "document";
156
+ SegmentType["Step"] = "step";
157
+ })(SegmentType || (SegmentType = {}));
158
+ const SEGMENT_SPECIFICATIONS = {
159
+ [SegmentType.Document]: {
160
+ name: 'Prompt',
161
+ description: 'A prompt',
162
+ },
163
+ [SegmentType.Step]: {
164
+ name: 'Step',
165
+ description: 'A step in a prompt',
166
+ },
150
167
  };
151
- const AIResultPostfixes = {
152
- text: 'text',
153
- toolCalls: 'toolCalls',
154
- object: 'object',
168
+ const baseSegmentBaggageSchema = zod.z.object({
169
+ id: zod.z.string(),
170
+ parentId: zod.z.string().optional(),
171
+ source: zod.z.nativeEnum(SegmentSource),
172
+ });
173
+ zod.z.discriminatedUnion('type', [
174
+ baseSegmentBaggageSchema.extend({
175
+ type: zod.z.literal(SegmentType.Document),
176
+ data: zod.z.object({
177
+ logUuid: zod.z.string().optional(), // TODO(tracing): temporal related log, remove when observability is ready
178
+ commitUuid: zod.z.string(),
179
+ documentUuid: zod.z.string(),
180
+ experimentUuid: zod.z.string().optional(),
181
+ externalId: zod.z.string().optional(),
182
+ }),
183
+ }),
184
+ baseSegmentBaggageSchema.extend({
185
+ type: zod.z.literal(SegmentType.Step),
186
+ data: zod.z.undefined().optional(),
187
+ }),
188
+ ]);
189
+
190
+ var SpanKind;
191
+ (function (SpanKind) {
192
+ SpanKind["Internal"] = "internal";
193
+ SpanKind["Server"] = "server";
194
+ SpanKind["Client"] = "client";
195
+ SpanKind["Producer"] = "producer";
196
+ SpanKind["Consumer"] = "consumer";
197
+ })(SpanKind || (SpanKind = {}));
198
+ // Note: loosely based on OpenTelemetry GenAI semantic conventions
199
+ var SpanType;
200
+ (function (SpanType) {
201
+ SpanType["Tool"] = "tool";
202
+ SpanType["Completion"] = "completion";
203
+ SpanType["Embedding"] = "embedding";
204
+ SpanType["Retrieval"] = "retrieval";
205
+ SpanType["Reranking"] = "reranking";
206
+ SpanType["Http"] = "http";
207
+ SpanType["Segment"] = "segment";
208
+ SpanType["Unknown"] = "unknown";
209
+ })(SpanType || (SpanType = {}));
210
+ const SPAN_SPECIFICATIONS = {
211
+ [SpanType.Tool]: {
212
+ name: 'Tool',
213
+ description: 'A tool call',
214
+ isGenAI: true,
215
+ isHidden: false,
216
+ },
217
+ [SpanType.Completion]: {
218
+ name: 'Completion',
219
+ description: 'A completion call',
220
+ isGenAI: true,
221
+ isHidden: false,
222
+ },
223
+ [SpanType.Embedding]: {
224
+ name: 'Embedding',
225
+ description: 'An embedding call',
226
+ isGenAI: true,
227
+ isHidden: false,
228
+ },
229
+ [SpanType.Retrieval]: {
230
+ name: 'Retrieval',
231
+ description: 'A retrieval call',
232
+ isGenAI: true,
233
+ isHidden: false,
234
+ },
235
+ [SpanType.Reranking]: {
236
+ name: 'Reranking',
237
+ description: 'A reranking call',
238
+ isGenAI: true,
239
+ isHidden: false,
240
+ },
241
+ [SpanType.Http]: {
242
+ name: 'HTTP',
243
+ description: 'An HTTP request',
244
+ isGenAI: false,
245
+ isHidden: true,
246
+ },
247
+ [SpanType.Segment]: {
248
+ name: 'Segment',
249
+ description: 'A (partial) segment of a trace',
250
+ isGenAI: false,
251
+ isHidden: true,
252
+ },
253
+ [SpanType.Unknown]: {
254
+ name: 'Unknown',
255
+ description: 'An unknown span',
256
+ isGenAI: false,
257
+ isHidden: true,
258
+ },
155
259
  };
156
- const AIPromptPostfixes = {
157
- messages: 'messages',
260
+ var SpanStatus;
261
+ (function (SpanStatus) {
262
+ SpanStatus["Unset"] = "unset";
263
+ SpanStatus["Ok"] = "ok";
264
+ SpanStatus["Error"] = "error";
265
+ })(SpanStatus || (SpanStatus = {}));
266
+
267
+ // Note: Traces are unmaterialized but this context is used to propagate the trace
268
+ // See www.w3.org/TR/trace-context and w3c.github.io/baggage
269
+ zod.z.object({
270
+ traceparent: zod.z.string(), // <version>-<trace-id>-<span-id>-<trace-flags>
271
+ tracestate: zod.z.string().optional(), // <key>=urlencoded(<value>)[,<key>=urlencoded(<value>)]*
272
+ baggage: zod.z.string().optional(), // <key>=urlencoded(<value>)[,<key>=urlencoded(<value>)]*
273
+ });
274
+
275
+ /* Note: Instrumentation scopes from all language SDKs */
276
+ const SCOPE_LATITUDE = 'so.latitude.instrumentation';
277
+ var InstrumentationScope;
278
+ (function (InstrumentationScope) {
279
+ InstrumentationScope["Manual"] = "manual";
280
+ InstrumentationScope["Latitude"] = "latitude";
281
+ InstrumentationScope["OpenAI"] = "openai";
282
+ InstrumentationScope["Anthropic"] = "anthropic";
283
+ InstrumentationScope["AzureOpenAI"] = "azure";
284
+ InstrumentationScope["VercelAI"] = "vercelai";
285
+ InstrumentationScope["VertexAI"] = "vertexai";
286
+ InstrumentationScope["AIPlatform"] = "aiplatform";
287
+ InstrumentationScope["MistralAI"] = "mistralai";
288
+ InstrumentationScope["Bedrock"] = "bedrock";
289
+ InstrumentationScope["Sagemaker"] = "sagemaker";
290
+ InstrumentationScope["TogetherAI"] = "togetherai";
291
+ InstrumentationScope["Replicate"] = "replicate";
292
+ InstrumentationScope["Groq"] = "groq";
293
+ InstrumentationScope["Cohere"] = "cohere";
294
+ InstrumentationScope["LiteLLM"] = "litellm";
295
+ InstrumentationScope["Langchain"] = "langchain";
296
+ InstrumentationScope["LlamaIndex"] = "llamaindex";
297
+ InstrumentationScope["DSPy"] = "dspy";
298
+ InstrumentationScope["Haystack"] = "haystack";
299
+ InstrumentationScope["Ollama"] = "ollama";
300
+ InstrumentationScope["Transformers"] = "transformers";
301
+ InstrumentationScope["AlephAlpha"] = "alephalpha";
302
+ })(InstrumentationScope || (InstrumentationScope = {}));
303
+ /* Note: non-standard OpenTelemetry semantic conventions used in Latitude */
304
+ const ATTR_LATITUDE = 'latitude';
305
+ const ATTR_LATITUDE_TYPE = `${ATTR_LATITUDE}.type`;
306
+ const ATTR_LATITUDE_SEGMENT_ID = `${ATTR_LATITUDE}.segment.id`;
307
+ const ATTR_LATITUDE_SEGMENT_PARENT_ID = `${ATTR_LATITUDE}.segment.parent_id`;
308
+ const ATTR_LATITUDE_SEGMENTS = `${ATTR_LATITUDE}.segments`;
309
+ const GEN_AI_TOOL_TYPE_VALUE_FUNCTION = 'function';
310
+ const ATTR_GEN_AI_TOOL_CALL_ARGUMENTS = 'gen_ai.tool.call.arguments';
311
+ const ATTR_GEN_AI_TOOL_RESULT_VALUE = 'gen_ai.tool.result.value';
312
+ const ATTR_GEN_AI_TOOL_RESULT_IS_ERROR = 'gen_ai.tool.result.is_error';
313
+ const ATTR_GEN_AI_REQUEST = 'gen_ai.request';
314
+ const ATTR_GEN_AI_REQUEST_CONFIGURATION = 'gen_ai.request.configuration';
315
+ const ATTR_GEN_AI_REQUEST_TEMPLATE = 'gen_ai.request.template';
316
+ const ATTR_GEN_AI_REQUEST_PARAMETERS = 'gen_ai.request.parameters';
317
+ const ATTR_GEN_AI_REQUEST_MESSAGES = 'gen_ai.request.messages';
318
+ const ATTR_GEN_AI_RESPONSE = 'gen_ai.response';
319
+ const ATTR_GEN_AI_RESPONSE_MESSAGES = 'gen_ai.response.messages';
320
+ const ATTR_GEN_AI_USAGE_PROMPT_TOKENS = 'gen_ai.usage.prompt_tokens';
321
+ const ATTR_GEN_AI_USAGE_CACHED_TOKENS = 'gen_ai.usage.cached_tokens';
322
+ const ATTR_GEN_AI_USAGE_REASONING_TOKENS = 'gen_ai.usage.reasoning_tokens'; // prettier-ignore
323
+ const ATTR_GEN_AI_USAGE_COMPLETION_TOKENS = 'gen_ai.usage.completion_tokens'; // prettier-ignore
324
+ const ATTR_GEN_AI_PROMPTS = 'gen_ai.prompt'; // gen_ai.prompt.{index}.{role/content/...}
325
+ const ATTR_GEN_AI_COMPLETIONS = 'gen_ai.completion'; // gen_ai.completion.{index}.{role/content/...}
326
+ const ATTR_GEN_AI_MESSAGE_ROLE = 'role';
327
+ const ATTR_GEN_AI_MESSAGE_CONTENT = 'content'; // string or object
328
+ const ATTR_GEN_AI_MESSAGE_TOOL_NAME = 'tool_name';
329
+ const ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID = 'tool_call_id';
330
+ const ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR = 'is_error';
331
+ const ATTR_GEN_AI_MESSAGE_TOOL_CALLS = 'tool_calls'; // gen_ai.completion.{index}.tool_calls.{index}.{id/name/arguments}
332
+ const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID = 'id';
333
+ const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME = 'name';
334
+ const ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS = 'arguments';
335
+ const GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP = 'stop';
336
+ const GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS = 'tool_calls';
337
+ const ATTR_HTTP_REQUEST_URL = 'http.request.url';
338
+ const ATTR_HTTP_REQUEST_BODY = 'http.request.body';
339
+ const ATTR_HTTP_REQUEST_HEADER = 'http.request.header';
340
+ const ATTR_HTTP_RESPONSE_BODY = 'http.response.body';
341
+ const ATTR_HTTP_RESPONSE_HEADER = 'http.response.header';
342
+ /* Note: Schemas for span ingestion following OpenTelemetry service request specification */
343
+ var Otlp;
344
+ (function (Otlp) {
345
+ Otlp.attributeValueSchema = zod.z.object({
346
+ stringValue: zod.z.string().optional(),
347
+ intValue: zod.z.number().optional(),
348
+ boolValue: zod.z.boolean().optional(),
349
+ arrayValue: zod.z
350
+ .object({
351
+ values: zod.z.array(zod.z.object({
352
+ stringValue: zod.z.string().optional(),
353
+ intValue: zod.z.number().optional(),
354
+ boolValue: zod.z.boolean().optional(),
355
+ })),
356
+ })
357
+ .optional(),
358
+ });
359
+ Otlp.attributeSchema = zod.z.object({
360
+ key: zod.z.string(),
361
+ value: Otlp.attributeValueSchema,
362
+ });
363
+ Otlp.eventSchema = zod.z.object({
364
+ name: zod.z.string(),
365
+ timeUnixNano: zod.z.string(),
366
+ attributes: zod.z.array(Otlp.attributeSchema).optional(),
367
+ });
368
+ Otlp.linkSchema = zod.z.object({
369
+ traceId: zod.z.string(),
370
+ spanId: zod.z.string(),
371
+ attributes: zod.z.array(Otlp.attributeSchema).optional(),
372
+ });
373
+ (function (StatusCode) {
374
+ StatusCode[StatusCode["Unset"] = 0] = "Unset";
375
+ StatusCode[StatusCode["Ok"] = 1] = "Ok";
376
+ StatusCode[StatusCode["Error"] = 2] = "Error";
377
+ })(Otlp.StatusCode || (Otlp.StatusCode = {}));
378
+ Otlp.statusSchema = zod.z.object({
379
+ code: zod.z.number(),
380
+ message: zod.z.string().optional(),
381
+ });
382
+ (function (SpanKind) {
383
+ SpanKind[SpanKind["Internal"] = 0] = "Internal";
384
+ SpanKind[SpanKind["Server"] = 1] = "Server";
385
+ SpanKind[SpanKind["Client"] = 2] = "Client";
386
+ SpanKind[SpanKind["Producer"] = 3] = "Producer";
387
+ SpanKind[SpanKind["Consumer"] = 4] = "Consumer";
388
+ })(Otlp.SpanKind || (Otlp.SpanKind = {}));
389
+ Otlp.spanSchema = zod.z.object({
390
+ traceId: zod.z.string(),
391
+ spanId: zod.z.string(),
392
+ parentSpanId: zod.z.string().optional(),
393
+ name: zod.z.string(),
394
+ kind: zod.z.number(),
395
+ startTimeUnixNano: zod.z.string(),
396
+ endTimeUnixNano: zod.z.string(),
397
+ status: Otlp.statusSchema.optional(),
398
+ events: zod.z.array(Otlp.eventSchema).optional(),
399
+ links: zod.z.array(Otlp.linkSchema).optional(),
400
+ attributes: zod.z.array(Otlp.attributeSchema).optional(),
401
+ });
402
+ Otlp.scopeSchema = zod.z.object({
403
+ name: zod.z.string(),
404
+ version: zod.z.string().optional(),
405
+ });
406
+ Otlp.scopeSpanSchema = zod.z.object({
407
+ scope: Otlp.scopeSchema,
408
+ spans: zod.z.array(Otlp.spanSchema),
409
+ });
410
+ Otlp.resourceSchema = zod.z.object({
411
+ attributes: zod.z.array(Otlp.attributeSchema),
412
+ });
413
+ Otlp.resourceSpanSchema = zod.z.object({
414
+ resource: Otlp.resourceSchema,
415
+ scopeSpans: zod.z.array(Otlp.scopeSpanSchema),
416
+ });
417
+ Otlp.serviceRequestSchema = zod.z.object({
418
+ resourceSpans: zod.z.array(Otlp.resourceSpanSchema),
419
+ });
420
+ })(Otlp || (Otlp = {}));
421
+
422
+ var StreamEventTypes;
423
+ (function (StreamEventTypes) {
424
+ StreamEventTypes["Latitude"] = "latitude-event";
425
+ StreamEventTypes["Provider"] = "provider-event";
426
+ })(StreamEventTypes || (StreamEventTypes = {}));
427
+ zod.z.object({
428
+ id: zod.z.string(),
429
+ name: zod.z.string(),
430
+ result: zod.z.unknown(),
431
+ isError: zod.z.boolean().optional(),
432
+ text: zod.z.string().optional(),
433
+ });
434
+ var FinishReason;
435
+ (function (FinishReason) {
436
+ FinishReason["Stop"] = "stop";
437
+ FinishReason["Length"] = "length";
438
+ FinishReason["ContentFilter"] = "content-filter";
439
+ FinishReason["ToolCalls"] = "tool-calls";
440
+ FinishReason["Error"] = "error";
441
+ FinishReason["Other"] = "other";
442
+ FinishReason["Unknown"] = "unknown";
443
+ })(FinishReason || (FinishReason = {}));
444
+
445
+ var ParameterType;
446
+ (function (ParameterType) {
447
+ ParameterType["Text"] = "text";
448
+ ParameterType["Image"] = "image";
449
+ ParameterType["File"] = "file";
450
+ })(ParameterType || (ParameterType = {}));
451
+ var LatitudeTool;
452
+ (function (LatitudeTool) {
453
+ LatitudeTool["RunCode"] = "code";
454
+ LatitudeTool["WebSearch"] = "search";
455
+ LatitudeTool["WebExtract"] = "extract";
456
+ })(LatitudeTool || (LatitudeTool = {}));
457
+ var LatitudeToolInternalName;
458
+ (function (LatitudeToolInternalName) {
459
+ LatitudeToolInternalName["RunCode"] = "lat_tool_run_code";
460
+ LatitudeToolInternalName["WebSearch"] = "lat_tool_web_search";
461
+ LatitudeToolInternalName["WebExtract"] = "lat_tool_web_extract";
462
+ })(LatitudeToolInternalName || (LatitudeToolInternalName = {}));
463
+
464
+ const actualOutputConfiguration = zod.z.object({
465
+ messageSelection: zod.z.enum(['last', 'all']), // Which assistant messages to select
466
+ contentFilter: zod.z.enum(['text', 'image', 'file', 'tool_call']).optional(),
467
+ parsingFormat: zod.z.enum(['string', 'json']),
468
+ fieldAccessor: zod.z.string().optional(), // Field accessor to get the output from if it's a key-value format
469
+ });
470
+ const expectedOutputConfiguration = zod.z.object({
471
+ parsingFormat: zod.z.enum(['string', 'json']),
472
+ fieldAccessor: zod.z.string().optional(), // Field accessor to get the output from if it's a key-value format
473
+ });
474
+ const baseEvaluationConfiguration = zod.z.object({
475
+ reverseScale: zod.z.boolean(), // If true, lower is better, otherwise, higher is better
476
+ actualOutput: actualOutputConfiguration.optional(), // Optional for backwards compatibility
477
+ expectedOutput: expectedOutputConfiguration.optional(), // Optional for backwards compatibility
478
+ });
479
+ const baseEvaluationResultMetadata = zod.z.object({
480
+ // Configuration snapshot is defined in every metric specification
481
+ actualOutput: zod.z.string(),
482
+ expectedOutput: zod.z.string().optional(),
483
+ datasetLabel: zod.z.string().optional(),
484
+ });
485
+ const baseEvaluationResultError = zod.z.object({
486
+ message: zod.z.string(),
487
+ });
488
+
489
+ const humanEvaluationConfiguration = baseEvaluationConfiguration.extend({
490
+ criteria: zod.z.string().optional(),
491
+ });
492
+ const humanEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
493
+ reason: zod.z.string().optional(),
494
+ });
495
+ const humanEvaluationResultError = baseEvaluationResultError.extend({});
496
+ // BINARY
497
+ const humanEvaluationBinaryConfiguration = humanEvaluationConfiguration.extend({
498
+ passDescription: zod.z.string().optional(),
499
+ failDescription: zod.z.string().optional(),
500
+ });
501
+ humanEvaluationResultMetadata.extend({
502
+ configuration: humanEvaluationBinaryConfiguration,
503
+ });
504
+ humanEvaluationResultError.extend({});
505
+ const HumanEvaluationBinarySpecification = {
506
+ };
507
+ // RATING
508
+ const humanEvaluationRatingConfiguration = humanEvaluationConfiguration.extend({
509
+ minRating: zod.z.number(),
510
+ minRatingDescription: zod.z.string().optional(),
511
+ maxRating: zod.z.number(),
512
+ maxRatingDescription: zod.z.string().optional(),
513
+ minThreshold: zod.z.number().optional(), // Threshold in rating range
514
+ maxThreshold: zod.z.number().optional(), // Threshold in rating range
515
+ });
516
+ humanEvaluationResultMetadata.extend({
517
+ configuration: humanEvaluationRatingConfiguration,
518
+ });
519
+ humanEvaluationResultError.extend({});
520
+ const HumanEvaluationRatingSpecification = {
521
+ };
522
+ /* ------------------------------------------------------------------------- */
523
+ var HumanEvaluationMetric;
524
+ (function (HumanEvaluationMetric) {
525
+ HumanEvaluationMetric["Binary"] = "binary";
526
+ HumanEvaluationMetric["Rating"] = "rating";
527
+ })(HumanEvaluationMetric || (HumanEvaluationMetric = {}));
528
+ const HumanEvaluationSpecification = {
529
+ // prettier-ignore
530
+ metrics: {
531
+ [HumanEvaluationMetric.Binary]: HumanEvaluationBinarySpecification,
532
+ [HumanEvaluationMetric.Rating]: HumanEvaluationRatingSpecification,
533
+ },
158
534
  };
159
- const AIToolCallPostfixes = {
160
- id: 'id',
161
- name: 'name',
162
- args: 'args',
163
- result: 'result',
535
+
536
+ const llmEvaluationConfiguration = baseEvaluationConfiguration.extend({
537
+ provider: zod.z.string(),
538
+ model: zod.z.string(),
539
+ });
540
+ const llmEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
541
+ evaluationLogId: zod.z.number(),
542
+ reason: zod.z.string(),
543
+ tokens: zod.z.number(),
544
+ cost: zod.z.number(),
545
+ duration: zod.z.number(),
546
+ });
547
+ const llmEvaluationResultError = baseEvaluationResultError.extend({
548
+ runErrorId: zod.z.number().optional(),
549
+ });
550
+ // BINARY
551
+ const llmEvaluationBinaryConfiguration = llmEvaluationConfiguration.extend({
552
+ criteria: zod.z.string(),
553
+ passDescription: zod.z.string(),
554
+ failDescription: zod.z.string(),
555
+ });
556
+ llmEvaluationResultMetadata.extend({
557
+ configuration: llmEvaluationBinaryConfiguration,
558
+ });
559
+ llmEvaluationResultError.extend({});
560
+ const LlmEvaluationBinarySpecification = {
561
+ };
562
+ // RATING
563
+ const llmEvaluationRatingConfiguration = llmEvaluationConfiguration.extend({
564
+ criteria: zod.z.string(),
565
+ minRating: zod.z.number(),
566
+ minRatingDescription: zod.z.string(),
567
+ maxRating: zod.z.number(),
568
+ maxRatingDescription: zod.z.string(),
569
+ minThreshold: zod.z.number().optional(), // Threshold in rating range
570
+ maxThreshold: zod.z.number().optional(), // Threshold in rating range
571
+ });
572
+ llmEvaluationResultMetadata.extend({
573
+ configuration: llmEvaluationRatingConfiguration,
574
+ });
575
+ llmEvaluationResultError.extend({});
576
+ const LlmEvaluationRatingSpecification = {
577
+ };
578
+ // COMPARISON
579
+ const llmEvaluationComparisonConfiguration = llmEvaluationConfiguration.extend({
580
+ criteria: zod.z.string(),
581
+ passDescription: zod.z.string(),
582
+ failDescription: zod.z.string(),
583
+ minThreshold: zod.z.number().optional(), // Threshold percentage
584
+ maxThreshold: zod.z.number().optional(), // Threshold percentage
585
+ });
586
+ llmEvaluationResultMetadata.extend({
587
+ configuration: llmEvaluationComparisonConfiguration,
588
+ });
589
+ llmEvaluationResultError.extend({});
590
+ const LlmEvaluationComparisonSpecification = {
591
+ };
592
+ // CUSTOM
593
+ const llmEvaluationCustomConfiguration = llmEvaluationConfiguration.extend({
594
+ prompt: zod.z.string(),
595
+ minScore: zod.z.number(),
596
+ maxScore: zod.z.number(),
597
+ minThreshold: zod.z.number().optional(), // Threshold percentage
598
+ maxThreshold: zod.z.number().optional(), // Threshold percentage
599
+ });
600
+ llmEvaluationResultMetadata.extend({
601
+ configuration: llmEvaluationCustomConfiguration,
602
+ });
603
+ llmEvaluationResultError.extend({});
604
+ const LlmEvaluationCustomSpecification = {
605
+ };
606
+ // CUSTOM LABELED
607
+ const LlmEvaluationCustomLabeledSpecification = {
608
+ };
609
+ /* ------------------------------------------------------------------------- */
610
+ var LlmEvaluationMetric;
611
+ (function (LlmEvaluationMetric) {
612
+ LlmEvaluationMetric["Binary"] = "binary";
613
+ LlmEvaluationMetric["Rating"] = "rating";
614
+ LlmEvaluationMetric["Comparison"] = "comparison";
615
+ LlmEvaluationMetric["Custom"] = "custom";
616
+ LlmEvaluationMetric["CustomLabeled"] = "custom_labeled";
617
+ })(LlmEvaluationMetric || (LlmEvaluationMetric = {}));
618
+ const LlmEvaluationSpecification = {
619
+ // prettier-ignore
620
+ metrics: {
621
+ [LlmEvaluationMetric.Binary]: LlmEvaluationBinarySpecification,
622
+ [LlmEvaluationMetric.Rating]: LlmEvaluationRatingSpecification,
623
+ [LlmEvaluationMetric.Comparison]: LlmEvaluationComparisonSpecification,
624
+ [LlmEvaluationMetric.Custom]: LlmEvaluationCustomSpecification,
625
+ [LlmEvaluationMetric.CustomLabeled]: LlmEvaluationCustomLabeledSpecification,
626
+ },
164
627
  };
165
- const SETTINGS = `${AI_PREFIX}.${AIPrefixes.settings}`;
166
- const MODEL_ID = `${AI_PREFIX}.${AIPrefixes.model}.id`;
167
- const METADATA = `${AI_PREFIX}.${AIPrefixes.telemetry}.metadata`;
168
- const TOKEN_COUNT_COMPLETION = `${AI_PREFIX}.${AIPrefixes.usage}.${AIUsagePostfixes.completionTokens}`;
169
- const TOKEN_COUNT_PROMPT = `${AI_PREFIX}.${AIPrefixes.usage}.${AIUsagePostfixes.promptTokens}`;
170
- const RESPONSE_TEXT = `${AI_PREFIX}.${AIPrefixes.response}.${AIResultPostfixes.text}`;
171
- const RESPONSE_TOOL_CALLS = `${AI_PREFIX}.${AIPrefixes.response}.${AIResultPostfixes.toolCalls}`;
172
- const RESULT_OBJECT = `${AI_PREFIX}.${AIPrefixes.response}.${AIResultPostfixes.object}`;
173
- const PROMPT = `${AI_PREFIX}.${AIPrefixes.prompt}`;
174
- const PROMPT_MESSAGES = `${PROMPT}.${AIPromptPostfixes.messages}`;
175
- const EMBEDDING_TEXT = `${AI_PREFIX}.value`;
176
- const EMBEDDING_VECTOR = `${AI_PREFIX}.embedding`;
177
- const EMBEDDING_TEXTS = `${AI_PREFIX}.values`;
178
- const EMBEDDING_VECTORS = `${AI_PREFIX}.embeddings`;
179
- const TOOL_CALL_ID = `${AI_PREFIX}.${AIPrefixes.toolCall}.${AIToolCallPostfixes.id}`;
180
- const TOOL_CALL_NAME = `${AI_PREFIX}.${AIPrefixes.toolCall}.${AIToolCallPostfixes.name}`;
181
- const TOOL_CALL_ARGS = `${AI_PREFIX}.${AIPrefixes.toolCall}.${AIToolCallPostfixes.args}`;
182
- const TOOL_CALL_RESULT = `${AI_PREFIX}.${AIPrefixes.toolCall}.${AIToolCallPostfixes.result}`;
183
- /**
184
- * The semantic conventions used by the Vercel AI SDK.
185
- * @see https://sdk.vercel.ai/docs/ai-sdk-core/telemetry#collected-data
186
- */
187
- const AISemanticConventions = {
188
- MODEL_ID,
189
- METADATA,
190
- SETTINGS,
191
- TOKEN_COUNT_COMPLETION,
192
- TOKEN_COUNT_PROMPT,
193
- RESPONSE_TEXT,
194
- RESPONSE_TOOL_CALLS,
195
- RESULT_OBJECT,
196
- PROMPT,
197
- PROMPT_MESSAGES,
198
- EMBEDDING_TEXT,
199
- EMBEDDING_VECTOR,
200
- EMBEDDING_TEXTS,
201
- EMBEDDING_VECTORS,
202
- TOOL_CALL_ID,
203
- TOOL_CALL_NAME,
204
- TOOL_CALL_ARGS,
205
- TOOL_CALL_RESULT,
628
+
629
+ const ruleEvaluationConfiguration = baseEvaluationConfiguration.extend({});
630
+ const ruleEvaluationResultMetadata = baseEvaluationResultMetadata.extend({});
631
+ const ruleEvaluationResultError = baseEvaluationResultError.extend({});
632
+ // EXACT MATCH
633
+ const ruleEvaluationExactMatchConfiguration = ruleEvaluationConfiguration.extend({
634
+ caseInsensitive: zod.z.boolean(),
635
+ });
636
+ ruleEvaluationResultMetadata.extend({
637
+ configuration: ruleEvaluationExactMatchConfiguration,
638
+ });
639
+ ruleEvaluationResultError.extend({});
640
+ const RuleEvaluationExactMatchSpecification = {
641
+ };
642
+ // REGULAR EXPRESSION
643
+ const ruleEvaluationRegularExpressionConfiguration = ruleEvaluationConfiguration.extend({
644
+ pattern: zod.z.string(),
645
+ });
646
+ ruleEvaluationResultMetadata.extend({
647
+ configuration: ruleEvaluationRegularExpressionConfiguration,
648
+ });
649
+ ruleEvaluationResultError.extend({});
650
+ const RuleEvaluationRegularExpressionSpecification = {
651
+ };
652
+ // SCHEMA VALIDATION
653
+ const ruleEvaluationSchemaValidationConfiguration = ruleEvaluationConfiguration.extend({
654
+ format: zod.z.enum(['json']),
655
+ schema: zod.z.string(),
656
+ });
657
+ ruleEvaluationResultMetadata.extend({
658
+ configuration: ruleEvaluationSchemaValidationConfiguration,
659
+ });
660
+ ruleEvaluationResultError.extend({});
661
+ const RuleEvaluationSchemaValidationSpecification = {
662
+ };
663
+ // LENGTH COUNT
664
+ const ruleEvaluationLengthCountConfiguration = ruleEvaluationConfiguration.extend({
665
+ algorithm: zod.z.enum(['character', 'word', 'sentence']),
666
+ minLength: zod.z.number().optional(),
667
+ maxLength: zod.z.number().optional(),
668
+ });
669
+ ruleEvaluationResultMetadata.extend({
670
+ configuration: ruleEvaluationLengthCountConfiguration,
671
+ });
672
+ ruleEvaluationResultError.extend({});
673
+ const RuleEvaluationLengthCountSpecification = {
674
+ };
675
+ // LEXICAL OVERLAP
676
+ const ruleEvaluationLexicalOverlapConfiguration = ruleEvaluationConfiguration.extend({
677
+ algorithm: zod.z.enum(['substring', 'levenshtein_distance', 'rouge']),
678
+ minOverlap: zod.z.number().optional(), // Percentage of overlap
679
+ maxOverlap: zod.z.number().optional(), // Percentage of overlap
680
+ });
681
+ ruleEvaluationResultMetadata.extend({
682
+ configuration: ruleEvaluationLexicalOverlapConfiguration,
683
+ });
684
+ ruleEvaluationResultError.extend({});
685
+ const RuleEvaluationLexicalOverlapSpecification = {
686
+ };
687
+ // SEMANTIC SIMILARITY
688
+ const ruleEvaluationSemanticSimilarityConfiguration = ruleEvaluationConfiguration.extend({
689
+ algorithm: zod.z.enum(['cosine_distance']),
690
+ minSimilarity: zod.z.number().optional(), // Percentage of similarity
691
+ maxSimilarity: zod.z.number().optional(), // Percentage of similarity
692
+ });
693
+ ruleEvaluationResultMetadata.extend({
694
+ configuration: ruleEvaluationSemanticSimilarityConfiguration,
695
+ });
696
+ ruleEvaluationResultError.extend({});
697
+ const RuleEvaluationSemanticSimilaritySpecification = {
698
+ };
699
+ // NUMERIC SIMILARITY
700
+ const ruleEvaluationNumericSimilarityConfiguration = ruleEvaluationConfiguration.extend({
701
+ algorithm: zod.z.enum(['relative_difference']),
702
+ minSimilarity: zod.z.number().optional(), // Percentage of similarity
703
+ maxSimilarity: zod.z.number().optional(), // Percentage of similarity
704
+ });
705
+ ruleEvaluationResultMetadata.extend({
706
+ configuration: ruleEvaluationNumericSimilarityConfiguration,
707
+ });
708
+ ruleEvaluationResultError.extend({});
709
+ const RuleEvaluationNumericSimilaritySpecification = {
710
+ };
711
+ /* ------------------------------------------------------------------------- */
712
+ var RuleEvaluationMetric;
713
+ (function (RuleEvaluationMetric) {
714
+ RuleEvaluationMetric["ExactMatch"] = "exact_match";
715
+ RuleEvaluationMetric["RegularExpression"] = "regular_expression";
716
+ RuleEvaluationMetric["SchemaValidation"] = "schema_validation";
717
+ RuleEvaluationMetric["LengthCount"] = "length_count";
718
+ RuleEvaluationMetric["LexicalOverlap"] = "lexical_overlap";
719
+ RuleEvaluationMetric["SemanticSimilarity"] = "semantic_similarity";
720
+ RuleEvaluationMetric["NumericSimilarity"] = "numeric_similarity";
721
+ })(RuleEvaluationMetric || (RuleEvaluationMetric = {}));
722
+ const RuleEvaluationSpecification = {
723
+ // prettier-ignore
724
+ metrics: {
725
+ [RuleEvaluationMetric.ExactMatch]: RuleEvaluationExactMatchSpecification,
726
+ [RuleEvaluationMetric.RegularExpression]: RuleEvaluationRegularExpressionSpecification,
727
+ [RuleEvaluationMetric.SchemaValidation]: RuleEvaluationSchemaValidationSpecification,
728
+ [RuleEvaluationMetric.LengthCount]: RuleEvaluationLengthCountSpecification,
729
+ [RuleEvaluationMetric.LexicalOverlap]: RuleEvaluationLexicalOverlapSpecification,
730
+ [RuleEvaluationMetric.SemanticSimilarity]: RuleEvaluationSemanticSimilaritySpecification,
731
+ [RuleEvaluationMetric.NumericSimilarity]: RuleEvaluationNumericSimilaritySpecification,
732
+ },
206
733
  };
207
- Object.freeze(Object.values(AISemanticConventions));
208
734
 
209
- class VercelSpanProcessor extends sdkTraceBase.SimpleSpanProcessor {
210
- onEnd(span) {
211
- if (!shouldProcess(span))
212
- return;
213
- if (shouldConvertToLatitudeFormat(span))
214
- convertToLatitudeFormat(span);
215
- super.onEnd(span);
735
+ var EvaluationType;
736
+ (function (EvaluationType) {
737
+ EvaluationType["Rule"] = "rule";
738
+ EvaluationType["Llm"] = "llm";
739
+ EvaluationType["Human"] = "human";
740
+ })(EvaluationType || (EvaluationType = {}));
741
+ const EvaluationTypeSchema = zod.z.nativeEnum(EvaluationType);
742
+ const EvaluationMetricSchema = zod.z.union([
743
+ zod.z.nativeEnum(RuleEvaluationMetric),
744
+ zod.z.nativeEnum(LlmEvaluationMetric),
745
+ zod.z.nativeEnum(HumanEvaluationMetric),
746
+ ]);
747
+ const EvaluationConfigurationSchema = zod.z.custom();
748
+ // prettier-ignore
749
+ zod.z.custom();
750
+ // prettier-ignore
751
+ zod.z.custom();
752
+ ({
753
+ [EvaluationType.Rule]: RuleEvaluationSpecification,
754
+ [EvaluationType.Llm]: LlmEvaluationSpecification,
755
+ [EvaluationType.Human]: HumanEvaluationSpecification,
756
+ });
757
+ zod.z.object({
758
+ name: zod.z.string(),
759
+ description: zod.z.string(),
760
+ type: EvaluationTypeSchema,
761
+ metric: EvaluationMetricSchema,
762
+ configuration: EvaluationConfigurationSchema,
763
+ });
764
+ zod.z.object({
765
+ evaluateLiveLogs: zod.z.boolean().nullable().optional(),
766
+ enableSuggestions: zod.z.boolean().nullable().optional(),
767
+ autoApplySuggestions: zod.z.boolean().nullable().optional(),
768
+ });
769
+ Object.values(SegmentSource).filter((source) => source !== SegmentSource.Evaluation && source !== SegmentSource.Experiment);
770
+
771
+ var LegacyChainEventTypes;
772
+ (function (LegacyChainEventTypes) {
773
+ LegacyChainEventTypes["Error"] = "chain-error";
774
+ LegacyChainEventTypes["Step"] = "chain-step";
775
+ LegacyChainEventTypes["Complete"] = "chain-complete";
776
+ LegacyChainEventTypes["StepComplete"] = "chain-step-complete";
777
+ })(LegacyChainEventTypes || (LegacyChainEventTypes = {}));
778
+
779
+ var ChainEventTypes;
780
+ (function (ChainEventTypes) {
781
+ ChainEventTypes["ChainStarted"] = "chain-started";
782
+ ChainEventTypes["StepStarted"] = "step-started";
783
+ ChainEventTypes["ProviderStarted"] = "provider-started";
784
+ ChainEventTypes["ProviderCompleted"] = "provider-completed";
785
+ ChainEventTypes["ToolsStarted"] = "tools-started";
786
+ ChainEventTypes["ToolCompleted"] = "tool-completed";
787
+ ChainEventTypes["StepCompleted"] = "step-completed";
788
+ ChainEventTypes["ChainCompleted"] = "chain-completed";
789
+ ChainEventTypes["ChainError"] = "chain-error";
790
+ ChainEventTypes["ToolsRequested"] = "tools-requested";
791
+ ChainEventTypes["IntegrationWakingUp"] = "integration-waking-up";
792
+ })(ChainEventTypes || (ChainEventTypes = {}));
793
+
794
+ var IntegrationType;
795
+ (function (IntegrationType) {
796
+ IntegrationType["Latitude"] = "latitude";
797
+ IntegrationType["ExternalMCP"] = "custom_mcp";
798
+ IntegrationType["HostedMCP"] = "mcp_server";
799
+ IntegrationType["Pipedream"] = "pipedream";
800
+ })(IntegrationType || (IntegrationType = {}));
801
+ var HostedIntegrationType;
802
+ (function (HostedIntegrationType) {
803
+ HostedIntegrationType["Stripe"] = "stripe";
804
+ HostedIntegrationType["Slack"] = "slack";
805
+ HostedIntegrationType["Github"] = "github";
806
+ HostedIntegrationType["Notion"] = "notion";
807
+ HostedIntegrationType["Twitter"] = "twitter";
808
+ HostedIntegrationType["Airtable"] = "airtable";
809
+ HostedIntegrationType["Linear"] = "linear";
810
+ HostedIntegrationType["YoutubeCaptions"] = "youtube_captions";
811
+ HostedIntegrationType["Reddit"] = "reddit";
812
+ HostedIntegrationType["Telegram"] = "telegram";
813
+ HostedIntegrationType["Tinybird"] = "tinybird";
814
+ HostedIntegrationType["Perplexity"] = "perplexity";
815
+ HostedIntegrationType["AwsKbRetrieval"] = "aws_kb_retrieval";
816
+ HostedIntegrationType["BraveSearch"] = "brave_search";
817
+ HostedIntegrationType["EverArt"] = "ever_art";
818
+ HostedIntegrationType["Fetch"] = "fetch";
819
+ HostedIntegrationType["GitLab"] = "gitlab";
820
+ HostedIntegrationType["GoogleMaps"] = "google_maps";
821
+ HostedIntegrationType["Sentry"] = "sentry";
822
+ HostedIntegrationType["Puppeteer"] = "puppeteer";
823
+ HostedIntegrationType["Time"] = "time";
824
+ HostedIntegrationType["browserbase"] = "browserbase";
825
+ HostedIntegrationType["Neon"] = "neon";
826
+ HostedIntegrationType["Postgres"] = "postgres";
827
+ HostedIntegrationType["Supabase"] = "supabase";
828
+ HostedIntegrationType["Redis"] = "redis";
829
+ HostedIntegrationType["Jira"] = "jira";
830
+ HostedIntegrationType["Attio"] = "attio";
831
+ HostedIntegrationType["Ghost"] = "ghost";
832
+ HostedIntegrationType["Figma"] = "figma";
833
+ HostedIntegrationType["Hyperbrowser"] = "hyperbrowser";
834
+ HostedIntegrationType["Audiense"] = "audiense";
835
+ HostedIntegrationType["Apify"] = "apify";
836
+ HostedIntegrationType["Exa"] = "exa";
837
+ HostedIntegrationType["YepCode"] = "yepcode";
838
+ HostedIntegrationType["Monday"] = "monday";
839
+ HostedIntegrationType["AgentQL"] = "agentql";
840
+ HostedIntegrationType["AgentRPC"] = "agentrpc";
841
+ HostedIntegrationType["AstraDB"] = "astra_db";
842
+ HostedIntegrationType["Bankless"] = "bankless";
843
+ HostedIntegrationType["Bicscan"] = "bicscan";
844
+ HostedIntegrationType["Chargebee"] = "chargebee";
845
+ HostedIntegrationType["Chronulus"] = "chronulus";
846
+ HostedIntegrationType["CircleCI"] = "circleci";
847
+ HostedIntegrationType["Codacy"] = "codacy";
848
+ HostedIntegrationType["CodeLogic"] = "codelogic";
849
+ HostedIntegrationType["Convex"] = "convex";
850
+ HostedIntegrationType["Dart"] = "dart";
851
+ HostedIntegrationType["DevHubCMS"] = "devhub_cms";
852
+ HostedIntegrationType["Elasticsearch"] = "elasticsearch";
853
+ HostedIntegrationType["ESignatures"] = "esignatures";
854
+ HostedIntegrationType["Fewsats"] = "fewsats";
855
+ HostedIntegrationType["Firecrawl"] = "firecrawl";
856
+ HostedIntegrationType["Graphlit"] = "graphlit";
857
+ HostedIntegrationType["Heroku"] = "heroku";
858
+ HostedIntegrationType["IntegrationAppHubspot"] = "integration_app_hubspot";
859
+ HostedIntegrationType["LaraTranslate"] = "lara_translate";
860
+ HostedIntegrationType["Logfire"] = "logfire";
861
+ HostedIntegrationType["Langfuse"] = "langfuse";
862
+ HostedIntegrationType["LingoSupabase"] = "lingo_supabase";
863
+ HostedIntegrationType["Make"] = "make";
864
+ HostedIntegrationType["Meilisearch"] = "meilisearch";
865
+ HostedIntegrationType["Momento"] = "momento";
866
+ HostedIntegrationType["Neo4jAura"] = "neo4j_aura";
867
+ HostedIntegrationType["Octagon"] = "octagon";
868
+ HostedIntegrationType["Paddle"] = "paddle";
869
+ HostedIntegrationType["PayPal"] = "paypal";
870
+ HostedIntegrationType["Qdrant"] = "qdrant";
871
+ HostedIntegrationType["Raygun"] = "raygun";
872
+ HostedIntegrationType["Rember"] = "rember";
873
+ HostedIntegrationType["Riza"] = "riza";
874
+ HostedIntegrationType["Search1API"] = "search1api";
875
+ HostedIntegrationType["Semgrep"] = "semgrep";
876
+ HostedIntegrationType["Tavily"] = "tavily";
877
+ HostedIntegrationType["Unstructured"] = "unstructured";
878
+ HostedIntegrationType["Vectorize"] = "vectorize";
879
+ HostedIntegrationType["Xero"] = "xero";
880
+ HostedIntegrationType["Readwise"] = "readwise";
881
+ HostedIntegrationType["Airbnb"] = "airbnb";
882
+ HostedIntegrationType["Mintlify"] = "mintlify";
883
+ // Require all auth file :point_down:
884
+ // Gmail = 'google_drive',
885
+ // GoogleCalendar = 'google_drive',
886
+ // GoogleDrive = 'google_drive',
887
+ // GoogleWorkspace = 'google_workspace', // env vars not supported (?)
888
+ // TODO: implement these
889
+ // Wordpress = 'wordpress', // Not on OpenTools
890
+ // Discord = 'discord', // Not on OpenTools
891
+ // Intercom = 'intercom', // Not on OpenTools
892
+ // Hubspot = 'hubspot', // Docker based
893
+ // Loops = 'loops', // Does not exist
894
+ })(HostedIntegrationType || (HostedIntegrationType = {}));
895
+
896
+ // TODO(evalsv2): Remove
897
+ var EvaluationResultableType;
898
+ (function (EvaluationResultableType) {
899
+ EvaluationResultableType["Boolean"] = "evaluation_resultable_booleans";
900
+ EvaluationResultableType["Text"] = "evaluation_resultable_texts";
901
+ EvaluationResultableType["Number"] = "evaluation_resultable_numbers";
902
+ })(EvaluationResultableType || (EvaluationResultableType = {}));
903
+
904
+ var ModifiedDocumentType;
905
+ (function (ModifiedDocumentType) {
906
+ ModifiedDocumentType["Created"] = "created";
907
+ ModifiedDocumentType["Updated"] = "updated";
908
+ ModifiedDocumentType["UpdatedPath"] = "updated_path";
909
+ ModifiedDocumentType["Deleted"] = "deleted";
910
+ })(ModifiedDocumentType || (ModifiedDocumentType = {}));
911
+
912
+ // TODO(tracing): deprecated
913
+ const HEAD_COMMIT = 'live';
914
+ var Providers;
915
+ (function (Providers) {
916
+ Providers["OpenAI"] = "openai";
917
+ Providers["Anthropic"] = "anthropic";
918
+ Providers["Groq"] = "groq";
919
+ Providers["Mistral"] = "mistral";
920
+ Providers["Azure"] = "azure";
921
+ Providers["Google"] = "google";
922
+ Providers["GoogleVertex"] = "google_vertex";
923
+ Providers["AnthropicVertex"] = "anthropic_vertex";
924
+ Providers["Custom"] = "custom";
925
+ Providers["XAI"] = "xai";
926
+ Providers["AmazonBedrock"] = "amazon_bedrock";
927
+ Providers["DeepSeek"] = "deepseek";
928
+ Providers["Perplexity"] = "perplexity";
929
+ })(Providers || (Providers = {}));
930
+ var DocumentType;
931
+ (function (DocumentType) {
932
+ DocumentType["Prompt"] = "prompt";
933
+ DocumentType["Agent"] = "agent";
934
+ })(DocumentType || (DocumentType = {}));
935
+ var DocumentTriggerType;
936
+ (function (DocumentTriggerType) {
937
+ DocumentTriggerType["Email"] = "email";
938
+ DocumentTriggerType["Scheduled"] = "scheduled";
939
+ })(DocumentTriggerType || (DocumentTriggerType = {}));
940
+ var DocumentTriggerParameters;
941
+ (function (DocumentTriggerParameters) {
942
+ DocumentTriggerParameters["SenderEmail"] = "senderEmail";
943
+ DocumentTriggerParameters["SenderName"] = "senderName";
944
+ DocumentTriggerParameters["Subject"] = "subject";
945
+ DocumentTriggerParameters["Body"] = "body";
946
+ DocumentTriggerParameters["Attachments"] = "attachments";
947
+ })(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
948
+
949
+ class ManualInstrumentation {
950
+ enabled;
951
+ source;
952
+ tracer;
953
+ constructor(source, tracer) {
954
+ this.enabled = false;
955
+ this.source = source;
956
+ this.tracer = tracer;
216
957
  }
217
- }
218
- class VercelBatchSpanProcessor extends sdkTraceBase.BatchSpanProcessor {
219
- onEnd(span) {
220
- if (!shouldProcess(span))
221
- return;
222
- if (shouldConvertToLatitudeFormat(span))
223
- convertToLatitudeFormat(span);
224
- super.onEnd(span);
958
+ isEnabled() {
959
+ return this.enabled;
225
960
  }
226
- }
227
- function shouldProcess(span) {
228
- return (Object.keys(span.attributes).some((k) => k.startsWith('latitude.')) ||
229
- Object.keys(span.attributes).some((k) => k.startsWith('ai.')));
230
- }
231
- function shouldConvertToLatitudeFormat(span) {
232
- return Object.keys(span.attributes).some((k) => k.startsWith('ai.'));
233
- }
234
- function convertToLatitudeFormat(span) {
235
- try {
236
- const computedAttrs = computeOpenLLMAttributes(span);
237
- if (computedAttrs) {
238
- ;
239
- span.attributes = {
240
- ...span.attributes,
241
- ...computedAttrs,
242
- };
243
- }
961
+ enable() {
962
+ this.enabled = true;
244
963
  }
245
- catch (e) {
246
- console.log('Latitude telemetry Error: ', e);
247
- // do nothing
964
+ disable() {
965
+ this.enabled = false;
248
966
  }
249
- }
250
- function computeOpenLLMAttributes(span) {
251
- const attrs = span.attributes || {};
252
- const result = {};
253
- // Extract model information
254
- if (attrs[AISemanticConventions.MODEL_ID]) {
255
- result['gen_ai.request.model'] = String(attrs[AISemanticConventions.MODEL_ID]);
256
- result['gen_ai.response.model'] = String(attrs[AISemanticConventions.MODEL_ID]);
257
- }
258
- // Extract settings
259
- try {
260
- const settings = attrs[AISemanticConventions.SETTINGS]
261
- ? JSON.parse(String(attrs[AISemanticConventions.SETTINGS]))
262
- : {};
263
- if (settings) {
264
- // Add max tokens if present
265
- if (settings.maxTokens) {
266
- result['gen_ai.request.max_tokens'] = settings.maxTokens;
267
- }
268
- if (!attrs['gen_ai.system'] && settings.provider) {
269
- result['gen_ai.system'] = String(settings.provider);
270
- }
967
+ baggage(ctx) {
968
+ if ('traceparent' in ctx) {
969
+ ctx = otel.propagation.extract(otel__namespace.ROOT_CONTEXT, ctx);
970
+ }
971
+ const baggage = Object.fromEntries(otel.propagation.getBaggage(ctx)?.getAllEntries() || []);
972
+ if (!(ATTR_LATITUDE_SEGMENT_ID in baggage) ||
973
+ !(ATTR_LATITUDE_SEGMENTS in baggage)) {
974
+ return undefined;
271
975
  }
976
+ const segment = {
977
+ id: baggage[ATTR_LATITUDE_SEGMENT_ID].value,
978
+ parentId: baggage[ATTR_LATITUDE_SEGMENT_PARENT_ID]?.value,
979
+ };
980
+ let segments = [];
981
+ try {
982
+ segments = JSON.parse(baggage[ATTR_LATITUDE_SEGMENTS].value);
983
+ }
984
+ catch (error) {
985
+ return undefined;
986
+ }
987
+ if (segments.length < 1) {
988
+ return undefined;
989
+ }
990
+ return { segment, segments };
272
991
  }
273
- catch (e) {
274
- console.error('Error parsing settings', e);
275
- }
276
- // Set request type to chat as that's what Vercel AI SDK uses
277
- result['llm.request.type'] = 'chat';
278
- // Extract messages
279
- try {
280
- const messages = attrs['ai.prompt.messages']
281
- ? JSON.parse(String(attrs['ai.prompt.messages']))
282
- : [];
283
- // Process prompt messages
284
- messages.forEach((msg, index) => {
285
- result[`gen_ai.prompt.${index}.role`] = msg.role;
286
- result[`gen_ai.prompt.${index}.content`] =
287
- typeof msg.content === 'string'
288
- ? msg.content
289
- : JSON.stringify(msg.content);
992
+ setBaggage(ctx, baggage, extra) {
993
+ let parent = Object.fromEntries(otel.propagation.getBaggage(ctx)?.getAllEntries() || []);
994
+ parent = Object.fromEntries(Object.entries(parent).filter(([attribute]) => attribute !== ATTR_LATITUDE_SEGMENT_ID &&
995
+ attribute !== ATTR_LATITUDE_SEGMENT_PARENT_ID &&
996
+ attribute !== ATTR_LATITUDE_SEGMENTS));
997
+ if (!baggage) {
998
+ const payload = otel.propagation.createBaggage({ ...parent, ...(extra || {}) });
999
+ return otel.propagation.setBaggage(ctx, payload);
1000
+ }
1001
+ let jsonSegments = '';
1002
+ try {
1003
+ jsonSegments = JSON.stringify(baggage.segments);
1004
+ }
1005
+ catch (error) {
1006
+ jsonSegments = '[]';
1007
+ }
1008
+ const payload = otel.propagation.createBaggage({
1009
+ ...parent,
1010
+ [ATTR_LATITUDE_SEGMENT_ID]: { value: baggage.segment.id },
1011
+ ...(baggage.segment.parentId && {
1012
+ [ATTR_LATITUDE_SEGMENT_PARENT_ID]: { value: baggage.segment.parentId },
1013
+ }),
1014
+ [ATTR_LATITUDE_SEGMENTS]: { value: jsonSegments },
1015
+ ...(extra || {}),
290
1016
  });
1017
+ return otel.propagation.setBaggage(ctx, payload);
291
1018
  }
292
- catch (e) {
293
- console.error('Error parsing messages', e);
294
- return undefined;
1019
+ pause(ctx) {
1020
+ const baggage = this.baggage(ctx);
1021
+ if (baggage) {
1022
+ baggage.segments.at(-1).paused = true;
1023
+ }
1024
+ ctx = this.setBaggage(ctx, baggage);
1025
+ let carrier = {};
1026
+ otel.propagation.inject(ctx, carrier);
1027
+ return carrier;
295
1028
  }
296
- // Extract completion/response
297
- const responseText = attrs['ai.response.text'];
298
- const responseObject = attrs['ai.response.object'];
299
- const responseToolCalls = attrs['ai.response.toolCalls'];
300
- if (responseText) {
301
- result[`gen_ai.completion.0.role`] = 'assistant';
302
- result[`gen_ai.completion.0.content`] = String(responseText);
1029
+ resume(ctx) {
1030
+ return otel.propagation.extract(otel__namespace.ROOT_CONTEXT, ctx);
303
1031
  }
304
- else if (responseToolCalls) {
305
- try {
306
- const toolCalls = JSON.parse(String(responseToolCalls));
307
- if (toolCalls.length > 0) {
308
- result['gen_ai.completion.0.finish_reason'] = 'tool_calls';
309
- result[`gen_ai.completion.0.role`] = 'assistant';
310
- toolCalls.forEach((toolCall, toolCallIndex) => {
311
- result[`gen_ai.completion.0.tool_calls.${toolCallIndex}.id`] =
312
- toolCall.toolCallId;
313
- result[`gen_ai.completion.0.tool_calls.${toolCallIndex}.name`] =
314
- toolCall.toolName;
315
- result[`gen_ai.completion.0.tool_calls.${toolCallIndex}.arguments`] =
316
- toolCall.args;
317
- });
318
- }
319
- }
320
- catch (e) {
321
- console.error('Error parsing tool calls', e);
322
- }
1032
+ restored(ctx) {
1033
+ const baggage = this.baggage(ctx);
1034
+ return !baggage?.segments.some((segment) => segment.paused);
323
1035
  }
324
- else if (responseObject) {
325
- result['gen_ai.completion.0.role'] = 'assistant';
326
- result['gen_ai.completion.0.content'] = String(responseObject);
1036
+ restore(ctx) {
1037
+ let baggage = this.baggage(ctx);
1038
+ if (!baggage)
1039
+ return ctx;
1040
+ const segments = baggage.segments;
1041
+ while (segments.at(-1)?.paused)
1042
+ segments.pop();
1043
+ const segment = segments.at(-1);
1044
+ if (!segment)
1045
+ return otel__namespace.ROOT_CONTEXT;
1046
+ baggage = {
1047
+ segment: { id: segment.id, parentId: segment.parentId },
1048
+ segments: segments,
1049
+ };
1050
+ ctx = this.setBaggage(ctx, baggage);
1051
+ let carrier = {};
1052
+ otel.propagation.inject(ctx, carrier);
1053
+ carrier.traceparent = segment.traceparent;
1054
+ carrier.tracestate = segment.tracestate;
1055
+ return this.resume(carrier);
327
1056
  }
328
- // Extract token usage
329
- const completionTokens = attrs['ai.usage.completionTokens'];
330
- const promptTokens = attrs['ai.usage.promptTokens'];
331
- if (typeof completionTokens === 'number') {
332
- result['gen_ai.usage.completion_tokens'] = completionTokens;
1057
+ capitalize(str) {
1058
+ if (str.length === 0)
1059
+ return str;
1060
+ return str.charAt(0).toUpperCase() + str.toLowerCase().slice(1);
333
1061
  }
334
- if (typeof promptTokens === 'number') {
335
- result['gen_ai.usage.prompt_tokens'] = promptTokens;
1062
+ toCamelCase(str) {
1063
+ return str
1064
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
1065
+ .replace(/[^A-Za-z0-9]+/g, ' ')
1066
+ .trim()
1067
+ .split(' ')
1068
+ .map((w, i) => (i ? this.capitalize(w) : w.toLowerCase()))
1069
+ .join('');
336
1070
  }
337
- if (typeof completionTokens === 'number' &&
338
- typeof promptTokens === 'number') {
339
- result['llm.usage.total_tokens'] = completionTokens + promptTokens;
1071
+ toSnakeCase(str) {
1072
+ return str
1073
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
1074
+ .replace(/[^A-Za-z0-9]+/g, '_')
1075
+ .replace(/_+/g, '_')
1076
+ .replace(/^_+|_+$/g, '')
1077
+ .toLowerCase();
340
1078
  }
341
- return result;
342
- }
343
-
344
- class LatitudeTelemetrySDK {
345
- exporter;
346
- constructor({ exporter, modules = {}, disableBatch = false, processors = [], }) {
347
- this.exporter = exporter;
348
- this._init(modules, { disableBatch, processors });
349
- }
350
- _init(modules, options) {
351
- const instrumentations = [];
352
- if (modules?.openAI) {
353
- const openAIInstrumentation = new instrumentationOpenai.OpenAIInstrumentation({
354
- enrichTokens: true,
355
- });
356
- // @ts-ignore
357
- openAIInstrumentation.manuallyInstrument(modules.openAI);
358
- instrumentations.push(openAIInstrumentation);
359
- }
360
- if (modules?.anthropic) {
361
- const anthropicInstrumentation = new instrumentationAnthropic.AnthropicInstrumentation();
362
- anthropicInstrumentation.manuallyInstrument(modules.anthropic);
363
- instrumentations.push(new instrumentationAnthropic.AnthropicInstrumentation());
364
- }
365
- if (modules?.azureOpenAI) {
366
- const azureOpenAIInstrumentation = new instrumentationAzure.AzureOpenAIInstrumentation();
367
- azureOpenAIInstrumentation.manuallyInstrument(modules.azureOpenAI);
368
- instrumentations.push(azureOpenAIInstrumentation);
369
- }
370
- if (modules?.cohere) {
371
- const cohereInstrumentation = new instrumentationCohere.CohereInstrumentation();
372
- cohereInstrumentation.manuallyInstrument(modules.cohere);
373
- instrumentations.push(cohereInstrumentation);
374
- }
375
- if (modules?.google_vertexai) {
376
- const vertexAIInstrumentation = new instrumentationVertexai.VertexAIInstrumentation();
377
- vertexAIInstrumentation.manuallyInstrument(modules.google_vertexai);
378
- instrumentations.push(vertexAIInstrumentation);
379
- }
380
- if (modules?.google_aiplatform) {
381
- const aiplatformInstrumentation = new instrumentationVertexai.AIPlatformInstrumentation();
382
- aiplatformInstrumentation.manuallyInstrument(modules.google_aiplatform);
383
- instrumentations.push(aiplatformInstrumentation);
384
- }
385
- if (modules?.bedrock) {
386
- const bedrockInstrumentation = new instrumentationBedrock.BedrockInstrumentation();
387
- bedrockInstrumentation.manuallyInstrument(modules.bedrock);
388
- instrumentations.push(bedrockInstrumentation);
389
- }
390
- // TODO: Enable these once we have manually tested them
391
- //if (modules?.langchain) {
392
- // const langchainInstrumentation = new LangChainInstrumentation()
393
- // langchainInstrumentation.manuallyInstrument(modules.langchain!)
394
- // instrumentations.push(langchainInstrumentation)
395
- //}
396
- //
397
- //if (modules?.llamaIndex) {
398
- // const llamaindexInstrumentation = new LlamaIndexInstrumentation()
399
- // llamaindexInstrumentation.manuallyInstrument(modules.llamaIndex!)
400
- // instrumentations.push(llamaindexInstrumentation)
401
- //}
402
- //if (modules?.pinecone) {
403
- // const pineconeInstrumentation = new PineconeInstrumentation()
404
- // pineconeInstrumentation.manuallyInstrument(modules.pinecone!)
405
- // instrumentations.push(pineconeInstrumentation)
406
- //}
407
- //if (modules?.chromadb) {
408
- // const chromadbInstrumentation = new ChromaDBInstrumentation()
409
- // chromadbInstrumentation.manuallyInstrument(modules.chromadb!)
410
- // instrumentations.push(chromadbInstrumentation)
411
- //}
412
- //if (modules?.qdrant) {
413
- // const qdrantInstrumentation = new QdrantInstrumentation()
414
- // qdrantInstrumentation.manuallyInstrument(modules.qdrant!)
415
- // instrumentations.push(qdrantInstrumentation)
416
- //}
417
- if (!instrumentations.length && !options?.processors?.length) {
418
- console.warn('Latitude: No instrumentations or processors to initialize');
419
- return;
420
- }
421
- const processors = options?.disableBatch
422
- ? [
423
- new sdkTraceNode.SimpleSpanProcessor(this.exporter),
424
- ...(options?.processors?.map((processor) => new processor(this.exporter)) || []),
425
- ]
426
- : [
427
- new sdkTraceNode.BatchSpanProcessor(this.exporter),
428
- ...(options?.processors?.map((processor) => new processor(this.exporter)) || []),
429
- ];
430
- const sdk = new sdkNode.NodeSDK({
431
- resource: new resources.Resource({
432
- 'service.name': process.env.npm_package_name,
433
- }),
434
- instrumentations,
435
- traceExporter: this.exporter,
436
- // @ts-ignore
437
- spanProcessors: processors,
1079
+ toKebabCase(input) {
1080
+ return input
1081
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
1082
+ .replace(/[^A-Za-z0-9]+/g, '-')
1083
+ .replace(/-+/g, '-')
1084
+ .replace(/^-+|-+$/g, '')
1085
+ .toLowerCase();
1086
+ }
1087
+ error(span, error, options) {
1088
+ options = options || {};
1089
+ span.recordException(error);
1090
+ span.setAttributes(options.attributes || {});
1091
+ span.setStatus({
1092
+ code: otel__namespace.SpanStatusCode.ERROR,
1093
+ message: error.message || undefined,
438
1094
  });
439
- sdk.start();
1095
+ span.end();
440
1096
  }
441
- span(s, fn) {
442
- const c = api.context.active();
443
- return api.context.with(c, () => api.trace
444
- .getTracer('latitude')
445
- .startActiveSpan(s.name ?? 'latitude.span', {}, c, async (span) => {
446
- try {
447
- if (s.prompt) {
1097
+ span(ctx, name, type, options) {
1098
+ if (!this.enabled) {
1099
+ return {
1100
+ context: ctx,
1101
+ end: (_options) => { },
1102
+ fail: (_error, _options) => { },
1103
+ };
1104
+ }
1105
+ const start = options || {};
1106
+ let operation = undefined;
1107
+ if (SPAN_SPECIFICATIONS[type].isGenAI) {
1108
+ operation = type;
1109
+ }
1110
+ const span = this.tracer.startSpan(name, {
1111
+ attributes: {
1112
+ [ATTR_LATITUDE_TYPE]: type,
1113
+ ...(operation && {
1114
+ [incubating.ATTR_GEN_AI_OPERATION_NAME]: operation,
1115
+ }),
1116
+ ...(start.attributes || {}),
1117
+ },
1118
+ kind: otel__namespace.SpanKind.CLIENT,
1119
+ }, ctx);
1120
+ const newCtx = otel.trace.setSpan(ctx, span);
1121
+ return {
1122
+ context: newCtx,
1123
+ end: (options) => {
1124
+ const end = options || {};
1125
+ span.setAttributes(end.attributes || {});
1126
+ span.setStatus({ code: otel__namespace.SpanStatusCode.OK });
1127
+ span.end();
1128
+ },
1129
+ fail: (error, options) => {
1130
+ this.error(span, error, options);
1131
+ },
1132
+ };
1133
+ }
1134
+ tool(ctx, options) {
1135
+ const start = options;
1136
+ let jsonArguments = '';
1137
+ try {
1138
+ jsonArguments = JSON.stringify(start.call.arguments);
1139
+ }
1140
+ catch (error) {
1141
+ jsonArguments = '{}';
1142
+ }
1143
+ const span = this.span(ctx, start.name, SpanType.Tool, {
1144
+ attributes: {
1145
+ [incubating.ATTR_GEN_AI_TOOL_NAME]: start.name,
1146
+ [incubating.ATTR_GEN_AI_TOOL_TYPE]: GEN_AI_TOOL_TYPE_VALUE_FUNCTION,
1147
+ [incubating.ATTR_GEN_AI_TOOL_CALL_ID]: start.call.id,
1148
+ [ATTR_GEN_AI_TOOL_CALL_ARGUMENTS]: jsonArguments,
1149
+ ...(start.attributes || {}),
1150
+ },
1151
+ });
1152
+ return {
1153
+ context: span.context,
1154
+ end: (options) => {
1155
+ const end = options;
1156
+ let stringResult = '';
1157
+ if (typeof end.result.value !== 'string') {
448
1158
  try {
449
- span.setAttribute('latitude.prompt', JSON.stringify(s.prompt));
1159
+ stringResult = JSON.stringify(end.result.value);
450
1160
  }
451
- catch (e) {
452
- console.error('Latitude: Could not serialize latitude.prompt attribute', e);
1161
+ catch (error) {
1162
+ stringResult = '{}';
453
1163
  }
454
1164
  }
455
- if (s.distinctId) {
456
- span.setAttribute('latitude.distinctId', s.distinctId);
1165
+ else {
1166
+ stringResult = end.result.value;
457
1167
  }
458
- if (s.metadata) {
459
- try {
460
- span.setAttribute('latitude.metadata', JSON.stringify(s.metadata));
1168
+ span.end({
1169
+ attributes: {
1170
+ [ATTR_GEN_AI_TOOL_RESULT_VALUE]: stringResult,
1171
+ [ATTR_GEN_AI_TOOL_RESULT_IS_ERROR]: end.result.isError,
1172
+ ...(end.attributes || {}),
1173
+ },
1174
+ });
1175
+ },
1176
+ fail: span.fail,
1177
+ };
1178
+ }
1179
+ attribifyMessageToolCalls(prefix, toolCalls) {
1180
+ const attributes = {};
1181
+ for (let i = 0; i < toolCalls.length; i++) {
1182
+ for (const key in toolCalls[i]) {
1183
+ const field = this.toCamelCase(key);
1184
+ let value = toolCalls[i][key];
1185
+ if (value === null || value === undefined)
1186
+ continue;
1187
+ switch (field) {
1188
+ case 'id':
1189
+ case 'toolCallId':
1190
+ case 'toolUseId': {
1191
+ if (typeof value !== 'string')
1192
+ continue;
1193
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ID}`] = value;
1194
+ break;
461
1195
  }
462
- catch (e) {
463
- console.error('Latitude: Could not serialize latitude.metadata attribute', e);
1196
+ case 'name':
1197
+ case 'toolName': {
1198
+ if (typeof value !== 'string')
1199
+ continue;
1200
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value;
1201
+ break;
1202
+ }
1203
+ case 'arguments':
1204
+ case 'toolArguments':
1205
+ case 'input': {
1206
+ if (typeof value === 'string') {
1207
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value;
1208
+ }
1209
+ else {
1210
+ try {
1211
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = JSON.stringify(value);
1212
+ }
1213
+ catch (error) {
1214
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = '{}';
1215
+ }
1216
+ }
1217
+ break;
1218
+ }
1219
+ /* OpenAI function calls */
1220
+ case 'function': {
1221
+ if (typeof value !== 'object')
1222
+ continue;
1223
+ if (!('name' in value))
1224
+ continue;
1225
+ if (typeof value.name !== 'string')
1226
+ continue;
1227
+ if (!('arguments' in value))
1228
+ continue;
1229
+ if (typeof value.arguments !== 'string')
1230
+ continue;
1231
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_NAME}`] = value.name;
1232
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALLS_ARGUMENTS}`] = value.arguments;
1233
+ break;
464
1234
  }
465
1235
  }
466
1236
  }
1237
+ }
1238
+ return attributes;
1239
+ }
1240
+ attribifyMessageContent(prefix, content) {
1241
+ let attributes = {};
1242
+ if (typeof content === 'string') {
1243
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = content;
1244
+ return attributes;
1245
+ }
1246
+ try {
1247
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] =
1248
+ JSON.stringify(content);
1249
+ }
1250
+ catch (error) {
1251
+ attributes[`${prefix}.${ATTR_GEN_AI_MESSAGE_CONTENT}`] = '[]';
1252
+ }
1253
+ if (!Array.isArray(content))
1254
+ return attributes;
1255
+ /* Tool calls for Anthropic and PromptL are in the content */
1256
+ const toolCalls = [];
1257
+ for (const item of content) {
1258
+ for (const key in item) {
1259
+ if (this.toCamelCase(key) !== 'type')
1260
+ continue;
1261
+ if (typeof item[key] !== 'string')
1262
+ continue;
1263
+ if (item[key] !== 'tool-call' && item[key] !== 'tool_use')
1264
+ continue;
1265
+ toolCalls.push(item);
1266
+ }
1267
+ }
1268
+ if (toolCalls.length > 0) {
1269
+ attributes = {
1270
+ ...attributes,
1271
+ ...this.attribifyMessageToolCalls(prefix, toolCalls),
1272
+ };
1273
+ }
1274
+ return attributes;
1275
+ }
1276
+ attribifyMessages(direction, messages) {
1277
+ const prefix = direction === 'input' ? ATTR_GEN_AI_PROMPTS : ATTR_GEN_AI_COMPLETIONS;
1278
+ let attributes = {};
1279
+ for (let i = 0; i < messages.length; i++) {
1280
+ for (const key in messages[i]) {
1281
+ const field = this.toCamelCase(key);
1282
+ let value = messages[i][key];
1283
+ if (value === null || value === undefined)
1284
+ continue;
1285
+ switch (field) {
1286
+ case 'role': {
1287
+ if (typeof value !== 'string')
1288
+ continue;
1289
+ attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_ROLE}`] = value;
1290
+ break;
1291
+ }
1292
+ /* Tool calls for Anthropic and PromptL are in the content */
1293
+ case 'content': {
1294
+ attributes = {
1295
+ ...attributes,
1296
+ ...this.attribifyMessageContent(`${prefix}.${i}`, value),
1297
+ };
1298
+ break;
1299
+ }
1300
+ /* Tool calls for OpenAI */
1301
+ case 'toolCalls': {
1302
+ if (!Array.isArray(value))
1303
+ continue;
1304
+ attributes = {
1305
+ ...attributes,
1306
+ ...this.attribifyMessageToolCalls(`${prefix}.${i}`, value),
1307
+ };
1308
+ break;
1309
+ }
1310
+ /* Tool result for OpenAI / Anthropic / PromptL */
1311
+ case 'toolCallId':
1312
+ case 'toolId':
1313
+ case 'toolUseId': {
1314
+ if (typeof value !== 'string')
1315
+ continue;
1316
+ attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_CALL_ID}`] =
1317
+ value;
1318
+ break;
1319
+ }
1320
+ case 'toolName': {
1321
+ if (typeof value !== 'string')
1322
+ continue;
1323
+ attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_NAME}`] =
1324
+ value;
1325
+ break;
1326
+ }
1327
+ // Note: 'toolResult' is 'content' itself
1328
+ case 'isError': {
1329
+ if (typeof value !== 'boolean')
1330
+ continue;
1331
+ attributes[`${prefix}.${i}.${ATTR_GEN_AI_MESSAGE_TOOL_RESULT_IS_ERROR}`] = value;
1332
+ break;
1333
+ }
1334
+ }
1335
+ }
1336
+ }
1337
+ return attributes;
1338
+ }
1339
+ attribifyConfiguration(direction, configuration) {
1340
+ const prefix = direction === 'input' ? ATTR_GEN_AI_REQUEST : ATTR_GEN_AI_RESPONSE;
1341
+ const attributes = {};
1342
+ for (const key in configuration) {
1343
+ const field = this.toSnakeCase(key);
1344
+ let value = configuration[key];
1345
+ if (value === null || value === undefined)
1346
+ continue;
1347
+ if (typeof value === 'object' && !Array.isArray(value)) {
1348
+ try {
1349
+ value = JSON.stringify(value);
1350
+ }
1351
+ catch (error) {
1352
+ value = '{}';
1353
+ }
1354
+ }
1355
+ attributes[`${prefix}.${field}`] = value;
1356
+ }
1357
+ return attributes;
1358
+ }
1359
+ completion(ctx, options) {
1360
+ const start = options;
1361
+ const configuration = {
1362
+ ...start.configuration,
1363
+ model: start.model,
1364
+ };
1365
+ let jsonConfiguration = '';
1366
+ try {
1367
+ jsonConfiguration = JSON.stringify(configuration);
1368
+ }
1369
+ catch (error) {
1370
+ jsonConfiguration = '{}';
1371
+ }
1372
+ const attrConfiguration = this.attribifyConfiguration('input', configuration);
1373
+ let jsonInput = '';
1374
+ try {
1375
+ jsonInput = JSON.stringify(start.input);
1376
+ }
1377
+ catch (error) {
1378
+ jsonInput = '[]';
1379
+ }
1380
+ const attrInput = this.attribifyMessages('input', start.input);
1381
+ const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
1382
+ attributes: {
1383
+ [incubating.ATTR_GEN_AI_SYSTEM]: start.provider,
1384
+ [ATTR_GEN_AI_REQUEST_CONFIGURATION]: jsonConfiguration,
1385
+ ...attrConfiguration,
1386
+ [ATTR_GEN_AI_REQUEST_MESSAGES]: jsonInput,
1387
+ ...attrInput,
1388
+ ...(start.attributes || {}),
1389
+ },
1390
+ });
1391
+ return {
1392
+ context: span.context,
1393
+ end: (options) => {
1394
+ const end = options;
1395
+ let jsonOutput = '';
1396
+ try {
1397
+ jsonOutput = JSON.stringify(end.output);
1398
+ }
1399
+ catch (error) {
1400
+ jsonOutput = '[]';
1401
+ }
1402
+ const attrOutput = this.attribifyMessages('output', end.output);
1403
+ const inputTokens = end.tokens.prompt + end.tokens.cached;
1404
+ const outputTokens = end.tokens.reasoning + end.tokens.completion;
1405
+ span.end({
1406
+ attributes: {
1407
+ [ATTR_GEN_AI_RESPONSE_MESSAGES]: jsonOutput,
1408
+ ...attrOutput,
1409
+ [incubating.ATTR_GEN_AI_USAGE_INPUT_TOKENS]: inputTokens,
1410
+ [ATTR_GEN_AI_USAGE_PROMPT_TOKENS]: end.tokens.prompt,
1411
+ [ATTR_GEN_AI_USAGE_CACHED_TOKENS]: end.tokens.cached,
1412
+ [ATTR_GEN_AI_USAGE_REASONING_TOKENS]: end.tokens.reasoning,
1413
+ [ATTR_GEN_AI_USAGE_COMPLETION_TOKENS]: end.tokens.completion,
1414
+ [incubating.ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: outputTokens,
1415
+ [incubating.ATTR_GEN_AI_RESPONSE_MODEL]: start.model,
1416
+ [incubating.ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: [end.finishReason],
1417
+ ...(end.attributes || {}),
1418
+ },
1419
+ });
1420
+ },
1421
+ fail: span.fail,
1422
+ };
1423
+ }
1424
+ embedding(ctx, options) {
1425
+ return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Embedding].name, SpanType.Embedding, options);
1426
+ }
1427
+ retrieval(ctx, options) {
1428
+ return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Retrieval].name, SpanType.Retrieval, options);
1429
+ }
1430
+ reranking(ctx, options) {
1431
+ return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Reranking].name, SpanType.Reranking, options);
1432
+ }
1433
+ attribifyHeaders(direction, headers) {
1434
+ const prefix = direction === 'request'
1435
+ ? ATTR_HTTP_REQUEST_HEADER
1436
+ : ATTR_HTTP_RESPONSE_HEADER;
1437
+ const attributes = {};
1438
+ for (const key in headers) {
1439
+ const field = this.toKebabCase(key);
1440
+ const value = headers[key];
1441
+ if (value === null || value === undefined)
1442
+ continue;
1443
+ attributes[`${prefix}.${field}`] = value;
1444
+ }
1445
+ return attributes;
1446
+ }
1447
+ http(ctx, options) {
1448
+ const start = options;
1449
+ const method = start.request.method.toUpperCase();
1450
+ // Note: do not serialize headers as a single attribute because fields won't be redacted
1451
+ const attrHeaders = this.attribifyHeaders('request', start.request.headers);
1452
+ let finalBody = '';
1453
+ if (typeof start.request.body === 'string') {
1454
+ finalBody = start.request.body;
1455
+ }
1456
+ else {
1457
+ try {
1458
+ finalBody = JSON.stringify(start.request.body);
1459
+ }
467
1460
  catch (error) {
468
- console.error(error);
1461
+ finalBody = '{}';
469
1462
  }
470
- const res = fn(span);
471
- if (res instanceof Promise) {
472
- return res.then((resolvedRes) => {
473
- span.end();
474
- return resolvedRes;
1463
+ }
1464
+ const span = this.span(ctx, start.name || `${method} ${start.request.url}`, SpanType.Http, {
1465
+ attributes: {
1466
+ [semanticConventions.ATTR_HTTP_REQUEST_METHOD]: method,
1467
+ [ATTR_HTTP_REQUEST_URL]: start.request.url,
1468
+ ...attrHeaders,
1469
+ [ATTR_HTTP_REQUEST_BODY]: finalBody,
1470
+ ...(start.attributes || {}),
1471
+ },
1472
+ });
1473
+ return {
1474
+ context: span.context,
1475
+ end: (options) => {
1476
+ const end = options;
1477
+ // Note: do not serialize headers as a single attribute because fields won't be redacted
1478
+ const attrHeaders = this.attribifyHeaders('response', end.response.headers);
1479
+ let finalBody = '';
1480
+ if (typeof end.response.body === 'string') {
1481
+ finalBody = end.response.body;
1482
+ }
1483
+ else {
1484
+ try {
1485
+ finalBody = JSON.stringify(end.response.body);
1486
+ }
1487
+ catch (error) {
1488
+ finalBody = '{}';
1489
+ }
1490
+ }
1491
+ span.end({
1492
+ attributes: {
1493
+ [semanticConventions.ATTR_HTTP_RESPONSE_STATUS_CODE]: end.response.status,
1494
+ ...attrHeaders,
1495
+ [ATTR_HTTP_RESPONSE_BODY]: finalBody,
1496
+ ...(end.attributes || {}),
1497
+ },
475
1498
  });
1499
+ },
1500
+ fail: span.fail,
1501
+ };
1502
+ }
1503
+ segment(ctx, type, data, options) {
1504
+ options = options || {};
1505
+ let baggage = this.baggage(ctx);
1506
+ const parent = baggage?.segments.at(-1);
1507
+ const segments = baggage?.segments || [];
1508
+ segments.push({
1509
+ ...{
1510
+ id: options._internal?.id || uuid.v4(),
1511
+ ...(parent?.id && { parentId: parent.id }),
1512
+ source: options._internal?.source || parent?.source || this.source,
1513
+ type: type,
1514
+ data: data,
1515
+ },
1516
+ traceparent: 'undefined',
1517
+ tracestate: undefined,
1518
+ });
1519
+ const segment = segments.at(-1);
1520
+ baggage = {
1521
+ segment: { id: segment.id, parentId: segment.parentId },
1522
+ segments: segments,
1523
+ };
1524
+ ctx = this.setBaggage(ctx, baggage, options.baggage);
1525
+ // Dummy wrapper to force the same trace and carry on some segment attributes
1526
+ const span = this.span(ctx, SEGMENT_SPECIFICATIONS[type].name, SpanType.Segment, { attributes: options.attributes });
1527
+ let carrier = {};
1528
+ otel.propagation.inject(span.context, carrier);
1529
+ baggage.segments.at(-1).traceparent = carrier.traceparent;
1530
+ baggage.segments.at(-1).tracestate = carrier.tracestate;
1531
+ // Fix current segment span segments attribute now that we know the trace
1532
+ otel.trace
1533
+ .getSpan(span.context)
1534
+ .setAttribute(ATTR_LATITUDE_SEGMENTS, JSON.stringify(baggage.segments));
1535
+ ctx = this.setBaggage(span.context, baggage, options.baggage);
1536
+ return { context: ctx, end: span.end, fail: span.fail };
1537
+ }
1538
+ prompt(ctx, { logUuid, versionUuid, promptUuid, experimentUuid, externalId, template, parameters, ...rest }) {
1539
+ const baggage = {
1540
+ ...(logUuid && { logUuid }), // TODO(tracing): temporal related log, remove when observability is ready
1541
+ commitUuid: versionUuid || HEAD_COMMIT,
1542
+ documentUuid: promptUuid,
1543
+ ...(experimentUuid && { experimentUuid }),
1544
+ ...(externalId && { externalId }),
1545
+ };
1546
+ let jsonParameters = '';
1547
+ try {
1548
+ jsonParameters = JSON.stringify(parameters || {});
1549
+ }
1550
+ catch (error) {
1551
+ jsonParameters = '{}';
1552
+ }
1553
+ const attributes = {
1554
+ [ATTR_GEN_AI_REQUEST_TEMPLATE]: template,
1555
+ [ATTR_GEN_AI_REQUEST_PARAMETERS]: jsonParameters,
1556
+ ...(rest.attributes || {}),
1557
+ };
1558
+ return this.segment(ctx, SegmentType.Document, baggage, {
1559
+ ...rest,
1560
+ attributes,
1561
+ });
1562
+ }
1563
+ step(ctx, options) {
1564
+ return this.segment(ctx, SegmentType.Step, undefined, options);
1565
+ }
1566
+ }
1567
+
1568
+ class LatitudeInstrumentation {
1569
+ options;
1570
+ telemetry;
1571
+ constructor(source, tracer, options) {
1572
+ this.telemetry = new ManualInstrumentation(source, tracer);
1573
+ this.options = options;
1574
+ }
1575
+ isEnabled() {
1576
+ return this.telemetry.isEnabled();
1577
+ }
1578
+ enable() {
1579
+ this.options.module.instrument(this);
1580
+ this.telemetry.enable();
1581
+ }
1582
+ disable() {
1583
+ this.telemetry.disable();
1584
+ this.options.module.uninstrument();
1585
+ }
1586
+ countTokens(messages) {
1587
+ let length = 0;
1588
+ for (const message of messages) {
1589
+ if (!('content' in message))
1590
+ continue;
1591
+ if (typeof message.content === 'string') {
1592
+ length += message.content.length;
1593
+ }
1594
+ else if (Array.isArray(message.content)) {
1595
+ for (const content of message.content) {
1596
+ if (content.type === 'text') {
1597
+ length += content.text.length;
1598
+ }
1599
+ }
476
1600
  }
477
- span.end();
478
- return res;
1601
+ }
1602
+ // Note: this is an estimation to not bundle a tokenizer
1603
+ return Math.ceil(length / 4);
1604
+ }
1605
+ withTraceContext(ctx, fn) {
1606
+ return otel.context.with(this.telemetry.resume(ctx), fn);
1607
+ }
1608
+ async wrapToolHandler(fn, ...args) {
1609
+ const toolArguments = args[0];
1610
+ const { toolId, toolName } = args[1];
1611
+ const $tool = this.telemetry.tool(otel.context.active(), {
1612
+ name: toolName,
1613
+ call: {
1614
+ id: toolId,
1615
+ arguments: toolArguments,
1616
+ },
1617
+ });
1618
+ let result;
1619
+ try {
1620
+ result = await otel.context.with($tool.context, async () => await fn(...args));
1621
+ }
1622
+ catch (error) {
1623
+ if (error.name === 'ToolExecutionPausedError') {
1624
+ $tool.fail(error);
1625
+ throw error;
1626
+ }
1627
+ $tool.end({
1628
+ result: {
1629
+ value: error.message,
1630
+ isError: true,
1631
+ },
1632
+ });
1633
+ throw error;
1634
+ }
1635
+ $tool.end({
1636
+ result: {
1637
+ value: result,
1638
+ isError: false,
1639
+ },
1640
+ });
1641
+ return result;
1642
+ }
1643
+ async wrapRenderChain(fn, ...args) {
1644
+ const { prompt, parameters } = args[0];
1645
+ const $prompt = this.telemetry.prompt(otel.context.active(), {
1646
+ versionUuid: prompt.versionUuid,
1647
+ promptUuid: prompt.uuid,
1648
+ template: prompt.content,
1649
+ parameters: parameters,
1650
+ });
1651
+ let result;
1652
+ try {
1653
+ result = await otel.context.with($prompt.context, async () => await fn(...args));
1654
+ }
1655
+ catch (error) {
1656
+ $prompt.fail(error);
1657
+ throw error;
1658
+ }
1659
+ $prompt.end();
1660
+ return result;
1661
+ }
1662
+ async wrapRenderAgent(fn, ...args) {
1663
+ const { prompt, parameters } = args[0];
1664
+ const $prompt = this.telemetry.prompt(otel.context.active(), {
1665
+ versionUuid: prompt.versionUuid,
1666
+ promptUuid: prompt.uuid,
1667
+ template: prompt.content,
1668
+ parameters: parameters,
1669
+ });
1670
+ let result;
1671
+ try {
1672
+ result = await otel.context.with($prompt.context, async () => await fn(...args));
1673
+ }
1674
+ catch (error) {
1675
+ $prompt.fail(error);
1676
+ throw error;
1677
+ }
1678
+ $prompt.end();
1679
+ return result;
1680
+ }
1681
+ async wrapRenderStep(fn, ...args) {
1682
+ const $step = this.telemetry.step(otel.context.active());
1683
+ let result;
1684
+ try {
1685
+ result = await otel.context.with($step.context, async () => await fn(...args));
1686
+ }
1687
+ catch (error) {
1688
+ $step.fail(error);
1689
+ throw error;
1690
+ }
1691
+ $step.end();
1692
+ return result;
1693
+ }
1694
+ async wrapRenderCompletion(fn, ...args) {
1695
+ if (!this.options.completions) {
1696
+ return await fn(...args);
1697
+ }
1698
+ const { provider, config, messages } = args[0];
1699
+ const model = config.model || 'unknown';
1700
+ const $completion = this.telemetry.completion(otel.context.active(), {
1701
+ name: `${provider} / ${model}`,
1702
+ provider: provider,
1703
+ model: model,
1704
+ configuration: config,
1705
+ input: messages,
1706
+ });
1707
+ let result;
1708
+ try {
1709
+ result = await otel.context.with($completion.context, async () => await fn(...args));
1710
+ }
1711
+ catch (error) {
1712
+ $completion.fail(error);
1713
+ throw error;
1714
+ }
1715
+ // Note: enhance, this is just an estimation
1716
+ const promptTokens = this.countTokens(messages);
1717
+ const completionTokens = this.countTokens(result.messages);
1718
+ $completion.end({
1719
+ output: result.messages,
1720
+ tokens: {
1721
+ prompt: promptTokens,
1722
+ cached: 0,
1723
+ reasoning: 0,
1724
+ completion: completionTokens,
1725
+ },
1726
+ finishReason: result.toolRequests.length > 0
1727
+ ? GEN_AI_RESPONSE_FINISH_REASON_VALUE_TOOL_CALLS
1728
+ : GEN_AI_RESPONSE_FINISH_REASON_VALUE_STOP,
1729
+ });
1730
+ return result;
1731
+ }
1732
+ async wrapRenderTool(fn, ...args) {
1733
+ const { toolRequest } = args[0];
1734
+ const $tool = this.telemetry.tool(otel.context.active(), {
1735
+ name: toolRequest.toolName,
1736
+ call: {
1737
+ id: toolRequest.toolCallId,
1738
+ arguments: toolRequest.toolArguments,
1739
+ },
1740
+ });
1741
+ let result;
1742
+ try {
1743
+ result = await otel.context.with($tool.context, async () => await fn(...args));
1744
+ }
1745
+ catch (error) {
1746
+ $tool.fail(error);
1747
+ throw error;
1748
+ }
1749
+ $tool.end({
1750
+ result: {
1751
+ value: result,
1752
+ isError: false, // Note: currently unknown
1753
+ },
1754
+ });
1755
+ return result;
1756
+ }
1757
+ }
1758
+
1759
+ const TRACES_URL = `${env.GATEWAY_BASE_URL}/api/v3/traces`;
1760
+ const SERVICE_NAME = process.env.npm_package_name || 'unknown';
1761
+ const SCOPE_VERSION = process.env.npm_package_version || 'unknown';
1762
+ const BACKGROUND = () => otel__namespace.ROOT_CONTEXT;
1763
+ class ScopedTracerProvider {
1764
+ scope;
1765
+ version;
1766
+ provider;
1767
+ constructor(scope, version, provider) {
1768
+ this.scope = scope;
1769
+ this.version = version;
1770
+ this.provider = provider;
1771
+ }
1772
+ getTracer(_name, _version, options) {
1773
+ return this.provider.getTracer(this.scope, this.version, options);
1774
+ }
1775
+ }
1776
+ const DEFAULT_SPAN_EXPORTER = (apiKey) => new exporterTraceOtlpHttp.OTLPTraceExporter({
1777
+ url: TRACES_URL,
1778
+ headers: {
1779
+ Authorization: `Bearer ${apiKey}`,
1780
+ 'Content-Type': 'application/json',
1781
+ },
1782
+ timeoutMillis: 30 * 1000,
1783
+ });
1784
+ // Note: Only exporting typescript instrumentations
1785
+ exports.Instrumentation = void 0;
1786
+ (function (Instrumentation) {
1787
+ Instrumentation["Latitude"] = "latitude";
1788
+ Instrumentation["OpenAI"] = "openai";
1789
+ Instrumentation["Anthropic"] = "anthropic";
1790
+ Instrumentation["AzureOpenAI"] = "azure";
1791
+ Instrumentation["VercelAI"] = "vercelai";
1792
+ Instrumentation["VertexAI"] = "vertexai";
1793
+ Instrumentation["AIPlatform"] = "aiplatform";
1794
+ Instrumentation["Bedrock"] = "bedrock";
1795
+ Instrumentation["TogetherAI"] = "togetherai";
1796
+ Instrumentation["Cohere"] = "cohere";
1797
+ Instrumentation["Langchain"] = "langchain";
1798
+ Instrumentation["LlamaIndex"] = "llamaindex";
1799
+ })(exports.Instrumentation || (exports.Instrumentation = {}));
1800
+ class LatitudeTelemetry {
1801
+ options;
1802
+ provider;
1803
+ telemetry;
1804
+ instrumentations;
1805
+ constructor(apiKey, options) {
1806
+ this.options = options || {};
1807
+ if (!this.options.exporter) {
1808
+ this.options.exporter = DEFAULT_SPAN_EXPORTER(apiKey);
1809
+ }
1810
+ otel.context.setGlobalContextManager(new contextAsyncHooks.AsyncLocalStorageContextManager().enable());
1811
+ otel.propagation.setGlobalPropagator(new core.CompositePropagator({
1812
+ propagators: [
1813
+ ...(this.options.propagators || []),
1814
+ new core.W3CTraceContextPropagator(),
1815
+ new core.W3CBaggagePropagator(),
1816
+ ],
479
1817
  }));
1818
+ this.provider = new sdkTraceNode.NodeTracerProvider({
1819
+ resource: new resources.Resource({ [semanticConventions.ATTR_SERVICE_NAME]: SERVICE_NAME }),
1820
+ });
1821
+ // Note: important, must run before the exporter span processors
1822
+ this.provider.addSpanProcessor(new baggageSpanProcessor.BaggageSpanProcessor(baggageSpanProcessor.ALLOW_ALL_BAGGAGE_KEYS));
1823
+ if (this.options.processors) {
1824
+ this.options.processors.forEach((processor) => {
1825
+ this.provider.addSpanProcessor(processor);
1826
+ });
1827
+ }
1828
+ else {
1829
+ this.provider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
1830
+ }
1831
+ if (this.options.disableBatch) {
1832
+ this.provider.addSpanProcessor(new sdkTraceNode.SimpleSpanProcessor(this.options.exporter));
1833
+ }
1834
+ else {
1835
+ this.provider.addSpanProcessor(new sdkTraceNode.BatchSpanProcessor(this.options.exporter));
1836
+ }
1837
+ this.provider.register();
1838
+ process.on('SIGTERM', async () => this.shutdown);
1839
+ process.on('SIGINT', async () => this.shutdown);
1840
+ this.telemetry = null;
1841
+ this.instrumentations = [];
1842
+ this.initInstrumentations();
1843
+ this.instrument();
1844
+ }
1845
+ async flush() {
1846
+ await this.provider.forceFlush();
1847
+ await this.options.exporter.forceFlush?.();
1848
+ }
1849
+ async shutdown() {
1850
+ await this.flush();
1851
+ await this.provider.shutdown();
1852
+ await this.options.exporter.shutdown?.();
1853
+ }
1854
+ tracerProvider(instrumentation) {
1855
+ return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${instrumentation}`, SCOPE_VERSION, this.provider);
1856
+ }
1857
+ tracer(instrumentation) {
1858
+ return this.tracerProvider(instrumentation).getTracer('');
1859
+ }
1860
+ // TODO(tracing): auto instrument outgoing HTTP requests
1861
+ initInstrumentations() {
1862
+ this.instrumentations = [];
1863
+ const tracer = this.tracer(InstrumentationScope.Manual);
1864
+ this.telemetry = new ManualInstrumentation(SegmentSource.API, tracer);
1865
+ this.instrumentations.push(this.telemetry);
1866
+ const latitude = this.options.instrumentations?.latitude;
1867
+ if (latitude) {
1868
+ const tracer = this.tracer(exports.Instrumentation.Latitude);
1869
+ const instrumentation = new LatitudeInstrumentation(SegmentSource.API, tracer, typeof latitude === 'object' ? latitude : { module: latitude });
1870
+ this.instrumentations.push(instrumentation);
1871
+ }
1872
+ const openai = this.options.instrumentations?.openai;
1873
+ if (openai) {
1874
+ const provider = this.tracerProvider(exports.Instrumentation.OpenAI);
1875
+ const instrumentation$1 = new instrumentationOpenai.OpenAIInstrumentation({ enrichTokens: true });
1876
+ instrumentation$1.setTracerProvider(provider);
1877
+ instrumentation$1.manuallyInstrument(openai);
1878
+ instrumentation.registerInstrumentations({
1879
+ instrumentations: [instrumentation$1],
1880
+ tracerProvider: provider,
1881
+ });
1882
+ this.instrumentations.push(instrumentation$1);
1883
+ }
1884
+ const anthropic = this.options.instrumentations?.anthropic;
1885
+ if (anthropic) {
1886
+ const provider = this.tracerProvider(exports.Instrumentation.Anthropic);
1887
+ const instrumentation$1 = new instrumentationAnthropic.AnthropicInstrumentation();
1888
+ instrumentation$1.setTracerProvider(provider);
1889
+ instrumentation$1.manuallyInstrument(anthropic);
1890
+ instrumentation.registerInstrumentations({
1891
+ instrumentations: [instrumentation$1],
1892
+ tracerProvider: provider,
1893
+ });
1894
+ this.instrumentations.push(instrumentation$1);
1895
+ }
1896
+ const azure = this.options.instrumentations?.azure;
1897
+ if (azure) {
1898
+ const provider = this.tracerProvider(exports.Instrumentation.AzureOpenAI);
1899
+ const instrumentation$1 = new instrumentationAzure.AzureOpenAIInstrumentation();
1900
+ instrumentation$1.setTracerProvider(provider);
1901
+ instrumentation$1.manuallyInstrument(azure);
1902
+ instrumentation.registerInstrumentations({
1903
+ instrumentations: [instrumentation$1],
1904
+ tracerProvider: provider,
1905
+ });
1906
+ this.instrumentations.push(instrumentation$1);
1907
+ }
1908
+ const vertexai = this.options.instrumentations?.vertexai;
1909
+ if (vertexai) {
1910
+ const provider = this.tracerProvider(exports.Instrumentation.VertexAI);
1911
+ const instrumentation$1 = new instrumentationVertexai.VertexAIInstrumentation();
1912
+ instrumentation$1.setTracerProvider(provider);
1913
+ instrumentation$1.manuallyInstrument(vertexai);
1914
+ instrumentation.registerInstrumentations({
1915
+ instrumentations: [instrumentation$1],
1916
+ tracerProvider: provider,
1917
+ });
1918
+ this.instrumentations.push(instrumentation$1);
1919
+ }
1920
+ const aiplatform = this.options.instrumentations?.aiplatform;
1921
+ if (aiplatform) {
1922
+ const provider = this.tracerProvider(exports.Instrumentation.AIPlatform);
1923
+ const instrumentation$1 = new instrumentationVertexai.AIPlatformInstrumentation();
1924
+ instrumentation$1.setTracerProvider(provider);
1925
+ instrumentation$1.manuallyInstrument(aiplatform);
1926
+ instrumentation.registerInstrumentations({
1927
+ instrumentations: [instrumentation$1],
1928
+ tracerProvider: provider,
1929
+ });
1930
+ this.instrumentations.push(instrumentation$1);
1931
+ }
1932
+ const bedrock = this.options.instrumentations?.bedrock;
1933
+ if (bedrock) {
1934
+ const provider = this.tracerProvider(exports.Instrumentation.Bedrock);
1935
+ const instrumentation$1 = new instrumentationBedrock.BedrockInstrumentation();
1936
+ instrumentation$1.setTracerProvider(provider);
1937
+ instrumentation$1.manuallyInstrument(bedrock);
1938
+ instrumentation.registerInstrumentations({
1939
+ instrumentations: [instrumentation$1],
1940
+ tracerProvider: provider,
1941
+ });
1942
+ this.instrumentations.push(instrumentation$1);
1943
+ }
1944
+ const togetherai = this.options.instrumentations?.togetherai;
1945
+ if (togetherai) {
1946
+ const provider = this.tracerProvider(exports.Instrumentation.TogetherAI);
1947
+ const instrumentation$1 = new instrumentationTogether.TogetherInstrumentation({
1948
+ enrichTokens: true,
1949
+ });
1950
+ instrumentation$1.setTracerProvider(provider);
1951
+ instrumentation$1.manuallyInstrument(togetherai);
1952
+ instrumentation.registerInstrumentations({
1953
+ instrumentations: [instrumentation$1],
1954
+ tracerProvider: provider,
1955
+ });
1956
+ this.instrumentations.push(instrumentation$1);
1957
+ }
1958
+ const cohere = this.options.instrumentations?.cohere;
1959
+ if (cohere) {
1960
+ const provider = this.tracerProvider(exports.Instrumentation.Cohere);
1961
+ const instrumentation$1 = new instrumentationCohere.CohereInstrumentation();
1962
+ instrumentation$1.setTracerProvider(provider);
1963
+ instrumentation$1.manuallyInstrument(cohere);
1964
+ instrumentation.registerInstrumentations({
1965
+ instrumentations: [instrumentation$1],
1966
+ tracerProvider: provider,
1967
+ });
1968
+ this.instrumentations.push(instrumentation$1);
1969
+ }
1970
+ const langchain = this.options.instrumentations?.langchain;
1971
+ if (langchain) {
1972
+ const provider = this.tracerProvider(exports.Instrumentation.Langchain);
1973
+ const instrumentation$1 = new instrumentationLangchain.LangChainInstrumentation();
1974
+ instrumentation$1.setTracerProvider(provider);
1975
+ instrumentation$1.manuallyInstrument(langchain);
1976
+ instrumentation.registerInstrumentations({
1977
+ instrumentations: [instrumentation$1],
1978
+ tracerProvider: provider,
1979
+ });
1980
+ this.instrumentations.push(instrumentation$1);
1981
+ }
1982
+ const llamaindex = this.options.instrumentations?.llamaindex;
1983
+ if (llamaindex) {
1984
+ const provider = this.tracerProvider(exports.Instrumentation.LlamaIndex);
1985
+ const instrumentation$1 = new instrumentationLlamaindex.LlamaIndexInstrumentation();
1986
+ instrumentation$1.setTracerProvider(provider);
1987
+ instrumentation$1.manuallyInstrument(llamaindex);
1988
+ instrumentation.registerInstrumentations({
1989
+ instrumentations: [instrumentation$1],
1990
+ tracerProvider: provider,
1991
+ });
1992
+ this.instrumentations.push(instrumentation$1);
1993
+ }
1994
+ }
1995
+ instrument() {
1996
+ this.instrumentations.forEach((instrumentation) => {
1997
+ if (!instrumentation.isEnabled())
1998
+ instrumentation.enable();
1999
+ });
2000
+ }
2001
+ uninstrument() {
2002
+ this.instrumentations.forEach((instrumentation) => {
2003
+ if (instrumentation.isEnabled())
2004
+ instrumentation.disable();
2005
+ });
2006
+ }
2007
+ baggage(ctx) {
2008
+ return this.telemetry.baggage(ctx);
2009
+ }
2010
+ pause(ctx) {
2011
+ return this.telemetry.pause(ctx);
2012
+ }
2013
+ resume(ctx) {
2014
+ return this.telemetry.resume(ctx);
2015
+ }
2016
+ restored(ctx) {
2017
+ return this.telemetry.restored(ctx);
2018
+ }
2019
+ restore(ctx) {
2020
+ return this.telemetry.restore(ctx);
2021
+ }
2022
+ tool(ctx, options) {
2023
+ return this.telemetry.tool(ctx, options);
2024
+ }
2025
+ completion(ctx, options) {
2026
+ return this.telemetry.completion(ctx, options);
2027
+ }
2028
+ embedding(ctx, options) {
2029
+ return this.telemetry.embedding(ctx, options);
2030
+ }
2031
+ retrieval(ctx, options) {
2032
+ return this.telemetry.retrieval(ctx, options);
2033
+ }
2034
+ reranking(ctx, options) {
2035
+ return this.telemetry.reranking(ctx, options);
2036
+ }
2037
+ http(ctx, options) {
2038
+ return this.telemetry.http(ctx, options);
2039
+ }
2040
+ prompt(ctx, options) {
2041
+ return this.telemetry.prompt(ctx, options);
2042
+ }
2043
+ step(ctx, options) {
2044
+ return this.telemetry.step(ctx, options);
480
2045
  }
481
2046
  }
482
2047
 
483
- exports.LatitudeExporter = LatitudeExporter;
484
- exports.LatitudeTelemetrySDK = LatitudeTelemetrySDK;
485
- exports.VercelBatchSpanProcessor = VercelBatchSpanProcessor;
486
- exports.VercelSpanProcessor = VercelSpanProcessor;
2048
+ exports.BACKGROUND = BACKGROUND;
2049
+ exports.DEFAULT_REDACT_SPAN_PROCESSOR = DEFAULT_REDACT_SPAN_PROCESSOR;
2050
+ exports.DEFAULT_SPAN_EXPORTER = DEFAULT_SPAN_EXPORTER;
2051
+ exports.LatitudeTelemetry = LatitudeTelemetry;
2052
+ exports.RedactSpanProcessor = RedactSpanProcessor;
487
2053
  //# sourceMappingURL=index.cjs.map