neatlogs 1.0.2 → 1.0.3
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 +28 -0
- package/dist/ai-sdk.cjs +190 -0
- package/dist/ai-sdk.cjs.map +1 -0
- package/dist/ai-sdk.d.ts +39 -0
- package/dist/ai-sdk.mjs +164 -0
- package/dist/ai-sdk.mjs.map +1 -0
- package/dist/index.cjs +425 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +422 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -4
- package/dist/index.d.mts +0 -531
package/README.md
CHANGED
|
@@ -458,6 +458,33 @@ The following libraries are registered in the instrumentation registry for futur
|
|
|
458
458
|
|
|
459
459
|
`cohere`, `groq`, `together`, `vertexai`, `google_generativeai`, `mistralai`, `ollama`, `watsonx`, `alephalpha`, `replicate`, `sagemaker`, `huggingface_hub`, `litellm`, `langgraph`, `llamaindex`, `autogen`, `haystack`, `dspy`, `chromadb`, `pinecone`, `weaviate`, `qdrant`, `milvus`, `opensearch`, `elasticsearch`, `redis`, `marqo`, `instructor`, `guardrails`, `google_adk`, `agno`, `openai_agents`, `pydantic_ai`, `smolagents`, `strands`, `pipecat`, `portkey`, `promptflow`
|
|
460
460
|
|
|
461
|
+
## Framework Integrations
|
|
462
|
+
|
|
463
|
+
For frameworks that don't fit the auto-instrument-on-init pattern, neatlogs ships dedicated companion packages. Install only the ones you need:
|
|
464
|
+
|
|
465
|
+
| Framework | Package | Helper |
|
|
466
|
+
|-----------|---------|--------|
|
|
467
|
+
| Mastra (`@mastra/core`) | `@neatlogs/instrumentation-mastra` | `getMastraObservability()` — pass to `new Mastra({ observability })` |
|
|
468
|
+
| Vercel AI SDK (`ai`) | `@neatlogs/instrumentation-ai-sdk` | `getAISDKWrapper()` — wraps `generateText` / `streamText` / `generateObject` / `streamObject` |
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
// Vercel AI SDK
|
|
472
|
+
import { init, getAISDKWrapper, shutdown } from 'neatlogs';
|
|
473
|
+
import * as ai from 'ai';
|
|
474
|
+
import { openai } from '@ai-sdk/openai';
|
|
475
|
+
|
|
476
|
+
await init({ apiKey: process.env.NEATLOGS_API_KEY });
|
|
477
|
+
const wrapAISDK = await getAISDKWrapper();
|
|
478
|
+
const { generateText } = wrapAISDK(ai);
|
|
479
|
+
|
|
480
|
+
const { text } = await generateText({
|
|
481
|
+
model: openai('gpt-4o-mini'),
|
|
482
|
+
prompt: 'What is TypeScript?',
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
await shutdown();
|
|
486
|
+
```
|
|
487
|
+
|
|
461
488
|
## Configuration
|
|
462
489
|
|
|
463
490
|
### Environment Variables
|
|
@@ -559,6 +586,7 @@ See the [`examples/`](./examples/) directory for complete, runnable examples:
|
|
|
559
586
|
| [`prompt-management.ts`](./examples/prompt-management.ts) | PromptTemplate + trace() for prompt versioning |
|
|
560
587
|
| [`multi-agent-workflow.ts`](./examples/multi-agent-workflow.ts) | Nested spans: WORKFLOW → AGENT → TOOL |
|
|
561
588
|
| [`custom-spans.ts`](./examples/custom-spans.ts) | All span kinds: WORKFLOW, CHAIN, AGENT, TOOL, RETRIEVER, EMBEDDING, GUARDRAIL |
|
|
589
|
+
| [`sdk_examples/ai_sdk_basic/`](./examples/sdk_examples/ai_sdk_basic/) | Vercel AI SDK via `wrapAISDK` — generateText + streamText + tools |
|
|
562
590
|
|
|
563
591
|
Run any example with:
|
|
564
592
|
|
package/dist/ai-sdk.cjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ai-sdk.ts
|
|
21
|
+
var ai_sdk_exports = {};
|
|
22
|
+
__export(ai_sdk_exports, {
|
|
23
|
+
createAITelemetry: () => createAITelemetry,
|
|
24
|
+
wrapAISDK: () => wrapAISDK
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(ai_sdk_exports);
|
|
27
|
+
var import_api = require("@opentelemetry/api");
|
|
28
|
+
var TRACER_NAME = "neatlogs.ai-sdk";
|
|
29
|
+
function createAITelemetry(opts = {}) {
|
|
30
|
+
const userMeta = opts.metadata ?? {};
|
|
31
|
+
return {
|
|
32
|
+
isEnabled: true,
|
|
33
|
+
recordInputs: true,
|
|
34
|
+
recordOutputs: true,
|
|
35
|
+
tracer: import_api.trace.getTracer(TRACER_NAME),
|
|
36
|
+
metadata: { ...userMeta, neatlogsWrapped: true }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function safeStringify(value) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.stringify(value);
|
|
42
|
+
} catch {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function setInputValue(span, opts) {
|
|
47
|
+
if (opts && ("prompt" in opts || "messages" in opts)) {
|
|
48
|
+
const input = opts.messages ?? opts.prompt;
|
|
49
|
+
const stringified2 = safeStringify(input);
|
|
50
|
+
if (stringified2) {
|
|
51
|
+
span.setAttribute("input.value", stringified2);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const stringified = safeStringify(opts);
|
|
56
|
+
if (stringified) {
|
|
57
|
+
span.setAttribute("input.value", stringified);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function setOutputValue(span, result) {
|
|
61
|
+
if (result && typeof result === "object") {
|
|
62
|
+
const r = result;
|
|
63
|
+
if ("text" in r && "finishReason" in r) {
|
|
64
|
+
const text = String(r.text ?? "");
|
|
65
|
+
if (text) {
|
|
66
|
+
span.setAttribute("output.value", text);
|
|
67
|
+
}
|
|
68
|
+
if (r.finishReason) {
|
|
69
|
+
span.setAttribute("gen_ai.finish_reason", String(r.finishReason));
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const stringified = safeStringify(result);
|
|
75
|
+
if (stringified) {
|
|
76
|
+
span.setAttribute("output.value", stringified);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
var WRAPPED_FUNCTIONS = [
|
|
80
|
+
"generateText",
|
|
81
|
+
"streamText",
|
|
82
|
+
"generateObject",
|
|
83
|
+
"streamObject",
|
|
84
|
+
"embed",
|
|
85
|
+
"embedMany",
|
|
86
|
+
"rerank"
|
|
87
|
+
];
|
|
88
|
+
function wrapAISDK(aiModule) {
|
|
89
|
+
const wrapped = { ...aiModule };
|
|
90
|
+
for (const name of WRAPPED_FUNCTIONS) {
|
|
91
|
+
const original = aiModule[name];
|
|
92
|
+
if (typeof original !== "function") continue;
|
|
93
|
+
if (name === "streamText" || name === "streamObject") {
|
|
94
|
+
wrapped[name] = createSyncWrapper(name, original);
|
|
95
|
+
} else {
|
|
96
|
+
wrapped[name] = createAsyncWrapper(name, original);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return wrapped;
|
|
100
|
+
}
|
|
101
|
+
function rootSpanKind(name) {
|
|
102
|
+
if (name === "embed" || name === "embedMany" || name === "rerank") return "CHAIN";
|
|
103
|
+
return "WORKFLOW";
|
|
104
|
+
}
|
|
105
|
+
function getParentContext() {
|
|
106
|
+
const activeSpan = import_api.trace.getActiveSpan();
|
|
107
|
+
if (activeSpan) {
|
|
108
|
+
const instrScope = activeSpan.instrumentationLibrary?.name ?? "";
|
|
109
|
+
if (instrScope === "next.js") {
|
|
110
|
+
return import_api.ROOT_CONTEXT;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return import_api.context.active();
|
|
114
|
+
}
|
|
115
|
+
function createAsyncWrapper(name, original) {
|
|
116
|
+
return async function wrappedAsyncFn(opts) {
|
|
117
|
+
const tracer = import_api.trace.getTracer(TRACER_NAME);
|
|
118
|
+
return tracer.startActiveSpan(
|
|
119
|
+
`ai.${name}`,
|
|
120
|
+
{ attributes: { "openinference.span.kind": rootSpanKind(name) } },
|
|
121
|
+
getParentContext(),
|
|
122
|
+
async (span) => {
|
|
123
|
+
try {
|
|
124
|
+
const isEmbedOrRerank = name === "embed" || name === "embedMany" || name === "rerank";
|
|
125
|
+
if (!isEmbedOrRerank) {
|
|
126
|
+
setInputValue(span, opts);
|
|
127
|
+
}
|
|
128
|
+
if (name === "rerank" && opts?.query) {
|
|
129
|
+
span.setAttribute("ai.rerank.query", String(opts.query));
|
|
130
|
+
}
|
|
131
|
+
const merged = mergeTelemetry(opts);
|
|
132
|
+
const result = await original(merged);
|
|
133
|
+
if (!isEmbedOrRerank) {
|
|
134
|
+
setOutputValue(span, result);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
} catch (err) {
|
|
138
|
+
recordSpanError(span, err);
|
|
139
|
+
throw err;
|
|
140
|
+
} finally {
|
|
141
|
+
span.end();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function createSyncWrapper(name, original) {
|
|
148
|
+
return function wrappedSyncFn(opts) {
|
|
149
|
+
const tracer = import_api.trace.getTracer(TRACER_NAME);
|
|
150
|
+
return tracer.startActiveSpan(`ai.${name}`, { attributes: { "openinference.span.kind": rootSpanKind(name) } }, getParentContext(), (span) => {
|
|
151
|
+
try {
|
|
152
|
+
setInputValue(span, opts);
|
|
153
|
+
const merged = mergeTelemetry(opts);
|
|
154
|
+
const result = original(merged);
|
|
155
|
+
return result;
|
|
156
|
+
} catch (err) {
|
|
157
|
+
recordSpanError(span, err);
|
|
158
|
+
throw err;
|
|
159
|
+
} finally {
|
|
160
|
+
span.end();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function mergeTelemetry(opts) {
|
|
166
|
+
const baseTelemetry = createAITelemetry({
|
|
167
|
+
metadata: opts?.experimental_telemetry?.metadata
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
...opts,
|
|
171
|
+
experimental_telemetry: {
|
|
172
|
+
...opts?.experimental_telemetry,
|
|
173
|
+
...baseTelemetry
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function recordSpanError(span, err) {
|
|
178
|
+
if (err instanceof Error) {
|
|
179
|
+
span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: err.message });
|
|
180
|
+
span.recordException(err);
|
|
181
|
+
} else {
|
|
182
|
+
span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: String(err) });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
186
|
+
0 && (module.exports = {
|
|
187
|
+
createAITelemetry,
|
|
188
|
+
wrapAISDK
|
|
189
|
+
});
|
|
190
|
+
//# sourceMappingURL=ai-sdk.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai-sdk.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 { trace, context as otelContext, ROOT_CONTEXT, SpanStatusCode, type Span, type Tracer } from '@opentelemetry/api';\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 tracer: trace.getTracer(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 }\n const stringified = safeStringify(result);\n if (stringified) {\n span.setAttribute('output.value', stringified);\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] = createSyncWrapper(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 const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n const instrScope = (activeSpan as any).instrumentationLibrary?.name ?? '';\n if (instrScope === 'next.js') {\n return ROOT_CONTEXT;\n }\n }\n return otelContext.active();\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 = trace.getTracer(TRACER_NAME);\n return tracer.startActiveSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n getParentContext(),\n async (span) => {\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 );\n };\n}\n\nfunction createSyncWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => unknown,\n): (opts: any) => unknown {\n return function wrappedSyncFn(opts: any): unknown {\n const tracer = trace.getTracer(TRACER_NAME);\n return tracer.startActiveSpan(`ai.${name}`, { attributes: { 'openinference.span.kind': rootSpanKind(name) } }, getParentContext(), (span) => {\n try {\n setInputValue(span, opts);\n const merged = mergeTelemetry(opts);\n const result = original(merged);\n return result;\n } catch (err) {\n recordSpanError(span, err);\n throw err;\n } finally {\n span.end();\n }\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"],"mappings":"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","names":["stringified","otelContext"]}
|
package/dist/ai-sdk.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Tracer } from '@opentelemetry/api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vercel AI SDK wrapper — inline implementation.
|
|
5
|
+
*
|
|
6
|
+
* Wraps `generateText`, `streamText`, `generateObject`, `streamObject` from
|
|
7
|
+
* the `ai` package with OTel parent spans + forced telemetry. Static export,
|
|
8
|
+
* no dynamic imports — bundler-friendly (works with Turbopack, webpack, esbuild).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { wrapAISDK } from 'neatlogs';
|
|
12
|
+
* import * as ai from 'ai';
|
|
13
|
+
* const { streamText, generateText } = wrapAISDK(ai);
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface CreateAITelemetryOptions {
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
interface AITelemetryConfig {
|
|
20
|
+
isEnabled: true;
|
|
21
|
+
recordInputs: true;
|
|
22
|
+
recordOutputs: true;
|
|
23
|
+
tracer: Tracer;
|
|
24
|
+
metadata: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
declare function createAITelemetry(opts?: CreateAITelemetryOptions): AITelemetryConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Wrap the `ai` module namespace so that every `generateText` / `streamText` /
|
|
29
|
+
* `generateObject` / `streamObject` call:
|
|
30
|
+
*
|
|
31
|
+
* 1. Opens a parent OTel span on the active TracerProvider.
|
|
32
|
+
* 2. Forces `experimental_telemetry: { isEnabled: true }`, merging user metadata.
|
|
33
|
+
* 3. Records input/output on the parent span and propagates errors.
|
|
34
|
+
*
|
|
35
|
+
* Other exports (types, helpers) pass through unchanged.
|
|
36
|
+
*/
|
|
37
|
+
declare function wrapAISDK<T extends Record<string, unknown>>(aiModule: T): T;
|
|
38
|
+
|
|
39
|
+
export { type AITelemetryConfig, type CreateAITelemetryOptions, createAITelemetry, wrapAISDK };
|
package/dist/ai-sdk.mjs
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// src/ai-sdk.ts
|
|
2
|
+
import { trace, context as otelContext, ROOT_CONTEXT, SpanStatusCode } from "@opentelemetry/api";
|
|
3
|
+
var TRACER_NAME = "neatlogs.ai-sdk";
|
|
4
|
+
function createAITelemetry(opts = {}) {
|
|
5
|
+
const userMeta = opts.metadata ?? {};
|
|
6
|
+
return {
|
|
7
|
+
isEnabled: true,
|
|
8
|
+
recordInputs: true,
|
|
9
|
+
recordOutputs: true,
|
|
10
|
+
tracer: trace.getTracer(TRACER_NAME),
|
|
11
|
+
metadata: { ...userMeta, neatlogsWrapped: true }
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function safeStringify(value) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.stringify(value);
|
|
17
|
+
} catch {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function setInputValue(span, opts) {
|
|
22
|
+
if (opts && ("prompt" in opts || "messages" in opts)) {
|
|
23
|
+
const input = opts.messages ?? opts.prompt;
|
|
24
|
+
const stringified2 = safeStringify(input);
|
|
25
|
+
if (stringified2) {
|
|
26
|
+
span.setAttribute("input.value", stringified2);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const stringified = safeStringify(opts);
|
|
31
|
+
if (stringified) {
|
|
32
|
+
span.setAttribute("input.value", stringified);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function setOutputValue(span, result) {
|
|
36
|
+
if (result && typeof result === "object") {
|
|
37
|
+
const r = result;
|
|
38
|
+
if ("text" in r && "finishReason" in r) {
|
|
39
|
+
const text = String(r.text ?? "");
|
|
40
|
+
if (text) {
|
|
41
|
+
span.setAttribute("output.value", text);
|
|
42
|
+
}
|
|
43
|
+
if (r.finishReason) {
|
|
44
|
+
span.setAttribute("gen_ai.finish_reason", String(r.finishReason));
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const stringified = safeStringify(result);
|
|
50
|
+
if (stringified) {
|
|
51
|
+
span.setAttribute("output.value", stringified);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
var WRAPPED_FUNCTIONS = [
|
|
55
|
+
"generateText",
|
|
56
|
+
"streamText",
|
|
57
|
+
"generateObject",
|
|
58
|
+
"streamObject",
|
|
59
|
+
"embed",
|
|
60
|
+
"embedMany",
|
|
61
|
+
"rerank"
|
|
62
|
+
];
|
|
63
|
+
function wrapAISDK(aiModule) {
|
|
64
|
+
const wrapped = { ...aiModule };
|
|
65
|
+
for (const name of WRAPPED_FUNCTIONS) {
|
|
66
|
+
const original = aiModule[name];
|
|
67
|
+
if (typeof original !== "function") continue;
|
|
68
|
+
if (name === "streamText" || name === "streamObject") {
|
|
69
|
+
wrapped[name] = createSyncWrapper(name, original);
|
|
70
|
+
} else {
|
|
71
|
+
wrapped[name] = createAsyncWrapper(name, original);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return wrapped;
|
|
75
|
+
}
|
|
76
|
+
function rootSpanKind(name) {
|
|
77
|
+
if (name === "embed" || name === "embedMany" || name === "rerank") return "CHAIN";
|
|
78
|
+
return "WORKFLOW";
|
|
79
|
+
}
|
|
80
|
+
function getParentContext() {
|
|
81
|
+
const activeSpan = trace.getActiveSpan();
|
|
82
|
+
if (activeSpan) {
|
|
83
|
+
const instrScope = activeSpan.instrumentationLibrary?.name ?? "";
|
|
84
|
+
if (instrScope === "next.js") {
|
|
85
|
+
return ROOT_CONTEXT;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return otelContext.active();
|
|
89
|
+
}
|
|
90
|
+
function createAsyncWrapper(name, original) {
|
|
91
|
+
return async function wrappedAsyncFn(opts) {
|
|
92
|
+
const tracer = trace.getTracer(TRACER_NAME);
|
|
93
|
+
return tracer.startActiveSpan(
|
|
94
|
+
`ai.${name}`,
|
|
95
|
+
{ attributes: { "openinference.span.kind": rootSpanKind(name) } },
|
|
96
|
+
getParentContext(),
|
|
97
|
+
async (span) => {
|
|
98
|
+
try {
|
|
99
|
+
const isEmbedOrRerank = name === "embed" || name === "embedMany" || name === "rerank";
|
|
100
|
+
if (!isEmbedOrRerank) {
|
|
101
|
+
setInputValue(span, opts);
|
|
102
|
+
}
|
|
103
|
+
if (name === "rerank" && opts?.query) {
|
|
104
|
+
span.setAttribute("ai.rerank.query", String(opts.query));
|
|
105
|
+
}
|
|
106
|
+
const merged = mergeTelemetry(opts);
|
|
107
|
+
const result = await original(merged);
|
|
108
|
+
if (!isEmbedOrRerank) {
|
|
109
|
+
setOutputValue(span, result);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
recordSpanError(span, err);
|
|
114
|
+
throw err;
|
|
115
|
+
} finally {
|
|
116
|
+
span.end();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function createSyncWrapper(name, original) {
|
|
123
|
+
return function wrappedSyncFn(opts) {
|
|
124
|
+
const tracer = trace.getTracer(TRACER_NAME);
|
|
125
|
+
return tracer.startActiveSpan(`ai.${name}`, { attributes: { "openinference.span.kind": rootSpanKind(name) } }, getParentContext(), (span) => {
|
|
126
|
+
try {
|
|
127
|
+
setInputValue(span, opts);
|
|
128
|
+
const merged = mergeTelemetry(opts);
|
|
129
|
+
const result = original(merged);
|
|
130
|
+
return result;
|
|
131
|
+
} catch (err) {
|
|
132
|
+
recordSpanError(span, err);
|
|
133
|
+
throw err;
|
|
134
|
+
} finally {
|
|
135
|
+
span.end();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function mergeTelemetry(opts) {
|
|
141
|
+
const baseTelemetry = createAITelemetry({
|
|
142
|
+
metadata: opts?.experimental_telemetry?.metadata
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
...opts,
|
|
146
|
+
experimental_telemetry: {
|
|
147
|
+
...opts?.experimental_telemetry,
|
|
148
|
+
...baseTelemetry
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function recordSpanError(span, err) {
|
|
153
|
+
if (err instanceof Error) {
|
|
154
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
155
|
+
span.recordException(err);
|
|
156
|
+
} else {
|
|
157
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
createAITelemetry,
|
|
162
|
+
wrapAISDK
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=ai-sdk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai-sdk.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 { trace, context as otelContext, ROOT_CONTEXT, SpanStatusCode, type Span, type Tracer } from '@opentelemetry/api';\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 tracer: trace.getTracer(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 }\n const stringified = safeStringify(result);\n if (stringified) {\n span.setAttribute('output.value', stringified);\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] = createSyncWrapper(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 const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n const instrScope = (activeSpan as any).instrumentationLibrary?.name ?? '';\n if (instrScope === 'next.js') {\n return ROOT_CONTEXT;\n }\n }\n return otelContext.active();\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 = trace.getTracer(TRACER_NAME);\n return tracer.startActiveSpan(\n `ai.${name}`,\n { attributes: { 'openinference.span.kind': rootSpanKind(name) } },\n getParentContext(),\n async (span) => {\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 );\n };\n}\n\nfunction createSyncWrapper(\n name: WrappedFunctionName,\n original: (opts: any) => unknown,\n): (opts: any) => unknown {\n return function wrappedSyncFn(opts: any): unknown {\n const tracer = trace.getTracer(TRACER_NAME);\n return tracer.startActiveSpan(`ai.${name}`, { attributes: { 'openinference.span.kind': rootSpanKind(name) } }, getParentContext(), (span) => {\n try {\n setInputValue(span, opts);\n const merged = mergeTelemetry(opts);\n const result = original(merged);\n return result;\n } catch (err) {\n recordSpanError(span, err);\n throw err;\n } finally {\n span.end();\n }\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"],"mappings":"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","names":["stringified"]}
|