neatlogs 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-sdk.cjs +46 -7
- package/dist/ai-sdk.cjs.map +1 -1
- package/dist/ai-sdk.mjs +46 -7
- package/dist/ai-sdk.mjs.map +1 -1
- package/dist/index.cjs +47 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +47 -8
- package/dist/index.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/package.json +1 -1
package/dist/ai-sdk.cjs
CHANGED
|
@@ -76,6 +76,19 @@ function setOutputValue(span, result) {
|
|
|
76
76
|
span.setAttribute("output.value", stringified);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
+
function setStreamOutputValue(span, event) {
|
|
80
|
+
if (!event || typeof event !== "object") return;
|
|
81
|
+
const e = event;
|
|
82
|
+
if (typeof e.text === "string" && e.text) {
|
|
83
|
+
span.setAttribute("output.value", e.text);
|
|
84
|
+
} else if ("object" in e && e.object !== void 0) {
|
|
85
|
+
const stringified = safeStringify(e.object);
|
|
86
|
+
if (stringified) span.setAttribute("output.value", stringified);
|
|
87
|
+
}
|
|
88
|
+
if (e.finishReason) {
|
|
89
|
+
span.setAttribute("gen_ai.finish_reason", String(e.finishReason));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
79
92
|
var WRAPPED_FUNCTIONS = [
|
|
80
93
|
"generateText",
|
|
81
94
|
"streamText",
|
|
@@ -91,7 +104,7 @@ function wrapAISDK(aiModule) {
|
|
|
91
104
|
const original = aiModule[name];
|
|
92
105
|
if (typeof original !== "function") continue;
|
|
93
106
|
if (name === "streamText" || name === "streamObject") {
|
|
94
|
-
wrapped[name] =
|
|
107
|
+
wrapped[name] = createStreamWrapper(name, original);
|
|
95
108
|
} else {
|
|
96
109
|
wrapped[name] = createAsyncWrapper(name, original);
|
|
97
110
|
}
|
|
@@ -144,20 +157,46 @@ function createAsyncWrapper(name, original) {
|
|
|
144
157
|
);
|
|
145
158
|
};
|
|
146
159
|
}
|
|
147
|
-
function
|
|
148
|
-
return function
|
|
160
|
+
function createStreamWrapper(name, original) {
|
|
161
|
+
return function wrappedStreamFn(opts) {
|
|
149
162
|
const tracer = import_api.trace.getTracer(TRACER_NAME);
|
|
150
163
|
return tracer.startActiveSpan(`ai.${name}`, { attributes: { "openinference.span.kind": rootSpanKind(name) } }, getParentContext(), (span) => {
|
|
164
|
+
let spanEnded = false;
|
|
165
|
+
const endOnce = () => {
|
|
166
|
+
if (spanEnded) return;
|
|
167
|
+
spanEnded = true;
|
|
168
|
+
span.end();
|
|
169
|
+
};
|
|
151
170
|
try {
|
|
152
171
|
setInputValue(span, opts);
|
|
153
172
|
const merged = mergeTelemetry(opts);
|
|
154
|
-
const
|
|
155
|
-
|
|
173
|
+
const userOnFinish = opts?.onFinish;
|
|
174
|
+
const userOnError = opts?.onError;
|
|
175
|
+
const wrappedOpts = {
|
|
176
|
+
...merged,
|
|
177
|
+
onFinish: async (event) => {
|
|
178
|
+
try {
|
|
179
|
+
setStreamOutputValue(span, event);
|
|
180
|
+
} finally {
|
|
181
|
+
endOnce();
|
|
182
|
+
}
|
|
183
|
+
if (typeof userOnFinish === "function") {
|
|
184
|
+
return userOnFinish(event);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
onError: (event) => {
|
|
188
|
+
recordSpanError(span, (event && event.error) ?? event);
|
|
189
|
+
endOnce();
|
|
190
|
+
if (typeof userOnError === "function") {
|
|
191
|
+
return userOnError(event);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
return original(wrappedOpts);
|
|
156
196
|
} catch (err) {
|
|
157
197
|
recordSpanError(span, err);
|
|
198
|
+
endOnce();
|
|
158
199
|
throw err;
|
|
159
|
-
} finally {
|
|
160
|
-
span.end();
|
|
161
200
|
}
|
|
162
201
|
});
|
|
163
202
|
};
|
package/dist/ai-sdk.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
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// 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 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\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 = trace.getTracer(TRACER_NAME);\n return tracer.startActiveSpan(`ai.${name}`, { attributes: { 'openinference.span.kind': rootSpanKind(name) } }, getParentContext(), (span) => {\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 };\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.mjs
CHANGED
|
@@ -51,6 +51,19 @@ function setOutputValue(span, result) {
|
|
|
51
51
|
span.setAttribute("output.value", stringified);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
function setStreamOutputValue(span, event) {
|
|
55
|
+
if (!event || typeof event !== "object") return;
|
|
56
|
+
const e = event;
|
|
57
|
+
if (typeof e.text === "string" && e.text) {
|
|
58
|
+
span.setAttribute("output.value", e.text);
|
|
59
|
+
} else if ("object" in e && e.object !== void 0) {
|
|
60
|
+
const stringified = safeStringify(e.object);
|
|
61
|
+
if (stringified) span.setAttribute("output.value", stringified);
|
|
62
|
+
}
|
|
63
|
+
if (e.finishReason) {
|
|
64
|
+
span.setAttribute("gen_ai.finish_reason", String(e.finishReason));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
54
67
|
var WRAPPED_FUNCTIONS = [
|
|
55
68
|
"generateText",
|
|
56
69
|
"streamText",
|
|
@@ -66,7 +79,7 @@ function wrapAISDK(aiModule) {
|
|
|
66
79
|
const original = aiModule[name];
|
|
67
80
|
if (typeof original !== "function") continue;
|
|
68
81
|
if (name === "streamText" || name === "streamObject") {
|
|
69
|
-
wrapped[name] =
|
|
82
|
+
wrapped[name] = createStreamWrapper(name, original);
|
|
70
83
|
} else {
|
|
71
84
|
wrapped[name] = createAsyncWrapper(name, original);
|
|
72
85
|
}
|
|
@@ -119,20 +132,46 @@ function createAsyncWrapper(name, original) {
|
|
|
119
132
|
);
|
|
120
133
|
};
|
|
121
134
|
}
|
|
122
|
-
function
|
|
123
|
-
return function
|
|
135
|
+
function createStreamWrapper(name, original) {
|
|
136
|
+
return function wrappedStreamFn(opts) {
|
|
124
137
|
const tracer = trace.getTracer(TRACER_NAME);
|
|
125
138
|
return tracer.startActiveSpan(`ai.${name}`, { attributes: { "openinference.span.kind": rootSpanKind(name) } }, getParentContext(), (span) => {
|
|
139
|
+
let spanEnded = false;
|
|
140
|
+
const endOnce = () => {
|
|
141
|
+
if (spanEnded) return;
|
|
142
|
+
spanEnded = true;
|
|
143
|
+
span.end();
|
|
144
|
+
};
|
|
126
145
|
try {
|
|
127
146
|
setInputValue(span, opts);
|
|
128
147
|
const merged = mergeTelemetry(opts);
|
|
129
|
-
const
|
|
130
|
-
|
|
148
|
+
const userOnFinish = opts?.onFinish;
|
|
149
|
+
const userOnError = opts?.onError;
|
|
150
|
+
const wrappedOpts = {
|
|
151
|
+
...merged,
|
|
152
|
+
onFinish: async (event) => {
|
|
153
|
+
try {
|
|
154
|
+
setStreamOutputValue(span, event);
|
|
155
|
+
} finally {
|
|
156
|
+
endOnce();
|
|
157
|
+
}
|
|
158
|
+
if (typeof userOnFinish === "function") {
|
|
159
|
+
return userOnFinish(event);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
onError: (event) => {
|
|
163
|
+
recordSpanError(span, (event && event.error) ?? event);
|
|
164
|
+
endOnce();
|
|
165
|
+
if (typeof userOnError === "function") {
|
|
166
|
+
return userOnError(event);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
return original(wrappedOpts);
|
|
131
171
|
} catch (err) {
|
|
132
172
|
recordSpanError(span, err);
|
|
173
|
+
endOnce();
|
|
133
174
|
throw err;
|
|
134
|
-
} finally {
|
|
135
|
-
span.end();
|
|
136
175
|
}
|
|
137
176
|
});
|
|
138
177
|
};
|
package/dist/ai-sdk.mjs.map
CHANGED
|
@@ -1 +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"]}
|
|
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// 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 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\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 = trace.getTracer(TRACER_NAME);\n return tracer.startActiveSpan(`ai.${name}`, { attributes: { 'openinference.span.kind': rootSpanKind(name) } }, getParentContext(), (span) => {\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 };\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"]}
|
package/dist/index.cjs
CHANGED
|
@@ -5823,7 +5823,7 @@ async function removeTag(name, tag) {
|
|
|
5823
5823
|
}
|
|
5824
5824
|
|
|
5825
5825
|
// src/version.ts
|
|
5826
|
-
var __version__ = "1.1.
|
|
5826
|
+
var __version__ = "1.1.6";
|
|
5827
5827
|
|
|
5828
5828
|
// src/init.ts
|
|
5829
5829
|
var logger13 = getLogger();
|
|
@@ -6316,6 +6316,19 @@ function setOutputValue(span2, result) {
|
|
|
6316
6316
|
span2.setAttribute("output.value", stringified);
|
|
6317
6317
|
}
|
|
6318
6318
|
}
|
|
6319
|
+
function setStreamOutputValue(span2, event) {
|
|
6320
|
+
if (!event || typeof event !== "object") return;
|
|
6321
|
+
const e = event;
|
|
6322
|
+
if (typeof e.text === "string" && e.text) {
|
|
6323
|
+
span2.setAttribute("output.value", e.text);
|
|
6324
|
+
} else if ("object" in e && e.object !== void 0) {
|
|
6325
|
+
const stringified = safeStringify2(e.object);
|
|
6326
|
+
if (stringified) span2.setAttribute("output.value", stringified);
|
|
6327
|
+
}
|
|
6328
|
+
if (e.finishReason) {
|
|
6329
|
+
span2.setAttribute("gen_ai.finish_reason", String(e.finishReason));
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6319
6332
|
var WRAPPED_FUNCTIONS = [
|
|
6320
6333
|
"generateText",
|
|
6321
6334
|
"streamText",
|
|
@@ -6331,7 +6344,7 @@ function wrapAISDK(aiModule) {
|
|
|
6331
6344
|
const original = aiModule[name];
|
|
6332
6345
|
if (typeof original !== "function") continue;
|
|
6333
6346
|
if (name === "streamText" || name === "streamObject") {
|
|
6334
|
-
wrapped[name] =
|
|
6347
|
+
wrapped[name] = createStreamWrapper(name, original);
|
|
6335
6348
|
} else {
|
|
6336
6349
|
wrapped[name] = createAsyncWrapper(name, original);
|
|
6337
6350
|
}
|
|
@@ -6384,20 +6397,46 @@ function createAsyncWrapper(name, original) {
|
|
|
6384
6397
|
);
|
|
6385
6398
|
};
|
|
6386
6399
|
}
|
|
6387
|
-
function
|
|
6388
|
-
return function
|
|
6400
|
+
function createStreamWrapper(name, original) {
|
|
6401
|
+
return function wrappedStreamFn(opts) {
|
|
6389
6402
|
const tracer = import_api9.trace.getTracer(TRACER_NAME2);
|
|
6390
6403
|
return tracer.startActiveSpan(`ai.${name}`, { attributes: { "openinference.span.kind": rootSpanKind(name) } }, getParentContext(), (span2) => {
|
|
6404
|
+
let spanEnded = false;
|
|
6405
|
+
const endOnce = () => {
|
|
6406
|
+
if (spanEnded) return;
|
|
6407
|
+
spanEnded = true;
|
|
6408
|
+
span2.end();
|
|
6409
|
+
};
|
|
6391
6410
|
try {
|
|
6392
6411
|
setInputValue(span2, opts);
|
|
6393
6412
|
const merged = mergeTelemetry(opts);
|
|
6394
|
-
const
|
|
6395
|
-
|
|
6413
|
+
const userOnFinish = opts?.onFinish;
|
|
6414
|
+
const userOnError = opts?.onError;
|
|
6415
|
+
const wrappedOpts = {
|
|
6416
|
+
...merged,
|
|
6417
|
+
onFinish: async (event) => {
|
|
6418
|
+
try {
|
|
6419
|
+
setStreamOutputValue(span2, event);
|
|
6420
|
+
} finally {
|
|
6421
|
+
endOnce();
|
|
6422
|
+
}
|
|
6423
|
+
if (typeof userOnFinish === "function") {
|
|
6424
|
+
return userOnFinish(event);
|
|
6425
|
+
}
|
|
6426
|
+
},
|
|
6427
|
+
onError: (event) => {
|
|
6428
|
+
recordSpanError(span2, (event && event.error) ?? event);
|
|
6429
|
+
endOnce();
|
|
6430
|
+
if (typeof userOnError === "function") {
|
|
6431
|
+
return userOnError(event);
|
|
6432
|
+
}
|
|
6433
|
+
}
|
|
6434
|
+
};
|
|
6435
|
+
return original(wrappedOpts);
|
|
6396
6436
|
} catch (err) {
|
|
6397
6437
|
recordSpanError(span2, err);
|
|
6438
|
+
endOnce();
|
|
6398
6439
|
throw err;
|
|
6399
|
-
} finally {
|
|
6400
|
-
span2.end();
|
|
6401
6440
|
}
|
|
6402
6441
|
});
|
|
6403
6442
|
};
|