@latitude-data/telemetry 2.0.4 → 3.0.0-alpha.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,2089 +1,485 @@
1
- import { z } from 'zod';
2
- import { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_TYPE, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_RESPONSE_FINISH_REASONS, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_GEN_AI_USAGE_INPUT_TOKENS } from '@opentelemetry/semantic-conventions/incubating';
3
- import { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
4
- import * as otel from '@opentelemetry/api';
5
- import { trace, propagation, context } from '@opentelemetry/api';
6
- import { Translator, Provider } from 'rosetta-ai';
7
- import { v4 } from 'uuid';
8
- import { BaggageSpanProcessor, ALLOW_ALL_BAGGAGE_KEYS } from '@opentelemetry/baggage-span-processor';
9
- import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
10
- import { CompositePropagator, W3CTraceContextPropagator, W3CBaggagePropagator } from '@opentelemetry/core';
11
- import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
12
- import { registerInstrumentations } from '@opentelemetry/instrumentation';
13
- import { Resource } from '@opentelemetry/resources';
14
- import { NodeTracerProvider, SimpleSpanProcessor, BatchSpanProcessor } from '@opentelemetry/sdk-trace-node';
15
- import { AnthropicInstrumentation } from '@traceloop/instrumentation-anthropic';
16
- import { BedrockInstrumentation } from '@traceloop/instrumentation-bedrock';
17
- import { CohereInstrumentation } from '@traceloop/instrumentation-cohere';
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 { AIPlatformInstrumentation, VertexAIInstrumentation } from '@traceloop/instrumentation-vertexai';
23
-
24
- class RedactSpanProcessor {
25
- options;
26
- constructor(options) {
27
- this.options = options;
28
- if (!options.mask) {
29
- this.options.mask = (_attribute, _value) => '******';
30
- }
31
- }
32
- onStart(_span, _context) {
33
- // Noop
34
- }
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));
41
- }
42
- for (const link of span.links) {
43
- if (!link.attributes)
44
- continue;
45
- Object.assign(link.attributes, this.redactAttributes(link.attributes));
46
- }
47
- }
48
- forceFlush() {
49
- return Promise.resolve();
50
- }
51
- shutdown() {
52
- return Promise.resolve();
53
- }
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
- });
64
- }
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;
73
- }
74
- }
75
- const DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
76
- attributes: [
77
- /^.*auth.*$/i,
78
- /^.*authorization.*$/i,
79
- /^(?!gen_ai\.).*usage.*$/i,
80
- /^(?!gen_ai\.).*token.*$/i,
81
- /^.*secret.*$/i,
82
- /^.*key.*$/i,
83
- /^.*password.*$/i,
84
- /^.*cookie.*$/i,
85
- /^.*session.*$/i,
86
- /^.*credential.*$/i,
87
- /^.*signature.*$/i,
88
- /^.*oauth.*$/i,
89
- /^.*saml.*$/i,
90
- /^.*openid.*$/i,
91
- /^.*refresh.*$/i,
92
- /^.*jwt.*$/i,
93
- /^.*otp.*$/i,
94
- /^.*mfa.*$/i,
95
- /^.*csrf.*$/i,
96
- /^.*xsrf.*$/i,
97
- /^.*refresh.*$/i,
98
- /^.*x[-_]forwarded[-_]for.*$/i,
99
- /^.*x[-_]real[-_]ip.*$/i,
100
- ],
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
101
6
  });
102
7
 
103
- const DEFAULT_GATEWAY_BASE_URL = {
104
- production: 'https://gateway.latitude.so',
105
- development: 'http://localhost:8787',
106
- test: 'http://localhost:8787',
107
- }["production"];
108
- function GET_GATEWAY_BASE_URL() {
109
- if (process.env.GATEWAY_BASE_URL) {
110
- return process.env.GATEWAY_BASE_URL;
111
- }
112
- if (!process.env.GATEWAY_HOSTNAME) {
113
- return DEFAULT_GATEWAY_BASE_URL;
114
- }
115
- const protocol = process.env.GATEWAY_SSL ? 'https' : 'http';
116
- const port = process.env.GATEWAY_PORT ?? (process.env.GATEWAY_SSL ? 443 : 80);
117
- const hostname = process.env.GATEWAY_HOSTNAME;
118
- return `${protocol}://${hostname}:${port}`;
8
+ // src/sdk/context.ts
9
+ import { context, createContextKey, trace } from "@opentelemetry/api";
10
+ var LATITUDE_CONTEXT_KEY = createContextKey("latitude-internal-context");
11
+ var CAPTURE_TRACER_NAME = "so.latitude.instrumentation.capture";
12
+ function getLatitudeContext(ctx) {
13
+ return ctx.getValue(LATITUDE_CONTEXT_KEY);
119
14
  }
120
- const env = { GATEWAY_BASE_URL: GET_GATEWAY_BASE_URL() };
121
-
122
- function toSnakeCase(str) {
123
- return str
124
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
125
- .replace(/[^A-Za-z0-9]+/g, '_')
126
- .replace(/_+/g, '_')
127
- .replace(/^_+|_+$/g, '')
128
- .toLowerCase();
15
+ function mergeArrays(a, b) {
16
+ if (!a && !b) return void 0;
17
+ if (!a) return b;
18
+ if (!b) return a;
19
+ return [.../* @__PURE__ */ new Set([...a, ...b])];
129
20
  }
130
- function toKebabCase(input) {
131
- return input
132
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
133
- .replace(/[^A-Za-z0-9]+/g, '-')
134
- .replace(/-+/g, '-')
135
- .replace(/^-+|-+$/g, '')
136
- .toLowerCase();
21
+ function capture(name, fn, options = {}) {
22
+ const currentContext = context.active();
23
+ const existingData = getLatitudeContext(currentContext);
24
+ const mergedData = {
25
+ name: options.name ?? name,
26
+ tags: mergeArrays(existingData?.tags, options.tags),
27
+ metadata: { ...existingData?.metadata, ...options.metadata },
28
+ sessionId: options.sessionId ?? existingData?.sessionId,
29
+ userId: options.userId ?? existingData?.userId
30
+ };
31
+ const newContext = currentContext.setValue(LATITUDE_CONTEXT_KEY, mergedData);
32
+ const existingSpan = trace.getSpan(currentContext);
33
+ if (existingSpan) {
34
+ return context.with(newContext, fn);
35
+ }
36
+ const tracer = trace.getTracer(CAPTURE_TRACER_NAME);
37
+ return tracer.startActiveSpan(name, { attributes: { "latitude.capture.root": true } }, newContext, (span) => {
38
+ let result;
39
+ try {
40
+ result = fn();
41
+ } catch (error) {
42
+ span.recordException(error);
43
+ span.end();
44
+ throw error;
45
+ }
46
+ if (result instanceof Promise) {
47
+ return result.catch((error) => {
48
+ span.recordException(error);
49
+ throw error;
50
+ }).finally(() => {
51
+ span.end();
52
+ });
53
+ }
54
+ span.end();
55
+ return result;
56
+ });
137
57
  }
138
58
 
139
- var StreamEventTypes;
140
- (function (StreamEventTypes) {
141
- StreamEventTypes["Latitude"] = "latitude-event";
142
- StreamEventTypes["Provider"] = "provider-event";
143
- })(StreamEventTypes || (StreamEventTypes = {}));
144
- z.object({
145
- id: z.string(),
146
- name: z.string(),
147
- result: z.unknown(),
148
- isError: z.boolean().optional(),
149
- text: z.string().optional(),
150
- });
151
- z.object({
152
- inputTokens: z.number(),
153
- outputTokens: z.number(),
154
- promptTokens: z.number(),
155
- completionTokens: z.number(),
156
- totalTokens: z.number(),
157
- reasoningTokens: z.number(),
158
- cachedInputTokens: z.number(),
159
- });
160
-
161
- var ParameterType;
162
- (function (ParameterType) {
163
- ParameterType["Text"] = "text";
164
- ParameterType["Image"] = "image";
165
- ParameterType["File"] = "file";
166
- })(ParameterType || (ParameterType = {}));
167
- var LatitudeTool;
168
- (function (LatitudeTool) {
169
- LatitudeTool["RunCode"] = "code";
170
- LatitudeTool["WebSearch"] = "search";
171
- LatitudeTool["WebExtract"] = "extract";
172
- LatitudeTool["Think"] = "think";
173
- LatitudeTool["TODO"] = "todo";
174
- })(LatitudeTool || (LatitudeTool = {}));
175
- var LatitudeToolInternalName;
176
- (function (LatitudeToolInternalName) {
177
- LatitudeToolInternalName["RunCode"] = "lat_tool_run_code";
178
- LatitudeToolInternalName["WebSearch"] = "lat_tool_web_search";
179
- LatitudeToolInternalName["WebExtract"] = "lat_tool_web_extract";
180
- LatitudeToolInternalName["Think"] = "think";
181
- LatitudeToolInternalName["TODO"] = "todo_write";
182
- })(LatitudeToolInternalName || (LatitudeToolInternalName = {}));
183
- [
184
- LatitudeTool.Think,
185
- LatitudeTool.TODO,
186
- ];
187
-
188
- const actualOutputConfiguration = z.object({
189
- messageSelection: z.enum(['last', 'all']), // Which assistant messages to select
190
- contentFilter: z
191
- .enum(['text', 'reasoning', 'image', 'file', 'tool_call'])
192
- .optional(),
193
- parsingFormat: z.enum(['string', 'json']),
194
- fieldAccessor: z.string().optional(), // Field accessor to get the output from if it's a key-value format
195
- });
196
- const expectedOutputConfiguration = z.object({
197
- parsingFormat: z.enum(['string', 'json']),
198
- fieldAccessor: z.string().optional(), // Field accessor to get the output from if it's a key-value format
199
- });
200
- const EVALUATION_TRIGGER_TARGETS = ['first', 'every', 'last'];
201
- const LAST_INTERACTION_DEBOUNCE_MIN_SECONDS = 30;
202
- const LAST_INTERACTION_DEBOUNCE_MAX_SECONDS = 60 * 60 * 24; // 1 day
203
- const MIN_EVALUATION_SAMPLE_RATE = 0; // 0%
204
- const MAX_EVALUATION_SAMPLE_RATE = 100; // 100%
205
- const triggerConfiguration = z.object({
206
- target: z.enum(EVALUATION_TRIGGER_TARGETS),
207
- lastInteractionDebounce: z
208
- .number()
209
- .min(LAST_INTERACTION_DEBOUNCE_MIN_SECONDS)
210
- .max(LAST_INTERACTION_DEBOUNCE_MAX_SECONDS)
211
- .optional(),
212
- sampleRate: z
213
- .number()
214
- .int()
215
- .min(MIN_EVALUATION_SAMPLE_RATE)
216
- .max(MAX_EVALUATION_SAMPLE_RATE)
217
- .optional(),
218
- });
219
- const baseEvaluationConfiguration = z.object({
220
- reverseScale: z.boolean(), // If true, lower is better, otherwise, higher is better
221
- actualOutput: actualOutputConfiguration,
222
- expectedOutput: expectedOutputConfiguration.optional(),
223
- trigger: triggerConfiguration.optional(),
224
- });
225
- const baseEvaluationResultMetadata = z.object({
226
- // configuration: Configuration snapshot is defined in every metric specification
227
- actualOutput: z.string(),
228
- expectedOutput: z.string().optional(),
229
- datasetLabel: z.string().optional(),
230
- });
231
- const baseEvaluationResultError = z.object({
232
- message: z.string(),
233
- });
234
-
235
- const compositeEvaluationConfiguration = baseEvaluationConfiguration.extend({
236
- evaluationUuids: z.array(z.string()),
237
- minThreshold: z.number().optional(), // Threshold percentage
238
- maxThreshold: z.number().optional(), // Threshold percentage
239
- });
240
- const compositeEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
241
- results: z.record(z.string(), // Evaluation uuid
242
- z.object({
243
- uuid: z.string(), // Result uuid (for side effects)
244
- name: z.string(), // Evaluation name
245
- score: z.number(), // Normalized score
246
- reason: z.string(),
247
- passed: z.boolean(),
248
- tokens: z.number().optional(), // Optional llm evaluation usage
249
- })),
250
- });
251
- const compositeEvaluationResultError = baseEvaluationResultError.extend({
252
- errors: z
253
- .record(z.string(), // Evaluation uuid
254
- z.object({
255
- uuid: z.string(), // Result uuid (for side effects)
256
- name: z.string(), // Evaluation name
257
- message: z.string(),
258
- }))
259
- .optional(),
260
- });
261
- // AVERAGE
262
- const compositeEvaluationAverageConfiguration = compositeEvaluationConfiguration.extend({});
263
- compositeEvaluationResultMetadata.extend({
264
- configuration: compositeEvaluationAverageConfiguration,
265
- });
266
- compositeEvaluationResultError.extend({});
267
- const CompositeEvaluationAverageSpecification = {
268
- };
269
- // WEIGHTED
270
- const compositeEvaluationWeightedConfiguration = compositeEvaluationConfiguration.extend({
271
- weights: z.record(z.string(), // Evaluation uuid
272
- z.number()),
273
- });
274
- compositeEvaluationResultMetadata.extend({
275
- configuration: compositeEvaluationWeightedConfiguration,
276
- });
277
- compositeEvaluationResultError.extend({});
278
- const CompositeEvaluationWeightedSpecification = {
279
- };
280
- // CUSTOM
281
- const compositeEvaluationCustomConfiguration = compositeEvaluationConfiguration.extend({
282
- formula: z.string(),
283
- });
284
- compositeEvaluationResultMetadata.extend({
285
- configuration: compositeEvaluationCustomConfiguration,
286
- });
287
- compositeEvaluationResultError.extend({});
288
- const CompositeEvaluationCustomSpecification = {
289
- };
290
- /* ------------------------------------------------------------------------- */
291
- var CompositeEvaluationMetric;
292
- (function (CompositeEvaluationMetric) {
293
- CompositeEvaluationMetric["Average"] = "average";
294
- CompositeEvaluationMetric["Weighted"] = "weighted";
295
- CompositeEvaluationMetric["Custom"] = "custom";
296
- })(CompositeEvaluationMetric || (CompositeEvaluationMetric = {}));
297
- const CompositeEvaluationSpecification = {
298
- // prettier-ignore
299
- metrics: {
300
- [CompositeEvaluationMetric.Average]: CompositeEvaluationAverageSpecification,
301
- [CompositeEvaluationMetric.Weighted]: CompositeEvaluationWeightedSpecification,
302
- [CompositeEvaluationMetric.Custom]: CompositeEvaluationCustomSpecification,
303
- },
304
- };
305
-
306
- const selectedContextSchema = z.object({
307
- messageIndex: z.number().int().nonnegative(),
308
- contentBlockIndex: z.number().int().nonnegative(),
309
- contentType: z.enum([
310
- 'text',
311
- 'reasoning',
312
- 'image',
313
- 'file',
314
- 'tool-call',
315
- 'tool-result',
316
- ]),
317
- textRange: z
318
- .object({
319
- start: z.number().int().nonnegative(),
320
- end: z.number().int().nonnegative(),
321
- })
322
- .optional(),
323
- selectedText: z.string().optional(),
324
- toolCallId: z.string().optional(),
325
- });
326
- const humanEvaluationConfiguration = baseEvaluationConfiguration.extend({
327
- enableControls: z.boolean().optional(), // UI annotation controls
328
- criteria: z.string().optional(),
329
- });
330
- const humanEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
331
- reason: z.string().optional(),
332
- enrichedReason: z.string().optional(),
333
- selectedContexts: z.array(selectedContextSchema).optional(),
334
- });
335
- const humanEvaluationResultError = baseEvaluationResultError.extend({});
336
- // BINARY
337
- const humanEvaluationBinaryConfiguration = humanEvaluationConfiguration.extend({
338
- passDescription: z.string().optional(),
339
- failDescription: z.string().optional(),
340
- });
341
- humanEvaluationResultMetadata.extend({
342
- configuration: humanEvaluationBinaryConfiguration,
343
- });
344
- humanEvaluationResultError.extend({});
345
- const HumanEvaluationBinarySpecification = {
346
- };
347
- // RATING
348
- const humanEvaluationRatingConfiguration = humanEvaluationConfiguration.extend({
349
- minRating: z.number(),
350
- minRatingDescription: z.string().optional(),
351
- maxRating: z.number(),
352
- maxRatingDescription: z.string().optional(),
353
- minThreshold: z.number().optional(), // Threshold in rating range
354
- maxThreshold: z.number().optional(), // Threshold in rating range
355
- });
356
- humanEvaluationResultMetadata.extend({
357
- configuration: humanEvaluationRatingConfiguration,
358
- });
359
- humanEvaluationResultError.extend({});
360
- const HumanEvaluationRatingSpecification = {
361
- };
362
- /* ------------------------------------------------------------------------- */
363
- var HumanEvaluationMetric;
364
- (function (HumanEvaluationMetric) {
365
- HumanEvaluationMetric["Binary"] = "binary";
366
- HumanEvaluationMetric["Rating"] = "rating";
367
- })(HumanEvaluationMetric || (HumanEvaluationMetric = {}));
368
- const HumanEvaluationSpecification = {
369
- // prettier-ignore
370
- metrics: {
371
- [HumanEvaluationMetric.Binary]: HumanEvaluationBinarySpecification,
372
- [HumanEvaluationMetric.Rating]: HumanEvaluationRatingSpecification,
373
- },
374
- };
375
-
376
- const llmEvaluationConfiguration = baseEvaluationConfiguration.extend({
377
- provider: z.string(),
378
- model: z.string(),
379
- });
380
- const llmEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
381
- evaluationLogId: z.number(),
382
- reason: z.string(),
383
- tokens: z.number(),
384
- cost: z.number(),
385
- duration: z.number(),
386
- });
387
- const llmEvaluationResultError = baseEvaluationResultError.extend({
388
- runErrorId: z.number().optional(),
389
- });
390
- // BINARY
391
- const llmEvaluationBinaryConfiguration = llmEvaluationConfiguration.extend({
392
- criteria: z.string(),
393
- passDescription: z.string(),
394
- failDescription: z.string(),
395
- });
396
- llmEvaluationResultMetadata.extend({
397
- configuration: llmEvaluationBinaryConfiguration,
398
- });
399
- llmEvaluationResultError.extend({});
400
- const LlmEvaluationBinarySpecification = {
401
- };
402
- // RATING
403
- const llmEvaluationRatingConfiguration = llmEvaluationConfiguration.extend({
404
- criteria: z.string(),
405
- minRating: z.number(),
406
- minRatingDescription: z.string(),
407
- maxRating: z.number(),
408
- maxRatingDescription: z.string(),
409
- minThreshold: z.number().optional(), // Threshold in rating range
410
- maxThreshold: z.number().optional(), // Threshold in rating range
411
- });
412
- llmEvaluationResultMetadata.extend({
413
- configuration: llmEvaluationRatingConfiguration,
414
- });
415
- llmEvaluationResultError.extend({});
416
- const LlmEvaluationRatingSpecification = {
417
- };
418
- // COMPARISON
419
- const llmEvaluationComparisonConfiguration = llmEvaluationConfiguration.extend({
420
- criteria: z.string(),
421
- passDescription: z.string(),
422
- failDescription: z.string(),
423
- minThreshold: z.number().optional(), // Threshold percentage
424
- maxThreshold: z.number().optional(), // Threshold percentage
425
- });
426
- llmEvaluationResultMetadata.extend({
427
- configuration: llmEvaluationComparisonConfiguration,
428
- });
429
- llmEvaluationResultError.extend({});
430
- const LlmEvaluationComparisonSpecification = {
431
- };
432
- // CUSTOM
433
- const llmEvaluationCustomConfiguration = llmEvaluationConfiguration.extend({
434
- prompt: z.string(),
435
- minScore: z.number(),
436
- maxScore: z.number(),
437
- minThreshold: z.number().optional(), // Threshold percentage
438
- maxThreshold: z.number().optional(), // Threshold percentage
439
- });
440
- llmEvaluationResultMetadata.extend({
441
- configuration: llmEvaluationCustomConfiguration,
442
- });
443
- llmEvaluationResultError.extend({});
444
- const LlmEvaluationCustomSpecification = {
445
- };
446
- // CUSTOM LABELED
447
- const LlmEvaluationCustomLabeledSpecification = {
448
- };
449
- /* ------------------------------------------------------------------------- */
450
- var LlmEvaluationMetric;
451
- (function (LlmEvaluationMetric) {
452
- LlmEvaluationMetric["Binary"] = "binary";
453
- LlmEvaluationMetric["Rating"] = "rating";
454
- LlmEvaluationMetric["Comparison"] = "comparison";
455
- LlmEvaluationMetric["Custom"] = "custom";
456
- LlmEvaluationMetric["CustomLabeled"] = "custom_labeled";
457
- })(LlmEvaluationMetric || (LlmEvaluationMetric = {}));
458
- const LlmEvaluationSpecification = {
459
- // prettier-ignore
460
- metrics: {
461
- [LlmEvaluationMetric.Binary]: LlmEvaluationBinarySpecification,
462
- [LlmEvaluationMetric.Rating]: LlmEvaluationRatingSpecification,
463
- [LlmEvaluationMetric.Comparison]: LlmEvaluationComparisonSpecification,
464
- [LlmEvaluationMetric.Custom]: LlmEvaluationCustomSpecification,
465
- [LlmEvaluationMetric.CustomLabeled]: LlmEvaluationCustomLabeledSpecification,
466
- },
467
- };
59
+ // src/sdk/init.ts
60
+ import { context as context2, propagation } from "@opentelemetry/api";
61
+ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
62
+ import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from "@opentelemetry/core";
63
+ import { Resource } from "@opentelemetry/resources";
64
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
65
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
468
66
 
469
- const ruleEvaluationConfiguration = baseEvaluationConfiguration.extend({});
470
- const ruleEvaluationResultMetadata = baseEvaluationResultMetadata.extend({
471
- reason: z.string().optional(),
472
- });
473
- const ruleEvaluationResultError = baseEvaluationResultError.extend({});
474
- // EXACT MATCH
475
- const ruleEvaluationExactMatchConfiguration = ruleEvaluationConfiguration.extend({
476
- caseInsensitive: z.boolean(),
477
- });
478
- ruleEvaluationResultMetadata.extend({
479
- configuration: ruleEvaluationExactMatchConfiguration,
480
- });
481
- ruleEvaluationResultError.extend({});
482
- const RuleEvaluationExactMatchSpecification = {
483
- };
484
- // REGULAR EXPRESSION
485
- const ruleEvaluationRegularExpressionConfiguration = ruleEvaluationConfiguration.extend({
486
- pattern: z.string(),
487
- });
488
- ruleEvaluationResultMetadata.extend({
489
- configuration: ruleEvaluationRegularExpressionConfiguration,
490
- });
491
- ruleEvaluationResultError.extend({});
492
- const RuleEvaluationRegularExpressionSpecification = {
493
- };
494
- // SCHEMA VALIDATION
495
- const ruleEvaluationSchemaValidationConfiguration = ruleEvaluationConfiguration.extend({
496
- format: z.enum(['json']),
497
- schema: z.string(),
498
- });
499
- ruleEvaluationResultMetadata.extend({
500
- configuration: ruleEvaluationSchemaValidationConfiguration,
501
- });
502
- ruleEvaluationResultError.extend({});
503
- const RuleEvaluationSchemaValidationSpecification = {
504
- };
505
- // LENGTH COUNT
506
- const ruleEvaluationLengthCountConfiguration = ruleEvaluationConfiguration.extend({
507
- algorithm: z.enum(['character', 'word', 'sentence']),
508
- minLength: z.number().optional(),
509
- maxLength: z.number().optional(),
510
- });
511
- ruleEvaluationResultMetadata.extend({
512
- configuration: ruleEvaluationLengthCountConfiguration,
513
- });
514
- ruleEvaluationResultError.extend({});
515
- const RuleEvaluationLengthCountSpecification = {
516
- };
517
- // LEXICAL OVERLAP
518
- const ruleEvaluationLexicalOverlapConfiguration = ruleEvaluationConfiguration.extend({
519
- algorithm: z.enum(['substring', 'levenshtein_distance', 'rouge']),
520
- minOverlap: z.number().optional(), // Percentage of overlap
521
- maxOverlap: z.number().optional(), // Percentage of overlap
522
- });
523
- ruleEvaluationResultMetadata.extend({
524
- configuration: ruleEvaluationLexicalOverlapConfiguration,
525
- });
526
- ruleEvaluationResultError.extend({});
527
- const RuleEvaluationLexicalOverlapSpecification = {
528
- };
529
- // SEMANTIC SIMILARITY
530
- const ruleEvaluationSemanticSimilarityConfiguration = ruleEvaluationConfiguration.extend({
531
- algorithm: z.enum(['cosine_distance']),
532
- minSimilarity: z.number().optional(), // Percentage of similarity
533
- maxSimilarity: z.number().optional(), // Percentage of similarity
534
- });
535
- ruleEvaluationResultMetadata.extend({
536
- configuration: ruleEvaluationSemanticSimilarityConfiguration,
537
- });
538
- ruleEvaluationResultError.extend({});
539
- const RuleEvaluationSemanticSimilaritySpecification = {
540
- };
541
- // NUMERIC SIMILARITY
542
- const ruleEvaluationNumericSimilarityConfiguration = ruleEvaluationConfiguration.extend({
543
- algorithm: z.enum(['relative_difference']),
544
- minSimilarity: z.number().optional(), // Percentage of similarity
545
- maxSimilarity: z.number().optional(), // Percentage of similarity
546
- });
547
- ruleEvaluationResultMetadata.extend({
548
- configuration: ruleEvaluationNumericSimilarityConfiguration,
549
- });
550
- ruleEvaluationResultError.extend({});
551
- const RuleEvaluationNumericSimilaritySpecification = {
552
- };
553
- /* ------------------------------------------------------------------------- */
554
- var RuleEvaluationMetric;
555
- (function (RuleEvaluationMetric) {
556
- RuleEvaluationMetric["ExactMatch"] = "exact_match";
557
- RuleEvaluationMetric["RegularExpression"] = "regular_expression";
558
- RuleEvaluationMetric["SchemaValidation"] = "schema_validation";
559
- RuleEvaluationMetric["LengthCount"] = "length_count";
560
- RuleEvaluationMetric["LexicalOverlap"] = "lexical_overlap";
561
- RuleEvaluationMetric["SemanticSimilarity"] = "semantic_similarity";
562
- RuleEvaluationMetric["NumericSimilarity"] = "numeric_similarity";
563
- })(RuleEvaluationMetric || (RuleEvaluationMetric = {}));
564
- const RuleEvaluationSpecification = {
565
- // prettier-ignore
566
- metrics: {
567
- [RuleEvaluationMetric.ExactMatch]: RuleEvaluationExactMatchSpecification,
568
- [RuleEvaluationMetric.RegularExpression]: RuleEvaluationRegularExpressionSpecification,
569
- [RuleEvaluationMetric.SchemaValidation]: RuleEvaluationSchemaValidationSpecification,
570
- [RuleEvaluationMetric.LengthCount]: RuleEvaluationLengthCountSpecification,
571
- [RuleEvaluationMetric.LexicalOverlap]: RuleEvaluationLexicalOverlapSpecification,
572
- [RuleEvaluationMetric.SemanticSimilarity]: RuleEvaluationSemanticSimilaritySpecification,
573
- [RuleEvaluationMetric.NumericSimilarity]: RuleEvaluationNumericSimilaritySpecification,
574
- },
67
+ // src/sdk/instrumentations.ts
68
+ import { registerInstrumentations } from "@opentelemetry/instrumentation";
69
+ import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
70
+ import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
71
+ import { CohereInstrumentation } from "@traceloop/instrumentation-cohere";
72
+ import { LangChainInstrumentation } from "@traceloop/instrumentation-langchain";
73
+ import { LlamaIndexInstrumentation } from "@traceloop/instrumentation-llamaindex";
74
+ import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
75
+ import { TogetherInstrumentation } from "@traceloop/instrumentation-together";
76
+ import { AIPlatformInstrumentation, VertexAIInstrumentation } from "@traceloop/instrumentation-vertexai";
77
+ var INSTRUMENTATION_MAP = {
78
+ openai: { ctor: OpenAIInstrumentation, moduleName: "openai", defaultEnrichTokens: true },
79
+ anthropic: { ctor: AnthropicInstrumentation, moduleName: "@anthropic-ai/sdk" },
80
+ bedrock: { ctor: BedrockInstrumentation, moduleName: "@aws-sdk/client-bedrock-runtime" },
81
+ cohere: { ctor: CohereInstrumentation, moduleName: "cohere-ai" },
82
+ langchain: { ctor: LangChainInstrumentation, moduleName: "langchain" },
83
+ llamaindex: { ctor: LlamaIndexInstrumentation, moduleName: "llamaindex" },
84
+ togetherai: { ctor: TogetherInstrumentation, moduleName: "together-ai", defaultEnrichTokens: false },
85
+ vertexai: { ctor: VertexAIInstrumentation, moduleName: "@google-cloud/vertexai" },
86
+ aiplatform: { ctor: AIPlatformInstrumentation, moduleName: "@google-cloud/aiplatform" }
575
87
  };
88
+ async function createLatitudeInstrumentations(options) {
89
+ const result = [];
90
+ for (const type of options.instrumentations) {
91
+ const config = INSTRUMENTATION_MAP[type];
92
+ if (!config) {
93
+ console.warn(`[Latitude] Unknown instrumentation type: ${type}`);
94
+ continue;
95
+ }
96
+ const enrichTokens = options.enrichTokens?.[type] ?? config.defaultEnrichTokens;
97
+ const inst = new config.ctor(enrichTokens !== void 0 ? { enrichTokens } : void 0);
98
+ const moduleRef = options.modules?.[type] ?? await tryRequire(config.moduleName);
99
+ if (!moduleRef) {
100
+ console.warn(
101
+ `[Latitude] Module not found for ${type}: ${config.moduleName}. Install it or pass it explicitly in 'modules'.`
102
+ );
103
+ continue;
104
+ }
105
+ inst.manuallyInstrument?.(moduleRef);
106
+ result.push(inst);
107
+ }
108
+ return result;
109
+ }
110
+ async function tryRequire(moduleName) {
111
+ try {
112
+ return __require(moduleName);
113
+ } catch {
114
+ try {
115
+ const mod = await import(moduleName);
116
+ return mod.default ?? mod;
117
+ } catch {
118
+ return void 0;
119
+ }
120
+ }
121
+ }
122
+ async function registerLatitudeInstrumentations(options) {
123
+ const instrumentations = await createLatitudeInstrumentations(options);
124
+ registerInstrumentations({
125
+ instrumentations,
126
+ tracerProvider: options.tracerProvider
127
+ });
128
+ }
576
129
 
577
- var EvaluationType;
578
- (function (EvaluationType) {
579
- EvaluationType["Rule"] = "rule";
580
- EvaluationType["Llm"] = "llm";
581
- EvaluationType["Human"] = "human";
582
- EvaluationType["Composite"] = "composite";
583
- })(EvaluationType || (EvaluationType = {}));
584
- const EvaluationTypeSchema = z.enum(EvaluationType);
585
- const EvaluationMetricSchema = z.union([
586
- z.enum(RuleEvaluationMetric),
587
- z.enum(LlmEvaluationMetric),
588
- z.enum(HumanEvaluationMetric),
589
- z.enum(CompositeEvaluationMetric),
590
- ]);
591
- const EvaluationConfigurationSchema = z.custom();
592
- // prettier-ignore
593
- z.custom();
594
- // prettier-ignore
595
- z.custom();
596
- ({
597
- [EvaluationType.Rule]: RuleEvaluationSpecification,
598
- [EvaluationType.Llm]: LlmEvaluationSpecification,
599
- [EvaluationType.Human]: HumanEvaluationSpecification,
600
- [EvaluationType.Composite]: CompositeEvaluationSpecification,
601
- });
602
- z.object({
603
- name: z.string(),
604
- description: z.string(),
605
- type: EvaluationTypeSchema,
606
- metric: EvaluationMetricSchema,
607
- configuration: EvaluationConfigurationSchema,
608
- });
609
- z.object({
610
- evaluateLiveLogs: z.boolean().nullable().optional(),
611
- });
612
-
613
- var ChainEventTypes;
614
- (function (ChainEventTypes) {
615
- ChainEventTypes["ChainCompleted"] = "chain-completed";
616
- ChainEventTypes["ChainError"] = "chain-error";
617
- ChainEventTypes["ChainStarted"] = "chain-started";
618
- ChainEventTypes["IntegrationWakingUp"] = "integration-waking-up";
619
- ChainEventTypes["ProviderCompleted"] = "provider-completed";
620
- ChainEventTypes["ProviderStarted"] = "provider-started";
621
- ChainEventTypes["StepCompleted"] = "step-completed";
622
- ChainEventTypes["StepStarted"] = "step-started";
623
- ChainEventTypes["ToolCompleted"] = "tool-completed";
624
- ChainEventTypes["ToolResult"] = "tool-result";
625
- ChainEventTypes["ToolsStarted"] = "tools-started";
626
- })(ChainEventTypes || (ChainEventTypes = {}));
627
-
628
- z.object({
629
- name: z.string(),
630
- provider: z.string(),
631
- model: z.string(),
632
- temperature: z.number(),
633
- });
634
- // Experiment ran from a dataset
635
- const experimentDatasetSourceSchema = z.object({
636
- source: z.literal('dataset'),
637
- datasetId: z.number(),
638
- fromRow: z.number(),
639
- toRow: z.number(),
640
- datasetLabels: z.record(z.string(), z.string()),
641
- parametersMap: z.record(z.string(), z.number()),
642
- });
643
- // Experiment ran from last logs (from commit and creation time of experiment)
644
- const experimentLogsSourceSchema = z.object({
645
- source: z.literal('logs'),
646
- count: z.number(),
647
- });
648
- // Experiment ran with manual parameters (currently only used for prompts with no parameters)
649
- const experimentManualSourceSchema = z.object({
650
- source: z.literal('manual'),
651
- count: z.number(),
652
- parametersMap: z.record(z.string(), z.number()),
653
- });
654
- z.discriminatedUnion('source', [
655
- experimentDatasetSourceSchema,
656
- experimentLogsSourceSchema,
657
- experimentManualSourceSchema,
658
- ]);
659
-
660
- var QuotaType;
661
- (function (QuotaType) {
662
- QuotaType["Seats"] = "seats";
663
- QuotaType["Runs"] = "runs";
664
- QuotaType["Credits"] = "credits";
665
- })(QuotaType || (QuotaType = {}));
666
- var GrantSource;
667
- (function (GrantSource) {
668
- GrantSource["System"] = "system";
669
- GrantSource["Subscription"] = "subscription";
670
- GrantSource["Purchase"] = "purchase";
671
- GrantSource["Reward"] = "reward";
672
- GrantSource["Promocode"] = "promocode";
673
- })(GrantSource || (GrantSource = {}));
674
-
675
- var ModifiedDocumentType;
676
- (function (ModifiedDocumentType) {
677
- ModifiedDocumentType["Created"] = "created";
678
- ModifiedDocumentType["Updated"] = "updated";
679
- ModifiedDocumentType["UpdatedPath"] = "updated_path";
680
- ModifiedDocumentType["Deleted"] = "deleted";
681
- })(ModifiedDocumentType || (ModifiedDocumentType = {}));
682
-
683
- var IntegrationType;
684
- (function (IntegrationType) {
685
- IntegrationType["Latitude"] = "latitude";
686
- IntegrationType["ExternalMCP"] = "custom_mcp";
687
- IntegrationType["Pipedream"] = "pipedream";
688
- IntegrationType["HostedMCP"] = "mcp_server";
689
- })(IntegrationType || (IntegrationType = {}));
690
- var HostedIntegrationType;
691
- (function (HostedIntegrationType) {
692
- HostedIntegrationType["Stripe"] = "stripe";
693
- HostedIntegrationType["Slack"] = "slack";
694
- HostedIntegrationType["Github"] = "github";
695
- HostedIntegrationType["Notion"] = "notion";
696
- HostedIntegrationType["Twitter"] = "twitter";
697
- HostedIntegrationType["Airtable"] = "airtable";
698
- HostedIntegrationType["Linear"] = "linear";
699
- HostedIntegrationType["YoutubeCaptions"] = "youtube_captions";
700
- HostedIntegrationType["Reddit"] = "reddit";
701
- HostedIntegrationType["Telegram"] = "telegram";
702
- HostedIntegrationType["Tinybird"] = "tinybird";
703
- HostedIntegrationType["Perplexity"] = "perplexity";
704
- HostedIntegrationType["AwsKbRetrieval"] = "aws_kb_retrieval";
705
- HostedIntegrationType["BraveSearch"] = "brave_search";
706
- HostedIntegrationType["EverArt"] = "ever_art";
707
- HostedIntegrationType["Fetch"] = "fetch";
708
- HostedIntegrationType["GitLab"] = "gitlab";
709
- HostedIntegrationType["GoogleMaps"] = "google_maps";
710
- HostedIntegrationType["Sentry"] = "sentry";
711
- HostedIntegrationType["Puppeteer"] = "puppeteer";
712
- HostedIntegrationType["Time"] = "time";
713
- HostedIntegrationType["browserbase"] = "browserbase";
714
- HostedIntegrationType["Neon"] = "neon";
715
- HostedIntegrationType["Postgres"] = "postgres";
716
- HostedIntegrationType["Supabase"] = "supabase";
717
- HostedIntegrationType["Redis"] = "redis";
718
- HostedIntegrationType["Jira"] = "jira";
719
- HostedIntegrationType["Attio"] = "attio";
720
- HostedIntegrationType["Ghost"] = "ghost";
721
- HostedIntegrationType["Figma"] = "figma";
722
- HostedIntegrationType["Hyperbrowser"] = "hyperbrowser";
723
- HostedIntegrationType["Audiense"] = "audiense";
724
- HostedIntegrationType["Apify"] = "apify";
725
- HostedIntegrationType["Exa"] = "exa";
726
- HostedIntegrationType["YepCode"] = "yepcode";
727
- HostedIntegrationType["Monday"] = "monday";
728
- HostedIntegrationType["AgentQL"] = "agentql";
729
- HostedIntegrationType["AgentRPC"] = "agentrpc";
730
- HostedIntegrationType["AstraDB"] = "astra_db";
731
- HostedIntegrationType["Bankless"] = "bankless";
732
- HostedIntegrationType["Bicscan"] = "bicscan";
733
- HostedIntegrationType["Chargebee"] = "chargebee";
734
- HostedIntegrationType["Chronulus"] = "chronulus";
735
- HostedIntegrationType["CircleCI"] = "circleci";
736
- HostedIntegrationType["Codacy"] = "codacy";
737
- HostedIntegrationType["CodeLogic"] = "codelogic";
738
- HostedIntegrationType["Convex"] = "convex";
739
- HostedIntegrationType["Dart"] = "dart";
740
- HostedIntegrationType["DevHubCMS"] = "devhub_cms";
741
- HostedIntegrationType["Elasticsearch"] = "elasticsearch";
742
- HostedIntegrationType["ESignatures"] = "esignatures";
743
- HostedIntegrationType["Fewsats"] = "fewsats";
744
- HostedIntegrationType["Firecrawl"] = "firecrawl";
745
- HostedIntegrationType["Graphlit"] = "graphlit";
746
- HostedIntegrationType["Heroku"] = "heroku";
747
- HostedIntegrationType["IntegrationAppHubspot"] = "integration_app_hubspot";
748
- HostedIntegrationType["LaraTranslate"] = "lara_translate";
749
- HostedIntegrationType["Logfire"] = "logfire";
750
- HostedIntegrationType["Langfuse"] = "langfuse";
751
- HostedIntegrationType["LingoSupabase"] = "lingo_supabase";
752
- HostedIntegrationType["Make"] = "make";
753
- HostedIntegrationType["Meilisearch"] = "meilisearch";
754
- HostedIntegrationType["Momento"] = "momento";
755
- HostedIntegrationType["Neo4jAura"] = "neo4j_aura";
756
- HostedIntegrationType["Octagon"] = "octagon";
757
- HostedIntegrationType["Paddle"] = "paddle";
758
- HostedIntegrationType["PayPal"] = "paypal";
759
- HostedIntegrationType["Qdrant"] = "qdrant";
760
- HostedIntegrationType["Raygun"] = "raygun";
761
- HostedIntegrationType["Rember"] = "rember";
762
- HostedIntegrationType["Riza"] = "riza";
763
- HostedIntegrationType["Search1API"] = "search1api";
764
- HostedIntegrationType["Semgrep"] = "semgrep";
765
- HostedIntegrationType["Tavily"] = "tavily";
766
- HostedIntegrationType["Unstructured"] = "unstructured";
767
- HostedIntegrationType["Vectorize"] = "vectorize";
768
- HostedIntegrationType["Xero"] = "xero";
769
- HostedIntegrationType["Readwise"] = "readwise";
770
- HostedIntegrationType["Airbnb"] = "airbnb";
771
- HostedIntegrationType["Mintlify"] = "mintlify";
772
- // Require all auth file :point_down:
773
- // Gmail = 'google_drive',
774
- // GoogleCalendar = 'google_drive',
775
- // GoogleDrive = 'google_drive',
776
- // GoogleWorkspace = 'google_workspace', // env vars not supported (?)
777
- // TODO: implement these
778
- // Wordpress = 'wordpress', // Not on OpenTools
779
- // Discord = 'discord', // Not on OpenTools
780
- // Intercom = 'intercom', // Not on OpenTools
781
- // Hubspot = 'hubspot', // Docker based
782
- // Loops = 'loops', // Does not exist
783
- })(HostedIntegrationType || (HostedIntegrationType = {}));
784
-
785
- var LogSources;
786
- (function (LogSources) {
787
- LogSources["API"] = "api";
788
- LogSources["AgentAsTool"] = "agent_as_tool";
789
- LogSources["Copilot"] = "copilot";
790
- LogSources["EmailTrigger"] = "email_trigger";
791
- LogSources["Evaluation"] = "evaluation";
792
- LogSources["Experiment"] = "experiment";
793
- LogSources["IntegrationTrigger"] = "integration_trigger";
794
- LogSources["Playground"] = "playground";
795
- LogSources["ScheduledTrigger"] = "scheduled_trigger";
796
- LogSources["SharedPrompt"] = "shared_prompt";
797
- LogSources["ShadowTest"] = "shadow_test";
798
- LogSources["ABTestChallenger"] = "ab_test_challenger";
799
- LogSources["User"] = "user";
800
- LogSources["Optimization"] = "optimization";
801
- })(LogSources || (LogSources = {}));
802
-
803
- var RunSourceGroup;
804
- (function (RunSourceGroup) {
805
- RunSourceGroup["Production"] = "production";
806
- RunSourceGroup["Playground"] = "playground";
807
- })(RunSourceGroup || (RunSourceGroup = {}));
808
- ({
809
- [RunSourceGroup.Production]: [
810
- LogSources.API,
811
- LogSources.ShadowTest,
812
- LogSources.ABTestChallenger,
813
- LogSources.EmailTrigger,
814
- LogSources.IntegrationTrigger,
815
- LogSources.ScheduledTrigger,
816
- LogSources.SharedPrompt,
817
- LogSources.User,
818
- ],
819
- [RunSourceGroup.Playground]: [LogSources.Playground, LogSources.Experiment],
820
- });
130
+ // src/sdk/processor.ts
131
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
132
+ import {
133
+ BatchSpanProcessor,
134
+ SimpleSpanProcessor
135
+ } from "@opentelemetry/sdk-trace-node";
821
136
 
822
- var SpanKind;
823
- (function (SpanKind) {
824
- SpanKind["Internal"] = "internal";
825
- SpanKind["Server"] = "server";
826
- SpanKind["Client"] = "client";
827
- SpanKind["Producer"] = "producer";
828
- SpanKind["Consumer"] = "consumer";
829
- })(SpanKind || (SpanKind = {}));
830
- // Note: loosely based on OpenTelemetry GenAI semantic conventions
831
- var SpanType;
832
- (function (SpanType) {
833
- // Latitude wrappers
834
- SpanType["Prompt"] = "prompt";
835
- SpanType["Chat"] = "chat";
836
- SpanType["External"] = "external";
837
- SpanType["UnresolvedExternal"] = "unresolved_external";
838
- // Added a HTTP span to capture raw HTTP requests and responses when running from Latitude
839
- SpanType["Http"] = "http";
840
- // Any known span from supported specifications will be grouped into one of these types
841
- SpanType["Completion"] = "completion";
842
- SpanType["Tool"] = "tool";
843
- SpanType["Embedding"] = "embedding";
844
- SpanType["Unknown"] = "unknown";
845
- })(SpanType || (SpanType = {}));
846
- [
847
- SpanType.Prompt,
848
- SpanType.External,
849
- SpanType.Chat,
850
- ];
851
- const SPAN_SPECIFICATIONS = {
852
- [SpanType.Prompt]: {
853
- name: 'Prompt',
854
- description: 'A prompt span',
855
- isGenAI: false,
856
- isHidden: false,
857
- },
858
- [SpanType.Chat]: {
859
- name: 'Chat',
860
- description: 'A chat continuation span',
861
- isGenAI: false,
862
- isHidden: false,
863
- },
864
- [SpanType.External]: {
865
- name: 'External',
866
- description: 'An external capture span',
867
- isGenAI: false,
868
- isHidden: false,
869
- },
870
- [SpanType.UnresolvedExternal]: {
871
- name: 'Unresolved External',
872
- description: 'An external span that needs path resolution before storage',
873
- isGenAI: false,
874
- isHidden: true,
875
- },
876
- [SpanType.Completion]: {
877
- name: 'Completion',
878
- description: 'A completion call',
879
- isGenAI: true,
880
- isHidden: false,
881
- },
882
- [SpanType.Embedding]: {
883
- name: 'Embedding',
884
- description: 'An embedding call',
885
- isGenAI: true,
886
- isHidden: false,
887
- },
888
- [SpanType.Tool]: {
889
- name: 'Tool',
890
- description: 'A tool call',
891
- isGenAI: true,
892
- isHidden: false,
893
- },
894
- [SpanType.Http]: {
895
- name: 'HTTP',
896
- description: 'An HTTP request',
897
- isGenAI: false,
898
- isHidden: true,
899
- },
900
- [SpanType.Unknown]: {
901
- name: 'Unknown',
902
- description: 'An unknown span',
903
- isGenAI: false,
904
- isHidden: true,
905
- },
137
+ // src/constants/attributes.ts
138
+ var ATTRIBUTES = {
139
+ name: "latitude.capture.name",
140
+ tags: "latitude.tags",
141
+ metadata: "latitude.metadata",
142
+ sessionId: "session.id",
143
+ userId: "user.id"
906
144
  };
907
- var SpanStatus;
908
- (function (SpanStatus) {
909
- SpanStatus["Unset"] = "unset";
910
- SpanStatus["Ok"] = "ok";
911
- SpanStatus["Error"] = "error";
912
- })(SpanStatus || (SpanStatus = {}));
913
- new Set([
914
- SpanType.Prompt,
915
- SpanType.Chat,
916
- SpanType.External,
917
- ]);
918
145
 
919
- // Note: Traces are unmaterialized but this context is used to propagate the trace
920
- // See www.w3.org/TR/trace-context and w3c.github.io/baggage
921
- z.object({
922
- traceparent: z.string(), // <version>-<trace-id>-<span-id>-<trace-flags>
923
- tracestate: z.string().optional(), // <key>=urlencoded(<value>)[,<key>=urlencoded(<value>)]*
924
- baggage: z.string().optional(), // <key>=urlencoded(<value>)[,<key>=urlencoded(<value>)]*
925
- });
146
+ // src/constants/scope.ts
147
+ var SCOPE_LATITUDE = "so.latitude.instrumentation";
926
148
 
927
- const ATTRIBUTES = {
928
- // Custom attributes added and used by Latitude spans (Prompt / External / Chat)
929
- LATITUDE: {
930
- type: 'latitude.type',
931
- documentUuid: 'latitude.document_uuid',
932
- promptPath: 'latitude.prompt_path',
933
- commitUuid: 'latitude.commit_uuid',
934
- documentLogUuid: 'latitude.document_log_uuid',
935
- projectId: 'latitude.project_id',
936
- experimentUuid: 'latitude.experiment_uuid',
937
- source: 'latitude.source',
938
- externalId: 'latitude.external_id',
939
- testDeploymentId: 'latitude.test_deployment_id',
940
- previousTraceId: 'latitude.previous_trace_id',
941
- // Custom additions to the GenAI semantic conventions (deprecated)
942
- request: {
943
- _root: 'gen_ai.request',
944
- configuration: 'gen_ai.request.configuration',
945
- template: 'gen_ai.request.template',
946
- parameters: 'gen_ai.request.parameters'},
947
- response: {
948
- _root: 'gen_ai.response'},
949
- usage: {
950
- promptTokens: 'gen_ai.usage.prompt_tokens',
951
- cachedTokens: 'gen_ai.usage.cached_tokens',
952
- reasoningTokens: 'gen_ai.usage.reasoning_tokens',
953
- completionTokens: 'gen_ai.usage.completion_tokens',
954
- },
955
- },
956
- // Official OpenTelemetry semantic conventions
957
- OPENTELEMETRY: {
958
- HTTP: {
959
- request: {
960
- url: 'http.request.url',
961
- body: 'http.request.body',
962
- header: 'http.request.header',
963
- method: ATTR_HTTP_REQUEST_METHOD,
964
- },
965
- response: {
966
- body: 'http.response.body',
967
- header: 'http.response.header',
968
- statusCode: ATTR_HTTP_RESPONSE_STATUS_CODE,
969
- },
970
- },
971
- // GenAI semantic conventions
972
- // https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/
973
- GEN_AI: {
974
- operation: ATTR_GEN_AI_OPERATION_NAME,
975
- response: {
976
- model: ATTR_GEN_AI_RESPONSE_MODEL,
977
- finishReasons: ATTR_GEN_AI_RESPONSE_FINISH_REASONS,
978
- },
979
- usage: {
980
- inputTokens: ATTR_GEN_AI_USAGE_INPUT_TOKENS,
981
- outputTokens: ATTR_GEN_AI_USAGE_OUTPUT_TOKENS,
982
- },
983
- systemInstructions: 'gen_ai.system.instructions', // Contains the PARTS of the "system message"
984
- tool: {
985
- call: {
986
- id: ATTR_GEN_AI_TOOL_CALL_ID,
987
- arguments: 'gen_ai.tool.call.arguments'}},
988
- input: {
989
- messages: 'gen_ai.input.messages',
990
- },
991
- output: {
992
- messages: 'gen_ai.output.messages',
993
- },
994
- _deprecated: {
995
- system: ATTR_GEN_AI_SYSTEM,
996
- tool: {
997
- name: ATTR_GEN_AI_TOOL_NAME,
998
- type: ATTR_GEN_AI_TOOL_TYPE,
999
- result: {
1000
- value: 'gen_ai.tool.result.value',
1001
- isError: 'gen_ai.tool.result.is_error',
1002
- },
1003
- }},
1004
- },
1005
- }};
1006
- const VALUES = {
1007
- OPENTELEMETRY: {
1008
- GEN_AI: {
1009
- response: {
1010
- finishReasons: {
1011
- stop: 'stop',
1012
- toolCalls: 'tool_calls'},
1013
- },
1014
- tool: {
1015
- type: {
1016
- function: 'function',
1017
- },
1018
- }},
1019
- }};
149
+ // src/env/env.ts
150
+ var DEFAULT_EXPORTER_URL = {
151
+ production: "https://ingest.latitude.so",
152
+ development: "http://localhost:3002",
153
+ test: "http://localhost:3002"
154
+ }[process.env.NODE_ENV ?? "development"] ?? "http://localhost:3002";
155
+ function getExporterUrl() {
156
+ if (process.env.LATITUDE_TELEMETRY_URL) {
157
+ return process.env.LATITUDE_TELEMETRY_URL;
158
+ }
159
+ return DEFAULT_EXPORTER_URL;
160
+ }
161
+ var env = { EXPORTER_URL: getExporterUrl() };
1020
162
 
1021
- /* Note: Instrumentation scopes from all language SDKs */
1022
- const SCOPE_LATITUDE = 'so.latitude.instrumentation';
1023
- var InstrumentationScope;
1024
- (function (InstrumentationScope) {
1025
- InstrumentationScope["Manual"] = "manual";
1026
- InstrumentationScope["Latitude"] = "latitude";
1027
- InstrumentationScope["OpenAI"] = "openai";
1028
- InstrumentationScope["Anthropic"] = "anthropic";
1029
- InstrumentationScope["AzureOpenAI"] = "azure";
1030
- InstrumentationScope["VercelAI"] = "vercelai";
1031
- InstrumentationScope["VertexAI"] = "vertexai";
1032
- InstrumentationScope["AIPlatform"] = "aiplatform";
1033
- InstrumentationScope["MistralAI"] = "mistralai";
1034
- InstrumentationScope["Bedrock"] = "bedrock";
1035
- InstrumentationScope["Sagemaker"] = "sagemaker";
1036
- InstrumentationScope["TogetherAI"] = "togetherai";
1037
- InstrumentationScope["Replicate"] = "replicate";
1038
- InstrumentationScope["Groq"] = "groq";
1039
- InstrumentationScope["Cohere"] = "cohere";
1040
- InstrumentationScope["LiteLLM"] = "litellm";
1041
- InstrumentationScope["Langchain"] = "langchain";
1042
- InstrumentationScope["LlamaIndex"] = "llamaindex";
1043
- InstrumentationScope["DSPy"] = "dspy";
1044
- InstrumentationScope["Haystack"] = "haystack";
1045
- InstrumentationScope["Ollama"] = "ollama";
1046
- InstrumentationScope["Transformers"] = "transformers";
1047
- InstrumentationScope["AlephAlpha"] = "alephalpha";
1048
- })(InstrumentationScope || (InstrumentationScope = {}));
1049
- /* Note: Schemas for span ingestion following OpenTelemetry service request specification */
1050
- var Otlp;
1051
- (function (Otlp) {
1052
- Otlp.attributeValueSchema = z.object({
1053
- stringValue: z.string().optional(),
1054
- intValue: z.number().optional(),
1055
- boolValue: z.boolean().optional(),
1056
- arrayValue: z
1057
- .object({
1058
- values: z.array(z.object({
1059
- stringValue: z.string().optional(),
1060
- intValue: z.number().optional(),
1061
- boolValue: z.boolean().optional(),
1062
- })),
1063
- })
1064
- .optional(),
1065
- });
1066
- Otlp.attributeSchema = z.object({
1067
- key: z.string(),
1068
- value: Otlp.attributeValueSchema,
1069
- });
1070
- Otlp.eventSchema = z.object({
1071
- name: z.string(),
1072
- timeUnixNano: z.string(),
1073
- attributes: z.array(Otlp.attributeSchema).optional(),
1074
- });
1075
- Otlp.linkSchema = z.object({
1076
- traceId: z.string(),
1077
- spanId: z.string(),
1078
- attributes: z.array(Otlp.attributeSchema).optional(),
1079
- });
1080
- (function (StatusCode) {
1081
- StatusCode[StatusCode["Unset"] = 0] = "Unset";
1082
- StatusCode[StatusCode["Ok"] = 1] = "Ok";
1083
- StatusCode[StatusCode["Error"] = 2] = "Error";
1084
- })(Otlp.StatusCode || (Otlp.StatusCode = {}));
1085
- Otlp.statusSchema = z.object({
1086
- code: z.number(),
1087
- message: z.string().optional(),
163
+ // src/sdk/redact.ts
164
+ var RedactSpanProcessor = class {
165
+ options;
166
+ constructor(options) {
167
+ this.options = options;
168
+ if (!options.mask) {
169
+ this.options.mask = (_attribute, _value) => "******";
170
+ }
171
+ }
172
+ onStart(_span, _context) {
173
+ }
174
+ onEnd(span) {
175
+ Object.assign(span.attributes, this.redactAttributes(span.attributes));
176
+ for (const event of span.events) {
177
+ if (!event.attributes) continue;
178
+ Object.assign(event.attributes, this.redactAttributes(event.attributes));
179
+ }
180
+ for (const link of span.links) {
181
+ if (!link.attributes) continue;
182
+ Object.assign(link.attributes, this.redactAttributes(link.attributes));
183
+ }
184
+ }
185
+ forceFlush() {
186
+ return Promise.resolve();
187
+ }
188
+ shutdown() {
189
+ return Promise.resolve();
190
+ }
191
+ shouldRedact(attribute) {
192
+ return this.options.attributes.some((pattern) => {
193
+ if (typeof pattern === "string") {
194
+ return attribute === pattern;
195
+ } else if (pattern instanceof RegExp) {
196
+ return pattern.test(attribute);
197
+ }
198
+ return false;
1088
199
  });
1089
- (function (SpanKind) {
1090
- SpanKind[SpanKind["Internal"] = 0] = "Internal";
1091
- SpanKind[SpanKind["Server"] = 1] = "Server";
1092
- SpanKind[SpanKind["Client"] = 2] = "Client";
1093
- SpanKind[SpanKind["Producer"] = 3] = "Producer";
1094
- SpanKind[SpanKind["Consumer"] = 4] = "Consumer";
1095
- })(Otlp.SpanKind || (Otlp.SpanKind = {}));
1096
- Otlp.spanSchema = z.object({
1097
- traceId: z.string(),
1098
- spanId: z.string(),
1099
- parentSpanId: z.string().optional(),
1100
- name: z.string(),
1101
- kind: z.number(),
1102
- startTimeUnixNano: z.string(),
1103
- endTimeUnixNano: z.string(),
1104
- status: Otlp.statusSchema.optional(),
1105
- events: z.array(Otlp.eventSchema).optional(),
1106
- links: z.array(Otlp.linkSchema).optional(),
1107
- attributes: z.array(Otlp.attributeSchema).optional(),
1108
- });
1109
- Otlp.scopeSchema = z.object({
1110
- name: z.string(),
1111
- version: z.string().optional(),
1112
- });
1113
- Otlp.scopeSpanSchema = z.object({
1114
- scope: Otlp.scopeSchema,
1115
- spans: z.array(Otlp.spanSchema),
1116
- });
1117
- Otlp.resourceSchema = z.object({
1118
- attributes: z.array(Otlp.attributeSchema),
1119
- });
1120
- Otlp.resourceSpanSchema = z.object({
1121
- resource: Otlp.resourceSchema,
1122
- scopeSpans: z.array(Otlp.scopeSpanSchema),
1123
- });
1124
- Otlp.serviceRequestSchema = z.object({
1125
- resourceSpans: z.array(Otlp.resourceSpanSchema),
1126
- });
1127
- })(Otlp || (Otlp = {}));
1128
-
1129
- const MAX_SIMULATION_TURNS = 10;
1130
- const globalGoalSourceSchema = z.object({
1131
- type: z.literal('global'),
1132
- value: z.string(),
1133
- });
1134
- const columnGoalSourceSchema = z.object({
1135
- type: z.literal('column'),
1136
- columnIndex: z.number(),
1137
- });
1138
- const simulatedUserGoalSourceSchema = z.discriminatedUnion('type', [
1139
- globalGoalSourceSchema,
1140
- columnGoalSourceSchema,
1141
- ]);
1142
- const SimulationSettingsSchema = z.object({
1143
- simulateToolResponses: z.boolean().optional(),
1144
- simulatedTools: z.array(z.string()).optional(), // Empty array means all tools are simulated (if simulateToolResponses is true).
1145
- toolSimulationInstructions: z.string().optional(), // A prompt used to guide and generate the simulation result
1146
- maxTurns: z.number().min(1).max(MAX_SIMULATION_TURNS).optional(), // The maximum number of turns to simulate. Default is 1, and any greater value will add a new user message to the simulated conversation.
1147
- simulatedUserGoal: z.string().optional(), // Deprecated: Use simulatedUserGoalSource instead. Kept for backward compatibility.
1148
- simulatedUserGoalSource: simulatedUserGoalSourceSchema.optional(), // The source for the simulated user goal (global text or dataset column).
1149
- });
1150
-
1151
- var OptimizationEngine;
1152
- (function (OptimizationEngine) {
1153
- OptimizationEngine["Identity"] = "identity";
1154
- OptimizationEngine["Gepa"] = "gepa";
1155
- })(OptimizationEngine || (OptimizationEngine = {}));
1156
- const OptimizationBudgetSchema = z.object({
1157
- time: z.number().min(0).optional(),
1158
- tokens: z.number().min(0).optional(),
1159
- });
1160
- z.object({
1161
- dataset: z
1162
- .object({
1163
- target: z.number().min(0).optional(), // Note: number of rows to curate when not provided by the user
1164
- label: z.string().optional(), // Note: expected output column when using a labeled evaluation
1165
- })
1166
- .optional(),
1167
- parameters: z
1168
- .record(z.string(), z.object({
1169
- column: z.string().optional(), // Note: corresponding column in the user-provided trainset and testset
1170
- isPii: z.boolean().optional(),
1171
- }))
1172
- .optional(),
1173
- simulation: SimulationSettingsSchema.optional(),
1174
- scope: z
1175
- .object({
1176
- configuration: z.boolean().optional(),
1177
- instructions: z.boolean().optional(),
1178
- })
1179
- .optional(),
1180
- budget: OptimizationBudgetSchema.optional(),
1181
- });
1182
-
1183
- // TODO(tracing): deprecated
1184
- const HEAD_COMMIT = 'live';
1185
- var Providers;
1186
- (function (Providers) {
1187
- Providers["OpenAI"] = "openai";
1188
- Providers["Anthropic"] = "anthropic";
1189
- Providers["Groq"] = "groq";
1190
- Providers["Mistral"] = "mistral";
1191
- Providers["Azure"] = "azure";
1192
- Providers["Google"] = "google";
1193
- Providers["GoogleVertex"] = "google_vertex";
1194
- Providers["AnthropicVertex"] = "anthropic_vertex";
1195
- Providers["Custom"] = "custom";
1196
- Providers["XAI"] = "xai";
1197
- Providers["AmazonBedrock"] = "amazon_bedrock";
1198
- Providers["DeepSeek"] = "deepseek";
1199
- Providers["Perplexity"] = "perplexity";
1200
- })(Providers || (Providers = {}));
1201
- var DocumentType;
1202
- (function (DocumentType) {
1203
- DocumentType["Prompt"] = "prompt";
1204
- DocumentType["Agent"] = "agent";
1205
- })(DocumentType || (DocumentType = {}));
1206
- var DocumentTriggerType;
1207
- (function (DocumentTriggerType) {
1208
- DocumentTriggerType["Email"] = "email";
1209
- DocumentTriggerType["Scheduled"] = "scheduled";
1210
- DocumentTriggerType["Integration"] = "integration";
1211
- })(DocumentTriggerType || (DocumentTriggerType = {}));
1212
- var DocumentTriggerStatus;
1213
- (function (DocumentTriggerStatus) {
1214
- DocumentTriggerStatus["Pending"] = "pending";
1215
- DocumentTriggerStatus["Deployed"] = "deployed";
1216
- DocumentTriggerStatus["Deprecated"] = "deprecated";
1217
- })(DocumentTriggerStatus || (DocumentTriggerStatus = {}));
1218
- var DocumentTriggerParameters;
1219
- (function (DocumentTriggerParameters) {
1220
- DocumentTriggerParameters["SenderEmail"] = "senderEmail";
1221
- DocumentTriggerParameters["SenderName"] = "senderName";
1222
- DocumentTriggerParameters["Subject"] = "subject";
1223
- DocumentTriggerParameters["Body"] = "body";
1224
- DocumentTriggerParameters["Attachments"] = "attachments";
1225
- })(DocumentTriggerParameters || (DocumentTriggerParameters = {}));
1226
- const DOCUMENT_PATH_REGEXP = /^([\w-]+\/)*([\w-.])+$/;
1227
-
1228
- const translator = new Translator({
1229
- filterEmptyMessages: true,
1230
- providerMetadata: 'preserve',
200
+ }
201
+ redactAttributes(attributes) {
202
+ const redacted = {};
203
+ for (const [key, value] of Object.entries(attributes)) {
204
+ if (this.shouldRedact(key)) {
205
+ redacted[key] = this.options.mask?.(key, value);
206
+ }
207
+ }
208
+ return redacted;
209
+ }
210
+ };
211
+ var DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
212
+ attributes: [
213
+ // HTTP security headers
214
+ /^http\.request\.header\.authorization$/i,
215
+ /^http\.request\.header\.cookie$/i,
216
+ /^http\.request\.header\.x[-_]api[-_]key$/i,
217
+ // Database statements may contain sensitive data
218
+ /^db\.statement$/i
219
+ ]
1231
220
  });
1232
- class ManualInstrumentation {
1233
- enabled;
1234
- tracer;
1235
- options;
1236
- constructor(tracer, options) {
1237
- this.enabled = false;
1238
- this.tracer = tracer;
1239
- this.options = options ?? {};
1240
- }
1241
- isEnabled() {
1242
- return this.enabled;
1243
- }
1244
- enable() {
1245
- this.enabled = true;
1246
- }
1247
- disable() {
1248
- this.enabled = false;
1249
- }
1250
- resume(ctx) {
1251
- const parts = ctx.traceparent.split('-');
1252
- if (parts.length !== 4) {
1253
- return otel.ROOT_CONTEXT;
1254
- }
1255
- const [, traceId, spanId, flags] = parts;
1256
- if (!traceId || !spanId) {
1257
- return otel.ROOT_CONTEXT;
1258
- }
1259
- const spanContext = {
1260
- traceId,
1261
- spanId,
1262
- traceFlags: parseInt(flags ?? '01', 16),
1263
- isRemote: true,
1264
- };
1265
- let context = trace.setSpanContext(otel.ROOT_CONTEXT, spanContext);
1266
- if (ctx.baggage) {
1267
- const baggageEntries = {};
1268
- for (const pair of ctx.baggage.split(',')) {
1269
- const [key, value] = pair.split('=');
1270
- if (key && value) {
1271
- baggageEntries[key] = { value: decodeURIComponent(value) };
1272
- }
1273
- }
1274
- const baggage = propagation.createBaggage(baggageEntries);
1275
- context = propagation.setBaggage(context, baggage);
1276
- }
1277
- return context;
1278
- }
1279
- error(span, error, options) {
1280
- options = options || {};
1281
- span.recordException(error);
1282
- span.setAttributes(options.attributes || {});
1283
- span.setStatus({
1284
- code: otel.SpanStatusCode.ERROR,
1285
- message: error.message || undefined,
1286
- });
1287
- span.end();
1288
- }
1289
- span(ctx, name, type, options) {
1290
- if (!this.enabled) {
1291
- return {
1292
- context: ctx,
1293
- end: (_options) => { },
1294
- fail: (_error, _options) => { },
1295
- };
1296
- }
1297
- const start = options || {};
1298
- let operation = undefined;
1299
- if (SPAN_SPECIFICATIONS[type].isGenAI) {
1300
- operation = type;
1301
- }
1302
- const span = this.tracer.startSpan(name, {
1303
- attributes: {
1304
- [ATTRIBUTES.LATITUDE.type]: type,
1305
- ...(operation && {
1306
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.operation]: operation,
1307
- }),
1308
- ...(start.attributes || {}),
1309
- },
1310
- kind: otel.SpanKind.CLIENT,
1311
- }, ctx);
1312
- const newCtx = trace.setSpan(ctx, span);
1313
- return {
1314
- context: newCtx,
1315
- end: (options) => {
1316
- const end = options || {};
1317
- span.setAttributes(end.attributes || {});
1318
- span.setStatus({ code: otel.SpanStatusCode.OK });
1319
- span.end();
1320
- },
1321
- fail: (error, options) => {
1322
- this.error(span, error, options);
1323
- },
1324
- };
1325
- }
1326
- unknown(ctx, options) {
1327
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Unknown].name, SpanType.Unknown, options);
1328
- }
1329
- tool(ctx, options) {
1330
- const start = options;
1331
- let jsonArguments = '';
1332
- try {
1333
- jsonArguments = JSON.stringify(start.call.arguments);
1334
- }
1335
- catch (_error) {
1336
- jsonArguments = '{}';
1337
- }
1338
- const span = this.span(ctx, start.name, SpanType.Tool, {
1339
- attributes: {
1340
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.name]: start.name,
1341
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.type]: VALUES.OPENTELEMETRY.GEN_AI.tool.type.function,
1342
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.id]: start.call.id,
1343
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.tool.call.arguments]: jsonArguments,
1344
- ...(start.attributes || {}),
1345
- },
1346
- });
1347
- return {
1348
- ...span,
1349
- end: (options) => {
1350
- const end = options;
1351
- let stringResult = '';
1352
- if (typeof end.result.value !== 'string') {
1353
- try {
1354
- stringResult = JSON.stringify(end.result.value);
1355
- }
1356
- catch (_error) {
1357
- stringResult = '{}';
1358
- }
1359
- }
1360
- else {
1361
- stringResult = end.result.value;
1362
- }
1363
- span.end({
1364
- attributes: {
1365
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.value]: stringResult,
1366
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.tool.result.isError]: end.result.isError,
1367
- ...(end.attributes || {}),
1368
- },
1369
- });
1370
- },
1371
- };
1372
- }
1373
- attribifyConfiguration(direction, configuration) {
1374
- const prefix = direction === 'input'
1375
- ? ATTRIBUTES.LATITUDE.request._root
1376
- : ATTRIBUTES.LATITUDE.response._root;
1377
- const attributes = {};
1378
- for (const key in configuration) {
1379
- const field = toSnakeCase(key);
1380
- let value = configuration[key];
1381
- if (value === null || value === undefined)
1382
- continue;
1383
- if (typeof value === 'object' && !Array.isArray(value)) {
1384
- try {
1385
- value = JSON.stringify(value);
1386
- }
1387
- catch (_error) {
1388
- value = '{}';
1389
- }
1390
- }
1391
- attributes[`${prefix}.${field}`] = value;
1392
- }
1393
- return attributes;
1394
- }
1395
- completion(ctx, options) {
1396
- const start = options;
1397
- const configuration = {
1398
- ...(start.configuration ?? {}),
1399
- model: start.model,
1400
- };
1401
- let jsonConfiguration = '';
1402
- try {
1403
- jsonConfiguration = JSON.stringify(configuration);
1404
- }
1405
- catch (_error) {
1406
- jsonConfiguration = '{}';
1407
- }
1408
- const attrConfiguration = this.attribifyConfiguration('input', configuration);
1409
- const input = start.input ?? [];
1410
- let jsonSystem = '';
1411
- let jsonInput = '';
1412
- try {
1413
- const translated = translator.translate(input, {
1414
- from: this.options.provider,
1415
- to: Provider.GenAI,
1416
- direction: 'input',
1417
- });
1418
- jsonSystem = JSON.stringify(translated.system ?? []);
1419
- jsonInput = JSON.stringify(translated.messages ?? []);
1420
- }
1421
- catch (_error) {
1422
- jsonSystem = '[]';
1423
- jsonInput = '[]';
1424
- }
1425
- const span = this.span(ctx, start.name || `${start.provider} / ${start.model}`, SpanType.Completion, {
1426
- attributes: {
1427
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI._deprecated.system]: start.provider,
1428
- [ATTRIBUTES.LATITUDE.request.configuration]: jsonConfiguration,
1429
- ...attrConfiguration,
1430
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.systemInstructions]: jsonSystem,
1431
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.input.messages]: jsonInput,
1432
- ...(start.attributes || {}),
1433
- [ATTRIBUTES.LATITUDE.commitUuid]: start.versionUuid,
1434
- [ATTRIBUTES.LATITUDE.documentUuid]: start.promptUuid,
1435
- [ATTRIBUTES.LATITUDE.experimentUuid]: start.experimentUuid,
1436
- },
1437
- });
1438
- return {
1439
- ...span,
1440
- end: (options) => {
1441
- const end = options ?? {};
1442
- const output = end.output ?? [];
1443
- let jsonOutput = '';
1444
- try {
1445
- const translated = translator.translate(output, {
1446
- from: this.options.provider,
1447
- to: Provider.GenAI,
1448
- direction: 'output',
1449
- });
1450
- jsonOutput = JSON.stringify(translated.messages ?? []);
1451
- }
1452
- catch (_error) {
1453
- jsonOutput = '[]';
1454
- }
1455
- const tokens = {
1456
- prompt: end.tokens?.prompt ?? 0,
1457
- cached: end.tokens?.cached ?? 0,
1458
- reasoning: end.tokens?.reasoning ?? 0,
1459
- completion: end.tokens?.completion ?? 0,
1460
- };
1461
- const inputTokens = tokens.prompt + tokens.cached;
1462
- const outputTokens = tokens.reasoning + tokens.completion;
1463
- const finishReason = end.finishReason ?? '';
1464
- span.end({
1465
- attributes: {
1466
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.output.messages]: jsonOutput,
1467
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.inputTokens]: inputTokens,
1468
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.usage.outputTokens]: outputTokens,
1469
- [ATTRIBUTES.LATITUDE.usage.promptTokens]: tokens.prompt,
1470
- [ATTRIBUTES.LATITUDE.usage.cachedTokens]: tokens.cached,
1471
- [ATTRIBUTES.LATITUDE.usage.reasoningTokens]: tokens.reasoning,
1472
- [ATTRIBUTES.LATITUDE.usage.completionTokens]: tokens.completion,
1473
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.model]: start.model,
1474
- [ATTRIBUTES.OPENTELEMETRY.GEN_AI.response.finishReasons]: [
1475
- finishReason,
1476
- ],
1477
- ...(end.attributes || {}),
1478
- },
1479
- });
1480
- },
1481
- };
1482
- }
1483
- embedding(ctx, options) {
1484
- return this.span(ctx, options?.name || SPAN_SPECIFICATIONS[SpanType.Embedding].name, SpanType.Embedding, options);
1485
- }
1486
- attribifyHeaders(direction, headers) {
1487
- const prefix = direction === 'request'
1488
- ? ATTRIBUTES.OPENTELEMETRY.HTTP.request.header
1489
- : ATTRIBUTES.OPENTELEMETRY.HTTP.response.header;
1490
- const attributes = {};
1491
- for (const key in headers) {
1492
- const field = toKebabCase(key);
1493
- const value = headers[key];
1494
- if (value === null || value === undefined)
1495
- continue;
1496
- attributes[`${prefix}.${field}`] = value;
1497
- }
1498
- return attributes;
1499
- }
1500
- http(ctx, options) {
1501
- const start = options;
1502
- const method = start.request.method.toUpperCase();
1503
- // Note: do not serialize headers as a single attribute because fields won't be redacted
1504
- const attrHeaders = this.attribifyHeaders('request', start.request.headers);
1505
- let finalBody = '';
1506
- if (typeof start.request.body === 'string') {
1507
- finalBody = start.request.body;
1508
- }
1509
- else {
1510
- try {
1511
- finalBody = JSON.stringify(start.request.body);
1512
- }
1513
- catch (_error) {
1514
- finalBody = '{}';
1515
- }
1516
- }
1517
- const span = this.span(ctx, start.name || `${method} ${start.request.url}`, SpanType.Http, {
1518
- attributes: {
1519
- [ATTRIBUTES.OPENTELEMETRY.HTTP.request.method]: method,
1520
- [ATTRIBUTES.OPENTELEMETRY.HTTP.request.url]: start.request.url,
1521
- ...attrHeaders,
1522
- [ATTRIBUTES.OPENTELEMETRY.HTTP.request.body]: finalBody,
1523
- ...(start.attributes || {}),
1524
- },
1525
- });
1526
- return {
1527
- ...span,
1528
- end: (options) => {
1529
- const end = options;
1530
- // Note: do not serialize headers as a single attribute because fields won't be redacted
1531
- const attrHeaders = this.attribifyHeaders('response', end.response.headers);
1532
- let finalBody = '';
1533
- if (typeof end.response.body === 'string') {
1534
- finalBody = end.response.body;
1535
- }
1536
- else {
1537
- try {
1538
- finalBody = JSON.stringify(end.response.body);
1539
- }
1540
- catch (_error) {
1541
- finalBody = '{}';
1542
- }
1543
- }
1544
- span.end({
1545
- attributes: {
1546
- [ATTRIBUTES.OPENTELEMETRY.HTTP.response.statusCode]: end.response.status,
1547
- ...attrHeaders,
1548
- [ATTRIBUTES.OPENTELEMETRY.HTTP.response.body]: finalBody,
1549
- ...(end.attributes || {}),
1550
- },
1551
- });
1552
- },
1553
- };
1554
- }
1555
- prompt(ctx, { documentLogUuid, versionUuid, promptUuid, projectId, experimentUuid, testDeploymentId, externalId, template, parameters, name, source, ...rest }) {
1556
- let jsonParameters = '';
1557
- try {
1558
- jsonParameters = JSON.stringify(parameters || {});
1559
- }
1560
- catch (_error) {
1561
- jsonParameters = '{}';
1562
- }
1563
- const attributes = {
1564
- [ATTRIBUTES.LATITUDE.request.template]: template,
1565
- [ATTRIBUTES.LATITUDE.request.parameters]: jsonParameters,
1566
- [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid || HEAD_COMMIT,
1567
- [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1568
- [ATTRIBUTES.LATITUDE.projectId]: projectId,
1569
- [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1570
- ...(experimentUuid && {
1571
- [ATTRIBUTES.LATITUDE.experimentUuid]: experimentUuid,
1572
- }),
1573
- ...(testDeploymentId && {
1574
- [ATTRIBUTES.LATITUDE.testDeploymentId]: testDeploymentId,
1575
- }),
1576
- ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1577
- ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1578
- ...(rest.attributes || {}),
1579
- };
1580
- return this.span(ctx, name || `prompt-${promptUuid}`, SpanType.Prompt, {
1581
- attributes,
1582
- });
1583
- }
1584
- chat(ctx, { documentLogUuid, previousTraceId, source, name, versionUuid, promptUuid, ...rest }) {
1585
- const attributes = {
1586
- [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1587
- [ATTRIBUTES.LATITUDE.previousTraceId]: previousTraceId,
1588
- ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1589
- ...(promptUuid && { [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid }),
1590
- ...(source && { [ATTRIBUTES.LATITUDE.source]: source }),
1591
- ...(rest.attributes || {}),
1592
- };
1593
- return this.span(ctx, name || `chat-${documentLogUuid}`, SpanType.Chat, {
1594
- attributes,
1595
- });
1596
- }
1597
- external(ctx, { promptUuid, documentLogUuid, source, versionUuid, externalId, name, ...rest }) {
1598
- const attributes = {
1599
- [ATTRIBUTES.LATITUDE.documentUuid]: promptUuid,
1600
- [ATTRIBUTES.LATITUDE.documentLogUuid]: documentLogUuid,
1601
- [ATTRIBUTES.LATITUDE.source]: source ?? LogSources.API,
1602
- ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1603
- ...(externalId && { [ATTRIBUTES.LATITUDE.externalId]: externalId }),
1604
- ...(rest.attributes || {}),
1605
- };
1606
- return this.span(ctx, name || `external-${promptUuid}`, SpanType.External, {
1607
- attributes,
1608
- });
1609
- }
1610
- // TODO(tracing): deprecate
1611
- unresolvedExternal(ctx, { path, projectId, versionUuid, conversationUuid, name, ...rest }) {
1612
- const attributes = {
1613
- [ATTRIBUTES.LATITUDE.promptPath]: path,
1614
- [ATTRIBUTES.LATITUDE.projectId]: projectId,
1615
- ...(versionUuid && { [ATTRIBUTES.LATITUDE.commitUuid]: versionUuid }),
1616
- ...(conversationUuid && {
1617
- [ATTRIBUTES.LATITUDE.documentLogUuid]: conversationUuid,
1618
- }),
1619
- ...(rest.attributes || {}),
1620
- };
1621
- return this.span(ctx, name || `capture-${path}`, SpanType.UnresolvedExternal, { attributes });
1622
- }
1623
- }
1624
-
1625
- class LatitudeInstrumentation {
1626
- options;
1627
- manualTelemetry;
1628
- constructor(tracer, options) {
1629
- this.manualTelemetry = new ManualInstrumentation(tracer);
1630
- this.options = options;
1631
- }
1632
- isEnabled() {
1633
- return this.manualTelemetry.isEnabled();
1634
- }
1635
- enable() {
1636
- this.manualTelemetry.enable();
1637
- this.options.module.instrument(this);
1638
- }
1639
- disable() {
1640
- this.manualTelemetry.disable();
1641
- this.options.module.uninstrument();
1642
- }
1643
- countTokens(messages) {
1644
- let length = 0;
1645
- for (const message of messages) {
1646
- if (!('content' in message))
1647
- continue;
1648
- if (typeof message.content === 'string') {
1649
- length += message.content.length;
1650
- }
1651
- else if (Array.isArray(message.content)) {
1652
- for (const content of message.content) {
1653
- if (content.type === 'text') {
1654
- length += content.text.length;
1655
- }
1656
- }
1657
- }
1658
- }
1659
- // Note: this is an estimation to not bundle a tokenizer
1660
- return Math.ceil(length / 4);
1661
- }
1662
- async wrapRenderChain(fn, ...args) {
1663
- const { prompt, parameters } = args[0];
1664
- const $prompt = this.manualTelemetry.prompt(context.active(), {
1665
- documentLogUuid: v4(),
1666
- versionUuid: prompt.versionUuid,
1667
- promptUuid: prompt.uuid,
1668
- template: prompt.content,
1669
- parameters: parameters,
1670
- });
1671
- let result;
1672
- try {
1673
- result = await context.with($prompt.context, async () => await fn(...args));
1674
- }
1675
- catch (error) {
1676
- $prompt.fail(error);
1677
- throw error;
1678
- }
1679
- $prompt.end();
1680
- return result;
1681
- }
1682
- async wrapRenderCompletion(fn, ...args) {
1683
- if (!this.options.completions) {
1684
- return await fn(...args);
1685
- }
1686
- const { provider, config, messages } = args[0];
1687
- const model = config.model || 'unknown';
1688
- const $completion = this.manualTelemetry.completion(context.active(), {
1689
- name: `${provider} / ${model}`,
1690
- provider: provider,
1691
- model: model,
1692
- configuration: config,
1693
- input: messages,
1694
- });
1695
- let result;
1696
- try {
1697
- result = await context.with($completion.context, async () => await fn(...args));
1698
- }
1699
- catch (error) {
1700
- $completion.fail(error);
1701
- throw error;
1702
- }
1703
- // Note: enhance, this is just an estimation
1704
- const promptTokens = this.countTokens(messages);
1705
- const completionTokens = this.countTokens(result.messages);
1706
- $completion.end({
1707
- output: result.messages,
1708
- tokens: {
1709
- prompt: promptTokens,
1710
- cached: 0,
1711
- reasoning: 0,
1712
- completion: completionTokens,
1713
- },
1714
- finishReason: result.toolRequests.length > 0
1715
- ? VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.toolCalls
1716
- : VALUES.OPENTELEMETRY.GEN_AI.response.finishReasons.stop,
1717
- });
1718
- return result;
1719
- }
1720
- async wrapRenderTool(fn, ...args) {
1721
- const { toolRequest } = args[0];
1722
- const $tool = this.manualTelemetry.tool(context.active(), {
1723
- name: toolRequest.toolName,
1724
- call: {
1725
- id: toolRequest.toolCallId,
1726
- arguments: toolRequest.toolArguments,
1727
- },
1728
- });
1729
- let result;
1730
- try {
1731
- result = await context.with($tool.context, async () => await fn(...args));
1732
- }
1733
- catch (error) {
1734
- $tool.fail(error);
1735
- throw error;
1736
- }
1737
- $tool.end({
1738
- result: {
1739
- value: result.result,
1740
- isError: result.isError,
1741
- },
1742
- });
1743
- return result;
1744
- }
1745
- }
1746
221
 
1747
- var LatitudeErrorCodes;
1748
- (function (LatitudeErrorCodes) {
1749
- LatitudeErrorCodes["UnexpectedError"] = "UnexpectedError";
1750
- LatitudeErrorCodes["OverloadedError"] = "OverloadedError";
1751
- LatitudeErrorCodes["RateLimitError"] = "RateLimitError";
1752
- LatitudeErrorCodes["UnauthorizedError"] = "UnauthorizedError";
1753
- LatitudeErrorCodes["ForbiddenError"] = "ForbiddenError";
1754
- LatitudeErrorCodes["BadRequestError"] = "BadRequestError";
1755
- LatitudeErrorCodes["NotFoundError"] = "NotFoundError";
1756
- LatitudeErrorCodes["ConflictError"] = "ConflictError";
1757
- LatitudeErrorCodes["UnprocessableEntityError"] = "UnprocessableEntityError";
1758
- LatitudeErrorCodes["NotImplementedError"] = "NotImplementedError";
1759
- LatitudeErrorCodes["PaymentRequiredError"] = "PaymentRequiredError";
1760
- LatitudeErrorCodes["AbortedError"] = "AbortedError";
1761
- LatitudeErrorCodes["BillingError"] = "BillingError";
1762
- })(LatitudeErrorCodes || (LatitudeErrorCodes = {}));
1763
- // NOTE: If you add a new error code, please add it to the pg enum in models/runErrors.ts
1764
- var RunErrorCodes;
1765
- (function (RunErrorCodes) {
1766
- RunErrorCodes["AIProviderConfigError"] = "ai_provider_config_error";
1767
- RunErrorCodes["AIRunError"] = "ai_run_error";
1768
- RunErrorCodes["ChainCompileError"] = "chain_compile_error";
1769
- RunErrorCodes["DefaultProviderExceededQuota"] = "default_provider_exceeded_quota_error";
1770
- RunErrorCodes["DefaultProviderInvalidModel"] = "default_provider_invalid_model_error";
1771
- RunErrorCodes["DocumentConfigError"] = "document_config_error";
1772
- RunErrorCodes["ErrorGeneratingMockToolResult"] = "error_generating_mock_tool_result";
1773
- RunErrorCodes["FailedToWakeUpIntegrationError"] = "failed_to_wake_up_integration_error";
1774
- RunErrorCodes["InvalidResponseFormatError"] = "invalid_response_format_error";
1775
- RunErrorCodes["MaxStepCountExceededError"] = "max_step_count_exceeded_error";
1776
- RunErrorCodes["MissingProvider"] = "missing_provider_error";
1777
- RunErrorCodes["RateLimit"] = "rate_limit_error";
1778
- RunErrorCodes["Unknown"] = "unknown_error";
1779
- RunErrorCodes["UnsupportedProviderResponseTypeError"] = "unsupported_provider_response_type_error";
1780
- RunErrorCodes["PaymentRequiredError"] = "payment_required_error";
1781
- RunErrorCodes["AbortError"] = "abort_error";
1782
- // DEPRECATED, but do not delete
1783
- RunErrorCodes["EvaluationRunMissingProviderLogError"] = "ev_run_missing_provider_log_error";
1784
- RunErrorCodes["EvaluationRunMissingWorkspaceError"] = "ev_run_missing_workspace_error";
1785
- RunErrorCodes["EvaluationRunResponseJsonFormatError"] = "ev_run_response_json_format_error";
1786
- RunErrorCodes["EvaluationRunUnsupportedResultTypeError"] = "ev_run_unsupported_result_type_error";
1787
- })(RunErrorCodes || (RunErrorCodes = {}));
1788
- var ApiErrorCodes;
1789
- (function (ApiErrorCodes) {
1790
- ApiErrorCodes["HTTPException"] = "http_exception";
1791
- ApiErrorCodes["InternalServerError"] = "internal_server_error";
1792
- })(ApiErrorCodes || (ApiErrorCodes = {}));
1793
-
1794
- class LatitudeError extends Error {
1795
- statusCode = 500;
1796
- name = LatitudeErrorCodes.UnexpectedError;
1797
- headers = {};
1798
- details;
1799
- constructor(message, details, status, name) {
1800
- super(message);
1801
- this.details = details ?? {};
1802
- this.statusCode = status ?? this.statusCode;
1803
- this.name = name ?? this.constructor.name;
1804
- }
1805
- serialize() {
1806
- return {
1807
- name: this.name,
1808
- code: this.name,
1809
- status: this.statusCode,
1810
- message: this.message,
1811
- details: this.details,
1812
- };
1813
- }
1814
- static deserialize(json) {
1815
- return new LatitudeError(json.message, json.details, json.status, json.name);
1816
- }
222
+ // src/sdk/span-filter.ts
223
+ var GEN_AI_PREFIX = "gen_ai.";
224
+ var LLM_PREFIX = "llm.";
225
+ var OPENINFERENCE_KIND = "openinference.span.kind";
226
+ var OTEL_LLM_INSTRUMENTATION_SCOPE_PREFIXES = [
227
+ "opentelemetry.instrumentation.alephalpha",
228
+ "opentelemetry.instrumentation.anthropic",
229
+ "opentelemetry.instrumentation.bedrock",
230
+ "opentelemetry.instrumentation.cohere",
231
+ "opentelemetry.instrumentation.crewai",
232
+ "opentelemetry.instrumentation.google_generativeai",
233
+ "opentelemetry.instrumentation.groq",
234
+ "opentelemetry.instrumentation.haystack",
235
+ "opentelemetry.instrumentation.langchain",
236
+ "opentelemetry.instrumentation.llamaindex",
237
+ "opentelemetry.instrumentation.mistralai",
238
+ "opentelemetry.instrumentation.ollama",
239
+ "opentelemetry.instrumentation.openai",
240
+ "opentelemetry.instrumentation.replicate",
241
+ "opentelemetry.instrumentation.sagemaker",
242
+ "opentelemetry.instrumentation.together",
243
+ "opentelemetry.instrumentation.transformers",
244
+ "opentelemetry.instrumentation.vertexai",
245
+ "opentelemetry.instrumentation.watsonx",
246
+ "openinference.instrumentation"
247
+ ];
248
+ var LLM_SCOPE_SUBSTRINGS = ["openinference", "traceloop", "langsmith", "litellm"];
249
+ function buildShouldExportSpanFromFields(fields) {
250
+ return buildShouldExportSpan({
251
+ ...fields.disableSmartFilter !== void 0 ? { disableSmartFilter: fields.disableSmartFilter } : {},
252
+ ...fields.shouldExportSpan !== void 0 ? { shouldExportSpan: fields.shouldExportSpan } : {},
253
+ ...fields.blockedInstrumentationScopes !== void 0 ? { blockedInstrumentationScopes: fields.blockedInstrumentationScopes } : {}
254
+ });
1817
255
  }
1818
- class BadRequestError extends LatitudeError {
1819
- statusCode = 400;
1820
- name = LatitudeErrorCodes.BadRequestError;
256
+ function attributeKeys(span) {
257
+ const attrs = span.attributes;
258
+ if (!attrs || typeof attrs !== "object") return [];
259
+ return Object.keys(attrs);
1821
260
  }
1822
-
1823
- const TRACES_URL = `${env.GATEWAY_BASE_URL}/api/v3/traces`;
1824
- const SERVICE_NAME = process.env.npm_package_name || 'unknown';
1825
- const SCOPE_VERSION = process.env.npm_package_version || 'unknown';
1826
- const BACKGROUND = () => otel.ROOT_CONTEXT;
1827
- class SpanFactory {
1828
- telemetry;
1829
- constructor(telemetry) {
1830
- this.telemetry = telemetry;
1831
- }
1832
- span(options, ctx) {
1833
- return this.telemetry.unknown(ctx ?? context.active(), options);
1834
- }
1835
- tool(options, ctx) {
1836
- return this.telemetry.tool(ctx ?? context.active(), options);
1837
- }
1838
- completion(options, ctx) {
1839
- return this.telemetry.completion(ctx ?? context.active(), options);
1840
- }
1841
- embedding(options, ctx) {
1842
- return this.telemetry.embedding(ctx ?? context.active(), options);
1843
- }
1844
- http(options, ctx) {
1845
- return this.telemetry.http(ctx ?? context.active(), options);
1846
- }
1847
- prompt(options, ctx) {
1848
- return this.telemetry.prompt(ctx ?? context.active(), options);
1849
- }
1850
- chat(options, ctx) {
1851
- return this.telemetry.chat(ctx ?? context.active(), options);
1852
- }
1853
- external(options, ctx) {
1854
- return this.telemetry.external(ctx ?? context.active(), options);
1855
- }
261
+ function instrumentationScopeName(span) {
262
+ return span.instrumentationLibrary?.name ?? "";
1856
263
  }
1857
- class ContextManager {
1858
- telemetry;
1859
- constructor(telemetry) {
1860
- this.telemetry = telemetry;
1861
- }
1862
- resume(ctx) {
1863
- return this.telemetry.resume(ctx);
1864
- }
1865
- active() {
1866
- return context.active();
1867
- }
1868
- with(ctx, fn, thisArg, ...args) {
1869
- return context.with(ctx, fn, thisArg, ...args);
1870
- }
264
+ function isGenAiOrLlmAttributeSpan(span) {
265
+ for (const key of attributeKeys(span)) {
266
+ if (key.startsWith(GEN_AI_PREFIX) || key.startsWith(LLM_PREFIX)) return true;
267
+ if (key === OPENINFERENCE_KIND || key.startsWith("openinference.")) return true;
268
+ if (key.startsWith("ai.")) return true;
269
+ if (key.startsWith("latitude.")) return true;
270
+ }
271
+ return false;
1871
272
  }
1872
- class InstrumentationManager {
1873
- instrumentations;
1874
- constructor(instrumentations) {
1875
- this.instrumentations = instrumentations;
1876
- }
1877
- enable() {
1878
- this.instrumentations.forEach((instrumentation) => {
1879
- if (!instrumentation.isEnabled())
1880
- instrumentation.enable();
1881
- });
1882
- }
1883
- disable() {
1884
- this.instrumentations.forEach((instrumentation) => {
1885
- if (instrumentation.isEnabled())
1886
- instrumentation.disable();
1887
- });
1888
- }
273
+ function isLatitudeInstrumentationSpan(span) {
274
+ const name = instrumentationScopeName(span);
275
+ return name === SCOPE_LATITUDE || name.startsWith(`${SCOPE_LATITUDE}.`);
1889
276
  }
1890
- class TracerManager {
1891
- nodeProvider;
1892
- scopeVersion;
1893
- constructor(nodeProvider, scopeVersion) {
1894
- this.nodeProvider = nodeProvider;
1895
- this.scopeVersion = scopeVersion;
1896
- }
1897
- get(scope) {
1898
- return this.provider(scope).getTracer('');
1899
- }
1900
- provider(scope) {
1901
- return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${scope}`, this.scopeVersion, this.nodeProvider);
1902
- }
277
+ function isKnownLlmInstrumentationScope(span) {
278
+ const name = instrumentationScopeName(span);
279
+ if (!name) return false;
280
+ for (const prefix of OTEL_LLM_INSTRUMENTATION_SCOPE_PREFIXES) {
281
+ if (name === prefix || name.startsWith(`${prefix}.`)) return true;
282
+ }
283
+ const lower = name.toLowerCase();
284
+ for (const part of LLM_SCOPE_SUBSTRINGS) {
285
+ if (lower.includes(part)) return true;
286
+ }
287
+ return false;
1903
288
  }
1904
- class ScopedTracerProvider {
1905
- scope;
1906
- version;
1907
- provider;
1908
- constructor(scope, version, provider) {
1909
- this.scope = scope;
1910
- this.version = version;
1911
- this.provider = provider;
1912
- }
1913
- getTracer(_name, _version, options) {
1914
- return this.provider.getTracer(this.scope, this.version, options);
1915
- }
289
+ function isDefaultExportSpan(span) {
290
+ if (isLatitudeInstrumentationSpan(span)) return true;
291
+ if (isGenAiOrLlmAttributeSpan(span)) return true;
292
+ if (isKnownLlmInstrumentationScope(span)) return true;
293
+ return false;
1916
294
  }
1917
- class LifecycleManager {
1918
- nodeProvider;
1919
- exporter;
1920
- constructor(nodeProvider, exporter) {
1921
- this.nodeProvider = nodeProvider;
1922
- this.exporter = exporter;
1923
- }
1924
- async flush() {
1925
- await this.nodeProvider.forceFlush();
1926
- await this.exporter.forceFlush?.();
1927
- }
1928
- async shutdown() {
1929
- await this.flush();
1930
- await this.nodeProvider.shutdown();
1931
- await this.exporter.shutdown?.();
1932
- }
295
+ function buildShouldExportSpan(options) {
296
+ if (options.disableSmartFilter) return () => true;
297
+ const blocked = new Set(options.blockedInstrumentationScopes ?? []);
298
+ const extra = options.shouldExportSpan;
299
+ return (span) => {
300
+ const scope = instrumentationScopeName(span);
301
+ if (blocked.has(scope)) return false;
302
+ if (isDefaultExportSpan(span)) return true;
303
+ if (extra?.(span)) return true;
304
+ return false;
305
+ };
1933
306
  }
1934
- const DEFAULT_SPAN_EXPORTER = (apiKey) => new OTLPTraceExporter({
1935
- url: TRACES_URL,
1936
- headers: {
307
+ var ExportFilterSpanProcessor = class {
308
+ shouldExport;
309
+ inner;
310
+ constructor(shouldExport, inner) {
311
+ this.shouldExport = shouldExport;
312
+ this.inner = inner;
313
+ }
314
+ onStart(span, parentContext) {
315
+ this.inner.onStart(span, parentContext);
316
+ }
317
+ onEnd(span) {
318
+ if (!this.shouldExport(span)) return;
319
+ this.inner.onEnd(span);
320
+ }
321
+ forceFlush() {
322
+ return this.inner.forceFlush();
323
+ }
324
+ shutdown() {
325
+ return this.inner.shutdown();
326
+ }
327
+ };
328
+ var RedactThenExportSpanProcessor = class {
329
+ redact;
330
+ exportProcessor;
331
+ constructor(redact, exportProcessor) {
332
+ this.redact = redact;
333
+ this.exportProcessor = exportProcessor;
334
+ }
335
+ onStart(span, parentContext) {
336
+ this.redact?.onStart(span, parentContext);
337
+ this.exportProcessor.onStart(span, parentContext);
338
+ }
339
+ onEnd(span) {
340
+ this.redact?.onEnd(span);
341
+ this.exportProcessor.onEnd(span);
342
+ }
343
+ forceFlush() {
344
+ return this.exportProcessor.forceFlush();
345
+ }
346
+ shutdown() {
347
+ return this.exportProcessor.shutdown();
348
+ }
349
+ };
350
+
351
+ // src/sdk/processor.ts
352
+ var LatitudeSpanProcessor = class {
353
+ tail;
354
+ constructor(apiKey, projectSlug, options) {
355
+ if (!apiKey || apiKey.trim() === "") {
356
+ throw new Error("[Latitude] apiKey is required and cannot be empty");
357
+ }
358
+ if (!projectSlug || projectSlug.trim() === "") {
359
+ throw new Error("[Latitude] projectSlug is required and cannot be empty");
360
+ }
361
+ const exporter = options?.exporter ?? new OTLPTraceExporter({
362
+ url: `${env.EXPORTER_URL}/v1/traces`,
363
+ headers: {
1937
364
  Authorization: `Bearer ${apiKey}`,
1938
- 'Content-Type': 'application/json',
1939
- },
1940
- timeoutMillis: 30 * 1000,
1941
- });
1942
- // Note: Only exporting typescript instrumentations
1943
- var Instrumentation;
1944
- (function (Instrumentation) {
1945
- Instrumentation["Anthropic"] = "anthropic";
1946
- Instrumentation["AIPlatform"] = "aiplatform";
1947
- Instrumentation["Bedrock"] = "bedrock";
1948
- Instrumentation["Cohere"] = "cohere";
1949
- Instrumentation["Langchain"] = "langchain";
1950
- Instrumentation["Latitude"] = "latitude";
1951
- Instrumentation["LlamaIndex"] = "llamaindex";
1952
- Instrumentation["Manual"] = "manual";
1953
- Instrumentation["OpenAI"] = "openai";
1954
- Instrumentation["TogetherAI"] = "togetherai";
1955
- Instrumentation["VertexAI"] = "vertexai";
1956
- })(Instrumentation || (Instrumentation = {}));
1957
- class LatitudeTelemetry {
1958
- options;
1959
- nodeProvider;
1960
- manualInstrumentation;
1961
- instrumentationsList;
1962
- span;
1963
- context;
1964
- instrumentation;
1965
- tracer;
1966
- lifecycle;
1967
- constructor(apiKey, options) {
1968
- this.options = options || {};
1969
- if (!this.options.exporter) {
1970
- this.options.exporter = DEFAULT_SPAN_EXPORTER(apiKey);
1971
- }
1972
- context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable());
1973
- propagation.setGlobalPropagator(new CompositePropagator({
1974
- propagators: [
1975
- ...(this.options.propagators || []),
1976
- new W3CTraceContextPropagator(),
1977
- new W3CBaggagePropagator(),
1978
- ],
1979
- }));
1980
- this.nodeProvider = new NodeTracerProvider({
1981
- resource: new Resource({ [ATTR_SERVICE_NAME]: SERVICE_NAME }),
1982
- });
1983
- this.lifecycle = new LifecycleManager(this.nodeProvider, this.options.exporter);
1984
- // Note: important, must run before the exporter span processors
1985
- this.nodeProvider.addSpanProcessor(new BaggageSpanProcessor(ALLOW_ALL_BAGGAGE_KEYS));
1986
- if (this.options.processors) {
1987
- this.options.processors.forEach((processor) => {
1988
- this.nodeProvider.addSpanProcessor(processor);
1989
- });
1990
- }
1991
- else {
1992
- this.nodeProvider.addSpanProcessor(DEFAULT_REDACT_SPAN_PROCESSOR());
1993
- }
1994
- if (this.options.disableBatch) {
1995
- this.nodeProvider.addSpanProcessor(new SimpleSpanProcessor(this.options.exporter));
1996
- }
1997
- else {
1998
- this.nodeProvider.addSpanProcessor(new BatchSpanProcessor(this.options.exporter));
1999
- }
2000
- this.nodeProvider.register();
2001
- process.on('SIGTERM', async () => this.shutdown);
2002
- process.on('SIGINT', async () => this.shutdown);
2003
- this.manualInstrumentation = null;
2004
- this.instrumentationsList = [];
2005
- this.tracer = new TracerManager(this.nodeProvider, SCOPE_VERSION);
2006
- this.initInstrumentations();
2007
- this.instrumentation = new InstrumentationManager(this.instrumentationsList);
2008
- this.instrumentation.enable();
2009
- this.span = new SpanFactory(this.manualInstrumentation);
2010
- this.context = new ContextManager(this.manualInstrumentation);
2011
- }
2012
- async flush() {
2013
- await this.lifecycle.flush();
2014
- }
2015
- async shutdown() {
2016
- await this.lifecycle.shutdown();
2017
- }
2018
- // TODO(tracing): auto instrument outgoing HTTP requests
2019
- initInstrumentations() {
2020
- this.instrumentationsList = [];
2021
- const tracer = this.tracer.get(Instrumentation.Manual);
2022
- this.manualInstrumentation = new ManualInstrumentation(tracer, this.options.instrumentations?.manual);
2023
- this.instrumentationsList.push(this.manualInstrumentation);
2024
- const latitude = this.options.instrumentations?.latitude;
2025
- if (latitude) {
2026
- const tracer = this.tracer.get(Instrumentation.Latitude);
2027
- const instrumentation = new LatitudeInstrumentation(tracer, typeof latitude === 'object' ? latitude : { module: latitude });
2028
- this.instrumentationsList.push(instrumentation);
2029
- }
2030
- const configureInstrumentation = (instrumentationType, InstrumentationConstructor, instrumentationOptions) => {
2031
- const providerPkg = this.options.instrumentations?.[instrumentationType];
2032
- if (!providerPkg)
2033
- return;
2034
- const provider = this.tracer.provider(instrumentationType);
2035
- const instrumentation = new InstrumentationConstructor(instrumentationOptions); // prettier-ignore
2036
- instrumentation.setTracerProvider(provider);
2037
- instrumentation.manuallyInstrument(providerPkg);
2038
- registerInstrumentations({
2039
- instrumentations: [instrumentation],
2040
- tracerProvider: provider,
2041
- });
2042
- this.instrumentationsList.push(instrumentation);
2043
- };
2044
- configureInstrumentation(Instrumentation.Anthropic, AnthropicInstrumentation); // prettier-ignore
2045
- configureInstrumentation(Instrumentation.AIPlatform, AIPlatformInstrumentation); // prettier-ignore
2046
- configureInstrumentation(Instrumentation.Bedrock, BedrockInstrumentation); // prettier-ignore
2047
- configureInstrumentation(Instrumentation.Cohere, CohereInstrumentation); // prettier-ignore
2048
- configureInstrumentation(Instrumentation.Langchain, LangChainInstrumentation); // prettier-ignore
2049
- configureInstrumentation(Instrumentation.LlamaIndex, LlamaIndexInstrumentation); // prettier-ignore
2050
- // NOTE: `stream: true` in OpenAI make enrichTokens fail, so disabling.
2051
- configureInstrumentation(Instrumentation.OpenAI, OpenAIInstrumentation, { enrichTokens: false }); // prettier-ignore
2052
- configureInstrumentation(Instrumentation.TogetherAI, TogetherInstrumentation, { enrichTokens: false }); // prettier-ignore
2053
- configureInstrumentation(Instrumentation.VertexAI, VertexAIInstrumentation); // prettier-ignore
2054
- }
2055
- async capture(options, fn) {
2056
- if (!DOCUMENT_PATH_REGEXP.test(options.path)) {
2057
- throw new BadRequestError("Invalid path, no spaces. Only letters, numbers, '.', '-' and '_'");
2058
- }
2059
- const captureBaggageEntries = {
2060
- [ATTRIBUTES.LATITUDE.promptPath]: { value: options.path },
2061
- [ATTRIBUTES.LATITUDE.projectId]: { value: String(options.projectId) },
2062
- };
2063
- if (options.versionUuid) {
2064
- captureBaggageEntries[ATTRIBUTES.LATITUDE.commitUuid] = {
2065
- value: options.versionUuid,
2066
- };
2067
- }
2068
- if (options.conversationUuid) {
2069
- captureBaggageEntries[ATTRIBUTES.LATITUDE.documentLogUuid] = {
2070
- value: options.conversationUuid,
2071
- };
2072
- }
2073
- const captureContext = propagation.setBaggage(BACKGROUND(), propagation.createBaggage(captureBaggageEntries));
2074
- const span = this.manualInstrumentation.unresolvedExternal(captureContext, options);
2075
- let result;
2076
- try {
2077
- result = await context.with(span.context, async () => await fn(span.context));
2078
- }
2079
- catch (error) {
2080
- span.fail(error);
2081
- throw error;
2082
- }
2083
- span.end();
2084
- return result;
2085
- }
2086
- }
365
+ "Content-Type": "application/json",
366
+ "X-Latitude-Project": projectSlug
367
+ },
368
+ timeoutMillis: 3e4
369
+ });
370
+ const redact = options?.disableRedact ? null : options?.redact ? new RedactSpanProcessor(options.redact) : DEFAULT_REDACT_SPAN_PROCESSOR();
371
+ const batchOrSimple = options?.disableBatch ? new SimpleSpanProcessor(exporter) : new BatchSpanProcessor(exporter);
372
+ const shouldExport = buildShouldExportSpanFromFields({
373
+ disableSmartFilter: options?.disableSmartFilter,
374
+ shouldExportSpan: options?.shouldExportSpan,
375
+ blockedInstrumentationScopes: options?.blockedInstrumentationScopes
376
+ });
377
+ const redactThenExport = new RedactThenExportSpanProcessor(redact, batchOrSimple);
378
+ this.tail = new ExportFilterSpanProcessor(shouldExport, redactThenExport);
379
+ }
380
+ onStart(span, parentContext) {
381
+ const latitudeData = getLatitudeContext(parentContext);
382
+ if (latitudeData) {
383
+ if (latitudeData.name) {
384
+ span.setAttribute(ATTRIBUTES.name, latitudeData.name);
385
+ if (span.attributes["latitude.capture.root"]) {
386
+ span.updateName(latitudeData.name);
387
+ }
388
+ }
389
+ if (latitudeData.tags && latitudeData.tags.length > 0) {
390
+ span.setAttribute(ATTRIBUTES.tags, JSON.stringify(latitudeData.tags));
391
+ }
392
+ if (latitudeData.metadata && Object.keys(latitudeData.metadata).length > 0) {
393
+ span.setAttribute(ATTRIBUTES.metadata, JSON.stringify(latitudeData.metadata));
394
+ }
395
+ if (latitudeData.sessionId) {
396
+ span.setAttribute(ATTRIBUTES.sessionId, latitudeData.sessionId);
397
+ }
398
+ if (latitudeData.userId) {
399
+ span.setAttribute(ATTRIBUTES.userId, latitudeData.userId);
400
+ }
401
+ }
402
+ this.tail.onStart(span, parentContext);
403
+ }
404
+ onEnd(span) {
405
+ this.tail.onEnd(span);
406
+ }
407
+ async forceFlush() {
408
+ await this.tail.forceFlush();
409
+ }
410
+ async shutdown() {
411
+ await this.tail.shutdown();
412
+ }
413
+ };
2087
414
 
2088
- export { BACKGROUND, DEFAULT_REDACT_SPAN_PROCESSOR, DEFAULT_SPAN_EXPORTER, Instrumentation, LatitudeTelemetry, RedactSpanProcessor };
2089
- //# sourceMappingURL=index.js.map
415
+ // src/sdk/init.ts
416
+ var SERVICE_NAME = process.env.npm_package_name || "unknown";
417
+ var shutdownHandlersRegistered = false;
418
+ function initLatitude(options) {
419
+ const { apiKey, projectSlug, instrumentations = [], ...processorOptions } = options;
420
+ if (!apiKey || apiKey.trim() === "") {
421
+ throw new Error("[Latitude] apiKey is required and cannot be empty");
422
+ }
423
+ if (!projectSlug || projectSlug.trim() === "") {
424
+ throw new Error("[Latitude] projectSlug is required and cannot be empty");
425
+ }
426
+ const contextManager = new AsyncLocalStorageContextManager();
427
+ contextManager.enable();
428
+ const propagator = new CompositePropagator({
429
+ propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()]
430
+ });
431
+ context2.setGlobalContextManager(contextManager);
432
+ propagation.setGlobalPropagator(propagator);
433
+ const provider = new NodeTracerProvider({
434
+ resource: new Resource({
435
+ [ATTR_SERVICE_NAME]: SERVICE_NAME
436
+ }),
437
+ spanProcessors: [new LatitudeSpanProcessor(apiKey, projectSlug, processorOptions)]
438
+ });
439
+ provider.register();
440
+ const ready = registerLatitudeInstrumentations({
441
+ instrumentations,
442
+ tracerProvider: provider
443
+ }).catch((err) => {
444
+ console.warn("[Latitude] Failed to register instrumentations:", err);
445
+ });
446
+ const shutdown = async () => {
447
+ await provider.shutdown();
448
+ };
449
+ const flush = async () => {
450
+ await provider.forceFlush();
451
+ };
452
+ const handleShutdown = async () => {
453
+ try {
454
+ await shutdown();
455
+ } catch (err) {
456
+ console.error("Error during Latitude Telemetry shutdown:", err);
457
+ }
458
+ };
459
+ if (!shutdownHandlersRegistered) {
460
+ process.once("SIGTERM", handleShutdown);
461
+ process.once("SIGINT", handleShutdown);
462
+ shutdownHandlersRegistered = true;
463
+ }
464
+ return {
465
+ provider,
466
+ flush,
467
+ shutdown,
468
+ ready
469
+ };
470
+ }
471
+ export {
472
+ ExportFilterSpanProcessor,
473
+ LatitudeSpanProcessor,
474
+ RedactSpanProcessor,
475
+ RedactThenExportSpanProcessor,
476
+ buildShouldExportSpan,
477
+ buildShouldExportSpanFromFields,
478
+ capture,
479
+ initLatitude,
480
+ isDefaultExportSpan,
481
+ isGenAiOrLlmAttributeSpan,
482
+ isLatitudeInstrumentationSpan,
483
+ registerLatitudeInstrumentations
484
+ };
485
+ //# sourceMappingURL=index.js.map