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