neatlogs 1.0.10 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/anthropic.cjs +32 -28
  2. package/dist/anthropic.cjs.map +1 -1
  3. package/dist/anthropic.mjs +22 -18
  4. package/dist/anthropic.mjs.map +1 -1
  5. package/dist/azure-openai.cjs +32 -28
  6. package/dist/azure-openai.cjs.map +1 -1
  7. package/dist/azure-openai.mjs +22 -18
  8. package/dist/azure-openai.mjs.map +1 -1
  9. package/dist/bedrock.cjs +36 -32
  10. package/dist/bedrock.cjs.map +1 -1
  11. package/dist/bedrock.mjs +22 -18
  12. package/dist/bedrock.mjs.map +1 -1
  13. package/dist/browser.cjs +27 -0
  14. package/dist/browser.cjs.map +1 -1
  15. package/dist/browser.d.ts +30 -0
  16. package/dist/browser.mjs +27 -0
  17. package/dist/browser.mjs.map +1 -1
  18. package/dist/index.cjs +389 -339
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +32 -2
  21. package/dist/index.mjs +275 -225
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/langchain.cjs +32 -28
  24. package/dist/langchain.cjs.map +1 -1
  25. package/dist/langchain.mjs +15 -11
  26. package/dist/langchain.mjs.map +1 -1
  27. package/dist/openai.cjs +33 -29
  28. package/dist/openai.cjs.map +1 -1
  29. package/dist/openai.mjs +22 -18
  30. package/dist/openai.mjs.map +1 -1
  31. package/dist/opencode-plugin.cjs +1 -1
  32. package/dist/opencode-plugin.cjs.map +1 -1
  33. package/dist/opencode-plugin.mjs +1 -1
  34. package/dist/opencode-plugin.mjs.map +1 -1
  35. package/dist/openrouter-agent.cjs +28 -24
  36. package/dist/openrouter-agent.cjs.map +1 -1
  37. package/dist/openrouter-agent.mjs +20 -16
  38. package/dist/openrouter-agent.mjs.map +1 -1
  39. package/dist/vertex-ai.cjs +38 -34
  40. package/dist/vertex-ai.cjs.map +1 -1
  41. package/dist/vertex-ai.mjs +28 -24
  42. package/dist/vertex-ai.mjs.map +1 -1
  43. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/anthropic.ts","../src/core/auto-root.ts","../src/core/context.ts","../src/prompt/template.ts","../src/core/logger.ts","../src/core/mask.ts","../src/decorators/base.ts"],"sourcesContent":["/**\n * Neatlogs Anthropic wrapper — ES6 Proxy-based.\n *\n * Usage:\n * import { wrapAnthropic, traceTool } from 'neatlogs/anthropic';\n * import Anthropic from '@anthropic-ai/sdk';\n * const client = wrapAnthropic(new Anthropic());\n *\n * Intercepts:\n * - client.messages.create() — non-streaming and streaming (stream: true)\n * - client.messages.stream() — Anthropic's streaming helper (MessageStream)\n *\n * Also exports traceTool() to wrap user-defined tool functions with TOOL spans.\n * Handles thinking blocks, tool_use blocks, and cache tokens.\n */\n\nimport { trace, context as otelContext, SpanStatusCode, type Span } from '@opentelemetry/api';\nimport { getProviderTracer } from './core/auto-root.js';\n\nconst TRACER_NAME = 'neatlogs.anthropic';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function wrapAnthropic<T extends object>(client: T): T {\n return wrapNamespace(client, []) as T;\n}\n\n/**\n * Wrap a tool/function implementation to emit TOOL spans when executed.\n *\n * Usage:\n * const getWeather = traceTool('get_weather', async (input: { city: string }) => {\n * return `Weather in ${input.city}: sunny`;\n * });\n */\nexport function traceTool<TInput = any, TResult = any>(\n name: string,\n fn: (input: TInput) => TResult | Promise<TResult>,\n): (input: TInput) => Promise<TResult> {\n return async function tracedTool(input: TInput): Promise<TResult> {\n const tracer = getProviderTracer(TRACER_NAME);\n return tracer.startActiveSpan(\n `tool.${name}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': name,\n 'input.value': safeStringify(input),\n },\n },\n otelContext.active(),\n async (span) => {\n try {\n const result = await fn(input);\n span.setAttribute('output.value', safeStringify(result));\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// Proxy wrapping\n// ---------------------------------------------------------------------------\n\nfunction wrapNamespace(target: any, path: string[]): any {\n return new Proxy(target, {\n get(obj, prop, receiver) {\n const value = Reflect.get(obj, prop, receiver);\n if (typeof prop === 'symbol' || String(prop).startsWith('_')) return value;\n\n const currentPath = [...path, String(prop)];\n const pathStr = currentPath.join('.');\n\n if (pathStr === 'messages.create' && typeof value === 'function') {\n return tracedMessagesCreate(value.bind(obj));\n }\n if (pathStr === 'messages.stream' && typeof value === 'function') {\n return tracedMessagesStream(value.bind(obj));\n }\n\n if (value && typeof value === 'object' && !Array.isArray(value) && isNamespace(currentPath)) {\n return wrapNamespace(value, currentPath);\n }\n\n return value;\n },\n });\n}\n\nfunction isNamespace(path: string[]): boolean {\n if (path.length > 3) return false;\n const key = path[path.length - 1];\n return ['messages', 'beta'].includes(key);\n}\n\n// ---------------------------------------------------------------------------\n// messages.create — non-streaming + streaming (stream: true)\n// ---------------------------------------------------------------------------\n\nfunction tracedMessagesCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n const messages: any[] = opts?.messages ?? [];\n const isStream = opts?.stream === true;\n\n const span = tracer.startSpan('anthropic.messages.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n 'neatlogs.llm.model_name': model,\n 'neatlogs.llm.is_streaming': isStream,\n },\n }, otelContext.active());\n\n setInputMessages(span, opts?.system, messages);\n setTools(span, opts?.tools);\n setInvocationParams(span, opts);\n\n const ctx = trace.setSpan(otelContext.active(), span);\n const promise = otelContext.with(ctx, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n if (isStream) {\n return wrapStreamIterable(response, span);\n }\n finalizeMessageResponse(span, response);\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// messages.stream() — returns a MessageStream object\n// ---------------------------------------------------------------------------\n\nfunction tracedMessagesStream(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n const messages: any[] = opts?.messages ?? [];\n\n const span = tracer.startSpan('anthropic.messages.stream', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n 'neatlogs.llm.model_name': model,\n 'neatlogs.llm.is_streaming': true,\n },\n }, otelContext.active());\n\n setInputMessages(span, opts?.system, messages);\n setTools(span, opts?.tools);\n setInvocationParams(span, opts);\n\n const ctx = trace.setSpan(otelContext.active(), span);\n const messageStream = otelContext.with(ctx, () => original(opts, ...rest));\n\n return wrapMessageStream(messageStream, span);\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming: async iterable (stream: true returns events)\n// ---------------------------------------------------------------------------\n\nfunction wrapStreamIterable(stream: any, span: Span): any {\n const events: any[] = [];\n const originalAsyncIterator = stream[Symbol.asyncIterator]?.bind(stream);\n\n if (!originalAsyncIterator) {\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return stream;\n }\n\n const wrapped = Object.create(Object.getPrototypeOf(stream));\n Object.assign(wrapped, stream);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await iterator.next();\n if (result.done) {\n finalizeStreamEvents(span, events);\n return result;\n }\n events.push(result.value);\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n finalizeStreamEvents(span, events);\n return iterator.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n recordError(span, err);\n return iterator.throw?.(err) ?? { done: true, value: undefined };\n },\n };\n };\n\n if (typeof stream.finalMessage === 'function') {\n const origFinal = stream.finalMessage.bind(stream);\n wrapped.finalMessage = async function () {\n return origFinal();\n };\n }\n\n return wrapped;\n}\n\n// ---------------------------------------------------------------------------\n// Streaming: MessageStream (.on('end'), .finalMessage())\n// ---------------------------------------------------------------------------\n\nfunction wrapMessageStream(messageStream: any, span: Span): any {\n const origOn = messageStream.on?.bind(messageStream);\n if (origOn) {\n origOn('end', () => {\n const finalMsg = messageStream._finalMessage ?? messageStream.currentMessage;\n if (finalMsg) {\n finalizeMessageResponse(span, finalMsg);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n }\n });\n origOn('error', (err: any) => {\n if (span.isRecording()) recordError(span, err);\n });\n }\n\n if (typeof messageStream.finalMessage === 'function') {\n const origFinal = messageStream.finalMessage.bind(messageStream);\n messageStream.finalMessage = async function () {\n const result = await origFinal();\n if (span.isRecording()) {\n finalizeMessageResponse(span, result);\n }\n return result;\n };\n }\n\n return messageStream;\n}\n\n// ---------------------------------------------------------------------------\n// Stream event finalization\n// ---------------------------------------------------------------------------\n\nfunction finalizeStreamEvents(span: Span, events: any[]): void {\n const textParts: string[] = [];\n const thinkingParts: string[] = [];\n const toolCalls: Array<{ id: string; name: string; input: string }> = [];\n let currentToolInput = '';\n let currentToolId = '';\n let currentToolName = '';\n let usage: any = null;\n let model = '';\n let stopReason = '';\n\n for (const event of events) {\n const type = event?.type ?? '';\n\n if (type === 'content_block_delta') {\n const delta = event?.delta;\n if (delta?.type === 'text_delta' && delta.text) textParts.push(delta.text);\n else if (delta?.type === 'thinking_delta' && delta.thinking) thinkingParts.push(delta.thinking);\n else if (delta?.type === 'input_json_delta' && delta.partial_json) currentToolInput += delta.partial_json;\n } else if (type === 'content_block_start') {\n const block = event?.content_block;\n if (block?.type === 'tool_use') {\n currentToolId = block.id ?? '';\n currentToolName = block.name ?? '';\n currentToolInput = '';\n }\n } else if (type === 'content_block_stop') {\n if (currentToolName) {\n toolCalls.push({ id: currentToolId, name: currentToolName, input: currentToolInput });\n currentToolId = '';\n currentToolName = '';\n currentToolInput = '';\n }\n } else if (type === 'message_start') {\n const msg = event?.message;\n if (msg?.model) model = msg.model;\n if (msg?.usage) usage = msg.usage;\n } else if (type === 'message_delta') {\n const delta = event?.delta;\n if (delta?.stop_reason) stopReason = delta.stop_reason;\n if (event?.usage) usage = { ...(usage ?? {}), ...event.usage };\n }\n }\n\n const fullText = textParts.join('');\n if (fullText) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', fullText);\n }\n\n const thinking = thinkingParts.join('');\n if (thinking) {\n span.setAttribute('neatlogs.llm.output_messages.0.thinking', thinking);\n }\n\n for (let j = 0; j < toolCalls.length; j++) {\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, toolCalls[j].id);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, toolCalls[j].name);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, toolCalls[j].input);\n }\n\n if (model) span.setAttribute('neatlogs.llm.model_name', model);\n if (stopReason) span.setAttribute('neatlogs.llm.stop_reason', stopReason);\n setUsageAttrs(span, usage);\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Non-streaming response finalization\n// ---------------------------------------------------------------------------\n\nfunction finalizeMessageResponse(span: Span, response: any): void {\n const content: any[] = response?.content ?? [];\n const textParts: string[] = [];\n const thinkingParts: string[] = [];\n const toolCalls: Array<{ id: string; name: string; input: string }> = [];\n\n for (const block of content) {\n if (block.type === 'text' && block.text) textParts.push(block.text);\n else if (block.type === 'thinking' && block.thinking) thinkingParts.push(block.thinking);\n else if (block.type === 'tool_use') {\n toolCalls.push({\n id: block.id ?? '',\n name: block.name ?? '',\n input: safeStringify(block.input ?? {}),\n });\n }\n }\n\n if (textParts.length) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', textParts.join(''));\n }\n if (thinkingParts.length) {\n span.setAttribute('neatlogs.llm.output_messages.0.thinking', thinkingParts.join(''));\n }\n\n for (let j = 0; j < toolCalls.length; j++) {\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, toolCalls[j].id);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, toolCalls[j].name);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, toolCalls[j].input);\n }\n\n setUsageAttrs(span, response?.usage);\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n if (response?.stop_reason) span.setAttribute('neatlogs.llm.stop_reason', response.stop_reason);\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction setInputMessages(span: Span, system: any, messages: any[]): void {\n let idx = 0;\n if (system) {\n const content = typeof system === 'string' ? system : safeStringify(system);\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, 'system');\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, content);\n idx++;\n }\n for (const msg of messages) {\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, msg.role ?? '');\n if (typeof msg.content === 'string') {\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, msg.content);\n } else if (msg.content) {\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, safeStringify(msg.content));\n }\n idx++;\n }\n}\n\nfunction setTools(span: Span, tools: any[] | undefined): void {\n if (!tools) return;\n for (let i = 0; i < tools.length; i++) {\n const tool = tools[i];\n span.setAttribute(`neatlogs.llm.tools.${i}.name`, tool.name ?? '');\n if (tool.description) span.setAttribute(`neatlogs.llm.tools.${i}.description`, tool.description);\n if (tool.input_schema) span.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify(tool.input_schema));\n }\n}\n\nfunction setInvocationParams(span: Span, opts: any): void {\n if (opts?.temperature != null) span.setAttribute('neatlogs.llm.temperature', opts.temperature);\n if (opts?.top_p != null) span.setAttribute('neatlogs.llm.top_p', opts.top_p);\n if (opts?.top_k != null) span.setAttribute('neatlogs.llm.top_k', opts.top_k);\n if (opts?.max_tokens != null) span.setAttribute('neatlogs.llm.max_tokens', opts.max_tokens);\n if (opts?.stop_sequences) span.setAttribute('neatlogs.llm.stop_sequences', safeStringify(opts.stop_sequences));\n}\n\nfunction setUsageAttrs(span: Span, usage: any): void {\n if (!usage) return;\n if (usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.input_tokens);\n if (usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.output_tokens);\n if (usage.cache_read_input_tokens != null) span.setAttribute('neatlogs.llm.token_count.cache_read', usage.cache_read_input_tokens);\n if (usage.cache_creation_input_tokens != null) span.setAttribute('neatlogs.llm.token_count.cache_write', usage.cache_creation_input_tokens);\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return typeof value === 'string' ? value : JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction recordError(span: Span, err: unknown): void {\n if (err instanceof Error) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });\n span.recordException(err);\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });\n }\n span.end();\n}\n","/**\n * Auto-root for direct-provider wrappers.\n *\n * The backend only renders a trace once it contains a *parentless* span of a\n * root-eligible kind (WORKFLOW / CHAIN / AGENT / MCP_TOOL). Direct-provider\n * wrappers (wrapOpenAI, wrapAnthropic, wrapBedrock, ...) only ever emit\n * non-root spans (LLM / EMBEDDING / TOOL). So a bare\n * `const client = wrapOpenAI(new OpenAI())` with no surrounding `span()` /\n * `trace()` produces an orphan span and the trace never renders.\n *\n * `getProviderTracer(name)` returns a Tracer facade used *only* by those\n * direct-provider wrappers: when `startSpan` would otherwise produce a\n * parentless, non-root span, it transparently opens a WORKFLOW root (named\n * after the configured `workflowName`) and ends it when the provider span\n * ends. Every wrapper path — non-streaming, streaming finalize, error —\n * funnels through `span.end()`, so wrapping `end()` covers them all with no\n * changes to the wrappers' streaming code.\n *\n * Framework wrappers (langchain, mastra, openai-agents, ai-sdk, ...) keep\n * using `trace.getTracer(...)` directly — they already emit their own root and\n * thread context explicitly, so auto-root must never fire for them.\n */\n\nimport {\n trace as otelTrace,\n context as otelContext,\n type Tracer,\n type Span,\n type Context,\n type SpanOptions,\n} from '@opentelemetry/api';\n\nimport { getSessionConfig } from './context.js';\n\n// A parentless span of one of these kinds already satisfies the backend's\n// root requirement, so it must NOT be wrapped in another root.\nconst ROOT_KINDS = new Set(['workflow', 'chain', 'agent', 'mcp_tool']);\n\nfunction autoRootEnabled(): boolean {\n const val = (process.env.NEATLOGS_AUTO_ROOT ?? '').trim().toLowerCase();\n return !['false', '0', 'no', 'off'].includes(val);\n}\n\nfunction resolveRootWorkflowName(): string {\n try {\n const name = getSessionConfig()?.workflowName;\n if (typeof name === 'string' && name.trim()) return name;\n } catch {\n /* ignore */\n }\n return 'workflow';\n}\n\n/**\n * Wrap a provider span so that ending it also ends the auto-created WORKFLOW\n * root. A Proxy keeps this transparent across OTel versions: every property /\n * method except `end` passes straight through to the child span.\n */\nfunction wrapSpanWithRoot(child: Span, root: Span): Span {\n let ended = false;\n return new Proxy(child, {\n get(target, prop, _receiver) {\n if (prop === 'end') {\n return (...args: any[]): void => {\n if (ended) return;\n ended = true;\n try {\n (target.end as (...a: any[]) => void)(...args);\n } finally {\n try {\n root.end();\n } catch {\n /* ignore */\n }\n }\n };\n }\n const value = Reflect.get(target, prop, target);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\n/**\n * Self-root helper for callback/event-driven handlers (LangChain, etc.).\n *\n * A callback handler builds its own parent context per run-id rather than going\n * through {@link AutoRootTracer}. When a run BEGINS with a non-root kind (a bare\n * `llm.invoke()` fires the LLM callback with no chain above it; a standalone\n * tool/retriever similarly), the span it creates is parentless and non-root, so\n * the backend can't anchor the trace and it never renders. Call this when there\n * is no LangChain parent: if the kind is non-root and nothing is actively\n * recording in `baseCtx`, it opens a WORKFLOW root and returns it plus the\n * child context to create the span in. The caller MUST end the returned\n * `root` after the child span ends (see {@link endAutoRoot}).\n *\n * Returns `{ root, ctx }` when a root was opened, or `{ root: undefined, ctx: baseCtx }`\n * when no auto-root is needed (root-eligible kind, already-recording parent, or\n * disabled).\n */\nexport function maybeOpenAutoRoot(\n tracer: Tracer,\n kind: string,\n baseCtx: Context,\n): { root: Span | undefined; ctx: Context } {\n const k = String(kind ?? '').toLowerCase();\n const existing = otelTrace.getSpan(baseCtx);\n if (\n !autoRootEnabled() ||\n ROOT_KINDS.has(k) ||\n (existing !== undefined && existing.isRecording())\n ) {\n return { root: undefined, ctx: baseCtx };\n }\n const root = tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n baseCtx,\n );\n return { root, ctx: otelTrace.setSpan(baseCtx, root) };\n}\n\n/** End an auto-root opened by {@link maybeOpenAutoRoot}. Safe on undefined. */\nexport function endAutoRoot(root: Span | undefined): void {\n if (!root) return;\n try {\n root.end();\n } catch {\n /* ignore */\n }\n}\n\nclass AutoRootTracer implements Tracer {\n constructor(private readonly _tracer: Tracer) {}\n\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const kind = String(\n (options?.attributes as Record<string, unknown> | undefined)?.[\n 'neatlogs.span.kind'\n ] ?? '',\n ).toLowerCase();\n\n const ctx = context ?? otelContext.active();\n const parent = otelTrace.getSpan(ctx);\n const needsRoot =\n autoRootEnabled() &&\n !ROOT_KINDS.has(kind) &&\n !(parent !== undefined && parent.isRecording());\n\n if (!needsRoot) {\n return this._tracer.startSpan(name, options, context);\n }\n\n const root = this._tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n ctx,\n );\n const childCtx = otelTrace.setSpan(ctx, root);\n const child = this._tracer.startSpan(name, options, childCtx);\n return wrapSpanWithRoot(child, root);\n }\n\n // startActiveSpan is used by traceTool (callback-scoped, always under an\n // active span in practice) — pass straight through, no auto-root.\n startActiveSpan = ((...args: any[]) =>\n (this._tracer.startActiveSpan as (...a: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'];\n}\n\n/**\n * Tracer for direct-provider wrappers (wrapOpenAI, wrapAnthropic, ...).\n * Identical to `trace.getTracer(name)` but adds transparent auto-root so a\n * bare `wrapOpenAI(new OpenAI())` renders a trace without a manual `span()` /\n * `trace()` wrapper. Do NOT use for framework wrappers.\n */\nexport function getProviderTracer(name: string): Tracer {\n return new AutoRootTracer(otelTrace.getTracer(name));\n}\n","/**\n * Context wrapper for manual span creation.\n *\n * Provides the `trace()` function — the TypeScript equivalent of the Python\n * `trace()` context manager. Creates an OTel span, sets prompt template/variable\n * context, and executes the user callback within the span.\n *\n * @example\n * ```typescript\n * await trace({ name: 'my-trace' }, async (span) => {\n * // user code runs here with the span active\n * });\n * ```\n */\n\nimport {\n trace as otelTrace,\n context as otelContext,\n createContextKey,\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span,\n} from '@opentelemetry/api';\nimport { PromptContext, UserPromptContext, PromptTemplate, UserPromptTemplate } from '../prompt/template.js';\nimport { registerMask } from './mask.js';\n\nimport { getLogger } from './logger.js';\nimport { safeJsonDumps, serializeObj } from '../decorators/base.js';\nimport type { TraceOptions, MaskFunction } from '../types.js';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Module-level session config (set by init.ts via _setSessionConfig)\n// ---------------------------------------------------------------------------\n\nlet _sessionConfig: Record<string, any> = {};\n\n/**\n * Set the session configuration. Called by init() during SDK setup.\n * @internal\n */\nexport function _setSessionConfig(config: Record<string, any>): void {\n _sessionConfig = config;\n}\n\n/**\n * Get a copy of the current session configuration.\n */\nexport function getSessionConfig(): Record<string, any> {\n return { ..._sessionConfig };\n}\n\n// ---------------------------------------------------------------------------\n// OTel context keys for prompt data propagation\n// ---------------------------------------------------------------------------\n\nexport const PROMPT_VARIABLES_KEY = createContextKey('neatlogs.prompt_variables');\nexport const PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.prompt_template');\nexport const PROMPT_VERSION_KEY = createContextKey('neatlogs.prompt_version');\nexport const USER_PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.user_prompt_template');\nexport const USER_PROMPT_VARIABLES_KEY = createContextKey('neatlogs.user_prompt_variables');\n\n// ---------------------------------------------------------------------------\n// Known TraceOptions keys (not forwarded as extra span attributes)\n// ---------------------------------------------------------------------------\n\nconst KNOWN_OPTION_KEYS = new Set([\n 'name',\n 'kind',\n 'input',\n 'promptTemplate',\n 'promptVariables',\n 'userPromptTemplate',\n 'userPromptVariables',\n 'version',\n 'mask',\n 'attributes',\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Set standard and extra attributes on a span.\n * @internal\n */\nexport function _setSpanAttributes(\n span: Span,\n kind: string | undefined,\n attributes: Record<string, any>,\n): void {\n span.setAttribute('neatlogs.internal', true);\n span.setAttribute('openinference.span.kind', kind ?? 'CHAIN');\n\n for (const [key, value] of Object.entries(attributes)) {\n span.setAttribute(key, value);\n }\n}\n\n/**\n * After the user callback completes, capture prompt variables that were\n * set by PromptTemplate.compile() / UserPromptTemplate.compile() and\n * record them as span attributes.\n * @internal\n */\nexport function _finalizePromptCapture(\n span: Span,\n isPromptTemplateObj: boolean,\n isUserPromptTemplateObj: boolean,\n): void {\n if (isPromptTemplateObj) {\n const capturedVars = PromptContext.getVariables();\n if (capturedVars) {\n span.setAttribute(\n 'llm.prompt_template_variables',\n JSON.stringify(capturedVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from PromptContext: ${Object.keys(capturedVars).join(', ')}`,\n );\n }\n }\n\n if (isUserPromptTemplateObj) {\n const capturedUserVars = UserPromptContext.getVariables();\n if (capturedUserVars) {\n span.setAttribute(\n 'llm.user_prompt_template_variables',\n JSON.stringify(capturedUserVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from UserPromptContext: ${Object.keys(capturedUserVars).join(', ')}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main trace() function\n// ---------------------------------------------------------------------------\n\n/**\n * Async callback wrapper for manual span creation with prompt tracking.\n *\n * Creates an OTel span, optionally sets prompt template/variable context,\n * and executes the provided callback within the span.\n *\n * **Session-Aware Trace Creation:**\n * - If `session_id` is set in init() AND no active parent span exists,\n * this creates a NEW root trace (for multi-turn conversations).\n * - Otherwise, creates a normal child span within the existing trace.\n *\n * @param options - Trace configuration options\n * @param fn - Callback that receives the active span\n * @returns The return value of the callback\n *\n * @example\n * ```typescript\n * // Basic usage\n * await trace({ name: 'my-pipeline' }, async (span) => {\n * await step1();\n * await step2();\n * });\n *\n * // With prompt template\n * const template = new PromptTemplate('Hello {{name}}');\n * await trace({ name: 'prompt', promptTemplate: template }, async (span) => {\n * const rendered = template.compile({ name: 'world' });\n * // ...\n * });\n * ```\n */\nexport async function trace<T>(\n options: TraceOptions,\n fn: (span: Span) => T | Promise<T>,\n): Promise<T> {\n const {\n name,\n kind,\n input,\n promptTemplate,\n promptVariables,\n userPromptTemplate,\n userPromptVariables,\n version,\n mask,\n attributes: explicitAttributes,\n ...extraOptions\n } = options;\n\n const sessionConfig = getSessionConfig();\n const sessionId = sessionConfig.sessionId;\n\n // Determine whether we are inside an existing active trace\n const currentSpan = otelTrace.getSpan(otelContext.active());\n const isInActiveTrace = currentSpan !== undefined && currentSpan.isRecording();\n const shouldCreateRootTrace = !!sessionId && !isInActiveTrace;\n\n // ---------------------------------------------------------------------------\n // Process prompt templates\n // ---------------------------------------------------------------------------\n\n let templateString: string | undefined;\n let isPromptTemplateObj = false;\n\n if (promptTemplate !== undefined) {\n if (promptTemplate instanceof PromptTemplate) {\n isPromptTemplateObj = true;\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using PromptTemplate object with variables: ${promptTemplate.variables.join(', ')}`,\n );\n } else if (typeof promptTemplate === 'string') {\n templateString = promptTemplate;\n } else {\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n let userTemplateString: string | undefined;\n let isUserPromptTemplateObj = false;\n\n if (userPromptTemplate !== undefined) {\n if (userPromptTemplate instanceof UserPromptTemplate) {\n isUserPromptTemplateObj = true;\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using UserPromptTemplate object with variables: ${userPromptTemplate.variables.join(', ')}`,\n );\n } else if (typeof userPromptTemplate === 'string') {\n userTemplateString = userPromptTemplate;\n } else {\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Build OTel context with prompt values\n // ---------------------------------------------------------------------------\n\n let ctx = otelContext.active();\n\n const variablesJson = promptVariables ? JSON.stringify(promptVariables) : undefined;\n const userVariablesJson = userPromptVariables ? JSON.stringify(userPromptVariables) : undefined;\n\n if (variablesJson) {\n ctx = ctx.setValue(PROMPT_VARIABLES_KEY, variablesJson);\n logger.debug(`[trace] Set neatlogs.prompt_variables in context: ${variablesJson}`);\n }\n if (templateString) {\n ctx = ctx.setValue(PROMPT_TEMPLATE_KEY, templateString);\n logger.debug(`[trace] Set neatlogs.prompt_template in context: ${templateString}`);\n }\n if (userVariablesJson) {\n ctx = ctx.setValue(USER_PROMPT_VARIABLES_KEY, userVariablesJson);\n logger.debug(`[trace] Set neatlogs.user_prompt_variables in context: ${userVariablesJson}`);\n }\n if (userTemplateString) {\n ctx = ctx.setValue(USER_PROMPT_TEMPLATE_KEY, userTemplateString);\n logger.debug(`[trace] Set neatlogs.user_prompt_template in context: ${userTemplateString}`);\n }\n if (version) {\n ctx = ctx.setValue(PROMPT_VERSION_KEY, version);\n logger.debug(`[trace] Set neatlogs.prompt_version in context: ${version}`);\n }\n\n // ---------------------------------------------------------------------------\n // Collect extra attributes (non-standard option keys)\n // ---------------------------------------------------------------------------\n\n const extraAttributes: Record<string, any> = { ...(explicitAttributes ?? {}) };\n for (const [key, value] of Object.entries(extraOptions)) {\n if (!KNOWN_OPTION_KEYS.has(key)) {\n extraAttributes[key] = value;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Create span and execute callback\n // ---------------------------------------------------------------------------\n\n const tracer = otelTrace.getTracer('neatlogs.trace');\n\n const spanCallback = async (span: Span): Promise<T> => {\n _setSpanAttributes(span, kind, extraAttributes);\n\n if (input !== undefined && input !== null) {\n span.setAttribute('input.value', safeJsonDumps(serializeObj(input)));\n }\n\n if (mask) {\n const maskId = registerMask(mask);\n span.setAttribute('neatlogs.mask_id', maskId);\n }\n\n try {\n const result: T = await fn(span);\n\n _finalizePromptCapture(span, isPromptTemplateObj, isUserPromptTemplateObj);\n\n if (result !== undefined && result !== null) {\n const spanAttrs = (span as any).attributes ?? (span as any)._attributes;\n const hasOutput = spanAttrs && (\n 'output.value' in spanAttrs || 'neatlogs.output.value' in spanAttrs\n );\n if (!hasOutput) {\n span.setAttribute('output.value', safeJsonDumps(serializeObj(result)));\n }\n }\n\n return result;\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n } finally {\n if (isPromptTemplateObj) {\n PromptContext.clear();\n }\n if (isUserPromptTemplateObj) {\n UserPromptContext.clear();\n }\n span.end();\n }\n };\n\n if (shouldCreateRootTrace) {\n logger.debug(`[trace] Creating NEW root trace '${name}' (sessionId=${sessionId})`);\n return tracer.startActiveSpan(name, {}, ROOT_CONTEXT, spanCallback);\n } else {\n logger.debug(`[trace] Creating child span '${name}'`);\n return tracer.startActiveSpan(name, {}, ctx, spanCallback);\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { PromptMessage } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Async-local context for system prompts\n// ---------------------------------------------------------------------------\n\ninterface PromptContextData {\n template: string;\n variables: Record<string, any>;\n}\n\nconst _promptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages system prompt metadata in async-local context for automatic tracing.\n */\nexport class PromptContext {\n /**\n * Store system prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n // enterWith mutates the current async context in-place\n _promptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve system prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _promptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve system prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _promptStorage.getStore()?.variables;\n }\n\n /**\n * Clear system prompt context.\n */\n static clear(): void {\n _promptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Async-local context for user prompts\n// ---------------------------------------------------------------------------\n\nconst _userPromptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages user/human prompt metadata in async-local context for automatic tracing.\n */\nexport class UserPromptContext {\n /**\n * Store user prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n _userPromptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve user prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _userPromptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve user prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _userPromptStorage.getStore()?.variables;\n }\n\n /**\n * Clear user prompt context.\n */\n static clear(): void {\n _userPromptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared context setter type for the base template class\n// ---------------------------------------------------------------------------\n\ninterface ContextSetter {\n set(template: string, variables: Record<string, any>): void;\n}\n\n// ---------------------------------------------------------------------------\n// BasePromptTemplate — shared logic for system and user prompts\n// ---------------------------------------------------------------------------\n\nconst VARIABLE_PATTERN = /\\{\\{(\\w+)\\}\\}/g;\n\n/**\n * Base class for prompt templates with `{{variable}}` placeholders.\n *\n * Extracts variables, compiles templates, and renders strings.\n * Subclasses only need to specify the context class and display name.\n */\nabstract class BasePromptTemplate {\n protected readonly _template: string | PromptMessage[];\n protected readonly _variables: string[];\n\n /**\n * @param template - Either a string with `{{variable}}` placeholders or\n * an array of `PromptMessage` objects whose `content` fields contain placeholders.\n */\n constructor(template: string | PromptMessage[]) {\n this._template = template;\n this._variables = this._extractVariables();\n }\n\n /** List of unique variable names found in this template. */\n get variables(): string[] {\n return this._variables;\n }\n\n /** The raw template (string or message array). */\n get template(): string | PromptMessage[] {\n return this._template;\n }\n\n /** The context class to store template/variables for automatic tracing. */\n protected abstract get _contextSetter(): ContextSetter;\n\n /** Display name for toString(). */\n protected abstract get _displayName(): string;\n\n /**\n * Compile the prompt template with the given variables.\n *\n * @param variables - Key/value pairs to substitute for `{{key}}` placeholders.\n * @returns The rendered string or rendered message array.\n * @throws {Error} If any required variables are missing.\n */\n compile(variables?: Record<string, any>): string | PromptMessage[] {\n const vars = variables ?? {};\n\n const missing = this._variables.filter((v) => !(v in vars));\n if (missing.length > 0) {\n throw new Error(\n `Missing required variables: ${missing.join(', ')}. Template requires: ${this._variables.join(', ')}`,\n );\n }\n\n this._contextSetter.set(\n typeof this._template === 'string'\n ? this._template\n : this._template.map((msg) => `${msg.role}: ${msg.content}`).join('\\n'),\n vars,\n );\n\n if (typeof this._template === 'string') {\n return this._renderString(this._template, vars);\n }\n\n return this._template.map((msg) => ({\n role: msg.role,\n content: this._renderString(msg.content, vars),\n }));\n }\n\n /**\n * Replace `{{key}}` placeholders in a string with the corresponding values.\n */\n _renderString(text: string, variables: Record<string, any>): string {\n let result = text;\n for (const [key, value] of Object.entries(variables)) {\n result = result.replaceAll(`{{${key}}}`, String(value));\n }\n return result;\n }\n\n toString(): string {\n if (typeof this._template === 'string') {\n return this._template.length > 50\n ? `${this._displayName}('${this._template.slice(0, 50)}...')`\n : `${this._displayName}('${this._template}')`;\n }\n return `${this._displayName}(${this._template.length} messages, variables=${JSON.stringify(this._variables)})`;\n }\n\n // ---- private ----\n\n private _extractVariables(): string[] {\n if (typeof this._template === 'string') {\n return [...new Set(Array.from(this._template.matchAll(VARIABLE_PATTERN), (m) => m[1]))];\n }\n\n const found: string[] = [];\n for (const msg of this._template) {\n if (msg.content) {\n for (const match of msg.content.matchAll(VARIABLE_PATTERN)) {\n found.push(match[1]);\n }\n }\n }\n return [...new Set(found)];\n }\n}\n\n// ---------------------------------------------------------------------------\n// PromptTemplate — system/AI instruction prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the system/AI instruction prompt with `{{variable}}` placeholders.\n */\nexport class PromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return PromptContext;\n }\n\n protected get _displayName(): string {\n return 'PromptTemplate';\n }\n}\n\n// ---------------------------------------------------------------------------\n// UserPromptTemplate — user/human turn prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the user/human turn prompt with `{{variable}}` placeholders.\n *\n * Identical to {@link PromptTemplate} but stores context in {@link UserPromptContext}.\n */\nexport class UserPromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return UserPromptContext;\n }\n\n protected get _displayName(): string {\n return 'UserPromptTemplate';\n }\n}\n","/**\n * Centralized logging configuration for Neatlogs SDK.\n * Provides structured logging with configurable levels.\n */\n\ntype LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n DEBUG: 0,\n INFO: 1,\n WARN: 2,\n ERROR: 3,\n NONE: 4,\n};\n\nlet _logLevel: LogLevel = 'INFO';\nlet _disabled = false;\n\nfunction _getConfiguredLevel(): LogLevel {\n const envLevel = process.env.NEATLOGS_LOG_LEVEL?.toUpperCase();\n if (envLevel && envLevel in LOG_LEVELS) {\n return envLevel as LogLevel;\n }\n return 'INFO';\n}\n\n// Initialize from env on first import\n_logLevel = _getConfiguredLevel();\n\nexport function getLogger() {\n return {\n debug(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.DEBUG) {\n console.debug(`[neatlogs] ${message}`, ...args);\n }\n },\n info(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.INFO) {\n console.info(`[neatlogs] ${message}`, ...args);\n }\n },\n warn(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.WARN) {\n console.warn(`[neatlogs] ${message}`, ...args);\n }\n },\n error(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.ERROR) {\n console.error(`[neatlogs] ${message}`, ...args);\n }\n },\n };\n}\n\nexport function setLogLevel(level: LogLevel): void {\n _logLevel = level;\n}\n\nexport function enableDebugLogging(): void {\n _logLevel = 'DEBUG';\n}\n\nexport function disableLogging(): void {\n _disabled = true;\n}\n\nexport function enableLogging(): void {\n _disabled = false;\n}\n","/**\n * PII masking support for Neatlogs spans.\n *\n * Users supply a callable that receives the full span dict and returns\n * the (possibly modified) span dict. Return null to drop the span entirely.\n */\n\nimport type { MaskFunction } from '../types.js';\nimport { getLogger } from './logger.js';\n\nconst logger = getLogger();\n\n/** Module-level registry: key -> mask function */\nconst _MASK_REGISTRY = new Map<string, MaskFunction>();\n\nlet _nextId = 0;\n\n/**\n * Register a mask function and return its lookup key.\n */\nexport function registerMask(fn: MaskFunction): string {\n const key = String(++_nextId);\n _MASK_REGISTRY.set(key, fn);\n return key;\n}\n\n/**\n * Apply the effective mask function to span data.\n *\n * Per-span mask (stored in attributes[\"neatlogs.mask_id\"]) takes\n * precedence over the global mask. Returns the (possibly modified) dict.\n */\nexport function applyMask(\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n): Record<string, any> | null {\n const maskId = spanData?.attributes?.['neatlogs.mask_id'];\n let maskFn: MaskFunction | undefined | null = null;\n\n if (maskId) {\n maskFn = _MASK_REGISTRY.get(String(maskId)) ?? null;\n }\n\n if (!maskFn) {\n maskFn = globalMask ?? null;\n }\n\n if (!maskFn) {\n return spanData;\n }\n\n try {\n const result = maskFn(spanData);\n // null means \"drop this span entirely\"; undefined means \"keep original\"\n if (result === null) return null;\n return result !== undefined ? result : spanData;\n } catch (exc) {\n logger.warn(\n `mask callable raised an exception for span '${spanData?.name}': ${exc} — original span data will be exported unchanged.`,\n );\n return spanData;\n }\n}\n\n/**\n * Clear all registered masks. Used for testing.\n * @internal\n */\nexport function _clearMaskRegistry(): void {\n _MASK_REGISTRY.clear();\n _nextId = 0;\n}\n","/**\n * Core span decoration utilities.\n * Provides the low-level wrapping logic used by span() and Span().\n */\n\nimport { trace, SpanStatusCode, type Span as OtelSpan } from '@opentelemetry/api';\nimport { getLogger } from '../core/logger.js';\nimport { registerMask } from '../core/mask.js';\nimport type { SpanOptions, MaskFunction } from '../types.js';\n\nconst logger = getLogger();\nconst TRACER_NAME = 'neatlogs';\n\n/** Safely serialize any value to JSON string. */\nexport function safeJsonDumps(value: any): string {\n try {\n return JSON.stringify(value, (_key, val) => {\n if (typeof val === 'bigint') return val.toString();\n if (val instanceof Error) return { message: val.message, name: val.name, stack: val.stack };\n if (typeof val === 'function') return `[Function: ${val.name || 'anonymous'}]`;\n return val;\n });\n } catch {\n return String(value);\n }\n}\n\n/** Serialize an object for span attributes. Handles toJSON(), plain objects, primitives. */\nexport function serializeObj(obj: any): any {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') return obj;\n if (typeof obj.toJSON === 'function') return obj.toJSON();\n if (Array.isArray(obj)) return obj.map(serializeObj);\n if (typeof obj === 'object') {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = serializeObj(value);\n }\n return result;\n }\n return String(obj);\n}\n\n/** Check if content capture is enabled via env var. */\nexport function shouldCaptureContent(): boolean {\n const envVal = process.env.NEATLOGS_TRACE_CONTENT;\n if (envVal === undefined || envVal === '') return true;\n return envVal.toLowerCase() !== 'false' && envVal !== '0';\n}\n\n/** Set common span attributes from options. */\nexport function setCommonSpanAttrs(span: OtelSpan, opts: SpanOptions): void {\n if (opts.kind) {\n span.setAttribute('openinference.span.kind', opts.kind);\n }\n if (opts.internal) {\n span.setAttribute('neatlogs.internal', true);\n }\n if (opts.description) {\n span.setAttribute('neatlogs.description', opts.description);\n }\n if (opts.mask) {\n const maskId = registerMask(opts.mask);\n span.setAttribute('neatlogs.mask_id', maskId);\n }\n}\n\nexport interface DecorateSpanOptions extends SpanOptions {\n /** Override the span name (defaults to function name). */\n spanName?: string;\n /** Post-process callback after the function returns. */\n postprocessResult?: (span: OtelSpan, result: any, boundInputs: Record<string, any>) => void;\n}\n\n/**\n * Core wrapper factory that creates an instrumented version of a function.\n * This is the low-level building block used by span() and Span().\n */\nexport function decorateSpan<TArgs extends any[], TReturn>(\n opts: DecorateSpanOptions,\n fn: (...args: TArgs) => TReturn,\n): (...args: TArgs) => TReturn extends Promise<any> ? TReturn : Promise<Awaited<TReturn>> {\n const tracer = trace.getTracer(TRACER_NAME);\n const spanName = opts.spanName ?? opts.name ?? (fn.name || 'anonymous');\n const captureInput = opts.captureInput !== false;\n const captureOutput = opts.captureOutput !== false;\n const doCapture = shouldCaptureContent();\n\n const wrapped = (...args: TArgs): any => {\n return tracer.startActiveSpan(spanName, (span: OtelSpan) => {\n try {\n // Set common attributes\n setCommonSpanAttrs(span, opts);\n\n // Capture input\n if (captureInput && doCapture && args.length > 0) {\n try {\n const inputValue = args.length === 1 ? serializeObj(args[0]) : args.map(serializeObj);\n span.setAttribute('input.value', safeJsonDumps(inputValue));\n } catch (err) {\n logger.debug(`Failed to capture input: ${err}`);\n }\n }\n\n // Execute the function\n const result = fn(...args);\n\n // Handle async results\n if (result instanceof Promise) {\n return result\n .then((resolved: any) => {\n // Capture output\n if (captureOutput && doCapture) {\n try {\n span.setAttribute('output.value', safeJsonDumps(serializeObj(resolved)));\n } catch (err) {\n logger.debug(`Failed to capture output: ${err}`);\n }\n }\n\n // Post-process\n if (opts.postprocessResult) {\n try {\n const boundInputs = _extractBoundInputs(fn, args);\n opts.postprocessResult(span, resolved, boundInputs);\n } catch (err) {\n logger.debug(`Postprocess failed: ${err}`);\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return resolved;\n })\n .catch((error: any) => {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error?.message ?? String(error),\n });\n span.recordException(error instanceof Error ? error : new Error(String(error)));\n span.end();\n throw error;\n });\n }\n\n // Sync result\n if (captureOutput && doCapture) {\n try {\n span.setAttribute('output.value', safeJsonDumps(serializeObj(result)));\n } catch (err) {\n logger.debug(`Failed to capture output: ${err}`);\n }\n }\n\n if (opts.postprocessResult) {\n try {\n const boundInputs = _extractBoundInputs(fn, args);\n opts.postprocessResult(span, result, boundInputs);\n } catch (err) {\n logger.debug(`Postprocess failed: ${err}`);\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return result;\n } catch (error: any) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error?.message ?? String(error),\n });\n span.recordException(error instanceof Error ? error : new Error(String(error)));\n span.end();\n throw error;\n }\n });\n };\n\n // Preserve function name for debugging\n Object.defineProperty(wrapped, 'name', { value: spanName, configurable: true });\n\n return wrapped as any;\n}\n\n/** Extract named arguments from function call (best-effort). */\nfunction _extractBoundInputs(fn: Function, args: any[]): Record<string, any> {\n const result: Record<string, any> = {};\n // Parse function parameter names from toString()\n const fnStr = fn.toString();\n const match = fnStr.match(/\\(([^)]*)\\)/);\n if (match) {\n const paramNames = match[1]\n .split(',')\n .map((p) => p.trim().replace(/\\s*[:=].*$/, '').replace(/^\\.\\.\\./, ''))\n .filter(Boolean);\n for (let i = 0; i < Math.min(paramNames.length, args.length); i++) {\n result[paramNames[i]] = args[i];\n }\n }\n return result;\n}\n"],"mappings":"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","names":["trace","otelContext","SpanStatusCode","otelTrace","otelContext","SpanStatusCode","logger","logger","otelContext","otelTrace","otelContext","SpanStatusCode","trace"]}
1
+ {"version":3,"sources":["../src/anthropic.ts","../src/core/auto-root.ts","../src/core/context.ts","../src/prompt/template.ts","../src/core/logger.ts","../src/core/mask.ts","../src/core/end-user.ts","../src/decorators/base.ts"],"sourcesContent":["/**\n * Neatlogs Anthropic wrapper — ES6 Proxy-based.\n *\n * Usage:\n * import { wrapAnthropic, traceTool } from 'neatlogs/anthropic';\n * import Anthropic from '@anthropic-ai/sdk';\n * const client = wrapAnthropic(new Anthropic());\n *\n * Intercepts:\n * - client.messages.create() — non-streaming and streaming (stream: true)\n * - client.messages.stream() — Anthropic's streaming helper (MessageStream)\n *\n * Also exports traceTool() to wrap user-defined tool functions with TOOL spans.\n * Handles thinking blocks, tool_use blocks, and cache tokens.\n */\n\nimport { trace, context as otelContext, SpanStatusCode, type Span } from '@opentelemetry/api';\nimport { getProviderTracer } from './core/auto-root.js';\n\nconst TRACER_NAME = 'neatlogs.anthropic';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function wrapAnthropic<T extends object>(client: T): T {\n return wrapNamespace(client, []) as T;\n}\n\n/**\n * Wrap a tool/function implementation to emit TOOL spans when executed.\n *\n * Usage:\n * const getWeather = traceTool('get_weather', async (input: { city: string }) => {\n * return `Weather in ${input.city}: sunny`;\n * });\n */\nexport function traceTool<TInput = any, TResult = any>(\n name: string,\n fn: (input: TInput) => TResult | Promise<TResult>,\n): (input: TInput) => Promise<TResult> {\n return async function tracedTool(input: TInput): Promise<TResult> {\n const tracer = getProviderTracer(TRACER_NAME);\n return tracer.startActiveSpan(\n `tool.${name}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': name,\n 'input.value': safeStringify(input),\n },\n },\n otelContext.active(),\n async (span) => {\n try {\n const result = await fn(input);\n span.setAttribute('output.value', safeStringify(result));\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// Proxy wrapping\n// ---------------------------------------------------------------------------\n\nfunction wrapNamespace(target: any, path: string[]): any {\n return new Proxy(target, {\n get(obj, prop, receiver) {\n const value = Reflect.get(obj, prop, receiver);\n if (typeof prop === 'symbol' || String(prop).startsWith('_')) return value;\n\n const currentPath = [...path, String(prop)];\n const pathStr = currentPath.join('.');\n\n if (pathStr === 'messages.create' && typeof value === 'function') {\n return tracedMessagesCreate(value.bind(obj));\n }\n if (pathStr === 'messages.stream' && typeof value === 'function') {\n return tracedMessagesStream(value.bind(obj));\n }\n\n if (value && typeof value === 'object' && !Array.isArray(value) && isNamespace(currentPath)) {\n return wrapNamespace(value, currentPath);\n }\n\n return value;\n },\n });\n}\n\nfunction isNamespace(path: string[]): boolean {\n if (path.length > 3) return false;\n const key = path[path.length - 1];\n return ['messages', 'beta'].includes(key);\n}\n\n// ---------------------------------------------------------------------------\n// messages.create — non-streaming + streaming (stream: true)\n// ---------------------------------------------------------------------------\n\nfunction tracedMessagesCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n const messages: any[] = opts?.messages ?? [];\n const isStream = opts?.stream === true;\n\n const span = tracer.startSpan('anthropic.messages.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n 'neatlogs.llm.model_name': model,\n 'neatlogs.llm.is_streaming': isStream,\n },\n }, otelContext.active());\n\n setInputMessages(span, opts?.system, messages);\n setTools(span, opts?.tools);\n setInvocationParams(span, opts);\n\n const ctx = trace.setSpan(otelContext.active(), span);\n const promise = otelContext.with(ctx, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n if (isStream) {\n return wrapStreamIterable(response, span);\n }\n finalizeMessageResponse(span, response);\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// messages.stream() — returns a MessageStream object\n// ---------------------------------------------------------------------------\n\nfunction tracedMessagesStream(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = getProviderTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n const messages: any[] = opts?.messages ?? [];\n\n const span = tracer.startSpan('anthropic.messages.stream', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'anthropic',\n 'neatlogs.llm.system': 'anthropic',\n 'neatlogs.llm.model_name': model,\n 'neatlogs.llm.is_streaming': true,\n },\n }, otelContext.active());\n\n setInputMessages(span, opts?.system, messages);\n setTools(span, opts?.tools);\n setInvocationParams(span, opts);\n\n const ctx = trace.setSpan(otelContext.active(), span);\n const messageStream = otelContext.with(ctx, () => original(opts, ...rest));\n\n return wrapMessageStream(messageStream, span);\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming: async iterable (stream: true returns events)\n// ---------------------------------------------------------------------------\n\nfunction wrapStreamIterable(stream: any, span: Span): any {\n const events: any[] = [];\n const originalAsyncIterator = stream[Symbol.asyncIterator]?.bind(stream);\n\n if (!originalAsyncIterator) {\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return stream;\n }\n\n const wrapped = Object.create(Object.getPrototypeOf(stream));\n Object.assign(wrapped, stream);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await iterator.next();\n if (result.done) {\n finalizeStreamEvents(span, events);\n return result;\n }\n events.push(result.value);\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n finalizeStreamEvents(span, events);\n return iterator.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n recordError(span, err);\n return iterator.throw?.(err) ?? { done: true, value: undefined };\n },\n };\n };\n\n if (typeof stream.finalMessage === 'function') {\n const origFinal = stream.finalMessage.bind(stream);\n wrapped.finalMessage = async function () {\n return origFinal();\n };\n }\n\n return wrapped;\n}\n\n// ---------------------------------------------------------------------------\n// Streaming: MessageStream (.on('end'), .finalMessage())\n// ---------------------------------------------------------------------------\n\nfunction wrapMessageStream(messageStream: any, span: Span): any {\n const origOn = messageStream.on?.bind(messageStream);\n if (origOn) {\n origOn('end', () => {\n const finalMsg = messageStream._finalMessage ?? messageStream.currentMessage;\n if (finalMsg) {\n finalizeMessageResponse(span, finalMsg);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n }\n });\n origOn('error', (err: any) => {\n if (span.isRecording()) recordError(span, err);\n });\n }\n\n if (typeof messageStream.finalMessage === 'function') {\n const origFinal = messageStream.finalMessage.bind(messageStream);\n messageStream.finalMessage = async function () {\n const result = await origFinal();\n if (span.isRecording()) {\n finalizeMessageResponse(span, result);\n }\n return result;\n };\n }\n\n return messageStream;\n}\n\n// ---------------------------------------------------------------------------\n// Stream event finalization\n// ---------------------------------------------------------------------------\n\nfunction finalizeStreamEvents(span: Span, events: any[]): void {\n const textParts: string[] = [];\n const thinkingParts: string[] = [];\n const toolCalls: Array<{ id: string; name: string; input: string }> = [];\n let currentToolInput = '';\n let currentToolId = '';\n let currentToolName = '';\n let usage: any = null;\n let model = '';\n let stopReason = '';\n\n for (const event of events) {\n const type = event?.type ?? '';\n\n if (type === 'content_block_delta') {\n const delta = event?.delta;\n if (delta?.type === 'text_delta' && delta.text) textParts.push(delta.text);\n else if (delta?.type === 'thinking_delta' && delta.thinking) thinkingParts.push(delta.thinking);\n else if (delta?.type === 'input_json_delta' && delta.partial_json) currentToolInput += delta.partial_json;\n } else if (type === 'content_block_start') {\n const block = event?.content_block;\n if (block?.type === 'tool_use') {\n currentToolId = block.id ?? '';\n currentToolName = block.name ?? '';\n currentToolInput = '';\n }\n } else if (type === 'content_block_stop') {\n if (currentToolName) {\n toolCalls.push({ id: currentToolId, name: currentToolName, input: currentToolInput });\n currentToolId = '';\n currentToolName = '';\n currentToolInput = '';\n }\n } else if (type === 'message_start') {\n const msg = event?.message;\n if (msg?.model) model = msg.model;\n if (msg?.usage) usage = msg.usage;\n } else if (type === 'message_delta') {\n const delta = event?.delta;\n if (delta?.stop_reason) stopReason = delta.stop_reason;\n if (event?.usage) usage = { ...(usage ?? {}), ...event.usage };\n }\n }\n\n const fullText = textParts.join('');\n if (fullText) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', fullText);\n }\n\n const thinking = thinkingParts.join('');\n if (thinking) {\n span.setAttribute('neatlogs.llm.output_messages.0.thinking', thinking);\n }\n\n for (let j = 0; j < toolCalls.length; j++) {\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, toolCalls[j].id);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, toolCalls[j].name);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, toolCalls[j].input);\n }\n\n if (model) span.setAttribute('neatlogs.llm.model_name', model);\n if (stopReason) span.setAttribute('neatlogs.llm.stop_reason', stopReason);\n setUsageAttrs(span, usage);\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Non-streaming response finalization\n// ---------------------------------------------------------------------------\n\nfunction finalizeMessageResponse(span: Span, response: any): void {\n const content: any[] = response?.content ?? [];\n const textParts: string[] = [];\n const thinkingParts: string[] = [];\n const toolCalls: Array<{ id: string; name: string; input: string }> = [];\n\n for (const block of content) {\n if (block.type === 'text' && block.text) textParts.push(block.text);\n else if (block.type === 'thinking' && block.thinking) thinkingParts.push(block.thinking);\n else if (block.type === 'tool_use') {\n toolCalls.push({\n id: block.id ?? '',\n name: block.name ?? '',\n input: safeStringify(block.input ?? {}),\n });\n }\n }\n\n if (textParts.length) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', textParts.join(''));\n }\n if (thinkingParts.length) {\n span.setAttribute('neatlogs.llm.output_messages.0.thinking', thinkingParts.join(''));\n }\n\n for (let j = 0; j < toolCalls.length; j++) {\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, toolCalls[j].id);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, toolCalls[j].name);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, toolCalls[j].input);\n }\n\n setUsageAttrs(span, response?.usage);\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n if (response?.stop_reason) span.setAttribute('neatlogs.llm.stop_reason', response.stop_reason);\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction setInputMessages(span: Span, system: any, messages: any[]): void {\n let idx = 0;\n if (system) {\n const content = typeof system === 'string' ? system : safeStringify(system);\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, 'system');\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, content);\n idx++;\n }\n for (const msg of messages) {\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, msg.role ?? '');\n if (typeof msg.content === 'string') {\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, msg.content);\n } else if (msg.content) {\n span.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, safeStringify(msg.content));\n }\n idx++;\n }\n}\n\nfunction setTools(span: Span, tools: any[] | undefined): void {\n if (!tools) return;\n for (let i = 0; i < tools.length; i++) {\n const tool = tools[i];\n span.setAttribute(`neatlogs.llm.tools.${i}.name`, tool.name ?? '');\n if (tool.description) span.setAttribute(`neatlogs.llm.tools.${i}.description`, tool.description);\n if (tool.input_schema) span.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify(tool.input_schema));\n }\n}\n\nfunction setInvocationParams(span: Span, opts: any): void {\n if (opts?.temperature != null) span.setAttribute('neatlogs.llm.temperature', opts.temperature);\n if (opts?.top_p != null) span.setAttribute('neatlogs.llm.top_p', opts.top_p);\n if (opts?.top_k != null) span.setAttribute('neatlogs.llm.top_k', opts.top_k);\n if (opts?.max_tokens != null) span.setAttribute('neatlogs.llm.max_tokens', opts.max_tokens);\n if (opts?.stop_sequences) span.setAttribute('neatlogs.llm.stop_sequences', safeStringify(opts.stop_sequences));\n}\n\nfunction setUsageAttrs(span: Span, usage: any): void {\n if (!usage) return;\n if (usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.input_tokens);\n if (usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.output_tokens);\n if (usage.cache_read_input_tokens != null) span.setAttribute('neatlogs.llm.token_count.cache_read', usage.cache_read_input_tokens);\n if (usage.cache_creation_input_tokens != null) span.setAttribute('neatlogs.llm.token_count.cache_write', usage.cache_creation_input_tokens);\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return typeof value === 'string' ? value : JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction recordError(span: Span, err: unknown): void {\n if (err instanceof Error) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });\n span.recordException(err);\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });\n }\n span.end();\n}\n","/**\n * Auto-root for direct-provider wrappers.\n *\n * The backend only renders a trace once it contains a *parentless* span of a\n * root-eligible kind (WORKFLOW / CHAIN / AGENT / MCP_TOOL). Direct-provider\n * wrappers (wrapOpenAI, wrapAnthropic, wrapBedrock, ...) only ever emit\n * non-root spans (LLM / EMBEDDING / TOOL). So a bare\n * `const client = wrapOpenAI(new OpenAI())` with no surrounding `span()` /\n * `trace()` produces an orphan span and the trace never renders.\n *\n * `getProviderTracer(name)` returns a Tracer facade used *only* by those\n * direct-provider wrappers: when `startSpan` would otherwise produce a\n * parentless, non-root span, it transparently opens a WORKFLOW root (named\n * after the configured `workflowName`) and ends it when the provider span\n * ends. Every wrapper path — non-streaming, streaming finalize, error —\n * funnels through `span.end()`, so wrapping `end()` covers them all with no\n * changes to the wrappers' streaming code.\n *\n * Framework wrappers (langchain, mastra, openai-agents, ai-sdk, ...) keep\n * using `trace.getTracer(...)` directly — they already emit their own root and\n * thread context explicitly, so auto-root must never fire for them.\n */\n\nimport {\n trace as otelTrace,\n context as otelContext,\n type Tracer,\n type Span,\n type Context,\n type SpanOptions,\n} from '@opentelemetry/api';\n\nimport { getSessionConfig } from './context.js';\n\n// A parentless span of one of these kinds already satisfies the backend's\n// root requirement, so it must NOT be wrapped in another root.\nconst ROOT_KINDS = new Set(['workflow', 'chain', 'agent', 'mcp_tool']);\n\nfunction autoRootEnabled(): boolean {\n const val = (process.env.NEATLOGS_AUTO_ROOT ?? '').trim().toLowerCase();\n return !['false', '0', 'no', 'off'].includes(val);\n}\n\nfunction resolveRootWorkflowName(): string {\n try {\n const name = getSessionConfig()?.workflowName;\n if (typeof name === 'string' && name.trim()) return name;\n } catch {\n /* ignore */\n }\n return 'workflow';\n}\n\n/**\n * Wrap a provider span so that ending it also ends the auto-created WORKFLOW\n * root. A Proxy keeps this transparent across OTel versions: every property /\n * method except `end` passes straight through to the child span.\n */\nfunction wrapSpanWithRoot(child: Span, root: Span): Span {\n let ended = false;\n return new Proxy(child, {\n get(target, prop, _receiver) {\n if (prop === 'end') {\n return (...args: any[]): void => {\n if (ended) return;\n ended = true;\n try {\n (target.end as (...a: any[]) => void)(...args);\n } finally {\n try {\n root.end();\n } catch {\n /* ignore */\n }\n }\n };\n }\n const value = Reflect.get(target, prop, target);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\n/**\n * Self-root helper for callback/event-driven handlers (LangChain, etc.).\n *\n * A callback handler builds its own parent context per run-id rather than going\n * through {@link AutoRootTracer}. When a run BEGINS with a non-root kind (a bare\n * `llm.invoke()` fires the LLM callback with no chain above it; a standalone\n * tool/retriever similarly), the span it creates is parentless and non-root, so\n * the backend can't anchor the trace and it never renders. Call this when there\n * is no LangChain parent: if the kind is non-root and nothing is actively\n * recording in `baseCtx`, it opens a WORKFLOW root and returns it plus the\n * child context to create the span in. The caller MUST end the returned\n * `root` after the child span ends (see {@link endAutoRoot}).\n *\n * Returns `{ root, ctx }` when a root was opened, or `{ root: undefined, ctx: baseCtx }`\n * when no auto-root is needed (root-eligible kind, already-recording parent, or\n * disabled).\n */\nexport function maybeOpenAutoRoot(\n tracer: Tracer,\n kind: string,\n baseCtx: Context,\n): { root: Span | undefined; ctx: Context } {\n const k = String(kind ?? '').toLowerCase();\n const existing = otelTrace.getSpan(baseCtx);\n if (\n !autoRootEnabled() ||\n ROOT_KINDS.has(k) ||\n (existing !== undefined && existing.isRecording())\n ) {\n return { root: undefined, ctx: baseCtx };\n }\n const root = tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n baseCtx,\n );\n return { root, ctx: otelTrace.setSpan(baseCtx, root) };\n}\n\n/** End an auto-root opened by {@link maybeOpenAutoRoot}. Safe on undefined. */\nexport function endAutoRoot(root: Span | undefined): void {\n if (!root) return;\n try {\n root.end();\n } catch {\n /* ignore */\n }\n}\n\nclass AutoRootTracer implements Tracer {\n constructor(private readonly _tracer: Tracer) {}\n\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const kind = String(\n (options?.attributes as Record<string, unknown> | undefined)?.[\n 'neatlogs.span.kind'\n ] ?? '',\n ).toLowerCase();\n\n const ctx = context ?? otelContext.active();\n const parent = otelTrace.getSpan(ctx);\n const needsRoot =\n autoRootEnabled() &&\n !ROOT_KINDS.has(kind) &&\n !(parent !== undefined && parent.isRecording());\n\n if (!needsRoot) {\n return this._tracer.startSpan(name, options, context);\n }\n\n const root = this._tracer.startSpan(\n resolveRootWorkflowName(),\n { attributes: { 'neatlogs.span.kind': 'workflow', 'neatlogs.auto_root': true } },\n ctx,\n );\n const childCtx = otelTrace.setSpan(ctx, root);\n const child = this._tracer.startSpan(name, options, childCtx);\n return wrapSpanWithRoot(child, root);\n }\n\n // startActiveSpan is used by traceTool (callback-scoped, always under an\n // active span in practice) — pass straight through, no auto-root.\n startActiveSpan = ((...args: any[]) =>\n (this._tracer.startActiveSpan as (...a: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'];\n}\n\n/**\n * Tracer for direct-provider wrappers (wrapOpenAI, wrapAnthropic, ...).\n * Identical to `trace.getTracer(name)` but adds transparent auto-root so a\n * bare `wrapOpenAI(new OpenAI())` renders a trace without a manual `span()` /\n * `trace()` wrapper. Do NOT use for framework wrappers.\n */\nexport function getProviderTracer(name: string): Tracer {\n return new AutoRootTracer(otelTrace.getTracer(name));\n}\n","/**\n * Context wrapper for manual span creation.\n *\n * Provides the `trace()` function — the TypeScript equivalent of the Python\n * `trace()` context manager. Creates an OTel span, sets prompt template/variable\n * context, and executes the user callback within the span.\n *\n * @example\n * ```typescript\n * await trace({ name: 'my-trace' }, async (span) => {\n * // user code runs here with the span active\n * });\n * ```\n */\n\nimport {\n trace as otelTrace,\n context as otelContext,\n createContextKey,\n ROOT_CONTEXT,\n SpanStatusCode,\n type Span,\n} from '@opentelemetry/api';\nimport { PromptContext, UserPromptContext, PromptTemplate, UserPromptTemplate } from '../prompt/template.js';\nimport { registerMask } from './mask.js';\nimport { applyEndUserAttributes } from './end-user.js';\n\nimport { getLogger } from './logger.js';\nimport { safeJsonDumps, serializeObj } from '../decorators/base.js';\nimport type { TraceOptions, MaskFunction } from '../types.js';\n\nconst logger = getLogger();\n\n// ---------------------------------------------------------------------------\n// Module-level session config (set by init.ts via _setSessionConfig)\n// ---------------------------------------------------------------------------\n\nlet _sessionConfig: Record<string, any> = {};\n\n/**\n * Set the session configuration. Called by init() during SDK setup.\n * @internal\n */\nexport function _setSessionConfig(config: Record<string, any>): void {\n _sessionConfig = config;\n}\n\n/**\n * Get a copy of the current session configuration.\n */\nexport function getSessionConfig(): Record<string, any> {\n return { ..._sessionConfig };\n}\n\n// ---------------------------------------------------------------------------\n// OTel context keys for prompt data propagation\n// ---------------------------------------------------------------------------\n\nexport const PROMPT_VARIABLES_KEY = createContextKey('neatlogs.prompt_variables');\nexport const PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.prompt_template');\nexport const PROMPT_VERSION_KEY = createContextKey('neatlogs.prompt_version');\nexport const USER_PROMPT_TEMPLATE_KEY = createContextKey('neatlogs.user_prompt_template');\nexport const USER_PROMPT_VARIABLES_KEY = createContextKey('neatlogs.user_prompt_variables');\n\n// ---------------------------------------------------------------------------\n// Known TraceOptions keys (not forwarded as extra span attributes)\n// ---------------------------------------------------------------------------\n\nconst KNOWN_OPTION_KEYS = new Set([\n 'name',\n 'kind',\n 'input',\n 'promptTemplate',\n 'promptVariables',\n 'userPromptTemplate',\n 'userPromptVariables',\n 'version',\n 'mask',\n 'attributes',\n 'endUserId',\n 'endUserMetadata',\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Set standard and extra attributes on a span.\n * @internal\n */\nexport function _setSpanAttributes(\n span: Span,\n kind: string | undefined,\n attributes: Record<string, any>,\n): void {\n span.setAttribute('neatlogs.internal', true);\n span.setAttribute('openinference.span.kind', kind ?? 'CHAIN');\n\n for (const [key, value] of Object.entries(attributes)) {\n span.setAttribute(key, value);\n }\n}\n\n/**\n * After the user callback completes, capture prompt variables that were\n * set by PromptTemplate.compile() / UserPromptTemplate.compile() and\n * record them as span attributes.\n * @internal\n */\nexport function _finalizePromptCapture(\n span: Span,\n isPromptTemplateObj: boolean,\n isUserPromptTemplateObj: boolean,\n): void {\n if (isPromptTemplateObj) {\n const capturedVars = PromptContext.getVariables();\n if (capturedVars) {\n span.setAttribute(\n 'llm.prompt_template_variables',\n JSON.stringify(capturedVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from PromptContext: ${Object.keys(capturedVars).join(', ')}`,\n );\n }\n }\n\n if (isUserPromptTemplateObj) {\n const capturedUserVars = UserPromptContext.getVariables();\n if (capturedUserVars) {\n span.setAttribute(\n 'llm.user_prompt_template_variables',\n JSON.stringify(capturedUserVars),\n );\n logger.debug(\n `[trace] Auto-captured variables from UserPromptContext: ${Object.keys(capturedUserVars).join(', ')}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main trace() function\n// ---------------------------------------------------------------------------\n\n/**\n * Async callback wrapper for manual span creation with prompt tracking.\n *\n * Creates an OTel span, optionally sets prompt template/variable context,\n * and executes the provided callback within the span.\n *\n * **Session-Aware Trace Creation:**\n * - If `session_id` is set in init() AND no active parent span exists,\n * this creates a NEW root trace (for multi-turn conversations).\n * - Otherwise, creates a normal child span within the existing trace.\n *\n * @param options - Trace configuration options\n * @param fn - Callback that receives the active span\n * @returns The return value of the callback\n *\n * @example\n * ```typescript\n * // Basic usage\n * await trace({ name: 'my-pipeline' }, async (span) => {\n * await step1();\n * await step2();\n * });\n *\n * // With prompt template\n * const template = new PromptTemplate('Hello {{name}}');\n * await trace({ name: 'prompt', promptTemplate: template }, async (span) => {\n * const rendered = template.compile({ name: 'world' });\n * // ...\n * });\n * ```\n */\nexport async function trace<T>(\n options: TraceOptions,\n fn: (span: Span) => T | Promise<T>,\n): Promise<T> {\n const {\n name,\n kind,\n input,\n promptTemplate,\n promptVariables,\n userPromptTemplate,\n userPromptVariables,\n version,\n mask,\n endUserId,\n endUserMetadata,\n attributes: explicitAttributes,\n ...extraOptions\n } = options;\n\n const sessionConfig = getSessionConfig();\n const sessionId = sessionConfig.sessionId;\n\n // Determine whether we are inside an existing active trace\n const currentSpan = otelTrace.getSpan(otelContext.active());\n const isInActiveTrace = currentSpan !== undefined && currentSpan.isRecording();\n const shouldCreateRootTrace = !!sessionId && !isInActiveTrace;\n\n // ---------------------------------------------------------------------------\n // Process prompt templates\n // ---------------------------------------------------------------------------\n\n let templateString: string | undefined;\n let isPromptTemplateObj = false;\n\n if (promptTemplate !== undefined) {\n if (promptTemplate instanceof PromptTemplate) {\n isPromptTemplateObj = true;\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using PromptTemplate object with variables: ${promptTemplate.variables.join(', ')}`,\n );\n } else if (typeof promptTemplate === 'string') {\n templateString = promptTemplate;\n } else {\n const t = promptTemplate.template;\n templateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n let userTemplateString: string | undefined;\n let isUserPromptTemplateObj = false;\n\n if (userPromptTemplate !== undefined) {\n if (userPromptTemplate instanceof UserPromptTemplate) {\n isUserPromptTemplateObj = true;\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n logger.debug(\n `[trace] Using UserPromptTemplate object with variables: ${userPromptTemplate.variables.join(', ')}`,\n );\n } else if (typeof userPromptTemplate === 'string') {\n userTemplateString = userPromptTemplate;\n } else {\n const t = userPromptTemplate.template;\n userTemplateString = typeof t === 'string' ? t : JSON.stringify(t);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Build OTel context with prompt values\n // ---------------------------------------------------------------------------\n\n let ctx = otelContext.active();\n\n const variablesJson = promptVariables ? JSON.stringify(promptVariables) : undefined;\n const userVariablesJson = userPromptVariables ? JSON.stringify(userPromptVariables) : undefined;\n\n if (variablesJson) {\n ctx = ctx.setValue(PROMPT_VARIABLES_KEY, variablesJson);\n logger.debug(`[trace] Set neatlogs.prompt_variables in context: ${variablesJson}`);\n }\n if (templateString) {\n ctx = ctx.setValue(PROMPT_TEMPLATE_KEY, templateString);\n logger.debug(`[trace] Set neatlogs.prompt_template in context: ${templateString}`);\n }\n if (userVariablesJson) {\n ctx = ctx.setValue(USER_PROMPT_VARIABLES_KEY, userVariablesJson);\n logger.debug(`[trace] Set neatlogs.user_prompt_variables in context: ${userVariablesJson}`);\n }\n if (userTemplateString) {\n ctx = ctx.setValue(USER_PROMPT_TEMPLATE_KEY, userTemplateString);\n logger.debug(`[trace] Set neatlogs.user_prompt_template in context: ${userTemplateString}`);\n }\n if (version) {\n ctx = ctx.setValue(PROMPT_VERSION_KEY, version);\n logger.debug(`[trace] Set neatlogs.prompt_version in context: ${version}`);\n }\n\n // ---------------------------------------------------------------------------\n // Collect extra attributes (non-standard option keys)\n // ---------------------------------------------------------------------------\n\n const extraAttributes: Record<string, any> = { ...(explicitAttributes ?? {}) };\n for (const [key, value] of Object.entries(extraOptions)) {\n if (!KNOWN_OPTION_KEYS.has(key)) {\n extraAttributes[key] = value;\n }\n }\n\n // End-user belongs to the trace root only. This trace() call is a root when it\n // creates a new root trace, or when it isn't nested in an already-active trace.\n const isRootTrace = shouldCreateRootTrace || !isInActiveTrace;\n\n // ---------------------------------------------------------------------------\n // Create span and execute callback\n // ---------------------------------------------------------------------------\n\n const tracer = otelTrace.getTracer('neatlogs.trace');\n\n const spanCallback = async (span: Span): Promise<T> => {\n _setSpanAttributes(span, kind, extraAttributes);\n\n // End-user belongs to the trace root only; skipped on a non-root span.\n applyEndUserAttributes(span, endUserId, endUserMetadata, isRootTrace);\n\n if (input !== undefined && input !== null) {\n span.setAttribute('input.value', safeJsonDumps(serializeObj(input)));\n }\n\n if (mask) {\n const maskId = registerMask(mask);\n span.setAttribute('neatlogs.mask_id', maskId);\n }\n\n try {\n const result: T = await fn(span);\n\n _finalizePromptCapture(span, isPromptTemplateObj, isUserPromptTemplateObj);\n\n if (result !== undefined && result !== null) {\n const spanAttrs = (span as any).attributes ?? (span as any)._attributes;\n const hasOutput = spanAttrs && (\n 'output.value' in spanAttrs || 'neatlogs.output.value' in spanAttrs\n );\n if (!hasOutput) {\n span.setAttribute('output.value', safeJsonDumps(serializeObj(result)));\n }\n }\n\n return result;\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n });\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n } finally {\n if (isPromptTemplateObj) {\n PromptContext.clear();\n }\n if (isUserPromptTemplateObj) {\n UserPromptContext.clear();\n }\n span.end();\n }\n };\n\n if (shouldCreateRootTrace) {\n logger.debug(`[trace] Creating NEW root trace '${name}' (sessionId=${sessionId})`);\n return tracer.startActiveSpan(name, {}, ROOT_CONTEXT, spanCallback);\n } else {\n logger.debug(`[trace] Creating child span '${name}'`);\n return tracer.startActiveSpan(name, {}, ctx, spanCallback);\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { PromptMessage } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// Async-local context for system prompts\n// ---------------------------------------------------------------------------\n\ninterface PromptContextData {\n template: string;\n variables: Record<string, any>;\n}\n\nconst _promptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages system prompt metadata in async-local context for automatic tracing.\n */\nexport class PromptContext {\n /**\n * Store system prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n // enterWith mutates the current async context in-place\n _promptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve system prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _promptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve system prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _promptStorage.getStore()?.variables;\n }\n\n /**\n * Clear system prompt context.\n */\n static clear(): void {\n _promptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Async-local context for user prompts\n// ---------------------------------------------------------------------------\n\nconst _userPromptStorage = new AsyncLocalStorage<PromptContextData>();\n\n/**\n * Manages user/human prompt metadata in async-local context for automatic tracing.\n */\nexport class UserPromptContext {\n /**\n * Store user prompt template and variables in context.\n */\n static set(template: string, variables: Record<string, any>): void {\n _userPromptStorage.enterWith({ template, variables });\n }\n\n /**\n * Retrieve user prompt template from context.\n */\n static getTemplate(): string | undefined {\n return _userPromptStorage.getStore()?.template;\n }\n\n /**\n * Retrieve user prompt variables from context.\n */\n static getVariables(): Record<string, any> | undefined {\n return _userPromptStorage.getStore()?.variables;\n }\n\n /**\n * Clear user prompt context.\n */\n static clear(): void {\n _userPromptStorage.enterWith(undefined as unknown as PromptContextData);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared context setter type for the base template class\n// ---------------------------------------------------------------------------\n\ninterface ContextSetter {\n set(template: string, variables: Record<string, any>): void;\n}\n\n// ---------------------------------------------------------------------------\n// BasePromptTemplate — shared logic for system and user prompts\n// ---------------------------------------------------------------------------\n\nconst VARIABLE_PATTERN = /\\{\\{(\\w+)\\}\\}/g;\n\n/**\n * Base class for prompt templates with `{{variable}}` placeholders.\n *\n * Extracts variables, compiles templates, and renders strings.\n * Subclasses only need to specify the context class and display name.\n */\nabstract class BasePromptTemplate {\n protected readonly _template: string | PromptMessage[];\n protected readonly _variables: string[];\n\n /**\n * @param template - Either a string with `{{variable}}` placeholders or\n * an array of `PromptMessage` objects whose `content` fields contain placeholders.\n */\n constructor(template: string | PromptMessage[]) {\n this._template = template;\n this._variables = this._extractVariables();\n }\n\n /** List of unique variable names found in this template. */\n get variables(): string[] {\n return this._variables;\n }\n\n /** The raw template (string or message array). */\n get template(): string | PromptMessage[] {\n return this._template;\n }\n\n /** The context class to store template/variables for automatic tracing. */\n protected abstract get _contextSetter(): ContextSetter;\n\n /** Display name for toString(). */\n protected abstract get _displayName(): string;\n\n /**\n * Compile the prompt template with the given variables.\n *\n * @param variables - Key/value pairs to substitute for `{{key}}` placeholders.\n * @returns The rendered string or rendered message array.\n * @throws {Error} If any required variables are missing.\n */\n compile(variables?: Record<string, any>): string | PromptMessage[] {\n const vars = variables ?? {};\n\n const missing = this._variables.filter((v) => !(v in vars));\n if (missing.length > 0) {\n throw new Error(\n `Missing required variables: ${missing.join(', ')}. Template requires: ${this._variables.join(', ')}`,\n );\n }\n\n this._contextSetter.set(\n typeof this._template === 'string'\n ? this._template\n : this._template.map((msg) => `${msg.role}: ${msg.content}`).join('\\n'),\n vars,\n );\n\n if (typeof this._template === 'string') {\n return this._renderString(this._template, vars);\n }\n\n return this._template.map((msg) => ({\n role: msg.role,\n content: this._renderString(msg.content, vars),\n }));\n }\n\n /**\n * Replace `{{key}}` placeholders in a string with the corresponding values.\n */\n _renderString(text: string, variables: Record<string, any>): string {\n let result = text;\n for (const [key, value] of Object.entries(variables)) {\n result = result.replaceAll(`{{${key}}}`, String(value));\n }\n return result;\n }\n\n toString(): string {\n if (typeof this._template === 'string') {\n return this._template.length > 50\n ? `${this._displayName}('${this._template.slice(0, 50)}...')`\n : `${this._displayName}('${this._template}')`;\n }\n return `${this._displayName}(${this._template.length} messages, variables=${JSON.stringify(this._variables)})`;\n }\n\n // ---- private ----\n\n private _extractVariables(): string[] {\n if (typeof this._template === 'string') {\n return [...new Set(Array.from(this._template.matchAll(VARIABLE_PATTERN), (m) => m[1]))];\n }\n\n const found: string[] = [];\n for (const msg of this._template) {\n if (msg.content) {\n for (const match of msg.content.matchAll(VARIABLE_PATTERN)) {\n found.push(match[1]);\n }\n }\n }\n return [...new Set(found)];\n }\n}\n\n// ---------------------------------------------------------------------------\n// PromptTemplate — system/AI instruction prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the system/AI instruction prompt with `{{variable}}` placeholders.\n */\nexport class PromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return PromptContext;\n }\n\n protected get _displayName(): string {\n return 'PromptTemplate';\n }\n}\n\n// ---------------------------------------------------------------------------\n// UserPromptTemplate — user/human turn prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Template for the user/human turn prompt with `{{variable}}` placeholders.\n *\n * Identical to {@link PromptTemplate} but stores context in {@link UserPromptContext}.\n */\nexport class UserPromptTemplate extends BasePromptTemplate {\n protected get _contextSetter(): ContextSetter {\n return UserPromptContext;\n }\n\n protected get _displayName(): string {\n return 'UserPromptTemplate';\n }\n}\n","/**\n * Centralized logging configuration for Neatlogs SDK.\n * Provides structured logging with configurable levels.\n */\n\ntype LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n DEBUG: 0,\n INFO: 1,\n WARN: 2,\n ERROR: 3,\n NONE: 4,\n};\n\nlet _logLevel: LogLevel = 'INFO';\nlet _disabled = false;\n\nfunction _getConfiguredLevel(): LogLevel {\n const envLevel = process.env.NEATLOGS_LOG_LEVEL?.toUpperCase();\n if (envLevel && envLevel in LOG_LEVELS) {\n return envLevel as LogLevel;\n }\n return 'INFO';\n}\n\n// Initialize from env on first import\n_logLevel = _getConfiguredLevel();\n\nexport function getLogger() {\n return {\n debug(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.DEBUG) {\n console.debug(`[neatlogs] ${message}`, ...args);\n }\n },\n info(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.INFO) {\n console.info(`[neatlogs] ${message}`, ...args);\n }\n },\n warn(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.WARN) {\n console.warn(`[neatlogs] ${message}`, ...args);\n }\n },\n error(message: string, ...args: any[]) {\n if (!_disabled && LOG_LEVELS[_logLevel] <= LOG_LEVELS.ERROR) {\n console.error(`[neatlogs] ${message}`, ...args);\n }\n },\n };\n}\n\nexport function setLogLevel(level: LogLevel): void {\n _logLevel = level;\n}\n\nexport function enableDebugLogging(): void {\n _logLevel = 'DEBUG';\n}\n\nexport function disableLogging(): void {\n _disabled = true;\n}\n\nexport function enableLogging(): void {\n _disabled = false;\n}\n","/**\n * PII masking support for Neatlogs spans.\n *\n * Users supply a callable that receives the full span dict and returns\n * the (possibly modified) span dict. Return null to drop the span entirely.\n */\n\nimport type { MaskFunction } from '../types.js';\nimport { getLogger } from './logger.js';\n\nconst logger = getLogger();\n\n/** Module-level registry: key -> mask function */\nconst _MASK_REGISTRY = new Map<string, MaskFunction>();\n\nlet _nextId = 0;\n\n/**\n * Register a mask function and return its lookup key.\n */\nexport function registerMask(fn: MaskFunction): string {\n const key = String(++_nextId);\n _MASK_REGISTRY.set(key, fn);\n return key;\n}\n\n/**\n * Apply the effective mask function to span data.\n *\n * Per-span mask (stored in attributes[\"neatlogs.mask_id\"]) takes\n * precedence over the global mask. Returns the (possibly modified) dict.\n */\nexport function applyMask(\n spanData: Record<string, any>,\n globalMask: MaskFunction | null | undefined,\n): Record<string, any> | null {\n const maskId = spanData?.attributes?.['neatlogs.mask_id'];\n let maskFn: MaskFunction | undefined | null = null;\n\n if (maskId) {\n maskFn = _MASK_REGISTRY.get(String(maskId)) ?? null;\n }\n\n if (!maskFn) {\n maskFn = globalMask ?? null;\n }\n\n if (!maskFn) {\n return spanData;\n }\n\n try {\n const result = maskFn(spanData);\n // null means \"drop this span entirely\"; undefined means \"keep original\"\n if (result === null) return null;\n return result !== undefined ? result : spanData;\n } catch (exc) {\n logger.warn(\n `mask callable raised an exception for span '${spanData?.name}': ${exc} — original span data will be exported unchanged.`,\n );\n return spanData;\n }\n}\n\n/**\n * Clear all registered masks. Used for testing.\n * @internal\n */\nexport function _clearMaskRegistry(): void {\n _MASK_REGISTRY.clear();\n _nextId = 0;\n}\n","/**\n * End-user identity.\n *\n * The *end user* is the user of OUR CUSTOMER'S application — the person\n * interacting with the AI product our customer built. This is deliberately\n * distinct from the operator-level `userId` set in {@link init}, which identifies\n * whoever is running the SDK (a developer, a service account, a CI job).\n *\n * Model: **one end-user per trace.** Identity is declared once at a trace\n * boundary — `init()` (process-global default), `trace()` (per-request), or\n * `span()` (per root) — via new options on those existing constructs. There is\n * no separate `identify()` call and no nested per-span override: a trace belongs\n * to a single end-user.\n *\n * End-user identity belongs to the trace as a whole, so the SDK only stamps it\n * on the trace's ROOT span (any span kind, created via `trace()` / `span()`). A\n * non-root child span never carries it. The backend reads end-user from the root\n * span and rolls the value up to the trace (and its session) so\n * filtering/analytics are trace- and session-level, not per-span.\n *\n * Canonical span attributes:\n * `neatlogs.end_user.id` — the end-user identifier (string)\n * `neatlogs.end_user.metadata` — JSON object of arbitrary end-user fields\n */\n\nimport { trace as otelTrace, context as otelContext, type Span } from '@opentelemetry/api';\nimport { getLogger } from './logger.js';\n\nconst logger = getLogger();\n\nexport const END_USER_ID_KEY = 'neatlogs.end_user.id';\nexport const END_USER_METADATA_KEY = 'neatlogs.end_user.metadata';\n\n/** Coerce end-user metadata to a JSON string. Returns undefined when empty. */\nexport function normalizeEndUserMetadata(\n metadata: string | Record<string, any> | undefined | null,\n): string | undefined {\n if (metadata === undefined || metadata === null) return undefined;\n if (typeof metadata === 'string') return metadata || undefined;\n try {\n return JSON.stringify(metadata);\n } catch {\n return JSON.stringify(String(metadata));\n }\n}\n\n/**\n * True when no recording span is currently active — i.e. a span created now\n * would be the trace root.\n */\nexport function isRootSpan(): boolean {\n const current = otelTrace.getSpan(otelContext.active());\n return !(current && current.isRecording());\n}\n\n/**\n * Stamp end-user id/metadata onto a ROOT span (best-effort). When `isRoot` is\n * false the span is a child and the value is ignored — the backend reads\n * end-user from the root span and rolls it up to the trace and session.\n */\nexport function applyEndUserAttributes(\n span: Span,\n endUserId?: string,\n endUserMetadata?: string | Record<string, any> | null,\n isRoot = true,\n): void {\n if (!endUserId && !endUserMetadata) return;\n if (!isRoot) {\n logger.debug(\n '[end_user] Ignoring endUserId/endUserMetadata on a non-root span — ' +\n 'declare it on the trace root (top-level trace()/span()) or init().',\n );\n return;\n }\n if (endUserId) {\n span.setAttribute(END_USER_ID_KEY, String(endUserId));\n }\n const metaJson = normalizeEndUserMetadata(endUserMetadata);\n if (metaJson) {\n span.setAttribute(END_USER_METADATA_KEY, metaJson);\n }\n}\n","/**\n * Core span decoration utilities.\n * Provides the low-level wrapping logic used by span() and Span().\n */\n\nimport { trace, SpanStatusCode, type Span as OtelSpan } from '@opentelemetry/api';\nimport { getLogger } from '../core/logger.js';\nimport { registerMask } from '../core/mask.js';\nimport { applyEndUserAttributes, isRootSpan } from '../core/end-user.js';\nimport type { SpanOptions, MaskFunction } from '../types.js';\n\nconst logger = getLogger();\nconst TRACER_NAME = 'neatlogs';\n\n/** Safely serialize any value to JSON string. */\nexport function safeJsonDumps(value: any): string {\n try {\n return JSON.stringify(value, (_key, val) => {\n if (typeof val === 'bigint') return val.toString();\n if (val instanceof Error) return { message: val.message, name: val.name, stack: val.stack };\n if (typeof val === 'function') return `[Function: ${val.name || 'anonymous'}]`;\n return val;\n });\n } catch {\n return String(value);\n }\n}\n\n/** Serialize an object for span attributes. Handles toJSON(), plain objects, primitives. */\nexport function serializeObj(obj: any): any {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') return obj;\n if (typeof obj.toJSON === 'function') return obj.toJSON();\n if (Array.isArray(obj)) return obj.map(serializeObj);\n if (typeof obj === 'object') {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = serializeObj(value);\n }\n return result;\n }\n return String(obj);\n}\n\n/** Check if content capture is enabled via env var. */\nexport function shouldCaptureContent(): boolean {\n const envVal = process.env.NEATLOGS_TRACE_CONTENT;\n if (envVal === undefined || envVal === '') return true;\n return envVal.toLowerCase() !== 'false' && envVal !== '0';\n}\n\n/** Set common span attributes from options. */\nexport function setCommonSpanAttrs(span: OtelSpan, opts: SpanOptions): void {\n if (opts.kind) {\n span.setAttribute('openinference.span.kind', opts.kind);\n }\n if (opts.internal) {\n span.setAttribute('neatlogs.internal', true);\n }\n if (opts.description) {\n span.setAttribute('neatlogs.description', opts.description);\n }\n if (opts.mask) {\n const maskId = registerMask(opts.mask);\n span.setAttribute('neatlogs.mask_id', maskId);\n }\n}\n\nexport interface DecorateSpanOptions extends SpanOptions {\n /** Override the span name (defaults to function name). */\n spanName?: string;\n /** Post-process callback after the function returns. */\n postprocessResult?: (span: OtelSpan, result: any, boundInputs: Record<string, any>) => void;\n}\n\n/**\n * Core wrapper factory that creates an instrumented version of a function.\n * This is the low-level building block used by span() and Span().\n */\nexport function decorateSpan<TArgs extends any[], TReturn>(\n opts: DecorateSpanOptions,\n fn: (...args: TArgs) => TReturn,\n): (...args: TArgs) => TReturn extends Promise<any> ? TReturn : Promise<Awaited<TReturn>> {\n const tracer = trace.getTracer(TRACER_NAME);\n const spanName = opts.spanName ?? opts.name ?? (fn.name || 'anonymous');\n const captureInput = opts.captureInput !== false;\n const captureOutput = opts.captureOutput !== false;\n const doCapture = shouldCaptureContent();\n\n const wrapped = (...args: TArgs): any => {\n // End-user belongs to the trace root only. Capture root status BEFORE the\n // span is created (once it's active, it would be the \"current\" span).\n const isRoot = isRootSpan();\n return tracer.startActiveSpan(spanName, (span: OtelSpan) => {\n try {\n // Set common attributes\n setCommonSpanAttrs(span, opts);\n\n // End-user identity (root span only; skipped on a non-root child).\n applyEndUserAttributes(span, opts.endUserId, opts.endUserMetadata, isRoot);\n\n // Capture input\n if (captureInput && doCapture && args.length > 0) {\n try {\n const inputValue = args.length === 1 ? serializeObj(args[0]) : args.map(serializeObj);\n span.setAttribute('input.value', safeJsonDumps(inputValue));\n } catch (err) {\n logger.debug(`Failed to capture input: ${err}`);\n }\n }\n\n // Execute the function\n const result = fn(...args);\n\n // Handle async results\n if (result instanceof Promise) {\n return result\n .then((resolved: any) => {\n // Capture output\n if (captureOutput && doCapture) {\n try {\n span.setAttribute('output.value', safeJsonDumps(serializeObj(resolved)));\n } catch (err) {\n logger.debug(`Failed to capture output: ${err}`);\n }\n }\n\n // Post-process\n if (opts.postprocessResult) {\n try {\n const boundInputs = _extractBoundInputs(fn, args);\n opts.postprocessResult(span, resolved, boundInputs);\n } catch (err) {\n logger.debug(`Postprocess failed: ${err}`);\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return resolved;\n })\n .catch((error: any) => {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error?.message ?? String(error),\n });\n span.recordException(error instanceof Error ? error : new Error(String(error)));\n span.end();\n throw error;\n });\n }\n\n // Sync result\n if (captureOutput && doCapture) {\n try {\n span.setAttribute('output.value', safeJsonDumps(serializeObj(result)));\n } catch (err) {\n logger.debug(`Failed to capture output: ${err}`);\n }\n }\n\n if (opts.postprocessResult) {\n try {\n const boundInputs = _extractBoundInputs(fn, args);\n opts.postprocessResult(span, result, boundInputs);\n } catch (err) {\n logger.debug(`Postprocess failed: ${err}`);\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return result;\n } catch (error: any) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error?.message ?? String(error),\n });\n span.recordException(error instanceof Error ? error : new Error(String(error)));\n span.end();\n throw error;\n }\n });\n };\n\n // Preserve function name for debugging\n Object.defineProperty(wrapped, 'name', { value: spanName, configurable: true });\n\n return wrapped as any;\n}\n\n/** Extract named arguments from function call (best-effort). */\nfunction _extractBoundInputs(fn: Function, args: any[]): Record<string, any> {\n const result: Record<string, any> = {};\n // Parse function parameter names from toString()\n const fnStr = fn.toString();\n const match = fnStr.match(/\\(([^)]*)\\)/);\n if (match) {\n const paramNames = match[1]\n .split(',')\n .map((p) => p.trim().replace(/\\s*[:=].*$/, '').replace(/^\\.\\.\\./, ''))\n .filter(Boolean);\n for (let i = 0; i < Math.min(paramNames.length, args.length); i++) {\n result[paramNames[i]] = args[i];\n }\n }\n return result;\n}\n"],"mappings":"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","names":["trace","otelContext","SpanStatusCode","otelTrace","otelContext","otelTrace","otelContext","SpanStatusCode","logger","logger","logger","otelContext","otelTrace","otelContext","SpanStatusCode","trace"]}
@@ -24,13 +24,13 @@ __export(azure_openai_exports, {
24
24
  wrapAzureOpenAI: () => wrapAzureOpenAI
25
25
  });
26
26
  module.exports = __toCommonJS(azure_openai_exports);
27
- var import_api4 = require("@opentelemetry/api");
27
+ var import_api5 = require("@opentelemetry/api");
28
28
 
29
29
  // src/core/auto-root.ts
30
- var import_api3 = require("@opentelemetry/api");
30
+ var import_api4 = require("@opentelemetry/api");
31
31
 
32
32
  // src/core/context.ts
33
- var import_api2 = require("@opentelemetry/api");
33
+ var import_api3 = require("@opentelemetry/api");
34
34
 
35
35
  // src/prompt/template.ts
36
36
  var import_node_async_hooks = require("async_hooks");
@@ -83,21 +83,25 @@ function getLogger() {
83
83
  // src/core/mask.ts
84
84
  var logger = getLogger();
85
85
 
86
- // src/decorators/base.ts
86
+ // src/core/end-user.ts
87
87
  var import_api = require("@opentelemetry/api");
88
88
  var logger2 = getLogger();
89
89
 
90
- // src/core/context.ts
90
+ // src/decorators/base.ts
91
+ var import_api2 = require("@opentelemetry/api");
91
92
  var logger3 = getLogger();
93
+
94
+ // src/core/context.ts
95
+ var logger4 = getLogger();
92
96
  var _sessionConfig = {};
93
97
  function getSessionConfig() {
94
98
  return { ..._sessionConfig };
95
99
  }
96
- var PROMPT_VARIABLES_KEY = (0, import_api2.createContextKey)("neatlogs.prompt_variables");
97
- var PROMPT_TEMPLATE_KEY = (0, import_api2.createContextKey)("neatlogs.prompt_template");
98
- var PROMPT_VERSION_KEY = (0, import_api2.createContextKey)("neatlogs.prompt_version");
99
- var USER_PROMPT_TEMPLATE_KEY = (0, import_api2.createContextKey)("neatlogs.user_prompt_template");
100
- var USER_PROMPT_VARIABLES_KEY = (0, import_api2.createContextKey)("neatlogs.user_prompt_variables");
100
+ var PROMPT_VARIABLES_KEY = (0, import_api3.createContextKey)("neatlogs.prompt_variables");
101
+ var PROMPT_TEMPLATE_KEY = (0, import_api3.createContextKey)("neatlogs.prompt_template");
102
+ var PROMPT_VERSION_KEY = (0, import_api3.createContextKey)("neatlogs.prompt_version");
103
+ var USER_PROMPT_TEMPLATE_KEY = (0, import_api3.createContextKey)("neatlogs.user_prompt_template");
104
+ var USER_PROMPT_VARIABLES_KEY = (0, import_api3.createContextKey)("neatlogs.user_prompt_variables");
101
105
 
102
106
  // src/core/auto-root.ts
103
107
  var ROOT_KINDS = /* @__PURE__ */ new Set(["workflow", "chain", "agent", "mcp_tool"]);
@@ -145,8 +149,8 @@ var AutoRootTracer = class {
145
149
  const kind = String(
146
150
  options?.attributes?.["neatlogs.span.kind"] ?? ""
147
151
  ).toLowerCase();
148
- const ctx = context ?? import_api3.context.active();
149
- const parent = import_api3.trace.getSpan(ctx);
152
+ const ctx = context ?? import_api4.context.active();
153
+ const parent = import_api4.trace.getSpan(ctx);
150
154
  const needsRoot = autoRootEnabled() && !ROOT_KINDS.has(kind) && !(parent !== void 0 && parent.isRecording());
151
155
  if (!needsRoot) {
152
156
  return this._tracer.startSpan(name, options, context);
@@ -156,7 +160,7 @@ var AutoRootTracer = class {
156
160
  { attributes: { "neatlogs.span.kind": "workflow", "neatlogs.auto_root": true } },
157
161
  ctx
158
162
  );
159
- const childCtx = import_api3.trace.setSpan(ctx, root);
163
+ const childCtx = import_api4.trace.setSpan(ctx, root);
160
164
  const child = this._tracer.startSpan(name, options, childCtx);
161
165
  return wrapSpanWithRoot(child, root);
162
166
  }
@@ -167,7 +171,7 @@ var AutoRootTracer = class {
167
171
  ));
168
172
  };
169
173
  function getProviderTracer(name) {
170
- return new AutoRootTracer(import_api3.trace.getTracer(name));
174
+ return new AutoRootTracer(import_api4.trace.getTracer(name));
171
175
  }
172
176
 
173
177
  // src/azure-openai.ts
@@ -189,12 +193,12 @@ function traceTool(name, fn) {
189
193
  "input.value": safeStringify(args)
190
194
  }
191
195
  },
192
- import_api4.context.active(),
196
+ import_api5.context.active(),
193
197
  async (span) => {
194
198
  try {
195
199
  const result = await fn(args);
196
200
  span.setAttribute("output.value", safeStringify(result));
197
- span.setStatus({ code: import_api4.SpanStatusCode.OK });
201
+ span.setStatus({ code: import_api5.SpanStatusCode.OK });
198
202
  return result;
199
203
  } catch (err) {
200
204
  recordError(span, err);
@@ -245,7 +249,7 @@ function tracedChatCompletionsCreate(original) {
245
249
  "neatlogs.llm.model_name": model,
246
250
  "neatlogs.llm.is_streaming": isStream
247
251
  }
248
- }, import_api4.context.active());
252
+ }, import_api5.context.active());
249
253
  for (let i = 0; i < messages.length; i++) {
250
254
  const msg = messages[i];
251
255
  span.setAttribute(`neatlogs.llm.input_messages.${i}.role`, msg.role ?? "");
@@ -274,8 +278,8 @@ function tracedChatCompletionsCreate(original) {
274
278
  opts.stream_options = { ...streamOpts, include_usage: true };
275
279
  }
276
280
  }
277
- const ctx = import_api4.trace.setSpan(import_api4.context.active(), span);
278
- const promise = import_api4.context.with(ctx, () => original(opts, ...rest));
281
+ const ctx = import_api5.trace.setSpan(import_api5.context.active(), span);
282
+ const promise = import_api5.context.with(ctx, () => original(opts, ...rest));
279
283
  return promise.then(
280
284
  (response) => {
281
285
  if (isStream) {
@@ -303,9 +307,9 @@ function tracedResponsesCreate(original) {
303
307
  "neatlogs.llm.model_name": model,
304
308
  "input.value": safeStringify(opts?.input ?? "")
305
309
  }
306
- }, import_api4.context.active());
307
- const ctx = import_api4.trace.setSpan(import_api4.context.active(), span);
308
- const promise = import_api4.context.with(ctx, () => original(opts, ...rest));
310
+ }, import_api5.context.active());
311
+ const ctx = import_api5.trace.setSpan(import_api5.context.active(), span);
312
+ const promise = import_api5.context.with(ctx, () => original(opts, ...rest));
309
313
  return promise.then(
310
314
  (response) => {
311
315
  if (response?.output_text) {
@@ -317,7 +321,7 @@ function tracedResponsesCreate(original) {
317
321
  if (response.usage.input_tokens != null) span.setAttribute("neatlogs.llm.token_count.prompt", response.usage.input_tokens);
318
322
  if (response.usage.output_tokens != null) span.setAttribute("neatlogs.llm.token_count.completion", response.usage.output_tokens);
319
323
  }
320
- span.setStatus({ code: import_api4.SpanStatusCode.OK });
324
+ span.setStatus({ code: import_api5.SpanStatusCode.OK });
321
325
  span.end();
322
326
  return response;
323
327
  },
@@ -332,7 +336,7 @@ function wrapAsyncIterableStream(stream, span) {
332
336
  const chunks = [];
333
337
  const originalAsyncIterator = stream?.[Symbol.asyncIterator]?.bind(stream);
334
338
  if (!originalAsyncIterator) {
335
- span.setStatus({ code: import_api4.SpanStatusCode.OK });
339
+ span.setStatus({ code: import_api5.SpanStatusCode.OK });
336
340
  span.end();
337
341
  return stream;
338
342
  }
@@ -418,7 +422,7 @@ function finalizeStreamChunks(span, chunks) {
418
422
  span.setAttribute("neatlogs.llm.token_count.reasoning", usage.completion_tokens_details.reasoning_tokens);
419
423
  }
420
424
  }
421
- span.setStatus({ code: import_api4.SpanStatusCode.OK });
425
+ span.setStatus({ code: import_api5.SpanStatusCode.OK });
422
426
  span.end();
423
427
  }
424
428
  function finalizeChatResponse(span, response) {
@@ -455,7 +459,7 @@ function finalizeChatResponse(span, response) {
455
459
  }
456
460
  }
457
461
  if (response?.model) span.setAttribute("neatlogs.llm.model_name", response.model);
458
- span.setStatus({ code: import_api4.SpanStatusCode.OK });
462
+ span.setStatus({ code: import_api5.SpanStatusCode.OK });
459
463
  span.end();
460
464
  }
461
465
  function setInvocationParams(span, opts) {
@@ -475,10 +479,10 @@ function safeStringify(value) {
475
479
  }
476
480
  function recordError(span, err) {
477
481
  if (err instanceof Error) {
478
- span.setStatus({ code: import_api4.SpanStatusCode.ERROR, message: err.message });
482
+ span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: err.message });
479
483
  span.recordException(err);
480
484
  } else {
481
- span.setStatus({ code: import_api4.SpanStatusCode.ERROR, message: String(err) });
485
+ span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: String(err) });
482
486
  }
483
487
  span.end();
484
488
  }