@latitude-data/telemetry 3.0.0-alpha.0 → 3.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,27 +15,178 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
31
21
  var index_exports = {};
32
22
  __export(index_exports, {
33
- DEFAULT_REDACT_SPAN_PROCESSOR: () => DEFAULT_REDACT_SPAN_PROCESSOR,
34
- DEFAULT_SPAN_EXPORTER: () => DEFAULT_SPAN_EXPORTER,
35
- Instrumentation: () => Instrumentation,
36
- LatitudeTelemetry: () => LatitudeTelemetry,
37
- RedactSpanProcessor: () => RedactSpanProcessor
23
+ ExportFilterSpanProcessor: () => ExportFilterSpanProcessor,
24
+ LatitudeSpanProcessor: () => LatitudeSpanProcessor,
25
+ RedactSpanProcessor: () => RedactSpanProcessor,
26
+ RedactThenExportSpanProcessor: () => RedactThenExportSpanProcessor,
27
+ buildShouldExportSpan: () => buildShouldExportSpan,
28
+ buildShouldExportSpanFromFields: () => buildShouldExportSpanFromFields,
29
+ capture: () => capture,
30
+ initLatitude: () => initLatitude,
31
+ isDefaultExportSpan: () => isDefaultExportSpan,
32
+ isGenAiOrLlmAttributeSpan: () => isGenAiOrLlmAttributeSpan,
33
+ isLatitudeInstrumentationSpan: () => isLatitudeInstrumentationSpan,
34
+ registerLatitudeInstrumentations: () => registerLatitudeInstrumentations
38
35
  });
39
36
  module.exports = __toCommonJS(index_exports);
40
37
 
38
+ // src/sdk/context.ts
39
+ var import_api = require("@opentelemetry/api");
40
+ var LATITUDE_CONTEXT_KEY = (0, import_api.createContextKey)("latitude-internal-context");
41
+ var CAPTURE_TRACER_NAME = "so.latitude.instrumentation.capture";
42
+ function getLatitudeContext(ctx) {
43
+ return ctx.getValue(LATITUDE_CONTEXT_KEY);
44
+ }
45
+ function mergeArrays(a, b) {
46
+ if (!a && !b) return void 0;
47
+ if (!a) return b;
48
+ if (!b) return a;
49
+ return [.../* @__PURE__ */ new Set([...a, ...b])];
50
+ }
51
+ function capture(name, fn, options = {}) {
52
+ const currentContext = import_api.context.active();
53
+ const existingData = getLatitudeContext(currentContext);
54
+ const mergedData = {
55
+ name: options.name ?? name,
56
+ tags: mergeArrays(existingData?.tags, options.tags),
57
+ metadata: { ...existingData?.metadata, ...options.metadata },
58
+ sessionId: options.sessionId ?? existingData?.sessionId,
59
+ userId: options.userId ?? existingData?.userId
60
+ };
61
+ const newContext = currentContext.setValue(LATITUDE_CONTEXT_KEY, mergedData);
62
+ const existingSpan = import_api.trace.getSpan(currentContext);
63
+ if (existingSpan) {
64
+ return import_api.context.with(newContext, fn);
65
+ }
66
+ const tracer = import_api.trace.getTracer(CAPTURE_TRACER_NAME);
67
+ return tracer.startActiveSpan(name, { attributes: { "latitude.capture.root": true } }, newContext, (span) => {
68
+ let result;
69
+ try {
70
+ result = fn();
71
+ } catch (error) {
72
+ span.recordException(error);
73
+ span.end();
74
+ throw error;
75
+ }
76
+ if (result instanceof Promise) {
77
+ return result.catch((error) => {
78
+ span.recordException(error);
79
+ throw error;
80
+ }).finally(() => {
81
+ span.end();
82
+ });
83
+ }
84
+ span.end();
85
+ return result;
86
+ });
87
+ }
88
+
89
+ // src/sdk/init.ts
90
+ var import_api2 = require("@opentelemetry/api");
91
+ var import_context_async_hooks = require("@opentelemetry/context-async-hooks");
92
+ var import_core = require("@opentelemetry/core");
93
+ var import_resources = require("@opentelemetry/resources");
94
+ var import_sdk_trace_node2 = require("@opentelemetry/sdk-trace-node");
95
+ var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
96
+
97
+ // src/sdk/instrumentations.ts
98
+ var import_instrumentation = require("@opentelemetry/instrumentation");
99
+ var import_instrumentation_anthropic = require("@traceloop/instrumentation-anthropic");
100
+ var import_instrumentation_bedrock = require("@traceloop/instrumentation-bedrock");
101
+ var import_instrumentation_cohere = require("@traceloop/instrumentation-cohere");
102
+ var import_instrumentation_langchain = require("@traceloop/instrumentation-langchain");
103
+ var import_instrumentation_llamaindex = require("@traceloop/instrumentation-llamaindex");
104
+ var import_instrumentation_openai = require("@traceloop/instrumentation-openai");
105
+ var import_instrumentation_together = require("@traceloop/instrumentation-together");
106
+ var import_instrumentation_vertexai = require("@traceloop/instrumentation-vertexai");
107
+ var INSTRUMENTATION_MAP = {
108
+ openai: { ctor: import_instrumentation_openai.OpenAIInstrumentation, moduleName: "openai", defaultEnrichTokens: true },
109
+ anthropic: { ctor: import_instrumentation_anthropic.AnthropicInstrumentation, moduleName: "@anthropic-ai/sdk" },
110
+ bedrock: { ctor: import_instrumentation_bedrock.BedrockInstrumentation, moduleName: "@aws-sdk/client-bedrock-runtime" },
111
+ cohere: { ctor: import_instrumentation_cohere.CohereInstrumentation, moduleName: "cohere-ai" },
112
+ langchain: { ctor: import_instrumentation_langchain.LangChainInstrumentation, moduleName: "langchain" },
113
+ llamaindex: { ctor: import_instrumentation_llamaindex.LlamaIndexInstrumentation, moduleName: "llamaindex" },
114
+ togetherai: { ctor: import_instrumentation_together.TogetherInstrumentation, moduleName: "together-ai", defaultEnrichTokens: false },
115
+ vertexai: { ctor: import_instrumentation_vertexai.VertexAIInstrumentation, moduleName: "@google-cloud/vertexai" },
116
+ aiplatform: { ctor: import_instrumentation_vertexai.AIPlatformInstrumentation, moduleName: "@google-cloud/aiplatform" }
117
+ };
118
+ async function createLatitudeInstrumentations(options) {
119
+ const result = [];
120
+ for (const type of options.instrumentations) {
121
+ const config = INSTRUMENTATION_MAP[type];
122
+ if (!config) {
123
+ console.warn(`[Latitude] Unknown instrumentation type: ${type}`);
124
+ continue;
125
+ }
126
+ const enrichTokens = options.enrichTokens?.[type] ?? config.defaultEnrichTokens;
127
+ const inst = new config.ctor(enrichTokens !== void 0 ? { enrichTokens } : void 0);
128
+ const moduleRef = options.modules?.[type] ?? await tryRequire(config.moduleName);
129
+ if (!moduleRef) {
130
+ console.warn(
131
+ `[Latitude] Module not found for ${type}: ${config.moduleName}. Install it or pass it explicitly in 'modules'.`
132
+ );
133
+ continue;
134
+ }
135
+ inst.manuallyInstrument?.(moduleRef);
136
+ result.push(inst);
137
+ }
138
+ return result;
139
+ }
140
+ async function tryRequire(moduleName) {
141
+ try {
142
+ return require(moduleName);
143
+ } catch {
144
+ try {
145
+ const mod = await import(moduleName);
146
+ return mod.default ?? mod;
147
+ } catch {
148
+ return void 0;
149
+ }
150
+ }
151
+ }
152
+ async function registerLatitudeInstrumentations(options) {
153
+ const instrumentations = await createLatitudeInstrumentations(options);
154
+ (0, import_instrumentation.registerInstrumentations)({
155
+ instrumentations,
156
+ tracerProvider: options.tracerProvider
157
+ });
158
+ }
159
+
160
+ // src/sdk/processor.ts
161
+ var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
162
+ var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
163
+
164
+ // src/constants/attributes.ts
165
+ var ATTRIBUTES = {
166
+ name: "latitude.capture.name",
167
+ tags: "latitude.tags",
168
+ metadata: "latitude.metadata",
169
+ sessionId: "session.id",
170
+ userId: "user.id"
171
+ };
172
+
173
+ // src/constants/scope.ts
174
+ var SCOPE_LATITUDE = "so.latitude.instrumentation";
175
+
176
+ // src/env/env.ts
177
+ var DEFAULT_EXPORTER_URL = {
178
+ production: "https://ingest.latitude.so",
179
+ development: "http://localhost:3002",
180
+ test: "http://localhost:3002"
181
+ }[process.env.NODE_ENV ?? "development"] ?? "http://localhost:3002";
182
+ function getExporterUrl() {
183
+ if (process.env.LATITUDE_TELEMETRY_URL) {
184
+ return process.env.LATITUDE_TELEMETRY_URL;
185
+ }
186
+ return DEFAULT_EXPORTER_URL;
187
+ }
188
+ var env = { EXPORTER_URL: getExporterUrl() };
189
+
41
190
  // src/sdk/redact.ts
42
191
  var RedactSpanProcessor = class {
43
192
  options;
@@ -88,346 +237,277 @@ var RedactSpanProcessor = class {
88
237
  };
89
238
  var DEFAULT_REDACT_SPAN_PROCESSOR = () => new RedactSpanProcessor({
90
239
  attributes: [
240
+ // HTTP security headers
91
241
  /^http\.request\.header\.authorization$/i,
92
242
  /^http\.request\.header\.cookie$/i,
93
243
  /^http\.request\.header\.x[-_]api[-_]key$/i,
244
+ // Database statements may contain sensitive data
94
245
  /^db\.statement$/i
95
246
  ]
96
247
  });
97
248
 
98
- // src/sdk/sdk.ts
99
- var otel2 = __toESM(require("@opentelemetry/api"), 1);
100
- var import_api2 = require("@opentelemetry/api");
101
- var import_baggage_span_processor = require("@opentelemetry/baggage-span-processor");
102
- var import_context_async_hooks = require("@opentelemetry/context-async-hooks");
103
- var import_core = require("@opentelemetry/core");
104
- var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
105
- var import_instrumentation = require("@opentelemetry/instrumentation");
106
- var import_resources = require("@opentelemetry/resources");
107
- var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
108
- var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
109
- var import_instrumentation_anthropic = require("@traceloop/instrumentation-anthropic");
110
- var import_instrumentation_bedrock = require("@traceloop/instrumentation-bedrock");
111
- var import_instrumentation_cohere = require("@traceloop/instrumentation-cohere");
112
- var import_instrumentation_langchain = require("@traceloop/instrumentation-langchain");
113
- var import_instrumentation_llamaindex = require("@traceloop/instrumentation-llamaindex");
114
- var import_instrumentation_openai = require("@traceloop/instrumentation-openai");
115
- var import_instrumentation_together = require("@traceloop/instrumentation-together");
116
- var import_instrumentation_vertexai = require("@traceloop/instrumentation-vertexai");
117
-
118
- // src/constants/attributes.ts
119
- var ATTRIBUTES = {
120
- tags: "latitude.tags",
121
- metadata: "latitude.metadata",
122
- sessionId: "session.id",
123
- userId: "user.id"
124
- };
125
-
126
- // src/constants/scope.ts
127
- var SCOPE_LATITUDE = "so.latitude.instrumentation";
128
-
129
- // src/env/env.ts
130
- var DEFAULT_EXPORTER_URL = {
131
- production: "https://ingest.latitude.so",
132
- development: "http://localhost:3002",
133
- test: "http://localhost:3002"
134
- }[process.env.NODE_ENV ?? "development"] ?? "http://localhost:3002";
135
- function getExporterUrl() {
136
- if (process.env.LATITUDE_TELEMETRY_URL) {
137
- return process.env.LATITUDE_TELEMETRY_URL;
138
- }
139
- return DEFAULT_EXPORTER_URL;
249
+ // src/sdk/span-filter.ts
250
+ var GEN_AI_PREFIX = "gen_ai.";
251
+ var LLM_PREFIX = "llm.";
252
+ var OPENINFERENCE_KIND = "openinference.span.kind";
253
+ var OTEL_LLM_INSTRUMENTATION_SCOPE_PREFIXES = [
254
+ "opentelemetry.instrumentation.alephalpha",
255
+ "opentelemetry.instrumentation.anthropic",
256
+ "opentelemetry.instrumentation.bedrock",
257
+ "opentelemetry.instrumentation.cohere",
258
+ "opentelemetry.instrumentation.crewai",
259
+ "opentelemetry.instrumentation.google_generativeai",
260
+ "opentelemetry.instrumentation.groq",
261
+ "opentelemetry.instrumentation.haystack",
262
+ "opentelemetry.instrumentation.langchain",
263
+ "opentelemetry.instrumentation.llamaindex",
264
+ "opentelemetry.instrumentation.mistralai",
265
+ "opentelemetry.instrumentation.ollama",
266
+ "opentelemetry.instrumentation.openai",
267
+ "opentelemetry.instrumentation.replicate",
268
+ "opentelemetry.instrumentation.sagemaker",
269
+ "opentelemetry.instrumentation.together",
270
+ "opentelemetry.instrumentation.transformers",
271
+ "opentelemetry.instrumentation.vertexai",
272
+ "opentelemetry.instrumentation.watsonx",
273
+ "openinference.instrumentation"
274
+ ];
275
+ var LLM_SCOPE_SUBSTRINGS = ["openinference", "traceloop", "langsmith", "litellm"];
276
+ function buildShouldExportSpanFromFields(fields) {
277
+ return buildShouldExportSpan({
278
+ ...fields.disableSmartFilter !== void 0 ? { disableSmartFilter: fields.disableSmartFilter } : {},
279
+ ...fields.shouldExportSpan !== void 0 ? { shouldExportSpan: fields.shouldExportSpan } : {},
280
+ ...fields.blockedInstrumentationScopes !== void 0 ? { blockedInstrumentationScopes: fields.blockedInstrumentationScopes } : {}
281
+ });
140
282
  }
141
- var env = { EXPORTER_URL: getExporterUrl() };
142
-
143
- // src/instrumentations/manual.ts
144
- var otel = __toESM(require("@opentelemetry/api"), 1);
145
- var import_api = require("@opentelemetry/api");
146
- var ManualInstrumentation = class {
147
- enabled;
148
- _tracer;
149
- constructor(tracer) {
150
- this.enabled = false;
151
- this._tracer = tracer;
152
- }
153
- get tracer() {
154
- return this._tracer;
155
- }
156
- isEnabled() {
157
- return this.enabled;
158
- }
159
- enable() {
160
- this.enabled = true;
161
- }
162
- disable() {
163
- this.enabled = false;
164
- }
165
- resume(ctx) {
166
- const parts = ctx.traceparent.split("-");
167
- if (parts.length !== 4) {
168
- return otel.ROOT_CONTEXT;
169
- }
170
- const [, traceId, spanId, flags] = parts;
171
- if (!traceId || !spanId) {
172
- return otel.ROOT_CONTEXT;
173
- }
174
- const spanContext = {
175
- traceId,
176
- spanId,
177
- traceFlags: parseInt(flags ?? "01", 16),
178
- isRemote: true
179
- };
180
- let context2 = import_api.trace.setSpanContext(otel.ROOT_CONTEXT, spanContext);
181
- if (ctx.baggage) {
182
- const baggageEntries = {};
183
- for (const pair of ctx.baggage.split(",")) {
184
- const [key, value] = pair.split("=", 2);
185
- if (key && value) {
186
- baggageEntries[decodeURIComponent(key)] = { value: decodeURIComponent(value) };
187
- }
188
- }
189
- const baggage = import_api.propagation.createBaggage(baggageEntries);
190
- context2 = import_api.propagation.setBaggage(context2, baggage);
191
- }
192
- return context2;
283
+ function attributeKeys(span) {
284
+ const attrs = span.attributes;
285
+ if (!attrs || typeof attrs !== "object") return [];
286
+ return Object.keys(attrs);
287
+ }
288
+ function instrumentationScopeName(span) {
289
+ return span.instrumentationScope?.name ?? "";
290
+ }
291
+ function isGenAiOrLlmAttributeSpan(span) {
292
+ for (const key of attributeKeys(span)) {
293
+ if (key.startsWith(GEN_AI_PREFIX) || key.startsWith(LLM_PREFIX)) return true;
294
+ if (key === OPENINFERENCE_KIND || key.startsWith("openinference.")) return true;
295
+ if (key.startsWith("ai.")) return true;
296
+ if (key.startsWith("latitude.")) return true;
297
+ }
298
+ return false;
299
+ }
300
+ function isLatitudeInstrumentationSpan(span) {
301
+ const name = instrumentationScopeName(span);
302
+ return name === SCOPE_LATITUDE || name.startsWith(`${SCOPE_LATITUDE}.`);
303
+ }
304
+ function isKnownLlmInstrumentationScope(span) {
305
+ const name = instrumentationScopeName(span);
306
+ if (!name) return false;
307
+ for (const prefix of OTEL_LLM_INSTRUMENTATION_SCOPE_PREFIXES) {
308
+ if (name === prefix || name.startsWith(`${prefix}.`)) return true;
193
309
  }
194
- };
195
-
196
- // src/sdk/sdk.ts
197
- var TRACES_URL = `${env.EXPORTER_URL}/v1/traces`;
198
- var SERVICE_NAME = process.env.npm_package_name || "unknown";
199
- var SCOPE_VERSION = process.env.npm_package_version || "unknown";
200
- var ContextManager = class {
201
- telemetry;
202
- constructor(telemetry) {
203
- this.telemetry = telemetry;
310
+ const lower = name.toLowerCase();
311
+ for (const part of LLM_SCOPE_SUBSTRINGS) {
312
+ if (lower.includes(part)) return true;
204
313
  }
205
- resume(ctx) {
206
- return this.telemetry.resume(ctx);
314
+ return false;
315
+ }
316
+ function isDefaultExportSpan(span) {
317
+ if (isLatitudeInstrumentationSpan(span)) return true;
318
+ if (isGenAiOrLlmAttributeSpan(span)) return true;
319
+ if (isKnownLlmInstrumentationScope(span)) return true;
320
+ return false;
321
+ }
322
+ function buildShouldExportSpan(options) {
323
+ if (options.disableSmartFilter) return () => true;
324
+ const blocked = new Set(options.blockedInstrumentationScopes ?? []);
325
+ const extra = options.shouldExportSpan;
326
+ return (span) => {
327
+ const scope = instrumentationScopeName(span);
328
+ if (blocked.has(scope)) return false;
329
+ if (isDefaultExportSpan(span)) return true;
330
+ if (extra?.(span)) return true;
331
+ return false;
332
+ };
333
+ }
334
+ var ExportFilterSpanProcessor = class {
335
+ shouldExport;
336
+ inner;
337
+ constructor(shouldExport, inner) {
338
+ this.shouldExport = shouldExport;
339
+ this.inner = inner;
207
340
  }
208
- active() {
209
- return import_api2.context.active();
341
+ onStart(span, parentContext) {
342
+ this.inner.onStart(span, parentContext);
210
343
  }
211
- with(ctx, fn, thisArg, ...args) {
212
- return import_api2.context.with(ctx, fn, thisArg, ...args);
344
+ onEnd(span) {
345
+ if (!this.shouldExport(span)) return;
346
+ this.inner.onEnd(span);
213
347
  }
214
- };
215
- var InstrumentationManager = class {
216
- instrumentations;
217
- constructor(instrumentations) {
218
- this.instrumentations = instrumentations;
219
- }
220
- enable() {
221
- for (const instrumentation of this.instrumentations) {
222
- if (!instrumentation.isEnabled()) instrumentation.enable();
223
- }
348
+ forceFlush() {
349
+ return this.inner.forceFlush();
224
350
  }
225
- disable() {
226
- for (const instrumentation of this.instrumentations) {
227
- if (instrumentation.isEnabled()) instrumentation.disable();
228
- }
351
+ shutdown() {
352
+ return this.inner.shutdown();
229
353
  }
230
354
  };
231
- var TracerManager = class {
232
- nodeProvider;
233
- scopeVersion;
234
- constructor(nodeProvider, scopeVersion) {
235
- this.nodeProvider = nodeProvider;
236
- this.scopeVersion = scopeVersion;
355
+ var RedactThenExportSpanProcessor = class {
356
+ redact;
357
+ exportProcessor;
358
+ constructor(redact, exportProcessor) {
359
+ this.redact = redact;
360
+ this.exportProcessor = exportProcessor;
237
361
  }
238
- get(scope) {
239
- return this.provider(scope).getTracer("");
362
+ onStart(span, parentContext) {
363
+ this.redact?.onStart(span, parentContext);
364
+ this.exportProcessor.onStart(span, parentContext);
240
365
  }
241
- provider(scope) {
242
- return new ScopedTracerProvider(`${SCOPE_LATITUDE}.${scope}`, this.scopeVersion, this.nodeProvider);
243
- }
244
- };
245
- var ScopedTracerProvider = class {
246
- scope;
247
- version;
248
- provider;
249
- constructor(scope, version, provider) {
250
- this.scope = scope;
251
- this.version = version;
252
- this.provider = provider;
253
- }
254
- getTracer(_name, _version, options) {
255
- return this.provider.getTracer(this.scope, this.version, options);
256
- }
257
- };
258
- var LifecycleManager = class {
259
- nodeProvider;
260
- exporter;
261
- constructor(nodeProvider, exporter) {
262
- this.nodeProvider = nodeProvider;
263
- this.exporter = exporter;
366
+ onEnd(span) {
367
+ this.redact?.onEnd(span);
368
+ this.exportProcessor.onEnd(span);
264
369
  }
265
- async flush() {
266
- await this.nodeProvider.forceFlush();
267
- await this.exporter.forceFlush?.();
370
+ forceFlush() {
371
+ return this.exportProcessor.forceFlush();
268
372
  }
269
- async shutdown() {
270
- await this.flush();
271
- await this.nodeProvider.shutdown();
272
- await this.exporter.shutdown?.();
373
+ shutdown() {
374
+ return this.exportProcessor.shutdown();
273
375
  }
274
376
  };
275
- var DEFAULT_SPAN_EXPORTER = (apiKey, projectSlug) => new import_exporter_trace_otlp_http.OTLPTraceExporter({
276
- url: TRACES_URL,
277
- headers: {
278
- Authorization: `Bearer ${apiKey}`,
279
- "Content-Type": "application/json",
280
- "X-Latitude-Project": projectSlug
281
- },
282
- timeoutMillis: 30 * 1e3
283
- });
284
- var Instrumentation = /* @__PURE__ */ ((Instrumentation2) => {
285
- Instrumentation2["Anthropic"] = "anthropic";
286
- Instrumentation2["AIPlatform"] = "aiplatform";
287
- Instrumentation2["Bedrock"] = "bedrock";
288
- Instrumentation2["Cohere"] = "cohere";
289
- Instrumentation2["Langchain"] = "langchain";
290
- Instrumentation2["LlamaIndex"] = "llamaindex";
291
- Instrumentation2["Manual"] = "manual";
292
- Instrumentation2["OpenAI"] = "openai";
293
- Instrumentation2["TogetherAI"] = "togetherai";
294
- Instrumentation2["VertexAI"] = "vertexai";
295
- return Instrumentation2;
296
- })(Instrumentation || {});
297
- var LatitudeTelemetry = class {
298
- options;
299
- nodeProvider;
300
- instrumentationsList;
301
- /** OpenTelemetry tracer for creating custom spans. */
302
- tracer;
303
- context;
304
- instrumentation;
305
- lifecycle;
377
+
378
+ // src/sdk/processor.ts
379
+ var LatitudeSpanProcessor = class {
380
+ tail;
306
381
  constructor(apiKey, projectSlug, options) {
307
- this.options = options || {};
308
- if (!this.options.exporter) {
309
- this.options.exporter = DEFAULT_SPAN_EXPORTER(apiKey, projectSlug);
382
+ if (!apiKey || apiKey.trim() === "") {
383
+ throw new Error("[Latitude] apiKey is required and cannot be empty");
310
384
  }
311
- import_api2.context.setGlobalContextManager(new import_context_async_hooks.AsyncLocalStorageContextManager().enable());
312
- import_api2.propagation.setGlobalPropagator(
313
- new import_core.CompositePropagator({
314
- propagators: [...this.options.propagators || [], new import_core.W3CTraceContextPropagator(), new import_core.W3CBaggagePropagator()]
315
- })
316
- );
317
- const spanProcessors = [
318
- // Must run before the exporter span processors
319
- new import_baggage_span_processor.BaggageSpanProcessor(import_baggage_span_processor.ALLOW_ALL_BAGGAGE_KEYS),
320
- ...this.options.processors ?? [DEFAULT_REDACT_SPAN_PROCESSOR()],
321
- this.options.disableBatch ? new import_sdk_trace_node.SimpleSpanProcessor(this.options.exporter) : new import_sdk_trace_node.BatchSpanProcessor(this.options.exporter)
322
- ];
323
- this.nodeProvider = new import_sdk_trace_node.NodeTracerProvider({
324
- resource: new import_resources.Resource({ [import_semantic_conventions.ATTR_SERVICE_NAME]: this.options.serviceName || SERVICE_NAME }),
325
- spanProcessors
326
- });
327
- this.lifecycle = new LifecycleManager(this.nodeProvider, this.options.exporter);
328
- this.nodeProvider.register();
329
- process.on("SIGTERM", () => this.shutdown());
330
- process.on("SIGINT", () => this.shutdown());
331
- this.instrumentationsList = [];
332
- const tracerManager = new TracerManager(this.nodeProvider, SCOPE_VERSION);
333
- const manualTracer = tracerManager.get("manual" /* Manual */);
334
- const manualInstrumentation = new ManualInstrumentation(manualTracer);
335
- this.instrumentationsList.push(manualInstrumentation);
336
- this.tracer = manualTracer;
337
- this.initProviderInstrumentations(tracerManager);
338
- this.instrumentation = new InstrumentationManager(this.instrumentationsList);
339
- this.instrumentation.enable();
340
- this.context = new ContextManager(manualInstrumentation);
341
- }
342
- async flush() {
343
- await this.lifecycle.flush();
344
- }
345
- async shutdown() {
346
- await this.lifecycle.shutdown();
347
- }
348
- initProviderInstrumentations(tracerManager) {
349
- const configure = (type, Ctor, opts) => {
350
- const providerPkg = this.options.instrumentations?.[type];
351
- if (!providerPkg) return;
352
- const provider = tracerManager.provider(type);
353
- const inst = new Ctor(opts);
354
- inst.setTracerProvider(provider);
355
- inst.manuallyInstrument(providerPkg);
356
- (0, import_instrumentation.registerInstrumentations)({
357
- instrumentations: [inst],
358
- tracerProvider: provider
359
- });
360
- this.instrumentationsList.push(inst);
361
- };
362
- configure("anthropic" /* Anthropic */, import_instrumentation_anthropic.AnthropicInstrumentation);
363
- configure("aiplatform" /* AIPlatform */, import_instrumentation_vertexai.AIPlatformInstrumentation);
364
- configure("bedrock" /* Bedrock */, import_instrumentation_bedrock.BedrockInstrumentation);
365
- configure("cohere" /* Cohere */, import_instrumentation_cohere.CohereInstrumentation);
366
- configure("langchain" /* Langchain */, import_instrumentation_langchain.LangChainInstrumentation);
367
- configure("llamaindex" /* LlamaIndex */, import_instrumentation_llamaindex.LlamaIndexInstrumentation);
368
- configure("openai" /* OpenAI */, import_instrumentation_openai.OpenAIInstrumentation, { enrichTokens: true });
369
- configure("togetherai" /* TogetherAI */, import_instrumentation_together.TogetherInstrumentation, { enrichTokens: false });
370
- configure("vertexai" /* VertexAI */, import_instrumentation_vertexai.VertexAIInstrumentation);
371
- }
372
- /**
373
- * Wrap a block of code with trace-wide context attributes.
374
- * Baggage entries (tags, metadata, sessionId, userId) are propagated
375
- * to all spans created within the callback via BaggageSpanProcessor.
376
- *
377
- * If there is no active span, a root span is created so all child spans
378
- * are grouped under a single trace. If a span is already active, only
379
- * baggage is set without creating an extra wrapper span.
380
- */
381
- async capture(options, fn) {
382
- const baggageEntries = {};
383
- if (options.tags?.length) {
384
- baggageEntries[ATTRIBUTES.tags] = { value: JSON.stringify(options.tags) };
385
+ if (!projectSlug || projectSlug.trim() === "") {
386
+ throw new Error("[Latitude] projectSlug is required and cannot be empty");
385
387
  }
386
- if (options.metadata) {
387
- baggageEntries[ATTRIBUTES.metadata] = { value: JSON.stringify(options.metadata) };
388
- }
389
- if (options.sessionId) {
390
- baggageEntries[ATTRIBUTES.sessionId] = { value: options.sessionId };
391
- }
392
- if (options.userId) {
393
- baggageEntries[ATTRIBUTES.userId] = { value: options.userId };
394
- }
395
- const baggage = import_api2.propagation.createBaggage(baggageEntries);
396
- const activeSpan = otel2.trace.getSpan(import_api2.context.active());
397
- if (activeSpan) {
398
- const ctx = import_api2.propagation.setBaggage(import_api2.context.active(), baggage);
399
- return await import_api2.context.with(ctx, async () => await fn(ctx));
400
- }
401
- const rootContext = import_api2.propagation.setBaggage(otel2.ROOT_CONTEXT, baggage);
402
- return await this.tracer.startActiveSpan(
403
- "latitude.trace",
404
- { kind: otel2.SpanKind.INTERNAL },
405
- rootContext,
406
- async (span) => {
407
- try {
408
- const result = await fn(import_api2.context.active());
409
- span.setStatus({ code: otel2.SpanStatusCode.OK });
410
- return result;
411
- } catch (error) {
412
- span.setStatus({
413
- code: otel2.SpanStatusCode.ERROR,
414
- message: error instanceof Error ? error.message : String(error)
415
- });
416
- span.recordException(error instanceof Error ? error : new Error(String(error)));
417
- throw error;
418
- } finally {
419
- span.end();
388
+ const exporter = options?.exporter ?? new import_exporter_trace_otlp_http.OTLPTraceExporter({
389
+ url: `${env.EXPORTER_URL}/v1/traces`,
390
+ headers: {
391
+ Authorization: `Bearer ${apiKey}`,
392
+ "Content-Type": "application/json",
393
+ "X-Latitude-Project": projectSlug
394
+ },
395
+ timeoutMillis: 3e4
396
+ });
397
+ const redact = options?.disableRedact ? null : options?.redact ? new RedactSpanProcessor(options.redact) : DEFAULT_REDACT_SPAN_PROCESSOR();
398
+ const batchOrSimple = options?.disableBatch ? new import_sdk_trace_node.SimpleSpanProcessor(exporter) : new import_sdk_trace_node.BatchSpanProcessor(exporter);
399
+ const shouldExport = buildShouldExportSpanFromFields({
400
+ disableSmartFilter: options?.disableSmartFilter,
401
+ shouldExportSpan: options?.shouldExportSpan,
402
+ blockedInstrumentationScopes: options?.blockedInstrumentationScopes
403
+ });
404
+ const redactThenExport = new RedactThenExportSpanProcessor(redact, batchOrSimple);
405
+ this.tail = new ExportFilterSpanProcessor(shouldExport, redactThenExport);
406
+ }
407
+ onStart(span, parentContext) {
408
+ const latitudeData = getLatitudeContext(parentContext);
409
+ if (latitudeData) {
410
+ if (latitudeData.name) {
411
+ span.setAttribute(ATTRIBUTES.name, latitudeData.name);
412
+ if (span.attributes["latitude.capture.root"]) {
413
+ span.updateName(latitudeData.name);
420
414
  }
421
415
  }
422
- );
416
+ if (latitudeData.tags && latitudeData.tags.length > 0) {
417
+ span.setAttribute(ATTRIBUTES.tags, JSON.stringify(latitudeData.tags));
418
+ }
419
+ if (latitudeData.metadata && Object.keys(latitudeData.metadata).length > 0) {
420
+ span.setAttribute(ATTRIBUTES.metadata, JSON.stringify(latitudeData.metadata));
421
+ }
422
+ if (latitudeData.sessionId) {
423
+ span.setAttribute(ATTRIBUTES.sessionId, latitudeData.sessionId);
424
+ }
425
+ if (latitudeData.userId) {
426
+ span.setAttribute(ATTRIBUTES.userId, latitudeData.userId);
427
+ }
428
+ }
429
+ this.tail.onStart(span, parentContext);
430
+ }
431
+ onEnd(span) {
432
+ this.tail.onEnd(span);
433
+ }
434
+ async forceFlush() {
435
+ await this.tail.forceFlush();
436
+ }
437
+ async shutdown() {
438
+ await this.tail.shutdown();
423
439
  }
424
440
  };
441
+
442
+ // src/sdk/init.ts
443
+ var SERVICE_NAME = process.env.npm_package_name || "unknown";
444
+ var shutdownHandlersRegistered = false;
445
+ function initLatitude(options) {
446
+ const { apiKey, projectSlug, instrumentations = [], ...processorOptions } = options;
447
+ if (!apiKey || apiKey.trim() === "") {
448
+ throw new Error("[Latitude] apiKey is required and cannot be empty");
449
+ }
450
+ if (!projectSlug || projectSlug.trim() === "") {
451
+ throw new Error("[Latitude] projectSlug is required and cannot be empty");
452
+ }
453
+ const contextManager = new import_context_async_hooks.AsyncLocalStorageContextManager();
454
+ contextManager.enable();
455
+ const propagator = new import_core.CompositePropagator({
456
+ propagators: [new import_core.W3CTraceContextPropagator(), new import_core.W3CBaggagePropagator()]
457
+ });
458
+ import_api2.context.setGlobalContextManager(contextManager);
459
+ import_api2.propagation.setGlobalPropagator(propagator);
460
+ const provider = new import_sdk_trace_node2.NodeTracerProvider({
461
+ resource: (0, import_resources.resourceFromAttributes)({
462
+ [import_semantic_conventions.ATTR_SERVICE_NAME]: SERVICE_NAME
463
+ }),
464
+ spanProcessors: [new LatitudeSpanProcessor(apiKey, projectSlug, processorOptions)]
465
+ });
466
+ provider.register();
467
+ const ready = registerLatitudeInstrumentations({
468
+ instrumentations,
469
+ tracerProvider: provider
470
+ }).catch((err) => {
471
+ console.warn("[Latitude] Failed to register instrumentations:", err);
472
+ });
473
+ const shutdown = async () => {
474
+ await provider.shutdown();
475
+ };
476
+ const flush = async () => {
477
+ await provider.forceFlush();
478
+ };
479
+ const handleShutdown = async () => {
480
+ try {
481
+ await shutdown();
482
+ } catch (err) {
483
+ console.error("Error during Latitude Telemetry shutdown:", err);
484
+ }
485
+ };
486
+ if (!shutdownHandlersRegistered) {
487
+ process.once("SIGTERM", handleShutdown);
488
+ process.once("SIGINT", handleShutdown);
489
+ shutdownHandlersRegistered = true;
490
+ }
491
+ return {
492
+ provider,
493
+ flush,
494
+ shutdown,
495
+ ready
496
+ };
497
+ }
425
498
  // Annotate the CommonJS export names for ESM import in node:
426
499
  0 && (module.exports = {
427
- DEFAULT_REDACT_SPAN_PROCESSOR,
428
- DEFAULT_SPAN_EXPORTER,
429
- Instrumentation,
430
- LatitudeTelemetry,
431
- RedactSpanProcessor
500
+ ExportFilterSpanProcessor,
501
+ LatitudeSpanProcessor,
502
+ RedactSpanProcessor,
503
+ RedactThenExportSpanProcessor,
504
+ buildShouldExportSpan,
505
+ buildShouldExportSpanFromFields,
506
+ capture,
507
+ initLatitude,
508
+ isDefaultExportSpan,
509
+ isGenAiOrLlmAttributeSpan,
510
+ isLatitudeInstrumentationSpan,
511
+ registerLatitudeInstrumentations
432
512
  });
433
513
  //# sourceMappingURL=index.cjs.map