neatlogs 1.1.19 → 1.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -2
- package/dist/ai-sdk.cjs +186 -11
- package/dist/ai-sdk.cjs.map +1 -1
- package/dist/ai-sdk.d.ts +17 -7
- package/dist/ai-sdk.mjs +189 -12
- package/dist/ai-sdk.mjs.map +1 -1
- package/dist/anthropic.cjs +31 -14
- package/dist/anthropic.cjs.map +1 -1
- package/dist/anthropic.mjs +31 -14
- package/dist/anthropic.mjs.map +1 -1
- package/dist/azure-openai.cjs +31 -14
- package/dist/azure-openai.cjs.map +1 -1
- package/dist/azure-openai.mjs +31 -14
- package/dist/azure-openai.mjs.map +1 -1
- package/dist/bedrock.cjs +31 -14
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.mjs +31 -14
- package/dist/bedrock.mjs.map +1 -1
- package/dist/browser.cjs +26 -1
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.mjs +26 -1
- package/dist/browser.mjs.map +1 -1
- package/dist/claude-agent-sdk.cjs +2 -2
- package/dist/claude-agent-sdk.cjs.map +1 -1
- package/dist/claude-agent-sdk.mjs +2 -2
- package/dist/claude-agent-sdk.mjs.map +1 -1
- package/dist/cli.cjs +452 -200
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +427 -173
- package/dist/cli.mjs.map +1 -1
- package/dist/google-genai.cjs +31 -14
- package/dist/google-genai.cjs.map +1 -1
- package/dist/google-genai.mjs +31 -14
- package/dist/google-genai.mjs.map +1 -1
- package/dist/index.cjs +652 -333
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -6
- package/dist/index.mjs +505 -184
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +3 -3
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.mjs +3 -3
- package/dist/langchain.mjs.map +1 -1
- package/dist/mastra-wrap.cjs +2 -2
- package/dist/mastra-wrap.cjs.map +1 -1
- package/dist/mastra-wrap.mjs +2 -2
- package/dist/mastra-wrap.mjs.map +1 -1
- package/dist/openai-agents.cjs.map +1 -1
- package/dist/openai-agents.mjs.map +1 -1
- package/dist/openai.cjs +31 -14
- package/dist/openai.cjs.map +1 -1
- package/dist/openai.mjs +31 -14
- package/dist/openai.mjs.map +1 -1
- package/dist/opencode-plugin.cjs +1 -1
- package/dist/opencode-plugin.cjs.map +1 -1
- package/dist/opencode-plugin.mjs +1 -1
- package/dist/opencode-plugin.mjs.map +1 -1
- package/dist/openrouter-agent.cjs +31 -14
- package/dist/openrouter-agent.cjs.map +1 -1
- package/dist/openrouter-agent.mjs +31 -14
- package/dist/openrouter-agent.mjs.map +1 -1
- package/dist/pi-agent.cjs.map +1 -1
- package/dist/pi-agent.mjs.map +1 -1
- package/dist/vertex-ai.cjs +31 -14
- package/dist/vertex-ai.cjs.map +1 -1
- package/dist/vertex-ai.mjs +31 -14
- package/dist/vertex-ai.mjs.map +1 -1
- package/package.json +1 -1
package/dist/ai-sdk.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/ai-sdk.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SpanStatusCode
|
|
4
|
+
} from "@opentelemetry/api";
|
|
3
5
|
|
|
4
6
|
// src/core/provider.ts
|
|
5
7
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
@@ -95,11 +97,11 @@ function getNeatlogsActiveContext() {
|
|
|
95
97
|
function getNeatlogsParentContext(baseContext) {
|
|
96
98
|
return baseContext ?? getNeatlogsActiveContext();
|
|
97
99
|
}
|
|
98
|
-
function withNeatlogsSpan(span, fn, baseContext) {
|
|
100
|
+
function withNeatlogsSpan(span, fn, baseContext, rootSpan) {
|
|
99
101
|
const base = baseContext ?? getNeatlogsActiveContext();
|
|
100
102
|
let ctx = otelTrace.setSpan(base, span);
|
|
101
103
|
if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === void 0) {
|
|
102
|
-
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);
|
|
104
|
+
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, rootSpan ?? span);
|
|
103
105
|
}
|
|
104
106
|
return privateContextStorage.run(ctx, fn);
|
|
105
107
|
}
|
|
@@ -108,6 +110,8 @@ function withNeatlogsSpan(span, fn, baseContext) {
|
|
|
108
110
|
var TRACER_NAME = "neatlogs.ai-sdk";
|
|
109
111
|
function createAITelemetry(opts = {}) {
|
|
110
112
|
const userMeta = opts.metadata ?? {};
|
|
113
|
+
const neatlogsTracer = getRoutingNeatlogsTracer(TRACER_NAME);
|
|
114
|
+
const callerTracer = opts.tracer;
|
|
111
115
|
return {
|
|
112
116
|
isEnabled: true,
|
|
113
117
|
recordInputs: true,
|
|
@@ -116,8 +120,11 @@ function createAITelemetry(opts = {}) {
|
|
|
116
120
|
// internally, which would otherwise parent its native spans from the foreign
|
|
117
121
|
// global context AND push them onto it (so a co-tenant's next span inherits
|
|
118
122
|
// ours). The facade routes both through the private Neatlogs context.
|
|
119
|
-
tracer:
|
|
120
|
-
|
|
123
|
+
tracer: callerTracer ? createMirroredTracer(callerTracer, neatlogsTracer) : neatlogsTracer,
|
|
124
|
+
...opts.functionId !== void 0 ? { functionId: opts.functionId } : {},
|
|
125
|
+
// The marker is an implementation detail used only when Neatlogs owns the
|
|
126
|
+
// telemetry stream. Do not leak it into caller-owned providers.
|
|
127
|
+
metadata: callerTracer ? { ...userMeta } : { ...userMeta, neatlogsWrapped: true }
|
|
121
128
|
};
|
|
122
129
|
}
|
|
123
130
|
function safeStringify(value) {
|
|
@@ -191,21 +198,80 @@ var WRAPPED_FUNCTIONS = [
|
|
|
191
198
|
"embedMany",
|
|
192
199
|
"rerank"
|
|
193
200
|
];
|
|
201
|
+
var WRAPPED_AGENT_CONSTRUCTORS = [
|
|
202
|
+
"ToolLoopAgent",
|
|
203
|
+
"Experimental_Agent"
|
|
204
|
+
];
|
|
205
|
+
var wrappedExports = /* @__PURE__ */ new WeakSet();
|
|
206
|
+
var wrapperByOriginal = /* @__PURE__ */ new WeakMap();
|
|
194
207
|
function wrapAISDK(aiModule) {
|
|
195
208
|
const wrapped = { ...aiModule };
|
|
196
209
|
for (const name of WRAPPED_FUNCTIONS) {
|
|
197
210
|
const original = aiModule[name];
|
|
198
211
|
if (typeof original !== "function") continue;
|
|
212
|
+
const existing = getExistingWrapper(original);
|
|
213
|
+
if (existing) {
|
|
214
|
+
wrapped[name] = existing;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
199
217
|
if (name === "streamText" || name === "streamObject") {
|
|
200
|
-
wrapped[name] =
|
|
218
|
+
wrapped[name] = cacheWrapper(
|
|
219
|
+
original,
|
|
220
|
+
createStreamWrapper(name, original)
|
|
221
|
+
);
|
|
201
222
|
} else {
|
|
202
|
-
wrapped[name] =
|
|
223
|
+
wrapped[name] = cacheWrapper(
|
|
224
|
+
original,
|
|
225
|
+
createAsyncWrapper(name, original)
|
|
226
|
+
);
|
|
203
227
|
}
|
|
204
228
|
}
|
|
229
|
+
for (const name of WRAPPED_AGENT_CONSTRUCTORS) {
|
|
230
|
+
const original = aiModule[name];
|
|
231
|
+
if (typeof original !== "function") continue;
|
|
232
|
+
const existing = getExistingWrapper(original);
|
|
233
|
+
wrapped[name] = existing ?? cacheWrapper(
|
|
234
|
+
original,
|
|
235
|
+
createAgentConstructorWrapper(original)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
205
238
|
return wrapped;
|
|
206
239
|
}
|
|
240
|
+
function getExistingWrapper(original) {
|
|
241
|
+
if (wrappedExports.has(original)) return original;
|
|
242
|
+
return wrapperByOriginal.get(original);
|
|
243
|
+
}
|
|
244
|
+
function cacheWrapper(original, wrapped) {
|
|
245
|
+
wrapperByOriginal.set(original, wrapped);
|
|
246
|
+
wrappedExports.add(wrapped);
|
|
247
|
+
return wrapped;
|
|
248
|
+
}
|
|
249
|
+
function createAgentConstructorWrapper(original) {
|
|
250
|
+
return new Proxy(original, {
|
|
251
|
+
construct(target, args, newTarget) {
|
|
252
|
+
if (args.length === 0 || typeof args[0] !== "object" || args[0] === null) {
|
|
253
|
+
return Reflect.construct(target, args, newTarget);
|
|
254
|
+
}
|
|
255
|
+
const settings = mergeAgentSettings(args[0]);
|
|
256
|
+
return Reflect.construct(target, [settings, ...args.slice(1)], newTarget);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function mergeAgentSettings(settings) {
|
|
261
|
+
const merged = mergeTelemetry(settings);
|
|
262
|
+
const userPrepareCall = settings.prepareCall;
|
|
263
|
+
if (typeof userPrepareCall !== "function") return merged;
|
|
264
|
+
return {
|
|
265
|
+
...merged,
|
|
266
|
+
prepareCall: async function wrappedPrepareCall(...args) {
|
|
267
|
+
const prepared = await Reflect.apply(userPrepareCall, this, args);
|
|
268
|
+
return prepared == null ? prepared : mergeTelemetry(prepared, settings.experimental_telemetry);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
207
272
|
function rootSpanKind(name) {
|
|
208
|
-
if (name === "embed" || name === "embedMany" || name === "rerank")
|
|
273
|
+
if (name === "embed" || name === "embedMany" || name === "rerank")
|
|
274
|
+
return "CHAIN";
|
|
209
275
|
return "WORKFLOW";
|
|
210
276
|
}
|
|
211
277
|
function getParentContext() {
|
|
@@ -302,15 +368,126 @@ function createStreamWrapper(name, original) {
|
|
|
302
368
|
);
|
|
303
369
|
};
|
|
304
370
|
}
|
|
305
|
-
function
|
|
371
|
+
function createMirroredSpan(primary, secondary) {
|
|
372
|
+
const mirrored = {
|
|
373
|
+
spanContext() {
|
|
374
|
+
return primary.spanContext();
|
|
375
|
+
},
|
|
376
|
+
setAttribute(key, value) {
|
|
377
|
+
primary.setAttribute(key, value);
|
|
378
|
+
secondary.setAttribute(key, value);
|
|
379
|
+
return mirrored;
|
|
380
|
+
},
|
|
381
|
+
setAttributes(attributes) {
|
|
382
|
+
primary.setAttributes(attributes);
|
|
383
|
+
secondary.setAttributes(attributes);
|
|
384
|
+
return mirrored;
|
|
385
|
+
},
|
|
386
|
+
addEvent(name, attributesOrStartTime, startTime) {
|
|
387
|
+
primary.addEvent(name, attributesOrStartTime, startTime);
|
|
388
|
+
secondary.addEvent(name, attributesOrStartTime, startTime);
|
|
389
|
+
return mirrored;
|
|
390
|
+
},
|
|
391
|
+
addLink(link) {
|
|
392
|
+
primary.addLink(link);
|
|
393
|
+
secondary.addLink(link);
|
|
394
|
+
return mirrored;
|
|
395
|
+
},
|
|
396
|
+
addLinks(links) {
|
|
397
|
+
primary.addLinks(links);
|
|
398
|
+
secondary.addLinks(links);
|
|
399
|
+
return mirrored;
|
|
400
|
+
},
|
|
401
|
+
setStatus(status) {
|
|
402
|
+
primary.setStatus(status);
|
|
403
|
+
secondary.setStatus(status);
|
|
404
|
+
return mirrored;
|
|
405
|
+
},
|
|
406
|
+
updateName(name) {
|
|
407
|
+
primary.updateName(name);
|
|
408
|
+
secondary.updateName(name);
|
|
409
|
+
return mirrored;
|
|
410
|
+
},
|
|
411
|
+
end(endTime) {
|
|
412
|
+
primary.end(endTime);
|
|
413
|
+
secondary.end(endTime);
|
|
414
|
+
},
|
|
415
|
+
isRecording() {
|
|
416
|
+
return primary.isRecording() || secondary.isRecording();
|
|
417
|
+
},
|
|
418
|
+
recordException(exception, time) {
|
|
419
|
+
primary.recordException(exception, time);
|
|
420
|
+
secondary.recordException(exception, time);
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
return mirrored;
|
|
424
|
+
}
|
|
425
|
+
function createMirroredTracer(primary, secondary) {
|
|
426
|
+
return {
|
|
427
|
+
startSpan(name, options, context) {
|
|
428
|
+
const primarySpan = primary.startSpan(name, options, context);
|
|
429
|
+
const secondarySpan = secondary.startSpan(name, options);
|
|
430
|
+
return createMirroredSpan(primarySpan, secondarySpan);
|
|
431
|
+
},
|
|
432
|
+
startActiveSpan: ((name, arg2, arg3, arg4) => {
|
|
433
|
+
let options;
|
|
434
|
+
let context;
|
|
435
|
+
let fn;
|
|
436
|
+
if (typeof arg2 === "function") {
|
|
437
|
+
fn = arg2;
|
|
438
|
+
} else if (typeof arg3 === "function") {
|
|
439
|
+
options = arg2;
|
|
440
|
+
fn = arg3;
|
|
441
|
+
} else {
|
|
442
|
+
options = arg2;
|
|
443
|
+
context = arg3;
|
|
444
|
+
fn = arg4;
|
|
445
|
+
}
|
|
446
|
+
const runSecondary = (primarySpan) => {
|
|
447
|
+
const onSecondarySpan = (secondarySpan) => fn(createMirroredSpan(primarySpan, secondarySpan));
|
|
448
|
+
return options === void 0 ? secondary.startActiveSpan(name, onSecondarySpan) : secondary.startActiveSpan(name, options, onSecondarySpan);
|
|
449
|
+
};
|
|
450
|
+
if (context !== void 0) {
|
|
451
|
+
return primary.startActiveSpan(
|
|
452
|
+
name,
|
|
453
|
+
options ?? {},
|
|
454
|
+
context,
|
|
455
|
+
runSecondary
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return options === void 0 ? primary.startActiveSpan(name, runSecondary) : primary.startActiveSpan(name, options, runSecondary);
|
|
459
|
+
})
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function mergeTelemetry(opts, fallbackTelemetry) {
|
|
463
|
+
const requestedTelemetry = {
|
|
464
|
+
...fallbackTelemetry,
|
|
465
|
+
...opts?.experimental_telemetry,
|
|
466
|
+
metadata: {
|
|
467
|
+
...fallbackTelemetry?.metadata,
|
|
468
|
+
...opts?.experimental_telemetry?.metadata
|
|
469
|
+
}
|
|
470
|
+
};
|
|
306
471
|
const baseTelemetry = createAITelemetry({
|
|
307
|
-
|
|
472
|
+
functionId: requestedTelemetry.functionId,
|
|
473
|
+
metadata: requestedTelemetry.metadata,
|
|
474
|
+
tracer: requestedTelemetry.tracer
|
|
308
475
|
});
|
|
476
|
+
const callerTracer = requestedTelemetry.tracer;
|
|
477
|
+
const hasCallerTracer = callerTracer !== void 0;
|
|
309
478
|
return {
|
|
310
479
|
...opts,
|
|
311
480
|
experimental_telemetry: {
|
|
312
|
-
...
|
|
313
|
-
...
|
|
481
|
+
...baseTelemetry,
|
|
482
|
+
...requestedTelemetry,
|
|
483
|
+
isEnabled: true,
|
|
484
|
+
recordInputs: requestedTelemetry.recordInputs ?? true,
|
|
485
|
+
recordOutputs: requestedTelemetry.recordOutputs ?? true,
|
|
486
|
+
tracer: baseTelemetry.tracer,
|
|
487
|
+
// Do not add Neatlogs-only marker metadata to a caller-owned telemetry
|
|
488
|
+
// pipeline such as Laminar. Both providers receive the same AI SDK span
|
|
489
|
+
// data, while their providers, parent contexts, and exporters stay separate.
|
|
490
|
+
metadata: hasCallerTracer ? requestedTelemetry.metadata : baseTelemetry.metadata
|
|
314
491
|
}
|
|
315
492
|
};
|
|
316
493
|
}
|
package/dist/ai-sdk.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ai-sdk.ts","../src/core/provider.ts","../src/core/active-client.ts"],"sourcesContent":["/**\n * Vercel AI SDK wrapper — inline implementation.\n *\n * Wraps `generateText`, `streamText`, `generateObject`, `streamObject` from\n * the `ai` package with OTel parent spans + forced telemetry. Static export,\n * no dynamic imports — bundler-friendly (works with Turbopack, webpack, esbuild).\n *\n * Usage:\n * import { wrapAISDK } from 'neatlogs';\n * import * as ai from 'ai';\n * const { streamText, generateText } = wrapAISDK(ai);\n */\n\nimport { SpanStatusCode, type Span, type Tracer } from '@opentelemetry/api';\nimport { getNeatlogsTracer, getNeatlogsParentContext, getRoutingNeatlogsTracer, withNeatlogsSpan } from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.ai-sdk';\n\n// -- Telemetry config --------------------------------------------------------\n\nexport interface CreateAITelemetryOptions {\n metadata?: Record<string, unknown>;\n}\n\nexport interface AITelemetryConfig {\n isEnabled: true;\n recordInputs: true;\n recordOutputs: true;\n tracer: Tracer;\n metadata: Record<string, unknown>;\n}\n\nexport function createAITelemetry(\n opts: CreateAITelemetryOptions = {},\n): AITelemetryConfig {\n const userMeta = opts.metadata ?? {};\n return {\n isEnabled: true,\n recordInputs: true,\n recordOutputs: true,\n // Hand the AI SDK an isolation-aware tracer: it calls startActiveSpan()\n // internally, which would otherwise parent its native spans from the foreign\n // global context AND push them onto it (so a co-tenant's next span inherits\n // ours). The facade routes both through the private Neatlogs context.\n tracer: getRoutingNeatlogsTracer(TRACER_NAME),\n metadata: { ...userMeta, neatlogsWrapped: true },\n };\n}\n\n// -- Span attributes ---------------------------------------------------------\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction setInputValue(span: Span, opts: Record<string, unknown>): void {\n // For generateText/streamText — only capture prompt or messages, not full model config\n if (opts && ('prompt' in opts || 'messages' in opts)) {\n const input = opts.messages ?? opts.prompt;\n const stringified = safeStringify(input);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n return;\n }\n const stringified = safeStringify(opts);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n}\n\nfunction setOutputValue(span: Span, result: unknown): void {\n if (result && typeof result === 'object') {\n const r = result as Record<string, unknown>;\n // GenerateTextResult / StreamTextResult — extract meaningful fields\n if ('text' in r && 'finishReason' in r) {\n const text = String(r.text ?? '');\n if (text) {\n span.setAttribute('output.value', text);\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n // GenerateObjectResult — the structured object is the output, not `text`.\n // Without this the envelope (object+usage+response+…) gets stringified whole.\n if ('object' in r && 'finishReason' in r) {\n if (r.object !== undefined) {\n span.setAttribute('output.value', safeStringify(r.object));\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n }\n const stringified = safeStringify(result);\n if (stringified) {\n span.setAttribute('output.value', stringified);\n }\n}\n\n// Extract output from a streamText/streamObject `onFinish` event. The event\n// extends StepResult, so `text` (streamText) / `object` (streamObject) sit at\n// the top level alongside `finishReason` — but the event also carries `steps`,\n// `usage`, etc., so we pull only the meaningful fields instead of stringifying\n// the whole envelope.\nfunction setStreamOutputValue(span: Span, event: unknown): void {\n if (!event || typeof event !== 'object') return;\n const e = event as Record<string, unknown>;\n if (typeof e.text === 'string' && e.text) {\n span.setAttribute('output.value', e.text);\n } else if ('object' in e && e.object !== undefined) {\n const stringified = safeStringify(e.object);\n if (stringified) span.setAttribute('output.value', stringified);\n }\n if (e.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(e.finishReason));\n }\n}\n\n// -- Wrapping ----------------------------------------------------------------\n\ntype WrappedFunctionName = 'generateText' | 'streamText' | 'generateObject' | 'streamObject' | 'embed' | 'embedMany' | 'rerank';\n\nconst WRAPPED_FUNCTIONS: readonly WrappedFunctionName[] = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n 'rerank',\n] as const;\n\n/**\n * Wrap the `ai` module namespace so that every `generateText` / `streamText` /\n * `generateObject` / `streamObject` call:\n *\n * 1. Opens a parent OTel span on the active TracerProvider.\n * 2. Forces `experimental_telemetry: { isEnabled: true }`, merging user metadata.\n * 3. Records input/output on the parent span and propagates errors.\n *\n * Other exports (types, helpers) pass through unchanged.\n */\nexport function wrapAISDK<T extends Record<string, unknown>>(aiModule: T): T {\n const wrapped: Record<string, unknown> = { ...aiModule };\n\n for (const name of WRAPPED_FUNCTIONS) {\n const original = aiModule[name];\n if (typeof original !== 'function') continue;\n\n if (name === 'streamText' || name === 'streamObject') {\n wrapped[name] = createStreamWrapper(name, original as (opts: any) => unknown);\n } else {\n wrapped[name] = createAsyncWrapper(name, original as (opts: any) => Promise<unknown>);\n }\n }\n\n return wrapped as T;\n}\n\nfunction rootSpanKind(name: WrappedFunctionName): string {\n if (name === 'embed' || name === 'embedMany' || name === 'rerank') return 'CHAIN';\n return 'WORKFLOW';\n}\n\n\nfunction getParentContext() {\n // Our parent comes solely from the private span store; a\n // foreign provider's active span must never become our ancestor.\n return getNeatlogsParentContext();\n}\n\nfunction createAsyncWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => Promise<unknown>,\n): (opts: any) => Promise<unknown> {\n return async function wrappedAsyncFn(opts: any): Promise<unknown> {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan (NOT startActiveSpan) + withNeatlogsSpan: startActiveSpan would\n // push our span onto the GLOBAL OTel context, so a foreign tracer's\n // startSpan() inside generateText() would read it as parent and inherit our\n // trace id. withNeatlogsSpan carries the parent in the private store in\n // the private context, leaving the global context untouched.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n async () => {\n try {\n const isEmbedOrRerank = name === 'embed' || name === 'embedMany' || name === 'rerank';\n if (!isEmbedOrRerank) {\n setInputValue(span, opts);\n }\n if (name === 'rerank' && opts?.query) {\n span.setAttribute('ai.rerank.query', String(opts.query));\n }\n const merged = mergeTelemetry(opts);\n const result = await original(merged);\n if (!isEmbedOrRerank) {\n setOutputValue(span, result);\n }\n return result;\n } catch (err) {\n recordSpanError(span, err);\n throw err;\n } finally {\n span.end();\n }\n },\n parentContext,\n );\n };\n}\n\n// streamText/streamObject return synchronously while the model keeps producing\n// tokens for seconds afterwards. Ending the span in a `finally` (as a plain sync\n// wrapper would) closes it in ~2ms with no output — the output only exists once\n// the stream finishes. Instead we keep the span open and end it from the AI SDK's\n// `onFinish` callback, where the final text/object is available. Any user-provided\n// `onFinish` is preserved and invoked first.\nfunction createStreamWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => unknown,\n): (opts: any) => unknown {\n return function wrappedStreamFn(opts: any): unknown {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan + withNeatlogsSpan (see createAsyncWrapper) so streamText's\n // internals never see our span on the global OTel context. The span stays\n // open past the run scope and is ended from onFinish/onError.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n () => {\n let spanEnded = false;\n const endOnce = () => {\n if (spanEnded) return;\n spanEnded = true;\n span.end();\n };\n try {\n setInputValue(span, opts);\n const merged = mergeTelemetry(opts);\n const userOnFinish = opts?.onFinish;\n const userOnError = opts?.onError;\n const wrappedOpts = {\n ...merged,\n onFinish: async (event: any) => {\n try {\n setStreamOutputValue(span, event);\n } finally {\n endOnce();\n }\n if (typeof userOnFinish === 'function') {\n return userOnFinish(event);\n }\n },\n onError: (event: any) => {\n recordSpanError(span, (event && event.error) ?? event);\n endOnce();\n if (typeof userOnError === 'function') {\n return userOnError(event);\n }\n },\n };\n return original(wrappedOpts);\n } catch (err) {\n // Synchronous throw (e.g. bad arguments) — the stream never started.\n recordSpanError(span, err);\n endOnce();\n throw err;\n }\n },\n parentContext,\n );\n };\n}\n\nfunction mergeTelemetry(opts: any): any {\n const baseTelemetry: AITelemetryConfig = createAITelemetry({\n metadata: opts?.experimental_telemetry?.metadata,\n });\n return {\n ...opts,\n experimental_telemetry: {\n ...opts?.experimental_telemetry,\n ...baseTelemetry,\n },\n };\n}\n\n\nfunction recordSpanError(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}\n","/**\n * Neatlogs-owned tracing state.\n *\n * Spans are created by the private Neatlogs provider and their parent is carried\n * in a private context key. The process-global OpenTelemetry span is\n * deliberately left untouched so other observability SDKs cannot export or\n * become parents of Neatlogs spans.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n ROOT_CONTEXT,\n INVALID_SPAN_CONTEXT,\n createContextKey,\n trace as otelTrace,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n} from '@opentelemetry/api';\nimport { getActiveClient } from './active-client.js';\n\n// Carries the trace-ROOT span down the private context so descendants can target\n// it (e.g. setTraceOutput). getActiveNeatlogsSpan() returns the innermost span,\n// which is not the root once nested; this key preserves the root reference.\nconst NEATLOGS_ROOT_SPAN_KEY = createContextKey('neatlogs.root_span');\n\n// Entry points are bundled independently (`neatlogs`, `neatlogs/openai`,\n// `neatlogs/ai`, `neatlogs/mastra`, … each in both CJS and ESM), so every piece\n// of shared tracing state — the private span store AND the resolved provider /\n// provider — must live on `globalThis` behind a `Symbol.for` key.\n// Otherwise `init()` (run from the `neatlogs` bundle) sets `_provider` in ITS\n// module copy while a wrapper imported from `neatlogs/openai` reads a different,\n// still-null copy and silently falls back to the foreign global provider.\nconst PRIVATE_SPAN_STORAGE_KEY = Symbol.for(\n 'neatlogs.private_span_async_local_storage',\n);\nconst PRIVATE_PROVIDER_STATE_KEY = Symbol.for(\n 'neatlogs.private_provider_state',\n);\ninterface PrivateProviderState {\n provider: TracerProvider | null;\n}\ntype NeatlogsGlobal = typeof globalThis & {\n [PRIVATE_SPAN_STORAGE_KEY]?: AsyncLocalStorage<Context>;\n [PRIVATE_PROVIDER_STATE_KEY]?: PrivateProviderState;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\n// Stores the full Neatlogs Context (parent span PLUS any threaded values such as\n// trace()'s prompt-template keys), not just the span. We never\n// activate the OTel global context, so this private store is the ONLY channel\n// through which those values propagate down to descendant spans.\nconst privateContextStorage =\n neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] ??\n (neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] = new AsyncLocalStorage<Context>());\nconst providerState: PrivateProviderState =\n neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] ??\n (neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] = {\n provider: null,\n });\n// Wrappers may be constructed or accidentally invoked before init(). They must\n// never fall back to a foreign process-global provider, so pre-init calls use a\n// local no-op tracer and safely emit no exported spans.\nconst preInitTracer: Tracer = {\n startSpan(): Span {\n return otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n _name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n const fn =\n typeof arg2 === 'function'\n ? arg2\n : typeof arg3 === 'function'\n ? arg3\n : arg4;\n return fn!(otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT)) as ReturnType<F>;\n },\n};\n\n/** @internal Configure the provider used by Neatlogs-created spans. */\nexport function _setNeatlogsProvider(provider: TracerProvider | null): void {\n providerState.provider = provider;\n}\n\n/** Resolve a tracer from the private provider when one is configured. */\nexport function getNeatlogsTracer(name: string): Tracer {\n const client = getActiveClient();\n if (client) return client.getTracer(name);\n return providerState.provider?.getTracer(name) ?? preInitTracer;\n}\n\n/** A reusable tracer facade that resolves the active Client on every call. */\nexport function getRoutingNeatlogsTracer(name: string): Tracer {\n return {\n startSpan(spanName: string, options?: SpanOptions, context?: Context): Span {\n return isolateTracer(getNeatlogsTracer(name)).startSpan(\n spanName,\n options,\n context,\n );\n },\n startActiveSpan: ((...args: any[]) =>\n (isolateTracer(getNeatlogsTracer(name)).startActiveSpan as (...inner: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'],\n };\n}\n\n/**\n * @internal The private Neatlogs provider, or null before\n * init(). Used by integrations that must repoint a self-instrumenting library's\n * captured provider onto ours.\n */\nexport function getNeatlogsProvider(): TracerProvider | null {\n const client = getActiveClient();\n if (client) return client.tracerProvider;\n return providerState.provider;\n}\n\n/** Run a Client callback without inheriting another pipeline's private span. */\nexport function runWithFreshNeatlogsContext<T>(fn: () => T): T {\n return privateContextStorage.run(ROOT_CONTEXT, fn);\n}\n\n/**\n * Wrap a tracer so that spans it creates parent from — and, for\n * `startActiveSpan`, activate on — the PRIVATE Neatlogs context instead of the\n * global one.\n *\n * We hand this to libraries that create their own spans off a tracer we give\n * them (the Vercel AI SDK's `experimental_telemetry.tracer`, which calls\n * `tracer.startActiveSpan()` internally). Without the facade the AI SDK's native\n * spans would parent from `context.active()` — the foreign co-tenant's context —\n * and `startActiveSpan` would push them onto the GLOBAL context, so a foreign\n * tracer's next span reads our native span as its parent. Both directions leak.\n *\n */\nexport function isolateTracer(tracer: Tracer): Tracer {\n const facade: Tracer = {\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const parent = context ?? getNeatlogsActiveContext();\n return tracer.startSpan(name, options, parent);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n // Normalize the 2/3/4-arg overloads of startActiveSpan.\n let options: SpanOptions | undefined;\n let context: Context | undefined;\n let fn: F;\n if (typeof arg2 === 'function') {\n fn = arg2 as F;\n } else if (typeof arg3 === 'function') {\n options = arg2 as SpanOptions;\n fn = arg3 as F;\n } else {\n options = arg2 as SpanOptions;\n context = arg3 as Context;\n fn = arg4 as F;\n }\n const parent = context ?? getNeatlogsActiveContext();\n const span = tracer.startSpan(name, options, parent);\n return withNeatlogsSpan(span, () => fn(span), parent) as ReturnType<F>;\n },\n };\n return facade;\n}\n\n/**\n * The base context to build new Neatlogs spans and values on. NEVER contains a\n * foreign provider's span: it reads our private store, or ROOT_CONTEXT when\n * nothing is active.\n */\nexport function getNeatlogsActiveContext(): Context {\n return privateContextStorage.getStore() ?? ROOT_CONTEXT;\n}\n\n/** Return only the active Neatlogs span (never a foreign provider's span). */\nexport function getActiveNeatlogsSpan(): Span | undefined {\n return otelTrace.getSpan(getNeatlogsActiveContext());\n}\n\n/**\n * Return the trace-ROOT Neatlogs span, or undefined when no trace is active.\n *\n * Unlike {@link getActiveNeatlogsSpan} (innermost), this is the outermost span\n * of the current trace — the one the backend derives trace-level output from\n * (`parent_span_id=''`). It is stashed on the private context the first time a\n * span becomes active, so nested calls still resolve to the root.\n */\nexport function getNeatlogsRootSpan(): Span | undefined {\n return getNeatlogsActiveContext().getValue(NEATLOGS_ROOT_SPAN_KEY) as\n | Span\n | undefined;\n}\n\n/**\n * Build a parent context that cannot contain a foreign provider's span.\n *\n * The active Neatlogs context already carries the parent span AND any values\n * the caller threaded in upstream (e.g. `trace()`'s prompt-template values), so\n * those values reach the span processor via `onStart(parentContext)`. An\n * explicit `baseContext` (a caller's own value-carrying context) is honored as-is.\n */\nexport function getNeatlogsParentContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * A base context for callers that thread parent linkage themselves\n * (callback/event handlers that keep their own run-id → span map: LangChain,\n * OpenAI-Agents, Claude Agent SDK).\n *\n * This is the ACTIVE Neatlogs context — the private store's\n * context if a Neatlogs `trace()`/`span()` encloses this call, else\n * ROOT_CONTEXT. So a handler's own root/entry span nests under an enclosing\n * Neatlogs trace (preserving its session + end-user id) when one exists, and\n * auto-roots cleanly when one doesn't. A foreign provider's active span can\n * never leak in as an ancestor because the global context is never read.\n */\nexport function getNeatlogsBaseContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * Build an execution context for a Neatlogs span while preserving the active\n * foreign context. The span rides our private context store.\n */\nexport function getNeatlogsExecutionContext(\n span: Span,\n baseContext: Context = ROOT_CONTEXT,\n): Context {\n return otelTrace.setSpan(baseContext, span);\n}\n\n/**\n * Run a callback with a Neatlogs span active under the appropriate policy.\n *\n * The stored context is `setSpan(base, span)` — carrying the span PLUS whatever\n * values `base` holds — so threaded values (prompt templates)\n * propagate to descendant spans through our private store instead of the global\n * OTel context we deliberately never touch.\n */\nexport function withNeatlogsSpan<T>(\n span: Span,\n fn: () => T,\n baseContext?: Context,\n): T {\n const base = baseContext ?? getNeatlogsActiveContext();\n let ctx = otelTrace.setSpan(base, span);\n // The first span activated in a context with no root recorded IS the root of\n // this trace; remember it so descendants (setTraceOutput) can target it.\n if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === undefined) {\n ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);\n }\n return privateContextStorage.run(ctx, fn);\n}\n\n/**\n * @internal Run with a private Neatlogs parent without treating that parent as\n * a locally-recording trace root. This is used for an extracted remote parent:\n * the first local recording span should remain the target for local trace-level\n * output, while still inheriting the remote trace/span IDs.\n */\nexport function withNeatlogsRemoteParent<T>(span: Span, fn: () => T): T {\n return privateContextStorage.run(otelTrace.setSpan(ROOT_CONTEXT, span), fn);\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Tracer, TracerProvider } from '@opentelemetry/api';\n\nexport interface ActiveNeatlogsClient {\n readonly workflowName: string;\n readonly tracerProvider: TracerProvider;\n getTracer(scope: string): Tracer;\n getLogger(): any | null;\n}\n\nconst ACTIVE_CLIENT_STORAGE_KEY = Symbol.for(\n 'neatlogs.active_client_async_local_storage',\n);\ntype NeatlogsGlobal = typeof globalThis & {\n [ACTIVE_CLIENT_STORAGE_KEY]?: AsyncLocalStorage<ActiveNeatlogsClient>;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\nconst storage =\n neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] ??\n (neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] =\n new AsyncLocalStorage<ActiveNeatlogsClient>());\n\nexport function getActiveClient(): ActiveNeatlogsClient | undefined {\n return storage.getStore();\n}\n\nexport function runWithClient<T>(\n client: ActiveNeatlogsClient,\n fn: () => T,\n): T {\n return storage.run(client, fn);\n}\n"],"mappings":"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","names":["AsyncLocalStorage","neatlogsGlobal","AsyncLocalStorage","stringified"]}
|
|
1
|
+
{"version":3,"sources":["../src/ai-sdk.ts","../src/core/provider.ts","../src/core/active-client.ts"],"sourcesContent":["/**\n * Vercel AI SDK wrapper — inline implementation.\n *\n * Wraps `generateText`, `streamText`, `generateObject`, `streamObject` from\n * the `ai` package with OTel parent spans + forced telemetry. Static export,\n * no dynamic imports — bundler-friendly (works with Turbopack, webpack, esbuild).\n *\n * Usage:\n * import { wrapAISDK } from 'neatlogs';\n * import * as ai from 'ai';\n * const { streamText, generateText, ToolLoopAgent } = wrapAISDK(ai);\n */\n\nimport {\n SpanStatusCode,\n type AttributeValue,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n} from '@opentelemetry/api';\nimport {\n getNeatlogsTracer,\n getNeatlogsParentContext,\n getRoutingNeatlogsTracer,\n withNeatlogsSpan,\n} from './core/provider.js';\n\nconst TRACER_NAME = 'neatlogs.ai-sdk';\n\n// -- Telemetry config --------------------------------------------------------\n\nexport interface CreateAITelemetryOptions {\n /** Identifier used by the AI SDK to group telemetry for this operation. */\n functionId?: string;\n metadata?: Record<string, AttributeValue>;\n /**\n * Existing telemetry tracer to preserve alongside Neatlogs (for example,\n * Laminar). Native AI SDK spans are mirrored to both isolated pipelines.\n */\n tracer?: Tracer;\n}\n\nexport interface AITelemetryConfig {\n isEnabled: true;\n recordInputs: true;\n recordOutputs: true;\n tracer: Tracer;\n functionId?: string;\n metadata: Record<string, AttributeValue>;\n}\n\nexport function createAITelemetry(\n opts: CreateAITelemetryOptions = {},\n): AITelemetryConfig {\n const userMeta = opts.metadata ?? {};\n const neatlogsTracer = getRoutingNeatlogsTracer(TRACER_NAME);\n const callerTracer = opts.tracer;\n return {\n isEnabled: true,\n recordInputs: true,\n recordOutputs: true,\n // Hand the AI SDK an isolation-aware tracer: it calls startActiveSpan()\n // internally, which would otherwise parent its native spans from the foreign\n // global context AND push them onto it (so a co-tenant's next span inherits\n // ours). The facade routes both through the private Neatlogs context.\n tracer: callerTracer\n ? createMirroredTracer(callerTracer, neatlogsTracer)\n : neatlogsTracer,\n ...(opts.functionId !== undefined ? { functionId: opts.functionId } : {}),\n // The marker is an implementation detail used only when Neatlogs owns the\n // telemetry stream. Do not leak it into caller-owned providers.\n metadata: callerTracer ? { ...userMeta } : { ...userMeta, neatlogsWrapped: true },\n };\n}\n\n// -- Span attributes ---------------------------------------------------------\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction setInputValue(span: Span, opts: Record<string, unknown>): void {\n // For generateText/streamText — only capture prompt or messages, not full model config\n if (opts && ('prompt' in opts || 'messages' in opts)) {\n const input = opts.messages ?? opts.prompt;\n const stringified = safeStringify(input);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n return;\n }\n const stringified = safeStringify(opts);\n if (stringified) {\n span.setAttribute('input.value', stringified);\n }\n}\n\nfunction setOutputValue(span: Span, result: unknown): void {\n if (result && typeof result === 'object') {\n const r = result as Record<string, unknown>;\n // GenerateTextResult / StreamTextResult — extract meaningful fields\n if ('text' in r && 'finishReason' in r) {\n const text = String(r.text ?? '');\n if (text) {\n span.setAttribute('output.value', text);\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n // GenerateObjectResult — the structured object is the output, not `text`.\n // Without this the envelope (object+usage+response+…) gets stringified whole.\n if ('object' in r && 'finishReason' in r) {\n if (r.object !== undefined) {\n span.setAttribute('output.value', safeStringify(r.object));\n }\n if (r.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(r.finishReason));\n }\n return;\n }\n }\n const stringified = safeStringify(result);\n if (stringified) {\n span.setAttribute('output.value', stringified);\n }\n}\n\n// Extract output from a streamText/streamObject `onFinish` event. The event\n// extends StepResult, so `text` (streamText) / `object` (streamObject) sit at\n// the top level alongside `finishReason` — but the event also carries `steps`,\n// `usage`, etc., so we pull only the meaningful fields instead of stringifying\n// the whole envelope.\nfunction setStreamOutputValue(span: Span, event: unknown): void {\n if (!event || typeof event !== 'object') return;\n const e = event as Record<string, unknown>;\n if (typeof e.text === 'string' && e.text) {\n span.setAttribute('output.value', e.text);\n } else if ('object' in e && e.object !== undefined) {\n const stringified = safeStringify(e.object);\n if (stringified) span.setAttribute('output.value', stringified);\n }\n if (e.finishReason) {\n span.setAttribute('gen_ai.finish_reason', String(e.finishReason));\n }\n}\n\n// -- Wrapping ----------------------------------------------------------------\n\ntype WrappedFunctionName =\n | 'generateText'\n | 'streamText'\n | 'generateObject'\n | 'streamObject'\n | 'embed'\n | 'embedMany'\n | 'rerank';\n\nconst WRAPPED_FUNCTIONS: readonly WrappedFunctionName[] = [\n 'generateText',\n 'streamText',\n 'generateObject',\n 'streamObject',\n 'embed',\n 'embedMany',\n 'rerank',\n] as const;\n\ntype WrappedAgentConstructorName = 'ToolLoopAgent' | 'Experimental_Agent';\n\nconst WRAPPED_AGENT_CONSTRUCTORS: readonly WrappedAgentConstructorName[] = [\n 'ToolLoopAgent',\n 'Experimental_Agent',\n] as const;\n\ntype AgentConstructor = new (...args: any[]) => unknown;\n\nconst wrappedExports = new WeakSet<Function>();\nconst wrapperByOriginal = new WeakMap<Function, Function>();\n\n/**\n * Wrap the `ai` module namespace so that every supported generation, embedding,\n * and reranking call:\n *\n * 1. Opens a parent OTel span on the active TracerProvider.\n * 2. Forces `experimental_telemetry: { isEnabled: true }`, merging user metadata.\n * 3. Records input/output on the parent span and propagates errors.\n *\n * AI SDK v6's `ToolLoopAgent` (and its `Experimental_Agent` alias) is wrapped at\n * construction time so its internal model and tool calls receive the same native\n * telemetry configuration. Other exports pass through unchanged.\n */\nexport function wrapAISDK<T extends Record<string, unknown>>(aiModule: T): T {\n const wrapped: Record<string, unknown> = { ...aiModule };\n\n for (const name of WRAPPED_FUNCTIONS) {\n const original = aiModule[name];\n if (typeof original !== 'function') continue;\n\n const existing = getExistingWrapper(original);\n if (existing) {\n wrapped[name] = existing;\n continue;\n }\n\n if (name === 'streamText' || name === 'streamObject') {\n wrapped[name] = cacheWrapper(\n original,\n createStreamWrapper(name, original as (opts: any) => unknown),\n );\n } else {\n wrapped[name] = cacheWrapper(\n original,\n createAsyncWrapper(name, original as (opts: any) => Promise<unknown>),\n );\n }\n }\n\n for (const name of WRAPPED_AGENT_CONSTRUCTORS) {\n const original = aiModule[name];\n if (typeof original !== 'function') continue;\n\n const existing = getExistingWrapper(original);\n wrapped[name] =\n existing ??\n cacheWrapper(\n original,\n createAgentConstructorWrapper(original as AgentConstructor),\n );\n }\n\n return wrapped as T;\n}\n\nfunction getExistingWrapper(original: Function): Function | undefined {\n if (wrappedExports.has(original)) return original;\n return wrapperByOriginal.get(original);\n}\n\nfunction cacheWrapper(original: Function, wrapped: Function): Function {\n wrapperByOriginal.set(original, wrapped);\n wrappedExports.add(wrapped);\n return wrapped;\n}\n\nfunction createAgentConstructorWrapper(\n original: AgentConstructor,\n): AgentConstructor {\n return new Proxy(original, {\n construct(target, args, newTarget) {\n if (\n args.length === 0 ||\n typeof args[0] !== 'object' ||\n args[0] === null\n ) {\n return Reflect.construct(target, args, newTarget);\n }\n\n const settings = mergeAgentSettings(args[0]);\n return Reflect.construct(target, [settings, ...args.slice(1)], newTarget);\n },\n });\n}\n\nfunction mergeAgentSettings(settings: any): any {\n const merged = mergeTelemetry(settings);\n const userPrepareCall = settings.prepareCall;\n if (typeof userPrepareCall !== 'function') return merged;\n\n return {\n ...merged,\n prepareCall: async function wrappedPrepareCall(\n this: unknown,\n ...args: any[]\n ) {\n const prepared = await Reflect.apply(userPrepareCall, this, args);\n return prepared == null\n ? prepared\n : mergeTelemetry(prepared, settings.experimental_telemetry);\n },\n };\n}\n\nfunction rootSpanKind(name: WrappedFunctionName): string {\n if (name === 'embed' || name === 'embedMany' || name === 'rerank')\n return 'CHAIN';\n return 'WORKFLOW';\n}\n\nfunction getParentContext() {\n // Our parent comes solely from the private span store; a\n // foreign provider's active span must never become our ancestor.\n return getNeatlogsParentContext();\n}\n\nfunction createAsyncWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => Promise<unknown>,\n): (opts: any) => Promise<unknown> {\n return async function wrappedAsyncFn(opts: any): Promise<unknown> {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan (NOT startActiveSpan) + withNeatlogsSpan: startActiveSpan would\n // push our span onto the GLOBAL OTel context, so a foreign tracer's\n // startSpan() inside generateText() would read it as parent and inherit our\n // trace id. withNeatlogsSpan carries the parent in the private store in\n // the private context, leaving the global context untouched.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n async () => {\n try {\n const isEmbedOrRerank =\n name === 'embed' || name === 'embedMany' || name === 'rerank';\n if (!isEmbedOrRerank) {\n setInputValue(span, opts);\n }\n if (name === 'rerank' && opts?.query) {\n span.setAttribute('ai.rerank.query', String(opts.query));\n }\n const merged = mergeTelemetry(opts);\n const result = await original(merged);\n if (!isEmbedOrRerank) {\n setOutputValue(span, result);\n }\n return result;\n } catch (err) {\n recordSpanError(span, err);\n throw err;\n } finally {\n span.end();\n }\n },\n parentContext,\n );\n };\n}\n\n// streamText/streamObject return synchronously while the model keeps producing\n// tokens for seconds afterwards. Ending the span in a `finally` (as a plain sync\n// wrapper would) closes it in ~2ms with no output — the output only exists once\n// the stream finishes. Instead we keep the span open and end it from the AI SDK's\n// `onFinish` callback, where the final text/object is available. Any user-provided\n// `onFinish` is preserved and invoked first.\nfunction createStreamWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => unknown,\n): (opts: any) => unknown {\n return function wrappedStreamFn(opts: any): unknown {\n const tracer = getNeatlogsTracer(TRACER_NAME);\n // startSpan + withNeatlogsSpan (see createAsyncWrapper) so streamText's\n // internals never see our span on the global OTel context. The span stays\n // open past the run scope and is ended from onFinish/onError.\n const parentContext = getParentContext();\n const span = tracer.startSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n parentContext,\n );\n return withNeatlogsSpan(\n span,\n () => {\n let spanEnded = false;\n const endOnce = () => {\n if (spanEnded) return;\n spanEnded = true;\n span.end();\n };\n try {\n setInputValue(span, opts);\n const merged = mergeTelemetry(opts);\n const userOnFinish = opts?.onFinish;\n const userOnError = opts?.onError;\n const wrappedOpts = {\n ...merged,\n onFinish: async (event: any) => {\n try {\n setStreamOutputValue(span, event);\n } finally {\n endOnce();\n }\n if (typeof userOnFinish === 'function') {\n return userOnFinish(event);\n }\n },\n onError: (event: any) => {\n recordSpanError(span, (event && event.error) ?? event);\n endOnce();\n if (typeof userOnError === 'function') {\n return userOnError(event);\n }\n },\n };\n return original(wrappedOpts);\n } catch (err) {\n // Synchronous throw (e.g. bad arguments) — the stream never started.\n recordSpanError(span, err);\n endOnce();\n throw err;\n }\n },\n parentContext,\n );\n };\n}\n\nfunction createMirroredSpan(primary: Span, secondary: Span): Span {\n const mirrored: Span = {\n spanContext() {\n // The caller-owned tracer remains the process-global context owner. Its\n // context is therefore the one external instrumentation must observe.\n return primary.spanContext();\n },\n setAttribute(key, value) {\n primary.setAttribute(key, value);\n secondary.setAttribute(key, value);\n return mirrored;\n },\n setAttributes(attributes) {\n primary.setAttributes(attributes);\n secondary.setAttributes(attributes);\n return mirrored;\n },\n addEvent(name, attributesOrStartTime, startTime) {\n primary.addEvent(name, attributesOrStartTime, startTime);\n secondary.addEvent(name, attributesOrStartTime, startTime);\n return mirrored;\n },\n addLink(link) {\n primary.addLink(link);\n secondary.addLink(link);\n return mirrored;\n },\n addLinks(links) {\n primary.addLinks(links);\n secondary.addLinks(links);\n return mirrored;\n },\n setStatus(status) {\n primary.setStatus(status);\n secondary.setStatus(status);\n return mirrored;\n },\n updateName(name) {\n primary.updateName(name);\n secondary.updateName(name);\n return mirrored;\n },\n end(endTime) {\n primary.end(endTime);\n secondary.end(endTime);\n },\n isRecording() {\n return primary.isRecording() || secondary.isRecording();\n },\n recordException(exception, time) {\n primary.recordException(exception, time);\n secondary.recordException(exception, time);\n },\n };\n return mirrored;\n}\n\n/**\n * Mirror one AI SDK telemetry stream to two isolated tracer pipelines.\n *\n * The caller-owned tracer is deliberately outermost so its normal global OTel\n * activation and parentage stay unchanged. The Neatlogs routing tracer keeps\n * its span active only in Neatlogs' private AsyncLocalStorage context.\n */\nfunction createMirroredTracer(primary: Tracer, secondary: Tracer): Tracer {\n return {\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const primarySpan = primary.startSpan(name, options, context);\n const secondarySpan = secondary.startSpan(name, options);\n return createMirroredSpan(primarySpan, secondarySpan);\n },\n startActiveSpan: (<F extends (span: Span) => unknown>(\n name: string,\n arg2?: SpanOptions | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> => {\n let options: SpanOptions | undefined;\n let context: Context | undefined;\n let fn: F;\n\n if (typeof arg2 === 'function') {\n fn = arg2;\n } else if (typeof arg3 === 'function') {\n options = arg2;\n fn = arg3;\n } else {\n options = arg2;\n context = arg3 as Context;\n fn = arg4!;\n }\n\n const runSecondary = (primarySpan: Span): ReturnType<F> => {\n const onSecondarySpan = (secondarySpan: Span) =>\n fn(createMirroredSpan(primarySpan, secondarySpan)) as ReturnType<F>;\n return options === undefined\n ? secondary.startActiveSpan(name, onSecondarySpan)\n : secondary.startActiveSpan(name, options, onSecondarySpan);\n };\n\n if (context !== undefined) {\n return primary.startActiveSpan(\n name,\n options ?? {},\n context,\n runSecondary,\n ) as ReturnType<F>;\n }\n return options === undefined\n ? (primary.startActiveSpan(name, runSecondary) as ReturnType<F>)\n : (primary.startActiveSpan(name, options, runSecondary) as ReturnType<F>);\n }) as Tracer['startActiveSpan'],\n };\n}\n\nfunction mergeTelemetry(opts: any, fallbackTelemetry?: any): any {\n const requestedTelemetry = {\n ...fallbackTelemetry,\n ...opts?.experimental_telemetry,\n metadata: {\n ...fallbackTelemetry?.metadata,\n ...opts?.experimental_telemetry?.metadata,\n },\n };\n const baseTelemetry: AITelemetryConfig = createAITelemetry({\n functionId: requestedTelemetry.functionId,\n metadata: requestedTelemetry.metadata,\n tracer: requestedTelemetry.tracer as Tracer | undefined,\n });\n const callerTracer = requestedTelemetry.tracer as Tracer | undefined;\n const hasCallerTracer = callerTracer !== undefined;\n\n return {\n ...opts,\n experimental_telemetry: {\n ...baseTelemetry,\n ...requestedTelemetry,\n isEnabled: true,\n recordInputs: requestedTelemetry.recordInputs ?? true,\n recordOutputs: requestedTelemetry.recordOutputs ?? true,\n tracer: baseTelemetry.tracer,\n // Do not add Neatlogs-only marker metadata to a caller-owned telemetry\n // pipeline such as Laminar. Both providers receive the same AI SDK span\n // data, while their providers, parent contexts, and exporters stay separate.\n metadata: hasCallerTracer\n ? requestedTelemetry.metadata\n : baseTelemetry.metadata,\n },\n };\n}\n\nfunction recordSpanError(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}\n","/**\n * Neatlogs-owned tracing state.\n *\n * Spans are created by the private Neatlogs provider and their parent is carried\n * in a private context key. The process-global OpenTelemetry span is\n * deliberately left untouched so other observability SDKs cannot export or\n * become parents of Neatlogs spans.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n ROOT_CONTEXT,\n INVALID_SPAN_CONTEXT,\n createContextKey,\n trace as otelTrace,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n} from '@opentelemetry/api';\nimport { getActiveClient } from './active-client.js';\n\n// Carries the trace-ROOT span down the private context so descendants can target\n// it (e.g. setTraceOutput). getActiveNeatlogsSpan() returns the innermost span,\n// which is not the root once nested; this key preserves the root reference.\nconst NEATLOGS_ROOT_SPAN_KEY = createContextKey('neatlogs.root_span');\n\n// Entry points are bundled independently (`neatlogs`, `neatlogs/openai`,\n// `neatlogs/ai`, `neatlogs/mastra`, … each in both CJS and ESM), so every piece\n// of shared tracing state — the private span store AND the resolved provider /\n// provider — must live on `globalThis` behind a `Symbol.for` key.\n// Otherwise `init()` (run from the `neatlogs` bundle) sets `_provider` in ITS\n// module copy while a wrapper imported from `neatlogs/openai` reads a different,\n// still-null copy and silently falls back to the foreign global provider.\nconst PRIVATE_SPAN_STORAGE_KEY = Symbol.for(\n 'neatlogs.private_span_async_local_storage',\n);\nconst PRIVATE_PROVIDER_STATE_KEY = Symbol.for(\n 'neatlogs.private_provider_state',\n);\ninterface PrivateProviderState {\n provider: TracerProvider | null;\n}\ntype NeatlogsGlobal = typeof globalThis & {\n [PRIVATE_SPAN_STORAGE_KEY]?: AsyncLocalStorage<Context>;\n [PRIVATE_PROVIDER_STATE_KEY]?: PrivateProviderState;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\n// Stores the full Neatlogs Context (parent span PLUS any threaded values such as\n// trace()'s prompt-template keys), not just the span. We never\n// activate the OTel global context, so this private store is the ONLY channel\n// through which those values propagate down to descendant spans.\nconst privateContextStorage =\n neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] ??\n (neatlogsGlobal[PRIVATE_SPAN_STORAGE_KEY] = new AsyncLocalStorage<Context>());\nconst providerState: PrivateProviderState =\n neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] ??\n (neatlogsGlobal[PRIVATE_PROVIDER_STATE_KEY] = {\n provider: null,\n });\n// Wrappers may be constructed or accidentally invoked before init(). They must\n// never fall back to a foreign process-global provider, so pre-init calls use a\n// local no-op tracer and safely emit no exported spans.\nconst preInitTracer: Tracer = {\n startSpan(): Span {\n return otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n _name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n const fn =\n typeof arg2 === 'function'\n ? arg2\n : typeof arg3 === 'function'\n ? arg3\n : arg4;\n return fn!(otelTrace.wrapSpanContext(INVALID_SPAN_CONTEXT)) as ReturnType<F>;\n },\n};\n\n/** @internal Configure the provider used by Neatlogs-created spans. */\nexport function _setNeatlogsProvider(provider: TracerProvider | null): void {\n providerState.provider = provider;\n}\n\n/** Resolve a tracer from the private provider when one is configured. */\nexport function getNeatlogsTracer(name: string): Tracer {\n const client = getActiveClient();\n if (client) return client.getTracer(name);\n return providerState.provider?.getTracer(name) ?? preInitTracer;\n}\n\n/** A reusable tracer facade that resolves the active Client on every call. */\nexport function getRoutingNeatlogsTracer(name: string): Tracer {\n return {\n startSpan(spanName: string, options?: SpanOptions, context?: Context): Span {\n return isolateTracer(getNeatlogsTracer(name)).startSpan(\n spanName,\n options,\n context,\n );\n },\n startActiveSpan: ((...args: any[]) =>\n (isolateTracer(getNeatlogsTracer(name)).startActiveSpan as (...inner: any[]) => any)(\n ...args,\n )) as Tracer['startActiveSpan'],\n };\n}\n\n/**\n * @internal The private Neatlogs provider, or null before\n * init(). Used by integrations that must repoint a self-instrumenting library's\n * captured provider onto ours.\n */\nexport function getNeatlogsProvider(): TracerProvider | null {\n const client = getActiveClient();\n if (client) return client.tracerProvider;\n return providerState.provider;\n}\n\n/** Run a Client callback without inheriting another pipeline's private span. */\nexport function runWithFreshNeatlogsContext<T>(fn: () => T): T {\n return privateContextStorage.run(ROOT_CONTEXT, fn);\n}\n\n/**\n * Wrap a tracer so that spans it creates parent from — and, for\n * `startActiveSpan`, activate on — the PRIVATE Neatlogs context instead of the\n * global one.\n *\n * We hand this to libraries that create their own spans off a tracer we give\n * them (the Vercel AI SDK's `experimental_telemetry.tracer`, which calls\n * `tracer.startActiveSpan()` internally). Without the facade the AI SDK's native\n * spans would parent from `context.active()` — the foreign co-tenant's context —\n * and `startActiveSpan` would push them onto the GLOBAL context, so a foreign\n * tracer's next span reads our native span as its parent. Both directions leak.\n *\n */\nexport function isolateTracer(tracer: Tracer): Tracer {\n const facade: Tracer = {\n startSpan(name: string, options?: SpanOptions, context?: Context): Span {\n const parent = context ?? getNeatlogsActiveContext();\n return tracer.startSpan(name, options, parent);\n },\n startActiveSpan<F extends (span: Span) => unknown>(\n name: string,\n arg2?: SpanOptions | Context | F,\n arg3?: Context | F,\n arg4?: F,\n ): ReturnType<F> {\n // Normalize the 2/3/4-arg overloads of startActiveSpan.\n let options: SpanOptions | undefined;\n let context: Context | undefined;\n let fn: F;\n if (typeof arg2 === 'function') {\n fn = arg2 as F;\n } else if (typeof arg3 === 'function') {\n options = arg2 as SpanOptions;\n fn = arg3 as F;\n } else {\n options = arg2 as SpanOptions;\n context = arg3 as Context;\n fn = arg4 as F;\n }\n const parent = context ?? getNeatlogsActiveContext();\n const span = tracer.startSpan(name, options, parent);\n return withNeatlogsSpan(span, () => fn(span), parent) as ReturnType<F>;\n },\n };\n return facade;\n}\n\n/**\n * The base context to build new Neatlogs spans and values on. NEVER contains a\n * foreign provider's span: it reads our private store, or ROOT_CONTEXT when\n * nothing is active.\n */\nexport function getNeatlogsActiveContext(): Context {\n return privateContextStorage.getStore() ?? ROOT_CONTEXT;\n}\n\n/** Return only the active Neatlogs span (never a foreign provider's span). */\nexport function getActiveNeatlogsSpan(): Span | undefined {\n return otelTrace.getSpan(getNeatlogsActiveContext());\n}\n\n/**\n * Return the trace-ROOT Neatlogs span, or undefined when no trace is active.\n *\n * Unlike {@link getActiveNeatlogsSpan} (innermost), this is the outermost span\n * of the current trace — the one the backend derives trace-level output from\n * (`parent_span_id=''`). It is stashed on the private context the first time a\n * span becomes active, so nested calls still resolve to the root.\n */\nexport function getNeatlogsRootSpan(): Span | undefined {\n return getNeatlogsActiveContext().getValue(NEATLOGS_ROOT_SPAN_KEY) as\n | Span\n | undefined;\n}\n\n/**\n * Build a parent context that cannot contain a foreign provider's span.\n *\n * The active Neatlogs context already carries the parent span AND any values\n * the caller threaded in upstream (e.g. `trace()`'s prompt-template values), so\n * those values reach the span processor via `onStart(parentContext)`. An\n * explicit `baseContext` (a caller's own value-carrying context) is honored as-is.\n */\nexport function getNeatlogsParentContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * A base context for callers that thread parent linkage themselves\n * (callback/event handlers that keep their own run-id → span map: LangChain,\n * OpenAI-Agents, Claude Agent SDK).\n *\n * This is the ACTIVE Neatlogs context — the private store's\n * context if a Neatlogs `trace()`/`span()` encloses this call, else\n * ROOT_CONTEXT. So a handler's own root/entry span nests under an enclosing\n * Neatlogs trace (preserving its session + end-user id) when one exists, and\n * auto-roots cleanly when one doesn't. A foreign provider's active span can\n * never leak in as an ancestor because the global context is never read.\n */\nexport function getNeatlogsBaseContext(baseContext?: Context): Context {\n return baseContext ?? getNeatlogsActiveContext();\n}\n\n/**\n * Build an execution context for a Neatlogs span while preserving the active\n * foreign context. The span rides our private context store.\n */\nexport function getNeatlogsExecutionContext(\n span: Span,\n baseContext: Context = ROOT_CONTEXT,\n): Context {\n return otelTrace.setSpan(baseContext, span);\n}\n\n/**\n * Run a callback with a Neatlogs span active under the appropriate policy.\n *\n * The stored context is `setSpan(base, span)` — carrying the span PLUS whatever\n * values `base` holds — so threaded values (prompt templates)\n * propagate to descendant spans through our private store instead of the global\n * OTel context we deliberately never touch.\n */\nexport function withNeatlogsSpan<T>(\n span: Span,\n fn: () => T,\n baseContext?: Context,\n rootSpan?: Span,\n): T {\n const base = baseContext ?? getNeatlogsActiveContext();\n let ctx = otelTrace.setSpan(base, span);\n // The first span activated in a context with no root recorded IS the root of\n // this trace; remember it so descendants (setTraceOutput) can target it.\n if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === undefined) {\n ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, rootSpan ?? span);\n }\n return privateContextStorage.run(ctx, fn);\n}\n\n/**\n * @internal Run with a private Neatlogs parent without treating that parent as\n * a locally-recording trace root. This is used for an extracted remote parent:\n * the first local recording span should remain the target for local trace-level\n * output, while still inheriting the remote trace/span IDs.\n */\nexport function withNeatlogsRemoteParent<T>(span: Span, fn: () => T): T {\n return privateContextStorage.run(otelTrace.setSpan(ROOT_CONTEXT, span), fn);\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Tracer, TracerProvider } from '@opentelemetry/api';\n\nexport interface ActiveNeatlogsClient {\n readonly workflowName: string;\n readonly tracerProvider: TracerProvider;\n getTracer(scope: string): Tracer;\n getLogger(): any | null;\n}\n\nconst ACTIVE_CLIENT_STORAGE_KEY = Symbol.for(\n 'neatlogs.active_client_async_local_storage',\n);\ntype NeatlogsGlobal = typeof globalThis & {\n [ACTIVE_CLIENT_STORAGE_KEY]?: AsyncLocalStorage<ActiveNeatlogsClient>;\n};\nconst neatlogsGlobal = globalThis as NeatlogsGlobal;\nconst storage =\n neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] ??\n (neatlogsGlobal[ACTIVE_CLIENT_STORAGE_KEY] =\n new AsyncLocalStorage<ActiveNeatlogsClient>());\n\nexport function getActiveClient(): ActiveNeatlogsClient | undefined {\n return storage.getStore();\n}\n\nexport function runWithClient<T>(\n client: ActiveNeatlogsClient,\n fn: () => T,\n): T {\n return storage.run(client, fn);\n}\n"],"mappings":"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","names":["AsyncLocalStorage","neatlogsGlobal","AsyncLocalStorage","stringified"]}
|
package/dist/anthropic.cjs
CHANGED
|
@@ -179,11 +179,11 @@ function getActiveNeatlogsSpan() {
|
|
|
179
179
|
function getNeatlogsParentContext(baseContext) {
|
|
180
180
|
return baseContext ?? getNeatlogsActiveContext();
|
|
181
181
|
}
|
|
182
|
-
function withNeatlogsSpan(span, fn, baseContext) {
|
|
182
|
+
function withNeatlogsSpan(span, fn, baseContext, rootSpan) {
|
|
183
183
|
const base = baseContext ?? getNeatlogsActiveContext();
|
|
184
184
|
let ctx = import_api.trace.setSpan(base, span);
|
|
185
185
|
if (base.getValue(NEATLOGS_ROOT_SPAN_KEY) === void 0) {
|
|
186
|
-
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, span);
|
|
186
|
+
ctx = ctx.setValue(NEATLOGS_ROOT_SPAN_KEY, rootSpan ?? span);
|
|
187
187
|
}
|
|
188
188
|
return privateContextStorage.run(ctx, fn);
|
|
189
189
|
}
|
|
@@ -917,7 +917,8 @@ var USER_PROMPT_TEMPLATE_KEY = (0, import_api3.createContextKey)("neatlogs.user_
|
|
|
917
917
|
var USER_PROMPT_VARIABLES_KEY = (0, import_api3.createContextKey)("neatlogs.user_prompt_variables");
|
|
918
918
|
|
|
919
919
|
// src/core/auto-root.ts
|
|
920
|
-
var
|
|
920
|
+
var boundAutoRoots = /* @__PURE__ */ new WeakMap();
|
|
921
|
+
var ROOT_KINDS = /* @__PURE__ */ new Set(["workflow", "chain", "agent", "mcp_tool", "llm"]);
|
|
921
922
|
function autoRootEnabled() {
|
|
922
923
|
const val = (process.env.NEATLOGS_AUTO_ROOT ?? "").trim().toLowerCase();
|
|
923
924
|
return !["false", "0", "no", "off"].includes(val);
|
|
@@ -932,11 +933,12 @@ function resolveRootWorkflowName() {
|
|
|
932
933
|
}
|
|
933
934
|
return "workflow";
|
|
934
935
|
}
|
|
935
|
-
function
|
|
936
|
+
function stampRootIdentity(root) {
|
|
936
937
|
applySessionAttributes(root, void 0, true);
|
|
937
938
|
applyEndUserAttributes(root, void 0, void 0, true);
|
|
938
939
|
}
|
|
939
|
-
function
|
|
940
|
+
function bindSpanToAutoRoot(child, root) {
|
|
941
|
+
if (!root) return child;
|
|
940
942
|
let ended = false;
|
|
941
943
|
const proxy = new Proxy(child, {
|
|
942
944
|
get(target, prop, _receiver) {
|
|
@@ -958,6 +960,7 @@ function wrapSpanWithRoot(child, root) {
|
|
|
958
960
|
return typeof value === "function" ? value.bind(target) : value;
|
|
959
961
|
}
|
|
960
962
|
});
|
|
963
|
+
boundAutoRoots.set(proxy, root);
|
|
961
964
|
aliasMediaCaptureSpan(proxy, child);
|
|
962
965
|
return proxy;
|
|
963
966
|
}
|
|
@@ -970,27 +973,41 @@ var AutoRootTracer = class {
|
|
|
970
973
|
const kind = String(
|
|
971
974
|
options?.attributes?.["neatlogs.span.kind"] ?? ""
|
|
972
975
|
).toLowerCase();
|
|
976
|
+
const hasExplicitContext = context !== void 0;
|
|
973
977
|
const ctx = context ?? getNeatlogsParentContext();
|
|
974
978
|
const parent = getActiveNeatlogsSpan();
|
|
975
|
-
const
|
|
979
|
+
const isParentless = !hasExplicitContext && !(parent !== void 0 && parent.isRecording());
|
|
980
|
+
const needsRoot = autoRootEnabled() && !ROOT_KINDS.has(kind) && isParentless;
|
|
976
981
|
if (!needsRoot) {
|
|
977
|
-
|
|
982
|
+
const span = this._tracer.startSpan(name, options, ctx);
|
|
983
|
+
if (isParentless && ROOT_KINDS.has(kind)) stampRootIdentity(span);
|
|
984
|
+
return span;
|
|
978
985
|
}
|
|
979
986
|
const root = this._tracer.startSpan(
|
|
980
987
|
resolveRootWorkflowName(),
|
|
981
988
|
{ attributes: { "neatlogs.span.kind": "workflow", "neatlogs.auto_root": true } },
|
|
982
989
|
ctx
|
|
983
990
|
);
|
|
984
|
-
|
|
991
|
+
stampRootIdentity(root);
|
|
985
992
|
const childCtx = import_api4.trace.setSpan(ctx, root);
|
|
986
993
|
const child = this._tracer.startSpan(name, options, childCtx);
|
|
987
|
-
return
|
|
994
|
+
return bindSpanToAutoRoot(child, root);
|
|
988
995
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
996
|
+
startActiveSpan = ((name, ...args) => {
|
|
997
|
+
const callback = args.at(-1);
|
|
998
|
+
if (typeof callback !== "function") {
|
|
999
|
+
throw new TypeError("startActiveSpan requires a callback");
|
|
1000
|
+
}
|
|
1001
|
+
const options = args.length >= 2 ? args[0] : void 0;
|
|
1002
|
+
const parentContext = args.length >= 3 ? args[1] : getNeatlogsParentContext();
|
|
1003
|
+
const span = args.length >= 3 ? this.startSpan(name, options, parentContext) : this.startSpan(name, options);
|
|
1004
|
+
return withNeatlogsSpan(
|
|
1005
|
+
span,
|
|
1006
|
+
() => callback(span),
|
|
1007
|
+
parentContext,
|
|
1008
|
+
boundAutoRoots.get(span)
|
|
1009
|
+
);
|
|
1010
|
+
});
|
|
994
1011
|
};
|
|
995
1012
|
function getProviderTracer(name) {
|
|
996
1013
|
return new AutoRootTracer(getNeatlogsTracer(name));
|