@lmnr-ai/lmnr 0.7.13 → 0.7.14

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.
@@ -39,9 +39,11 @@ const LaminarAttributes = {
39
39
  //#endregion
40
40
  //#region src/utils.ts
41
41
  function initializeLogger(options) {
42
- return pino(PinoPretty({
43
- colorize: options?.colorize ?? true,
44
- minimumLevel: options?.level ?? process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() ?? "info"
42
+ const colorize = options?.colorize ?? true;
43
+ const level = options?.level ?? process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() ?? "info";
44
+ return pino({ level }, PinoPretty({
45
+ colorize,
46
+ minimumLevel: level
45
47
  }));
46
48
  }
47
49
  const logger = initializeLogger();
@@ -253,4 +255,4 @@ const validateTracingConfig = (apiKey) => {
253
255
 
254
256
  //#endregion
255
257
  export { SPAN_SDK_VERSION as A, SESSION_ID as C, SPAN_LANGUAGE_VERSION as D, SPAN_INSTRUMENTATION_SOURCE as E, TRACE_HAS_BROWSER_SESSION as M, TRACE_TYPE as N, SPAN_OUTPUT as O, USER_ID as P, PARENT_SPAN_PATH as S, SPAN_INPUT as T, ASSOCIATION_PROPERTIES as _, getOtelEnvVar as a, LaminarAttributes as b, isStringUUID as c, otelSpanIdToUUID as d, otelTraceIdToUUID as f, validateTracingConfig as g, tryToOtelSpanContext as h, getDirname as i, SPAN_TYPE as j, SPAN_PATH as k, metadataToAttributes as l, slicePayload as m, Semaphore as n, initializeLogger as o, parseOtelHeaders as p, deserializeLaminarSpanContext as r, isOtelAttributeValueType as s, NIL_UUID as t, newUUID as u, ASSOCIATION_PROPERTIES_OVERRIDES as v, SPAN_IDS_PATH as w, PARENT_SPAN_IDS_PATH as x, HUMAN_EVALUATOR_OPTIONS as y };
256
- //# sourceMappingURL=utils-viuxL6V7.mjs.map
258
+ //# sourceMappingURL=utils-ChSbRj0P.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-ChSbRj0P.mjs","names":["ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string>","NIL_UUID: StringUUID","uuidv4","value","headers: Record<string, string>"],"sources":["../src/opentelemetry-lib/tracing/attributes.ts","../src/utils.ts"],"sourcesContent":["import { SpanAttributes } from '@traceloop/ai-semantic-conventions';\n\nexport const SPAN_INPUT = \"lmnr.span.input\";\nexport const SPAN_OUTPUT = \"lmnr.span.output\";\nexport const SPAN_TYPE = \"lmnr.span.type\";\nexport const SPAN_PATH = \"lmnr.span.path\";\nexport const SPAN_IDS_PATH = \"lmnr.span.ids_path\";\nexport const PARENT_SPAN_PATH = \"lmnr.span.parent_path\";\nexport const PARENT_SPAN_IDS_PATH = \"lmnr.span.parent_ids_path\";\nexport const SPAN_INSTRUMENTATION_SOURCE = \"lmnr.span.instrumentation_source\";\nexport const SPAN_SDK_VERSION = \"lmnr.span.sdk_version\";\nexport const SPAN_LANGUAGE_VERSION = \"lmnr.span.language_version\";\nexport const OVERRIDE_PARENT_SPAN = \"lmnr.internal.override_parent_span\";\nexport const TRACE_HAS_BROWSER_SESSION = \"lmnr.internal.has_browser_session\";\nexport const EXTRACTED_FROM_NEXT_JS = \"lmnr.span.extracted_from.next_js\";\nexport const HUMAN_EVALUATOR_OPTIONS = 'lmnr.span.human_evaluator_options';\nexport const ASSOCIATION_PROPERTIES = \"lmnr.association.properties\";\nexport const SESSION_ID = \"lmnr.association.properties.session_id\";\nexport const USER_ID = \"lmnr.association.properties.user_id\";\nexport const TRACE_TYPE = \"lmnr.association.properties.trace_type\";\n\nexport const ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string> = {\n \"span_type\": SPAN_TYPE,\n};\n\nexport const LaminarAttributes = {\n // == This is the minimum set of attributes for a proper LLM span ==\n //\n // not SpanAttributes.LLM_USAGE_PROMPT_TOKENS\n INPUT_TOKEN_COUNT: \"gen_ai.usage.input_tokens\",\n // not SpanAttributes.LLM_USAGE_COMPLETION_TOKENS\n OUTPUT_TOKEN_COUNT: \"gen_ai.usage.output_tokens\",\n TOTAL_TOKEN_COUNT: SpanAttributes.LLM_USAGE_TOTAL_TOKENS,\n PROVIDER: SpanAttributes.LLM_SYSTEM,\n REQUEST_MODEL: SpanAttributes.LLM_REQUEST_MODEL,\n RESPONSE_MODEL: SpanAttributes.LLM_RESPONSE_MODEL,\n //\n // == End of minimum set ==\n // == Additional attributes ==\n //\n INPUT_COST: \"gen_ai.usage.input_cost\",\n OUTPUT_COST: \"gen_ai.usage.output_cost\",\n TOTAL_COST: \"gen_ai.usage.cost\",\n //\n // == End of additional attributes ==\n};\n","import { AttributeValue, SpanContext, TraceFlags } from '@opentelemetry/api';\nimport * as path from \"path\";\nimport pino, { Level } from 'pino';\nimport { PinoPretty } from 'pino-pretty';\nimport { fileURLToPath } from \"url\";\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { ASSOCIATION_PROPERTIES } from './opentelemetry-lib/tracing/attributes';\nimport { LaminarSpanContext, TraceType, TracingLevel } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(\n {\n level: level,\n },\n PinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n );\n};\n\nexport const otelTraceIdToUUID = (traceId: string): StringUUID => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n ) as StringUUID;\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if ((typeof record.spanId === 'string' && typeof record.traceId === 'string') ||\n (typeof record.span_id === 'string' && typeof record.trace_id === 'string')) {\n return {\n spanId: uuidToOtelSpanId(record?.spanId as string ?? record?.['span_id'] as string),\n traceId: uuidToOtelTraceId(record?.traceId as string ?? record?.['trace_id'] as string),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n\n/**\n * Deserialize a LaminarSpanContext from a string or record.\n * Handles both camelCase and snake_case keys for cross-language compatibility.\n *\n * @param data - The data to deserialize (string or record)\n * @returns The deserialized LaminarSpanContext\n * @throws Error if the data is invalid\n */\nexport const deserializeLaminarSpanContext = (\n data: Record<string, unknown> | string,\n): LaminarSpanContext => {\n if (typeof data === 'string') {\n try {\n const record = JSON.parse(data) as Record<string, unknown>;\n return deserializeLaminarSpanContext(record);\n } catch (e) {\n throw new Error(\n `Failed to parse LaminarSpanContext: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n if (!isRecord(data)) {\n throw new Error('Invalid LaminarSpanContext: must be a string or object');\n }\n\n // Handle both camelCase and snake_case for all fields\n const traceId = data.traceId ?? data.trace_id;\n const spanId = data.spanId ?? data.span_id;\n const isRemote = data.isRemote ?? data.is_remote ?? false;\n const spanPath = data.spanPath ?? data.span_path;\n const spanIdsPath = data.spanIdsPath ?? data.span_ids_path;\n const userId = data.userId ?? data.user_id;\n const sessionId = data.sessionId ?? data.session_id;\n const metadata = data.metadata;\n const traceType = data.traceType ?? data.trace_type;\n const tracingLevel = data.tracingLevel ?? data.tracing_level;\n\n if (typeof traceId !== 'string' || typeof spanId !== 'string') {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be strings');\n }\n\n // Validate UUID format\n if (!isStringUUID(traceId) || !isStringUUID(spanId)) {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be valid UUIDs');\n }\n\n return {\n traceId: traceId,\n spanId: spanId,\n isRemote: Boolean(isRemote),\n spanPath: Array.isArray(spanPath) ? spanPath as string[] : undefined,\n spanIdsPath: Array.isArray(spanIdsPath) ? spanIdsPath as StringUUID[] : undefined,\n userId: userId as string | undefined,\n sessionId: sessionId as string | undefined,\n metadata: metadata as Record<string, unknown> | undefined,\n traceType: traceType as TraceType | undefined,\n tracingLevel: tracingLevel as TracingLevel | undefined,\n };\n};\n\n\nexport const getDirname = () => {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n\n if (typeof import.meta?.url !== 'undefined') {\n return path.dirname(fileURLToPath(import.meta.url));\n }\n\n return process.cwd();\n};\n\nexport const slicePayload = <T>(value: T, length: number) => {\n if (value === null || value === undefined) {\n return value;\n }\n\n const str = JSON.stringify(value);\n if (str.length <= length) {\n return value;\n }\n\n return (str.slice(0, length) + '...');\n};\n\nexport const isOtelAttributeValueType = (value: unknown): value is AttributeValue => {\n if (typeof value === 'string'\n || typeof value === 'number'\n || typeof value === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n const allStrings = value.every(value => (value == null) || typeof value === 'string');\n const allNumbers = value.every(value => (value == null) || typeof value === 'number');\n const allBooleans = value.every(value => (value == null) || typeof value === 'boolean');\n return allStrings || allNumbers || allBooleans;\n }\n return false;\n};\n\nexport const metadataToAttributes = (\n metadata: Record<string, unknown>,\n): Record<string, AttributeValue> => Object.fromEntries(\n Object.entries(metadata).map(([key, value]) => {\n if (isOtelAttributeValueType(value)) {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, value];\n } else {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, JSON.stringify(value)];\n }\n }),\n);\n\n/**\n * Get OTEL environment variable with priority order.\n * Checks in order:\n * 1. OTEL_EXPORTER_OTLP_TRACES_{varName}\n * 2. OTEL_EXPORTER_OTLP_{varName}\n * 3. OTEL_{varName}\n *\n * @param varName - The variable name (e.g., 'ENDPOINT', 'HEADERS', 'PROTOCOL')\n * @returns The environment variable value or undefined if not found\n */\nexport const getOtelEnvVar = (varName: string): string | undefined => {\n const candidates = [\n `OTEL_EXPORTER_OTLP_TRACES_${varName}`,\n `OTEL_EXPORTER_OTLP_${varName}`,\n `OTEL_${varName}`,\n ];\n\n for (const candidate of candidates) {\n const value = process?.env?.[candidate];\n if (value) {\n return value;\n }\n }\n return undefined;\n};\n\n/**\n * Check if OTEL configuration is available.\n * @returns true if OTEL endpoint is configured\n */\nexport const hasOtelConfig = (): boolean => !!getOtelEnvVar('ENDPOINT');\n\n/**\n * Parse OTEL headers string into a record object.\n * Format: key1=value1,key2=value2\n * Values are URL-decoded.\n *\n * @param headersStr - Headers string in OTEL format\n * @returns Parsed headers object\n */\nexport const parseOtelHeaders = (headersStr: string | undefined): Record<string, string> => {\n if (!headersStr) {\n return {};\n }\n\n const headers: Record<string, string> = {};\n for (const pair of headersStr.split(',')) {\n const equalIndex = pair.indexOf('=');\n if (equalIndex !== -1) {\n // Manually split instead of .split('=', 2) because\n // the latter only returns the first 2 elements of the array after the split\n const key = pair.substring(0, equalIndex).trim();\n const value = pair.substring(equalIndex + 1).trim();\n headers[key] = decodeURIComponent(value);\n }\n }\n return headers;\n};\n\n/**\n * Validate that either Laminar API key or OTEL configuration is present.\n * Throws an error if neither is configured.\n *\n * @param apiKey - The Laminar API key (if provided)\n * @throws Error if neither API key nor OTEL configuration is present\n */\nexport const validateTracingConfig = (apiKey?: string): void => {\n if (!apiKey && !hasOtelConfig()) {\n throw new Error(\n 'Please initialize the Laminar object with your project API key ' +\n 'or set the LMNR_PROJECT_API_KEY environment variable, ' +\n 'or configure OTEL environment variables (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, etc.)',\n );\n }\n};\n"],"mappings":"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"}
@@ -68,9 +68,11 @@ const LaminarAttributes = {
68
68
  //#endregion
69
69
  //#region src/utils.ts
70
70
  function initializeLogger(options) {
71
- return (0, pino.default)((0, pino_pretty.PinoPretty)({
72
- colorize: options?.colorize ?? true,
73
- minimumLevel: options?.level ?? process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() ?? "info"
71
+ const colorize = options?.colorize ?? true;
72
+ const level = options?.level ?? process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() ?? "info";
73
+ return (0, pino.default)({ level }, (0, pino_pretty.PinoPretty)({
74
+ colorize,
75
+ minimumLevel: level
74
76
  }));
75
77
  }
76
78
  const logger = initializeLogger();
@@ -491,4 +493,4 @@ Object.defineProperty(exports, 'validateTracingConfig', {
491
493
  return validateTracingConfig;
492
494
  }
493
495
  });
494
- //# sourceMappingURL=utils-BmfIfQcB.cjs.map
496
+ //# sourceMappingURL=utils-CjvkrXaI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-CjvkrXaI.cjs","names":["ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string>","SpanAttributes","NIL_UUID: StringUUID","uuid","TraceFlags","value","headers: Record<string, string>"],"sources":["../src/opentelemetry-lib/tracing/attributes.ts","../src/utils.ts"],"sourcesContent":["import { SpanAttributes } from '@traceloop/ai-semantic-conventions';\n\nexport const SPAN_INPUT = \"lmnr.span.input\";\nexport const SPAN_OUTPUT = \"lmnr.span.output\";\nexport const SPAN_TYPE = \"lmnr.span.type\";\nexport const SPAN_PATH = \"lmnr.span.path\";\nexport const SPAN_IDS_PATH = \"lmnr.span.ids_path\";\nexport const PARENT_SPAN_PATH = \"lmnr.span.parent_path\";\nexport const PARENT_SPAN_IDS_PATH = \"lmnr.span.parent_ids_path\";\nexport const SPAN_INSTRUMENTATION_SOURCE = \"lmnr.span.instrumentation_source\";\nexport const SPAN_SDK_VERSION = \"lmnr.span.sdk_version\";\nexport const SPAN_LANGUAGE_VERSION = \"lmnr.span.language_version\";\nexport const OVERRIDE_PARENT_SPAN = \"lmnr.internal.override_parent_span\";\nexport const TRACE_HAS_BROWSER_SESSION = \"lmnr.internal.has_browser_session\";\nexport const EXTRACTED_FROM_NEXT_JS = \"lmnr.span.extracted_from.next_js\";\nexport const HUMAN_EVALUATOR_OPTIONS = 'lmnr.span.human_evaluator_options';\nexport const ASSOCIATION_PROPERTIES = \"lmnr.association.properties\";\nexport const SESSION_ID = \"lmnr.association.properties.session_id\";\nexport const USER_ID = \"lmnr.association.properties.user_id\";\nexport const TRACE_TYPE = \"lmnr.association.properties.trace_type\";\n\nexport const ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string> = {\n \"span_type\": SPAN_TYPE,\n};\n\nexport const LaminarAttributes = {\n // == This is the minimum set of attributes for a proper LLM span ==\n //\n // not SpanAttributes.LLM_USAGE_PROMPT_TOKENS\n INPUT_TOKEN_COUNT: \"gen_ai.usage.input_tokens\",\n // not SpanAttributes.LLM_USAGE_COMPLETION_TOKENS\n OUTPUT_TOKEN_COUNT: \"gen_ai.usage.output_tokens\",\n TOTAL_TOKEN_COUNT: SpanAttributes.LLM_USAGE_TOTAL_TOKENS,\n PROVIDER: SpanAttributes.LLM_SYSTEM,\n REQUEST_MODEL: SpanAttributes.LLM_REQUEST_MODEL,\n RESPONSE_MODEL: SpanAttributes.LLM_RESPONSE_MODEL,\n //\n // == End of minimum set ==\n // == Additional attributes ==\n //\n INPUT_COST: \"gen_ai.usage.input_cost\",\n OUTPUT_COST: \"gen_ai.usage.output_cost\",\n TOTAL_COST: \"gen_ai.usage.cost\",\n //\n // == End of additional attributes ==\n};\n","import { AttributeValue, SpanContext, TraceFlags } from '@opentelemetry/api';\nimport * as path from \"path\";\nimport pino, { Level } from 'pino';\nimport { PinoPretty } from 'pino-pretty';\nimport { fileURLToPath } from \"url\";\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { ASSOCIATION_PROPERTIES } from './opentelemetry-lib/tracing/attributes';\nimport { LaminarSpanContext, TraceType, TracingLevel } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(\n {\n level: level,\n },\n PinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n );\n};\n\nexport const otelTraceIdToUUID = (traceId: string): StringUUID => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n ) as StringUUID;\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if ((typeof record.spanId === 'string' && typeof record.traceId === 'string') ||\n (typeof record.span_id === 'string' && typeof record.trace_id === 'string')) {\n return {\n spanId: uuidToOtelSpanId(record?.spanId as string ?? record?.['span_id'] as string),\n traceId: uuidToOtelTraceId(record?.traceId as string ?? record?.['trace_id'] as string),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n\n/**\n * Deserialize a LaminarSpanContext from a string or record.\n * Handles both camelCase and snake_case keys for cross-language compatibility.\n *\n * @param data - The data to deserialize (string or record)\n * @returns The deserialized LaminarSpanContext\n * @throws Error if the data is invalid\n */\nexport const deserializeLaminarSpanContext = (\n data: Record<string, unknown> | string,\n): LaminarSpanContext => {\n if (typeof data === 'string') {\n try {\n const record = JSON.parse(data) as Record<string, unknown>;\n return deserializeLaminarSpanContext(record);\n } catch (e) {\n throw new Error(\n `Failed to parse LaminarSpanContext: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n if (!isRecord(data)) {\n throw new Error('Invalid LaminarSpanContext: must be a string or object');\n }\n\n // Handle both camelCase and snake_case for all fields\n const traceId = data.traceId ?? data.trace_id;\n const spanId = data.spanId ?? data.span_id;\n const isRemote = data.isRemote ?? data.is_remote ?? false;\n const spanPath = data.spanPath ?? data.span_path;\n const spanIdsPath = data.spanIdsPath ?? data.span_ids_path;\n const userId = data.userId ?? data.user_id;\n const sessionId = data.sessionId ?? data.session_id;\n const metadata = data.metadata;\n const traceType = data.traceType ?? data.trace_type;\n const tracingLevel = data.tracingLevel ?? data.tracing_level;\n\n if (typeof traceId !== 'string' || typeof spanId !== 'string') {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be strings');\n }\n\n // Validate UUID format\n if (!isStringUUID(traceId) || !isStringUUID(spanId)) {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be valid UUIDs');\n }\n\n return {\n traceId: traceId,\n spanId: spanId,\n isRemote: Boolean(isRemote),\n spanPath: Array.isArray(spanPath) ? spanPath as string[] : undefined,\n spanIdsPath: Array.isArray(spanIdsPath) ? spanIdsPath as StringUUID[] : undefined,\n userId: userId as string | undefined,\n sessionId: sessionId as string | undefined,\n metadata: metadata as Record<string, unknown> | undefined,\n traceType: traceType as TraceType | undefined,\n tracingLevel: tracingLevel as TracingLevel | undefined,\n };\n};\n\n\nexport const getDirname = () => {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n\n if (typeof import.meta?.url !== 'undefined') {\n return path.dirname(fileURLToPath(import.meta.url));\n }\n\n return process.cwd();\n};\n\nexport const slicePayload = <T>(value: T, length: number) => {\n if (value === null || value === undefined) {\n return value;\n }\n\n const str = JSON.stringify(value);\n if (str.length <= length) {\n return value;\n }\n\n return (str.slice(0, length) + '...');\n};\n\nexport const isOtelAttributeValueType = (value: unknown): value is AttributeValue => {\n if (typeof value === 'string'\n || typeof value === 'number'\n || typeof value === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n const allStrings = value.every(value => (value == null) || typeof value === 'string');\n const allNumbers = value.every(value => (value == null) || typeof value === 'number');\n const allBooleans = value.every(value => (value == null) || typeof value === 'boolean');\n return allStrings || allNumbers || allBooleans;\n }\n return false;\n};\n\nexport const metadataToAttributes = (\n metadata: Record<string, unknown>,\n): Record<string, AttributeValue> => Object.fromEntries(\n Object.entries(metadata).map(([key, value]) => {\n if (isOtelAttributeValueType(value)) {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, value];\n } else {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, JSON.stringify(value)];\n }\n }),\n);\n\n/**\n * Get OTEL environment variable with priority order.\n * Checks in order:\n * 1. OTEL_EXPORTER_OTLP_TRACES_{varName}\n * 2. OTEL_EXPORTER_OTLP_{varName}\n * 3. OTEL_{varName}\n *\n * @param varName - The variable name (e.g., 'ENDPOINT', 'HEADERS', 'PROTOCOL')\n * @returns The environment variable value or undefined if not found\n */\nexport const getOtelEnvVar = (varName: string): string | undefined => {\n const candidates = [\n `OTEL_EXPORTER_OTLP_TRACES_${varName}`,\n `OTEL_EXPORTER_OTLP_${varName}`,\n `OTEL_${varName}`,\n ];\n\n for (const candidate of candidates) {\n const value = process?.env?.[candidate];\n if (value) {\n return value;\n }\n }\n return undefined;\n};\n\n/**\n * Check if OTEL configuration is available.\n * @returns true if OTEL endpoint is configured\n */\nexport const hasOtelConfig = (): boolean => !!getOtelEnvVar('ENDPOINT');\n\n/**\n * Parse OTEL headers string into a record object.\n * Format: key1=value1,key2=value2\n * Values are URL-decoded.\n *\n * @param headersStr - Headers string in OTEL format\n * @returns Parsed headers object\n */\nexport const parseOtelHeaders = (headersStr: string | undefined): Record<string, string> => {\n if (!headersStr) {\n return {};\n }\n\n const headers: Record<string, string> = {};\n for (const pair of headersStr.split(',')) {\n const equalIndex = pair.indexOf('=');\n if (equalIndex !== -1) {\n // Manually split instead of .split('=', 2) because\n // the latter only returns the first 2 elements of the array after the split\n const key = pair.substring(0, equalIndex).trim();\n const value = pair.substring(equalIndex + 1).trim();\n headers[key] = decodeURIComponent(value);\n }\n }\n return headers;\n};\n\n/**\n * Validate that either Laminar API key or OTEL configuration is present.\n * Throws an error if neither is configured.\n *\n * @param apiKey - The Laminar API key (if provided)\n * @throws Error if neither API key nor OTEL configuration is present\n */\nexport const validateTracingConfig = (apiKey?: string): void => {\n if (!apiKey && !hasOtelConfig()) {\n throw new Error(\n 'Please initialize the Laminar object with your project API key ' +\n 'or set the LMNR_PROJECT_API_KEY environment variable, ' +\n 'or configure OTEL environment variables (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, etc.)',\n );\n }\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lmnr-ai/lmnr",
3
- "version": "0.7.13",
3
+ "version": "0.7.14",
4
4
  "description": "TypeScript SDK for Laminar AI",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -39,7 +39,7 @@
39
39
  "homepage": "https://github.com/lmnr-ai/lmnr-ts#README",
40
40
  "dependencies": {
41
41
  "@grpc/grpc-js": "^1.13.4",
42
- "@lmnr-ai/claude-code-proxy": "^0.1.5",
42
+ "@lmnr-ai/claude-code-proxy": "^0.1.6",
43
43
  "@opentelemetry/api": "^1.9.0",
44
44
  "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0",
45
45
  "@opentelemetry/core": "^1.30.1 || ^2.0.0",
@@ -1,4 +0,0 @@
1
- require('./utils-BmfIfQcB.cjs');
2
- const require_cli = require('./cli.cjs');
3
-
4
- exports.loadFromPaths = require_cli.loadFromPaths;
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils-BmfIfQcB.cjs","names":["ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string>","SpanAttributes","NIL_UUID: StringUUID","uuid","TraceFlags","value","headers: Record<string, string>"],"sources":["../src/opentelemetry-lib/tracing/attributes.ts","../src/utils.ts"],"sourcesContent":["import { SpanAttributes } from '@traceloop/ai-semantic-conventions';\n\nexport const SPAN_INPUT = \"lmnr.span.input\";\nexport const SPAN_OUTPUT = \"lmnr.span.output\";\nexport const SPAN_TYPE = \"lmnr.span.type\";\nexport const SPAN_PATH = \"lmnr.span.path\";\nexport const SPAN_IDS_PATH = \"lmnr.span.ids_path\";\nexport const PARENT_SPAN_PATH = \"lmnr.span.parent_path\";\nexport const PARENT_SPAN_IDS_PATH = \"lmnr.span.parent_ids_path\";\nexport const SPAN_INSTRUMENTATION_SOURCE = \"lmnr.span.instrumentation_source\";\nexport const SPAN_SDK_VERSION = \"lmnr.span.sdk_version\";\nexport const SPAN_LANGUAGE_VERSION = \"lmnr.span.language_version\";\nexport const OVERRIDE_PARENT_SPAN = \"lmnr.internal.override_parent_span\";\nexport const TRACE_HAS_BROWSER_SESSION = \"lmnr.internal.has_browser_session\";\nexport const EXTRACTED_FROM_NEXT_JS = \"lmnr.span.extracted_from.next_js\";\nexport const HUMAN_EVALUATOR_OPTIONS = 'lmnr.span.human_evaluator_options';\nexport const ASSOCIATION_PROPERTIES = \"lmnr.association.properties\";\nexport const SESSION_ID = \"lmnr.association.properties.session_id\";\nexport const USER_ID = \"lmnr.association.properties.user_id\";\nexport const TRACE_TYPE = \"lmnr.association.properties.trace_type\";\n\nexport const ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string> = {\n \"span_type\": SPAN_TYPE,\n};\n\nexport const LaminarAttributes = {\n // == This is the minimum set of attributes for a proper LLM span ==\n //\n // not SpanAttributes.LLM_USAGE_PROMPT_TOKENS\n INPUT_TOKEN_COUNT: \"gen_ai.usage.input_tokens\",\n // not SpanAttributes.LLM_USAGE_COMPLETION_TOKENS\n OUTPUT_TOKEN_COUNT: \"gen_ai.usage.output_tokens\",\n TOTAL_TOKEN_COUNT: SpanAttributes.LLM_USAGE_TOTAL_TOKENS,\n PROVIDER: SpanAttributes.LLM_SYSTEM,\n REQUEST_MODEL: SpanAttributes.LLM_REQUEST_MODEL,\n RESPONSE_MODEL: SpanAttributes.LLM_RESPONSE_MODEL,\n //\n // == End of minimum set ==\n // == Additional attributes ==\n //\n INPUT_COST: \"gen_ai.usage.input_cost\",\n OUTPUT_COST: \"gen_ai.usage.output_cost\",\n TOTAL_COST: \"gen_ai.usage.cost\",\n //\n // == End of additional attributes ==\n};\n","import { AttributeValue, SpanContext, TraceFlags } from '@opentelemetry/api';\nimport * as path from \"path\";\nimport pino, { Level } from 'pino';\nimport { PinoPretty } from 'pino-pretty';\nimport { fileURLToPath } from \"url\";\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { ASSOCIATION_PROPERTIES } from './opentelemetry-lib/tracing/attributes';\nimport { LaminarSpanContext, TraceType, TracingLevel } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(PinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n );\n};\n\nexport const otelTraceIdToUUID = (traceId: string): StringUUID => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n ) as StringUUID;\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if ((typeof record.spanId === 'string' && typeof record.traceId === 'string') ||\n (typeof record.span_id === 'string' && typeof record.trace_id === 'string')) {\n return {\n spanId: uuidToOtelSpanId(record?.spanId as string ?? record?.['span_id'] as string),\n traceId: uuidToOtelTraceId(record?.traceId as string ?? record?.['trace_id'] as string),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n\n/**\n * Deserialize a LaminarSpanContext from a string or record.\n * Handles both camelCase and snake_case keys for cross-language compatibility.\n *\n * @param data - The data to deserialize (string or record)\n * @returns The deserialized LaminarSpanContext\n * @throws Error if the data is invalid\n */\nexport const deserializeLaminarSpanContext = (\n data: Record<string, unknown> | string,\n): LaminarSpanContext => {\n if (typeof data === 'string') {\n try {\n const record = JSON.parse(data) as Record<string, unknown>;\n return deserializeLaminarSpanContext(record);\n } catch (e) {\n throw new Error(\n `Failed to parse LaminarSpanContext: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n if (!isRecord(data)) {\n throw new Error('Invalid LaminarSpanContext: must be a string or object');\n }\n\n // Handle both camelCase and snake_case for all fields\n const traceId = data.traceId ?? data.trace_id;\n const spanId = data.spanId ?? data.span_id;\n const isRemote = data.isRemote ?? data.is_remote ?? false;\n const spanPath = data.spanPath ?? data.span_path;\n const spanIdsPath = data.spanIdsPath ?? data.span_ids_path;\n const userId = data.userId ?? data.user_id;\n const sessionId = data.sessionId ?? data.session_id;\n const metadata = data.metadata;\n const traceType = data.traceType ?? data.trace_type;\n const tracingLevel = data.tracingLevel ?? data.tracing_level;\n\n if (typeof traceId !== 'string' || typeof spanId !== 'string') {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be strings');\n }\n\n // Validate UUID format\n if (!isStringUUID(traceId) || !isStringUUID(spanId)) {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be valid UUIDs');\n }\n\n return {\n traceId: traceId,\n spanId: spanId,\n isRemote: Boolean(isRemote),\n spanPath: Array.isArray(spanPath) ? spanPath as string[] : undefined,\n spanIdsPath: Array.isArray(spanIdsPath) ? spanIdsPath as StringUUID[] : undefined,\n userId: userId as string | undefined,\n sessionId: sessionId as string | undefined,\n metadata: metadata as Record<string, unknown> | undefined,\n traceType: traceType as TraceType | undefined,\n tracingLevel: tracingLevel as TracingLevel | undefined,\n };\n};\n\n\nexport const getDirname = () => {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n\n if (typeof import.meta?.url !== 'undefined') {\n return path.dirname(fileURLToPath(import.meta.url));\n }\n\n return process.cwd();\n};\n\nexport const slicePayload = <T>(value: T, length: number) => {\n if (value === null || value === undefined) {\n return value;\n }\n\n const str = JSON.stringify(value);\n if (str.length <= length) {\n return value;\n }\n\n return (str.slice(0, length) + '...');\n};\n\nexport const isOtelAttributeValueType = (value: unknown): value is AttributeValue => {\n if (typeof value === 'string'\n || typeof value === 'number'\n || typeof value === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n const allStrings = value.every(value => (value == null) || typeof value === 'string');\n const allNumbers = value.every(value => (value == null) || typeof value === 'number');\n const allBooleans = value.every(value => (value == null) || typeof value === 'boolean');\n return allStrings || allNumbers || allBooleans;\n }\n return false;\n};\n\nexport const metadataToAttributes = (\n metadata: Record<string, unknown>,\n): Record<string, AttributeValue> => Object.fromEntries(\n Object.entries(metadata).map(([key, value]) => {\n if (isOtelAttributeValueType(value)) {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, value];\n } else {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, JSON.stringify(value)];\n }\n }),\n);\n\n/**\n * Get OTEL environment variable with priority order.\n * Checks in order:\n * 1. OTEL_EXPORTER_OTLP_TRACES_{varName}\n * 2. OTEL_EXPORTER_OTLP_{varName}\n * 3. OTEL_{varName}\n *\n * @param varName - The variable name (e.g., 'ENDPOINT', 'HEADERS', 'PROTOCOL')\n * @returns The environment variable value or undefined if not found\n */\nexport const getOtelEnvVar = (varName: string): string | undefined => {\n const candidates = [\n `OTEL_EXPORTER_OTLP_TRACES_${varName}`,\n `OTEL_EXPORTER_OTLP_${varName}`,\n `OTEL_${varName}`,\n ];\n\n for (const candidate of candidates) {\n const value = process?.env?.[candidate];\n if (value) {\n return value;\n }\n }\n return undefined;\n};\n\n/**\n * Check if OTEL configuration is available.\n * @returns true if OTEL endpoint is configured\n */\nexport const hasOtelConfig = (): boolean => !!getOtelEnvVar('ENDPOINT');\n\n/**\n * Parse OTEL headers string into a record object.\n * Format: key1=value1,key2=value2\n * Values are URL-decoded.\n *\n * @param headersStr - Headers string in OTEL format\n * @returns Parsed headers object\n */\nexport const parseOtelHeaders = (headersStr: string | undefined): Record<string, string> => {\n if (!headersStr) {\n return {};\n }\n\n const headers: Record<string, string> = {};\n for (const pair of headersStr.split(',')) {\n const equalIndex = pair.indexOf('=');\n if (equalIndex !== -1) {\n // Manually split instead of .split('=', 2) because\n // the latter only returns the first 2 elements of the array after the split\n const key = pair.substring(0, equalIndex).trim();\n const value = pair.substring(equalIndex + 1).trim();\n headers[key] = decodeURIComponent(value);\n }\n }\n return headers;\n};\n\n/**\n * Validate that either Laminar API key or OTEL configuration is present.\n * Throws an error if neither is configured.\n *\n * @param apiKey - The Laminar API key (if provided)\n * @throws Error if neither API key nor OTEL configuration is present\n */\nexport const validateTracingConfig = (apiKey?: string): void => {\n if (!apiKey && !hasOtelConfig()) {\n throw new Error(\n 'Please initialize the Laminar object with your project API key ' +\n 'or set the LMNR_PROJECT_API_KEY environment variable, ' +\n 'or configure OTEL environment variables (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, etc.)',\n );\n }\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils-viuxL6V7.mjs","names":["ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string>","NIL_UUID: StringUUID","uuidv4","value","headers: Record<string, string>"],"sources":["../src/opentelemetry-lib/tracing/attributes.ts","../src/utils.ts"],"sourcesContent":["import { SpanAttributes } from '@traceloop/ai-semantic-conventions';\n\nexport const SPAN_INPUT = \"lmnr.span.input\";\nexport const SPAN_OUTPUT = \"lmnr.span.output\";\nexport const SPAN_TYPE = \"lmnr.span.type\";\nexport const SPAN_PATH = \"lmnr.span.path\";\nexport const SPAN_IDS_PATH = \"lmnr.span.ids_path\";\nexport const PARENT_SPAN_PATH = \"lmnr.span.parent_path\";\nexport const PARENT_SPAN_IDS_PATH = \"lmnr.span.parent_ids_path\";\nexport const SPAN_INSTRUMENTATION_SOURCE = \"lmnr.span.instrumentation_source\";\nexport const SPAN_SDK_VERSION = \"lmnr.span.sdk_version\";\nexport const SPAN_LANGUAGE_VERSION = \"lmnr.span.language_version\";\nexport const OVERRIDE_PARENT_SPAN = \"lmnr.internal.override_parent_span\";\nexport const TRACE_HAS_BROWSER_SESSION = \"lmnr.internal.has_browser_session\";\nexport const EXTRACTED_FROM_NEXT_JS = \"lmnr.span.extracted_from.next_js\";\nexport const HUMAN_EVALUATOR_OPTIONS = 'lmnr.span.human_evaluator_options';\nexport const ASSOCIATION_PROPERTIES = \"lmnr.association.properties\";\nexport const SESSION_ID = \"lmnr.association.properties.session_id\";\nexport const USER_ID = \"lmnr.association.properties.user_id\";\nexport const TRACE_TYPE = \"lmnr.association.properties.trace_type\";\n\nexport const ASSOCIATION_PROPERTIES_OVERRIDES: Record<string, string> = {\n \"span_type\": SPAN_TYPE,\n};\n\nexport const LaminarAttributes = {\n // == This is the minimum set of attributes for a proper LLM span ==\n //\n // not SpanAttributes.LLM_USAGE_PROMPT_TOKENS\n INPUT_TOKEN_COUNT: \"gen_ai.usage.input_tokens\",\n // not SpanAttributes.LLM_USAGE_COMPLETION_TOKENS\n OUTPUT_TOKEN_COUNT: \"gen_ai.usage.output_tokens\",\n TOTAL_TOKEN_COUNT: SpanAttributes.LLM_USAGE_TOTAL_TOKENS,\n PROVIDER: SpanAttributes.LLM_SYSTEM,\n REQUEST_MODEL: SpanAttributes.LLM_REQUEST_MODEL,\n RESPONSE_MODEL: SpanAttributes.LLM_RESPONSE_MODEL,\n //\n // == End of minimum set ==\n // == Additional attributes ==\n //\n INPUT_COST: \"gen_ai.usage.input_cost\",\n OUTPUT_COST: \"gen_ai.usage.output_cost\",\n TOTAL_COST: \"gen_ai.usage.cost\",\n //\n // == End of additional attributes ==\n};\n","import { AttributeValue, SpanContext, TraceFlags } from '@opentelemetry/api';\nimport * as path from \"path\";\nimport pino, { Level } from 'pino';\nimport { PinoPretty } from 'pino-pretty';\nimport { fileURLToPath } from \"url\";\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { ASSOCIATION_PROPERTIES } from './opentelemetry-lib/tracing/attributes';\nimport { LaminarSpanContext, TraceType, TracingLevel } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(PinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n );\n};\n\nexport const otelTraceIdToUUID = (traceId: string): StringUUID => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(\n /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/,\n '$1-$2-$3-$4-$5',\n ) as StringUUID;\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if ((typeof record.spanId === 'string' && typeof record.traceId === 'string') ||\n (typeof record.span_id === 'string' && typeof record.trace_id === 'string')) {\n return {\n spanId: uuidToOtelSpanId(record?.spanId as string ?? record?.['span_id'] as string),\n traceId: uuidToOtelTraceId(record?.traceId as string ?? record?.['trace_id'] as string),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n\n/**\n * Deserialize a LaminarSpanContext from a string or record.\n * Handles both camelCase and snake_case keys for cross-language compatibility.\n *\n * @param data - The data to deserialize (string or record)\n * @returns The deserialized LaminarSpanContext\n * @throws Error if the data is invalid\n */\nexport const deserializeLaminarSpanContext = (\n data: Record<string, unknown> | string,\n): LaminarSpanContext => {\n if (typeof data === 'string') {\n try {\n const record = JSON.parse(data) as Record<string, unknown>;\n return deserializeLaminarSpanContext(record);\n } catch (e) {\n throw new Error(\n `Failed to parse LaminarSpanContext: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n if (!isRecord(data)) {\n throw new Error('Invalid LaminarSpanContext: must be a string or object');\n }\n\n // Handle both camelCase and snake_case for all fields\n const traceId = data.traceId ?? data.trace_id;\n const spanId = data.spanId ?? data.span_id;\n const isRemote = data.isRemote ?? data.is_remote ?? false;\n const spanPath = data.spanPath ?? data.span_path;\n const spanIdsPath = data.spanIdsPath ?? data.span_ids_path;\n const userId = data.userId ?? data.user_id;\n const sessionId = data.sessionId ?? data.session_id;\n const metadata = data.metadata;\n const traceType = data.traceType ?? data.trace_type;\n const tracingLevel = data.tracingLevel ?? data.tracing_level;\n\n if (typeof traceId !== 'string' || typeof spanId !== 'string') {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be strings');\n }\n\n // Validate UUID format\n if (!isStringUUID(traceId) || !isStringUUID(spanId)) {\n throw new Error('Invalid LaminarSpanContext: traceId and spanId must be valid UUIDs');\n }\n\n return {\n traceId: traceId,\n spanId: spanId,\n isRemote: Boolean(isRemote),\n spanPath: Array.isArray(spanPath) ? spanPath as string[] : undefined,\n spanIdsPath: Array.isArray(spanIdsPath) ? spanIdsPath as StringUUID[] : undefined,\n userId: userId as string | undefined,\n sessionId: sessionId as string | undefined,\n metadata: metadata as Record<string, unknown> | undefined,\n traceType: traceType as TraceType | undefined,\n tracingLevel: tracingLevel as TracingLevel | undefined,\n };\n};\n\n\nexport const getDirname = () => {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n\n if (typeof import.meta?.url !== 'undefined') {\n return path.dirname(fileURLToPath(import.meta.url));\n }\n\n return process.cwd();\n};\n\nexport const slicePayload = <T>(value: T, length: number) => {\n if (value === null || value === undefined) {\n return value;\n }\n\n const str = JSON.stringify(value);\n if (str.length <= length) {\n return value;\n }\n\n return (str.slice(0, length) + '...');\n};\n\nexport const isOtelAttributeValueType = (value: unknown): value is AttributeValue => {\n if (typeof value === 'string'\n || typeof value === 'number'\n || typeof value === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n const allStrings = value.every(value => (value == null) || typeof value === 'string');\n const allNumbers = value.every(value => (value == null) || typeof value === 'number');\n const allBooleans = value.every(value => (value == null) || typeof value === 'boolean');\n return allStrings || allNumbers || allBooleans;\n }\n return false;\n};\n\nexport const metadataToAttributes = (\n metadata: Record<string, unknown>,\n): Record<string, AttributeValue> => Object.fromEntries(\n Object.entries(metadata).map(([key, value]) => {\n if (isOtelAttributeValueType(value)) {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, value];\n } else {\n return [`${ASSOCIATION_PROPERTIES}.metadata.${key}`, JSON.stringify(value)];\n }\n }),\n);\n\n/**\n * Get OTEL environment variable with priority order.\n * Checks in order:\n * 1. OTEL_EXPORTER_OTLP_TRACES_{varName}\n * 2. OTEL_EXPORTER_OTLP_{varName}\n * 3. OTEL_{varName}\n *\n * @param varName - The variable name (e.g., 'ENDPOINT', 'HEADERS', 'PROTOCOL')\n * @returns The environment variable value or undefined if not found\n */\nexport const getOtelEnvVar = (varName: string): string | undefined => {\n const candidates = [\n `OTEL_EXPORTER_OTLP_TRACES_${varName}`,\n `OTEL_EXPORTER_OTLP_${varName}`,\n `OTEL_${varName}`,\n ];\n\n for (const candidate of candidates) {\n const value = process?.env?.[candidate];\n if (value) {\n return value;\n }\n }\n return undefined;\n};\n\n/**\n * Check if OTEL configuration is available.\n * @returns true if OTEL endpoint is configured\n */\nexport const hasOtelConfig = (): boolean => !!getOtelEnvVar('ENDPOINT');\n\n/**\n * Parse OTEL headers string into a record object.\n * Format: key1=value1,key2=value2\n * Values are URL-decoded.\n *\n * @param headersStr - Headers string in OTEL format\n * @returns Parsed headers object\n */\nexport const parseOtelHeaders = (headersStr: string | undefined): Record<string, string> => {\n if (!headersStr) {\n return {};\n }\n\n const headers: Record<string, string> = {};\n for (const pair of headersStr.split(',')) {\n const equalIndex = pair.indexOf('=');\n if (equalIndex !== -1) {\n // Manually split instead of .split('=', 2) because\n // the latter only returns the first 2 elements of the array after the split\n const key = pair.substring(0, equalIndex).trim();\n const value = pair.substring(equalIndex + 1).trim();\n headers[key] = decodeURIComponent(value);\n }\n }\n return headers;\n};\n\n/**\n * Validate that either Laminar API key or OTEL configuration is present.\n * Throws an error if neither is configured.\n *\n * @param apiKey - The Laminar API key (if provided)\n * @throws Error if neither API key nor OTEL configuration is present\n */\nexport const validateTracingConfig = (apiKey?: string): void => {\n if (!apiKey && !hasOtelConfig()) {\n throw new Error(\n 'Please initialize the Laminar object with your project API key ' +\n 'or set the LMNR_PROJECT_API_KEY environment variable, ' +\n 'or configure OTEL environment variables (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, etc.)',\n );\n }\n};\n"],"mappings":"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"}