neatlogs 1.0.4 → 1.0.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/anthropic.cjs +373 -0
- package/dist/anthropic.cjs.map +1 -0
- package/dist/anthropic.d.ts +27 -0
- package/dist/anthropic.mjs +347 -0
- package/dist/anthropic.mjs.map +1 -0
- package/dist/index.cjs +1999 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.mjs +1993 -7
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +269 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.ts +16 -0
- package/dist/langchain.mjs +244 -0
- package/dist/langchain.mjs.map +1 -0
- package/dist/mastra-wrap.cjs +597 -0
- package/dist/mastra-wrap.cjs.map +1 -0
- package/dist/mastra-wrap.d.ts +38 -0
- package/dist/mastra-wrap.mjs +571 -0
- package/dist/mastra-wrap.mjs.map +1 -0
- package/dist/openai-agents.cjs +215 -0
- package/dist/openai-agents.cjs.map +1 -0
- package/dist/openai-agents.d.ts +13 -0
- package/dist/openai-agents.mjs +190 -0
- package/dist/openai-agents.mjs.map +1 -0
- package/dist/openai.cjs +342 -0
- package/dist/openai.cjs.map +1 -0
- package/dist/openai.d.ts +26 -0
- package/dist/openai.mjs +316 -0
- package/dist/openai.mjs.map +1 -0
- package/dist/pi-agent.cjs +237 -0
- package/dist/pi-agent.cjs.map +1 -0
- package/dist/pi-agent.d.ts +31 -0
- package/dist/pi-agent.mjs +216 -0
- package/dist/pi-agent.mjs.map +1 -0
- package/dist/strands.cjs +167 -0
- package/dist/strands.cjs.map +1 -0
- package/dist/strands.d.ts +28 -0
- package/dist/strands.mjs +142 -0
- package/dist/strands.mjs.map +1 -0
- package/package.json +75 -3
package/dist/openai.mjs
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// src/openai.ts
|
|
2
|
+
import { trace, context as otelContext, SpanStatusCode } from "@opentelemetry/api";
|
|
3
|
+
var TRACER_NAME = "neatlogs.openai";
|
|
4
|
+
function wrapOpenAI(client) {
|
|
5
|
+
return wrapNamespace(client, []);
|
|
6
|
+
}
|
|
7
|
+
function traceTool(name, fn) {
|
|
8
|
+
return async function tracedTool(args) {
|
|
9
|
+
const tracer = trace.getTracer(TRACER_NAME);
|
|
10
|
+
return tracer.startActiveSpan(
|
|
11
|
+
`tool.${name}`,
|
|
12
|
+
{
|
|
13
|
+
attributes: {
|
|
14
|
+
"neatlogs.span.kind": "TOOL",
|
|
15
|
+
"neatlogs.tool.name": name,
|
|
16
|
+
"input.value": safeStringify(args)
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
otelContext.active(),
|
|
20
|
+
async (span) => {
|
|
21
|
+
try {
|
|
22
|
+
const result = await fn(args);
|
|
23
|
+
span.setAttribute("output.value", safeStringify(result));
|
|
24
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
25
|
+
return result;
|
|
26
|
+
} catch (err) {
|
|
27
|
+
recordError(span, err);
|
|
28
|
+
throw err;
|
|
29
|
+
} finally {
|
|
30
|
+
span.end();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function wrapNamespace(target, path) {
|
|
37
|
+
return new Proxy(target, {
|
|
38
|
+
get(obj, prop, receiver) {
|
|
39
|
+
const value = Reflect.get(obj, prop, receiver);
|
|
40
|
+
if (typeof prop === "symbol" || String(prop).startsWith("_")) return value;
|
|
41
|
+
const currentPath = [...path, String(prop)];
|
|
42
|
+
const pathStr = currentPath.join(".");
|
|
43
|
+
if (pathStr === "chat.completions.create" && typeof value === "function") {
|
|
44
|
+
return tracedChatCompletionsCreate(value.bind(obj));
|
|
45
|
+
}
|
|
46
|
+
if (pathStr === "responses.create" && typeof value === "function") {
|
|
47
|
+
return tracedResponsesCreate(value.bind(obj));
|
|
48
|
+
}
|
|
49
|
+
if (value && typeof value === "object" && !Array.isArray(value) && isNamespace(currentPath)) {
|
|
50
|
+
return wrapNamespace(value, currentPath);
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function isNamespace(path) {
|
|
57
|
+
if (path.length > 3) return false;
|
|
58
|
+
const key = path[path.length - 1];
|
|
59
|
+
return ["chat", "completions", "responses", "beta"].includes(key);
|
|
60
|
+
}
|
|
61
|
+
function tracedChatCompletionsCreate(original) {
|
|
62
|
+
return function(opts, ...rest) {
|
|
63
|
+
const tracer = trace.getTracer(TRACER_NAME);
|
|
64
|
+
const model = opts?.model ?? "";
|
|
65
|
+
const messages = opts?.messages ?? [];
|
|
66
|
+
const isStream = opts?.stream === true;
|
|
67
|
+
const span = tracer.startSpan("openai.chat.completions.create", {
|
|
68
|
+
attributes: {
|
|
69
|
+
"neatlogs.span.kind": "LLM",
|
|
70
|
+
"neatlogs.llm.provider": "openai",
|
|
71
|
+
"neatlogs.llm.system": "openai",
|
|
72
|
+
"neatlogs.llm.model_name": model,
|
|
73
|
+
"neatlogs.llm.is_streaming": isStream
|
|
74
|
+
}
|
|
75
|
+
}, otelContext.active());
|
|
76
|
+
for (let i = 0; i < messages.length; i++) {
|
|
77
|
+
const msg = messages[i];
|
|
78
|
+
span.setAttribute(`neatlogs.llm.input_messages.${i}.role`, msg.role ?? "");
|
|
79
|
+
if (typeof msg.content === "string") {
|
|
80
|
+
span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, msg.content);
|
|
81
|
+
} else if (msg.content) {
|
|
82
|
+
span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, safeStringify(msg.content));
|
|
83
|
+
}
|
|
84
|
+
if (msg.tool_call_id) {
|
|
85
|
+
span.setAttribute(`neatlogs.llm.input_messages.${i}.tool_call_id`, msg.tool_call_id);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (opts?.tools) {
|
|
89
|
+
for (let i = 0; i < opts.tools.length; i++) {
|
|
90
|
+
const fn = opts.tools[i]?.function ?? {};
|
|
91
|
+
span.setAttribute(`neatlogs.llm.tools.${i}.name`, fn.name ?? "");
|
|
92
|
+
if (fn.description) span.setAttribute(`neatlogs.llm.tools.${i}.description`, fn.description);
|
|
93
|
+
if (fn.parameters) span.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify(fn.parameters));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
setInvocationParams(span, opts);
|
|
97
|
+
if (isStream) {
|
|
98
|
+
opts = { ...opts };
|
|
99
|
+
const streamOpts = opts.stream_options ?? {};
|
|
100
|
+
if (!streamOpts.include_usage) {
|
|
101
|
+
opts.stream_options = { ...streamOpts, include_usage: true };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const ctx = trace.setSpan(otelContext.active(), span);
|
|
105
|
+
const promise = otelContext.with(ctx, () => original(opts, ...rest));
|
|
106
|
+
return promise.then(
|
|
107
|
+
(response) => {
|
|
108
|
+
if (isStream) {
|
|
109
|
+
return wrapAsyncIterableStream(response, span);
|
|
110
|
+
}
|
|
111
|
+
finalizeChatResponse(span, response);
|
|
112
|
+
return response;
|
|
113
|
+
},
|
|
114
|
+
(err) => {
|
|
115
|
+
recordError(span, err);
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function tracedResponsesCreate(original) {
|
|
122
|
+
return function(opts, ...rest) {
|
|
123
|
+
const tracer = trace.getTracer(TRACER_NAME);
|
|
124
|
+
const model = opts?.model ?? "";
|
|
125
|
+
const span = tracer.startSpan("openai.responses.create", {
|
|
126
|
+
attributes: {
|
|
127
|
+
"neatlogs.span.kind": "LLM",
|
|
128
|
+
"neatlogs.llm.provider": "openai",
|
|
129
|
+
"neatlogs.llm.system": "openai",
|
|
130
|
+
"neatlogs.llm.model_name": model,
|
|
131
|
+
"input.value": safeStringify(opts?.input ?? "")
|
|
132
|
+
}
|
|
133
|
+
}, otelContext.active());
|
|
134
|
+
const ctx = trace.setSpan(otelContext.active(), span);
|
|
135
|
+
const promise = otelContext.with(ctx, () => original(opts, ...rest));
|
|
136
|
+
return promise.then(
|
|
137
|
+
(response) => {
|
|
138
|
+
if (response?.output_text) {
|
|
139
|
+
span.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
|
|
140
|
+
span.setAttribute("neatlogs.llm.output_messages.0.content", response.output_text);
|
|
141
|
+
}
|
|
142
|
+
if (response?.model) span.setAttribute("neatlogs.llm.model_name", response.model);
|
|
143
|
+
if (response?.usage) {
|
|
144
|
+
if (response.usage.input_tokens != null) span.setAttribute("neatlogs.llm.token_count.prompt", response.usage.input_tokens);
|
|
145
|
+
if (response.usage.output_tokens != null) span.setAttribute("neatlogs.llm.token_count.completion", response.usage.output_tokens);
|
|
146
|
+
}
|
|
147
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
148
|
+
span.end();
|
|
149
|
+
return response;
|
|
150
|
+
},
|
|
151
|
+
(err) => {
|
|
152
|
+
recordError(span, err);
|
|
153
|
+
throw err;
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function wrapAsyncIterableStream(stream, span) {
|
|
159
|
+
const chunks = [];
|
|
160
|
+
const originalAsyncIterator = stream[Symbol.asyncIterator]?.bind(stream);
|
|
161
|
+
if (!originalAsyncIterator) {
|
|
162
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
163
|
+
span.end();
|
|
164
|
+
return stream;
|
|
165
|
+
}
|
|
166
|
+
const wrapped = Object.create(Object.getPrototypeOf(stream));
|
|
167
|
+
Object.assign(wrapped, stream);
|
|
168
|
+
wrapped[Symbol.asyncIterator] = function() {
|
|
169
|
+
const iterator = originalAsyncIterator();
|
|
170
|
+
return {
|
|
171
|
+
async next() {
|
|
172
|
+
try {
|
|
173
|
+
const result = await iterator.next();
|
|
174
|
+
if (result.done) {
|
|
175
|
+
finalizeStreamChunks(span, chunks);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
chunks.push(result.value);
|
|
179
|
+
return result;
|
|
180
|
+
} catch (err) {
|
|
181
|
+
recordError(span, err);
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
async return(value) {
|
|
186
|
+
finalizeStreamChunks(span, chunks);
|
|
187
|
+
return iterator.return?.(value) ?? { done: true, value: void 0 };
|
|
188
|
+
},
|
|
189
|
+
async throw(err) {
|
|
190
|
+
recordError(span, err);
|
|
191
|
+
return iterator.throw?.(err) ?? { done: true, value: void 0 };
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
return wrapped;
|
|
196
|
+
}
|
|
197
|
+
function finalizeStreamChunks(span, chunks) {
|
|
198
|
+
const textParts = [];
|
|
199
|
+
const toolCallsAcc = {};
|
|
200
|
+
let finishReason = "";
|
|
201
|
+
let model = "";
|
|
202
|
+
let usage = null;
|
|
203
|
+
for (const chunk of chunks) {
|
|
204
|
+
if (!chunk?.choices?.length) {
|
|
205
|
+
if (chunk?.usage) usage = chunk.usage;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const choice = chunk.choices[0];
|
|
209
|
+
const delta = choice?.delta;
|
|
210
|
+
if (delta?.content) textParts.push(delta.content);
|
|
211
|
+
if (delta?.tool_calls) {
|
|
212
|
+
for (const tc of delta.tool_calls) {
|
|
213
|
+
const idx = tc.index ?? 0;
|
|
214
|
+
if (!toolCallsAcc[idx]) toolCallsAcc[idx] = { id: "", name: "", arguments: "" };
|
|
215
|
+
if (tc.id) toolCallsAcc[idx].id = tc.id;
|
|
216
|
+
if (tc.function?.name) toolCallsAcc[idx].name = tc.function.name;
|
|
217
|
+
if (tc.function?.arguments) toolCallsAcc[idx].arguments += tc.function.arguments;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
221
|
+
if (chunk?.model) model = chunk.model;
|
|
222
|
+
}
|
|
223
|
+
const fullText = textParts.join("");
|
|
224
|
+
if (fullText) {
|
|
225
|
+
span.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
|
|
226
|
+
span.setAttribute("neatlogs.llm.output_messages.0.content", fullText);
|
|
227
|
+
}
|
|
228
|
+
let j = 0;
|
|
229
|
+
for (const tc of Object.values(toolCallsAcc)) {
|
|
230
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id);
|
|
231
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.name);
|
|
232
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, tc.arguments);
|
|
233
|
+
j++;
|
|
234
|
+
}
|
|
235
|
+
if (model) span.setAttribute("neatlogs.llm.model_name", model);
|
|
236
|
+
if (finishReason) span.setAttribute("neatlogs.llm.finish_reason", finishReason);
|
|
237
|
+
if (usage) {
|
|
238
|
+
if (usage.prompt_tokens != null) span.setAttribute("neatlogs.llm.token_count.prompt", usage.prompt_tokens);
|
|
239
|
+
if (usage.completion_tokens != null) span.setAttribute("neatlogs.llm.token_count.completion", usage.completion_tokens);
|
|
240
|
+
if (usage.total_tokens != null) span.setAttribute("neatlogs.llm.token_count.total", usage.total_tokens);
|
|
241
|
+
if (usage.prompt_tokens_details?.cached_tokens != null) {
|
|
242
|
+
span.setAttribute("neatlogs.llm.token_count.cache_read", usage.prompt_tokens_details.cached_tokens);
|
|
243
|
+
}
|
|
244
|
+
if (usage.completion_tokens_details?.reasoning_tokens != null) {
|
|
245
|
+
span.setAttribute("neatlogs.llm.token_count.reasoning", usage.completion_tokens_details.reasoning_tokens);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
249
|
+
span.end();
|
|
250
|
+
}
|
|
251
|
+
function finalizeChatResponse(span, response) {
|
|
252
|
+
const choices = response?.choices ?? [];
|
|
253
|
+
for (let i = 0; i < choices.length; i++) {
|
|
254
|
+
const message = choices[i]?.message;
|
|
255
|
+
if (!message) continue;
|
|
256
|
+
span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, "assistant");
|
|
257
|
+
if (message.content) {
|
|
258
|
+
span.setAttribute(`neatlogs.llm.output_messages.${i}.content`, message.content);
|
|
259
|
+
}
|
|
260
|
+
if (message.tool_calls) {
|
|
261
|
+
for (let j = 0; j < message.tool_calls.length; j++) {
|
|
262
|
+
const tc = message.tool_calls[j];
|
|
263
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id ?? "");
|
|
264
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.function?.name ?? "");
|
|
265
|
+
span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, tc.function?.arguments ?? "");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (choices[i].finish_reason) {
|
|
269
|
+
span.setAttribute("neatlogs.llm.finish_reason", choices[i].finish_reason);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const usage = response?.usage;
|
|
273
|
+
if (usage) {
|
|
274
|
+
if (usage.prompt_tokens != null) span.setAttribute("neatlogs.llm.token_count.prompt", usage.prompt_tokens);
|
|
275
|
+
if (usage.completion_tokens != null) span.setAttribute("neatlogs.llm.token_count.completion", usage.completion_tokens);
|
|
276
|
+
if (usage.total_tokens != null) span.setAttribute("neatlogs.llm.token_count.total", usage.total_tokens);
|
|
277
|
+
if (usage.prompt_tokens_details?.cached_tokens != null) {
|
|
278
|
+
span.setAttribute("neatlogs.llm.token_count.cache_read", usage.prompt_tokens_details.cached_tokens);
|
|
279
|
+
}
|
|
280
|
+
if (usage.completion_tokens_details?.reasoning_tokens != null) {
|
|
281
|
+
span.setAttribute("neatlogs.llm.token_count.reasoning", usage.completion_tokens_details.reasoning_tokens);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (response?.model) span.setAttribute("neatlogs.llm.model_name", response.model);
|
|
285
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
286
|
+
span.end();
|
|
287
|
+
}
|
|
288
|
+
function setInvocationParams(span, opts) {
|
|
289
|
+
if (opts?.temperature != null) span.setAttribute("neatlogs.llm.temperature", opts.temperature);
|
|
290
|
+
if (opts?.top_p != null) span.setAttribute("neatlogs.llm.top_p", opts.top_p);
|
|
291
|
+
if (opts?.max_tokens != null) span.setAttribute("neatlogs.llm.max_tokens", opts.max_tokens);
|
|
292
|
+
if (opts?.frequency_penalty != null) span.setAttribute("neatlogs.llm.frequency_penalty", opts.frequency_penalty);
|
|
293
|
+
if (opts?.presence_penalty != null) span.setAttribute("neatlogs.llm.presence_penalty", opts.presence_penalty);
|
|
294
|
+
if (opts?.stop) span.setAttribute("neatlogs.llm.stop_sequences", safeStringify(opts.stop));
|
|
295
|
+
}
|
|
296
|
+
function safeStringify(value) {
|
|
297
|
+
try {
|
|
298
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
299
|
+
} catch {
|
|
300
|
+
return "";
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function recordError(span, err) {
|
|
304
|
+
if (err instanceof Error) {
|
|
305
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
306
|
+
span.recordException(err);
|
|
307
|
+
} else {
|
|
308
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
|
|
309
|
+
}
|
|
310
|
+
span.end();
|
|
311
|
+
}
|
|
312
|
+
export {
|
|
313
|
+
traceTool,
|
|
314
|
+
wrapOpenAI
|
|
315
|
+
};
|
|
316
|
+
//# sourceMappingURL=openai.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/openai.ts"],"sourcesContent":["/**\n * Neatlogs OpenAI wrapper — ES6 Proxy-based.\n *\n * Usage:\n * import { wrapOpenAI, traceTool } from 'neatlogs/openai';\n * import OpenAI from 'openai';\n * const client = wrapOpenAI(new OpenAI());\n *\n * Intercepts:\n * - client.chat.completions.create() — non-streaming and streaming\n * - client.responses.create() — Responses API\n *\n * Also exports traceTool() to wrap user-defined tool functions with TOOL spans.\n */\n\nimport { trace, context as otelContext, SpanStatusCode, type Span } from '@opentelemetry/api';\n\nconst TRACER_NAME = 'neatlogs.openai';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function wrapOpenAI<T extends object>(client: T): T {\n return wrapNamespace(client, []) as T;\n}\n\n/**\n * Wrap a tool/function implementation to emit TOOL spans when executed.\n *\n * Usage:\n * const getWeather = traceTool('get_weather', async (args: { city: string }) => {\n * return `Weather in ${args.city}: sunny`;\n * });\n */\nexport function traceTool<TArgs = any, TResult = any>(\n name: string,\n fn: (args: TArgs) => TResult | Promise<TResult>,\n): (args: TArgs) => Promise<TResult> {\n return async function tracedTool(args: TArgs): Promise<TResult> {\n const tracer = trace.getTracer(TRACER_NAME);\n return tracer.startActiveSpan(\n `tool.${name}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': name,\n 'input.value': safeStringify(args),\n },\n },\n otelContext.active(),\n async (span) => {\n try {\n const result = await fn(args);\n span.setAttribute('output.value', safeStringify(result));\n span.setStatus({ code: SpanStatusCode.OK });\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n } finally {\n span.end();\n }\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// Proxy wrapping\n// ---------------------------------------------------------------------------\n\nfunction wrapNamespace(target: any, path: string[]): any {\n return new Proxy(target, {\n get(obj, prop, receiver) {\n const value = Reflect.get(obj, prop, receiver);\n if (typeof prop === 'symbol' || String(prop).startsWith('_')) return value;\n\n const currentPath = [...path, String(prop)];\n const pathStr = currentPath.join('.');\n\n if (pathStr === 'chat.completions.create' && typeof value === 'function') {\n return tracedChatCompletionsCreate(value.bind(obj));\n }\n if (pathStr === 'responses.create' && typeof value === 'function') {\n return tracedResponsesCreate(value.bind(obj));\n }\n\n if (value && typeof value === 'object' && !Array.isArray(value) && isNamespace(currentPath)) {\n return wrapNamespace(value, currentPath);\n }\n\n return value;\n },\n });\n}\n\nfunction isNamespace(path: string[]): boolean {\n if (path.length > 3) return false;\n const key = path[path.length - 1];\n return ['chat', 'completions', 'responses', 'beta'].includes(key);\n}\n\n// ---------------------------------------------------------------------------\n// chat.completions.create — non-streaming + streaming\n// ---------------------------------------------------------------------------\n\nfunction tracedChatCompletionsCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = trace.getTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n const messages: any[] = opts?.messages ?? [];\n const isStream = opts?.stream === true;\n\n const span = tracer.startSpan('openai.chat.completions.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'openai',\n 'neatlogs.llm.system': 'openai',\n 'neatlogs.llm.model_name': model,\n 'neatlogs.llm.is_streaming': isStream,\n },\n }, otelContext.active());\n\n // Input messages\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n span.setAttribute(`neatlogs.llm.input_messages.${i}.role`, msg.role ?? '');\n if (typeof msg.content === 'string') {\n span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, msg.content);\n } else if (msg.content) {\n span.setAttribute(`neatlogs.llm.input_messages.${i}.content`, safeStringify(msg.content));\n }\n if (msg.tool_call_id) {\n span.setAttribute(`neatlogs.llm.input_messages.${i}.tool_call_id`, msg.tool_call_id);\n }\n }\n\n // Tools\n if (opts?.tools) {\n for (let i = 0; i < opts.tools.length; i++) {\n const fn = opts.tools[i]?.function ?? {};\n span.setAttribute(`neatlogs.llm.tools.${i}.name`, fn.name ?? '');\n if (fn.description) span.setAttribute(`neatlogs.llm.tools.${i}.description`, fn.description);\n if (fn.parameters) span.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify(fn.parameters));\n }\n }\n\n // Invocation parameters\n setInvocationParams(span, opts);\n\n // Force stream_options.include_usage for streaming\n if (isStream) {\n opts = { ...opts };\n const streamOpts = opts.stream_options ?? {};\n if (!streamOpts.include_usage) {\n opts.stream_options = { ...streamOpts, include_usage: true };\n }\n }\n\n const ctx = trace.setSpan(otelContext.active(), span);\n const promise = otelContext.with(ctx, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n if (isStream) {\n return wrapAsyncIterableStream(response, span);\n }\n finalizeChatResponse(span, response);\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// responses.create\n// ---------------------------------------------------------------------------\n\nfunction tracedResponsesCreate(original: (...args: any[]) => any) {\n return function (opts: any, ...rest: any[]): any {\n const tracer = trace.getTracer(TRACER_NAME);\n const model = opts?.model ?? '';\n\n const span = tracer.startSpan('openai.responses.create', {\n attributes: {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': 'openai',\n 'neatlogs.llm.system': 'openai',\n 'neatlogs.llm.model_name': model,\n 'input.value': safeStringify(opts?.input ?? ''),\n },\n }, otelContext.active());\n\n const ctx = trace.setSpan(otelContext.active(), span);\n const promise = otelContext.with(ctx, () => original(opts, ...rest));\n\n return promise.then(\n (response: any) => {\n if (response?.output_text) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', response.output_text);\n }\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n if (response?.usage) {\n if (response.usage.input_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', response.usage.input_tokens);\n if (response.usage.output_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', response.usage.output_tokens);\n }\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return response;\n },\n (err: any) => {\n recordError(span, err);\n throw err;\n },\n );\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming support\n// ---------------------------------------------------------------------------\n\nfunction wrapAsyncIterableStream(stream: any, span: Span): any {\n const chunks: any[] = [];\n const originalAsyncIterator = stream[Symbol.asyncIterator]?.bind(stream);\n\n if (!originalAsyncIterator) {\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n return stream;\n }\n\n const wrapped = Object.create(Object.getPrototypeOf(stream));\n Object.assign(wrapped, stream);\n\n wrapped[Symbol.asyncIterator] = function () {\n const iterator = originalAsyncIterator();\n return {\n async next(): Promise<IteratorResult<any>> {\n try {\n const result = await iterator.next();\n if (result.done) {\n finalizeStreamChunks(span, chunks);\n return result;\n }\n chunks.push(result.value);\n return result;\n } catch (err) {\n recordError(span, err);\n throw err;\n }\n },\n async return(value?: any): Promise<IteratorResult<any>> {\n finalizeStreamChunks(span, chunks);\n return iterator.return?.(value) ?? { done: true, value: undefined };\n },\n async throw(err?: any): Promise<IteratorResult<any>> {\n recordError(span, err);\n return iterator.throw?.(err) ?? { done: true, value: undefined };\n },\n };\n };\n\n return wrapped;\n}\n\nfunction finalizeStreamChunks(span: Span, chunks: any[]): void {\n const textParts: string[] = [];\n const toolCallsAcc: Record<number, { id: string; name: string; arguments: string }> = {};\n let finishReason = '';\n let model = '';\n let usage: any = null;\n\n for (const chunk of chunks) {\n if (!chunk?.choices?.length) {\n if (chunk?.usage) usage = chunk.usage;\n continue;\n }\n const choice = chunk.choices[0];\n const delta = choice?.delta;\n if (delta?.content) textParts.push(delta.content);\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCallsAcc[idx]) toolCallsAcc[idx] = { id: '', name: '', arguments: '' };\n if (tc.id) toolCallsAcc[idx].id = tc.id;\n if (tc.function?.name) toolCallsAcc[idx].name = tc.function.name;\n if (tc.function?.arguments) toolCallsAcc[idx].arguments += tc.function.arguments;\n }\n }\n if (choice?.finish_reason) finishReason = choice.finish_reason;\n if (chunk?.model) model = chunk.model;\n }\n\n const fullText = textParts.join('');\n if (fullText) {\n span.setAttribute('neatlogs.llm.output_messages.0.role', 'assistant');\n span.setAttribute('neatlogs.llm.output_messages.0.content', fullText);\n }\n\n let j = 0;\n for (const tc of Object.values(toolCallsAcc)) {\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.name);\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, tc.arguments);\n j++;\n }\n\n if (model) span.setAttribute('neatlogs.llm.model_name', model);\n if (finishReason) span.setAttribute('neatlogs.llm.finish_reason', finishReason);\n\n if (usage) {\n if (usage.prompt_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.prompt_tokens);\n if (usage.completion_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.completion_tokens);\n if (usage.total_tokens != null) span.setAttribute('neatlogs.llm.token_count.total', usage.total_tokens);\n if (usage.prompt_tokens_details?.cached_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.prompt_tokens_details.cached_tokens);\n }\n if (usage.completion_tokens_details?.reasoning_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.reasoning', usage.completion_tokens_details.reasoning_tokens);\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Non-streaming response finalization\n// ---------------------------------------------------------------------------\n\nfunction finalizeChatResponse(span: Span, response: any): void {\n const choices = response?.choices ?? [];\n for (let i = 0; i < choices.length; i++) {\n const message = choices[i]?.message;\n if (!message) continue;\n\n span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, 'assistant');\n if (message.content) {\n span.setAttribute(`neatlogs.llm.output_messages.${i}.content`, message.content);\n }\n if (message.tool_calls) {\n for (let j = 0; j < message.tool_calls.length; j++) {\n const tc = message.tool_calls[j];\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.function?.name ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, tc.function?.arguments ?? '');\n }\n }\n if (choices[i].finish_reason) {\n span.setAttribute('neatlogs.llm.finish_reason', choices[i].finish_reason);\n }\n }\n\n const usage = response?.usage;\n if (usage) {\n if (usage.prompt_tokens != null) span.setAttribute('neatlogs.llm.token_count.prompt', usage.prompt_tokens);\n if (usage.completion_tokens != null) span.setAttribute('neatlogs.llm.token_count.completion', usage.completion_tokens);\n if (usage.total_tokens != null) span.setAttribute('neatlogs.llm.token_count.total', usage.total_tokens);\n if (usage.prompt_tokens_details?.cached_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.cache_read', usage.prompt_tokens_details.cached_tokens);\n }\n if (usage.completion_tokens_details?.reasoning_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.reasoning', usage.completion_tokens_details.reasoning_tokens);\n }\n }\n\n if (response?.model) span.setAttribute('neatlogs.llm.model_name', response.model);\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction setInvocationParams(span: Span, opts: any): void {\n if (opts?.temperature != null) span.setAttribute('neatlogs.llm.temperature', opts.temperature);\n if (opts?.top_p != null) span.setAttribute('neatlogs.llm.top_p', opts.top_p);\n if (opts?.max_tokens != null) span.setAttribute('neatlogs.llm.max_tokens', opts.max_tokens);\n if (opts?.frequency_penalty != null) span.setAttribute('neatlogs.llm.frequency_penalty', opts.frequency_penalty);\n if (opts?.presence_penalty != null) span.setAttribute('neatlogs.llm.presence_penalty', opts.presence_penalty);\n if (opts?.stop) span.setAttribute('neatlogs.llm.stop_sequences', safeStringify(opts.stop));\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return typeof value === 'string' ? value : JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\nfunction recordError(span: Span, err: unknown): void {\n if (err instanceof Error) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });\n span.recordException(err);\n } else {\n span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });\n }\n span.end();\n}\n"],"mappings":";AAeA,SAAS,OAAO,WAAW,aAAa,sBAAiC;AAEzE,IAAM,cAAc;AAMb,SAAS,WAA6B,QAAc;AACzD,SAAO,cAAc,QAAQ,CAAC,CAAC;AACjC;AAUO,SAAS,UACd,MACA,IACmC;AACnC,SAAO,eAAe,WAAW,MAA+B;AAC9D,UAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,WAAO,OAAO;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ;AAAA,QACE,YAAY;AAAA,UACV,sBAAsB;AAAA,UACtB,sBAAsB;AAAA,UACtB,eAAe,cAAc,IAAI;AAAA,QACnC;AAAA,MACF;AAAA,MACA,YAAY,OAAO;AAAA,MACnB,OAAO,SAAS;AACd,YAAI;AACF,gBAAM,SAAS,MAAM,GAAG,IAAI;AAC5B,eAAK,aAAa,gBAAgB,cAAc,MAAM,CAAC;AACvD,eAAK,UAAU,EAAE,MAAM,eAAe,GAAG,CAAC;AAC1C,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,sBAAY,MAAM,GAAG;AACrB,gBAAM;AAAA,QACR,UAAE;AACA,eAAK,IAAI;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,cAAc,QAAa,MAAqB;AACvD,SAAO,IAAI,MAAM,QAAQ;AAAA,IACvB,IAAI,KAAK,MAAM,UAAU;AACvB,YAAM,QAAQ,QAAQ,IAAI,KAAK,MAAM,QAAQ;AAC7C,UAAI,OAAO,SAAS,YAAY,OAAO,IAAI,EAAE,WAAW,GAAG,EAAG,QAAO;AAErE,YAAM,cAAc,CAAC,GAAG,MAAM,OAAO,IAAI,CAAC;AAC1C,YAAM,UAAU,YAAY,KAAK,GAAG;AAEpC,UAAI,YAAY,6BAA6B,OAAO,UAAU,YAAY;AACxE,eAAO,4BAA4B,MAAM,KAAK,GAAG,CAAC;AAAA,MACpD;AACA,UAAI,YAAY,sBAAsB,OAAO,UAAU,YAAY;AACjE,eAAO,sBAAsB,MAAM,KAAK,GAAG,CAAC;AAAA,MAC9C;AAEA,UAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,KAAK,YAAY,WAAW,GAAG;AAC3F,eAAO,cAAc,OAAO,WAAW;AAAA,MACzC;AAEA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEA,SAAS,YAAY,MAAyB;AAC5C,MAAI,KAAK,SAAS,EAAG,QAAO;AAC5B,QAAM,MAAM,KAAK,KAAK,SAAS,CAAC;AAChC,SAAO,CAAC,QAAQ,eAAe,aAAa,MAAM,EAAE,SAAS,GAAG;AAClE;AAMA,SAAS,4BAA4B,UAAmC;AACtE,SAAO,SAAU,SAAc,MAAkB;AAC/C,UAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,WAAkB,MAAM,YAAY,CAAC;AAC3C,UAAM,WAAW,MAAM,WAAW;AAElC,UAAM,OAAO,OAAO,UAAU,kCAAkC;AAAA,MAC9D,YAAY;AAAA,QACV,sBAAsB;AAAA,QACtB,yBAAyB;AAAA,QACzB,uBAAuB;AAAA,QACvB,2BAA2B;AAAA,QAC3B,6BAA6B;AAAA,MAC/B;AAAA,IACF,GAAG,YAAY,OAAO,CAAC;AAGvB,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,MAAM,SAAS,CAAC;AACtB,WAAK,aAAa,+BAA+B,CAAC,SAAS,IAAI,QAAQ,EAAE;AACzE,UAAI,OAAO,IAAI,YAAY,UAAU;AACnC,aAAK,aAAa,+BAA+B,CAAC,YAAY,IAAI,OAAO;AAAA,MAC3E,WAAW,IAAI,SAAS;AACtB,aAAK,aAAa,+BAA+B,CAAC,YAAY,cAAc,IAAI,OAAO,CAAC;AAAA,MAC1F;AACA,UAAI,IAAI,cAAc;AACpB,aAAK,aAAa,+BAA+B,CAAC,iBAAiB,IAAI,YAAY;AAAA,MACrF;AAAA,IACF;AAGA,QAAI,MAAM,OAAO;AACf,eAAS,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;AAC1C,cAAM,KAAK,KAAK,MAAM,CAAC,GAAG,YAAY,CAAC;AACvC,aAAK,aAAa,sBAAsB,CAAC,SAAS,GAAG,QAAQ,EAAE;AAC/D,YAAI,GAAG,YAAa,MAAK,aAAa,sBAAsB,CAAC,gBAAgB,GAAG,WAAW;AAC3F,YAAI,GAAG,WAAY,MAAK,aAAa,sBAAsB,CAAC,iBAAiB,cAAc,GAAG,UAAU,CAAC;AAAA,MAC3G;AAAA,IACF;AAGA,wBAAoB,MAAM,IAAI;AAG9B,QAAI,UAAU;AACZ,aAAO,EAAE,GAAG,KAAK;AACjB,YAAM,aAAa,KAAK,kBAAkB,CAAC;AAC3C,UAAI,CAAC,WAAW,eAAe;AAC7B,aAAK,iBAAiB,EAAE,GAAG,YAAY,eAAe,KAAK;AAAA,MAC7D;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,QAAQ,YAAY,OAAO,GAAG,IAAI;AACpD,UAAM,UAAU,YAAY,KAAK,KAAK,MAAM,SAAS,MAAM,GAAG,IAAI,CAAC;AAEnE,WAAO,QAAQ;AAAA,MACb,CAAC,aAAkB;AACjB,YAAI,UAAU;AACZ,iBAAO,wBAAwB,UAAU,IAAI;AAAA,QAC/C;AACA,6BAAqB,MAAM,QAAQ;AACnC,eAAO;AAAA,MACT;AAAA,MACA,CAAC,QAAa;AACZ,oBAAY,MAAM,GAAG;AACrB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,sBAAsB,UAAmC;AAChE,SAAO,SAAU,SAAc,MAAkB;AAC/C,UAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,UAAM,QAAQ,MAAM,SAAS;AAE7B,UAAM,OAAO,OAAO,UAAU,2BAA2B;AAAA,MACvD,YAAY;AAAA,QACV,sBAAsB;AAAA,QACtB,yBAAyB;AAAA,QACzB,uBAAuB;AAAA,QACvB,2BAA2B;AAAA,QAC3B,eAAe,cAAc,MAAM,SAAS,EAAE;AAAA,MAChD;AAAA,IACF,GAAG,YAAY,OAAO,CAAC;AAEvB,UAAM,MAAM,MAAM,QAAQ,YAAY,OAAO,GAAG,IAAI;AACpD,UAAM,UAAU,YAAY,KAAK,KAAK,MAAM,SAAS,MAAM,GAAG,IAAI,CAAC;AAEnE,WAAO,QAAQ;AAAA,MACb,CAAC,aAAkB;AACjB,YAAI,UAAU,aAAa;AACzB,eAAK,aAAa,uCAAuC,WAAW;AACpE,eAAK,aAAa,0CAA0C,SAAS,WAAW;AAAA,QAClF;AACA,YAAI,UAAU,MAAO,MAAK,aAAa,2BAA2B,SAAS,KAAK;AAChF,YAAI,UAAU,OAAO;AACnB,cAAI,SAAS,MAAM,gBAAgB,KAAM,MAAK,aAAa,mCAAmC,SAAS,MAAM,YAAY;AACzH,cAAI,SAAS,MAAM,iBAAiB,KAAM,MAAK,aAAa,uCAAuC,SAAS,MAAM,aAAa;AAAA,QACjI;AACA,aAAK,UAAU,EAAE,MAAM,eAAe,GAAG,CAAC;AAC1C,aAAK,IAAI;AACT,eAAO;AAAA,MACT;AAAA,MACA,CAAC,QAAa;AACZ,oBAAY,MAAM,GAAG;AACrB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,wBAAwB,QAAa,MAAiB;AAC7D,QAAM,SAAgB,CAAC;AACvB,QAAM,wBAAwB,OAAO,OAAO,aAAa,GAAG,KAAK,MAAM;AAEvE,MAAI,CAAC,uBAAuB;AAC1B,SAAK,UAAU,EAAE,MAAM,eAAe,GAAG,CAAC;AAC1C,SAAK,IAAI;AACT,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,OAAO,OAAO,OAAO,eAAe,MAAM,CAAC;AAC3D,SAAO,OAAO,SAAS,MAAM;AAE7B,UAAQ,OAAO,aAAa,IAAI,WAAY;AAC1C,UAAM,WAAW,sBAAsB;AACvC,WAAO;AAAA,MACL,MAAM,OAAqC;AACzC,YAAI;AACF,gBAAM,SAAS,MAAM,SAAS,KAAK;AACnC,cAAI,OAAO,MAAM;AACf,iCAAqB,MAAM,MAAM;AACjC,mBAAO;AAAA,UACT;AACA,iBAAO,KAAK,OAAO,KAAK;AACxB,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,sBAAY,MAAM,GAAG;AACrB,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA,MAAM,OAAO,OAA2C;AACtD,6BAAqB,MAAM,MAAM;AACjC,eAAO,SAAS,SAAS,KAAK,KAAK,EAAE,MAAM,MAAM,OAAO,OAAU;AAAA,MACpE;AAAA,MACA,MAAM,MAAM,KAAyC;AACnD,oBAAY,MAAM,GAAG;AACrB,eAAO,SAAS,QAAQ,GAAG,KAAK,EAAE,MAAM,MAAM,OAAO,OAAU;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAY,QAAqB;AAC7D,QAAM,YAAsB,CAAC;AAC7B,QAAM,eAAgF,CAAC;AACvF,MAAI,eAAe;AACnB,MAAI,QAAQ;AACZ,MAAI,QAAa;AAEjB,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,OAAO,SAAS,QAAQ;AAC3B,UAAI,OAAO,MAAO,SAAQ,MAAM;AAChC;AAAA,IACF;AACA,UAAM,SAAS,MAAM,QAAQ,CAAC;AAC9B,UAAM,QAAQ,QAAQ;AACtB,QAAI,OAAO,QAAS,WAAU,KAAK,MAAM,OAAO;AAChD,QAAI,OAAO,YAAY;AACrB,iBAAW,MAAM,MAAM,YAAY;AACjC,cAAM,MAAM,GAAG,SAAS;AACxB,YAAI,CAAC,aAAa,GAAG,EAAG,cAAa,GAAG,IAAI,EAAE,IAAI,IAAI,MAAM,IAAI,WAAW,GAAG;AAC9E,YAAI,GAAG,GAAI,cAAa,GAAG,EAAE,KAAK,GAAG;AACrC,YAAI,GAAG,UAAU,KAAM,cAAa,GAAG,EAAE,OAAO,GAAG,SAAS;AAC5D,YAAI,GAAG,UAAU,UAAW,cAAa,GAAG,EAAE,aAAa,GAAG,SAAS;AAAA,MACzE;AAAA,IACF;AACA,QAAI,QAAQ,cAAe,gBAAe,OAAO;AACjD,QAAI,OAAO,MAAO,SAAQ,MAAM;AAAA,EAClC;AAEA,QAAM,WAAW,UAAU,KAAK,EAAE;AAClC,MAAI,UAAU;AACZ,SAAK,aAAa,uCAAuC,WAAW;AACpE,SAAK,aAAa,0CAA0C,QAAQ;AAAA,EACtE;AAEA,MAAI,IAAI;AACR,aAAW,MAAM,OAAO,OAAO,YAAY,GAAG;AAC5C,SAAK,aAAa,2BAA2B,CAAC,OAAO,GAAG,EAAE;AAC1D,SAAK,aAAa,2BAA2B,CAAC,SAAS,GAAG,IAAI;AAC9D,SAAK,aAAa,2BAA2B,CAAC,cAAc,GAAG,SAAS;AACxE;AAAA,EACF;AAEA,MAAI,MAAO,MAAK,aAAa,2BAA2B,KAAK;AAC7D,MAAI,aAAc,MAAK,aAAa,8BAA8B,YAAY;AAE9E,MAAI,OAAO;AACT,QAAI,MAAM,iBAAiB,KAAM,MAAK,aAAa,mCAAmC,MAAM,aAAa;AACzG,QAAI,MAAM,qBAAqB,KAAM,MAAK,aAAa,uCAAuC,MAAM,iBAAiB;AACrH,QAAI,MAAM,gBAAgB,KAAM,MAAK,aAAa,kCAAkC,MAAM,YAAY;AACtG,QAAI,MAAM,uBAAuB,iBAAiB,MAAM;AACtD,WAAK,aAAa,uCAAuC,MAAM,sBAAsB,aAAa;AAAA,IACpG;AACA,QAAI,MAAM,2BAA2B,oBAAoB,MAAM;AAC7D,WAAK,aAAa,sCAAsC,MAAM,0BAA0B,gBAAgB;AAAA,IAC1G;AAAA,EACF;AAEA,OAAK,UAAU,EAAE,MAAM,eAAe,GAAG,CAAC;AAC1C,OAAK,IAAI;AACX;AAMA,SAAS,qBAAqB,MAAY,UAAqB;AAC7D,QAAM,UAAU,UAAU,WAAW,CAAC;AACtC,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,UAAU,QAAQ,CAAC,GAAG;AAC5B,QAAI,CAAC,QAAS;AAEd,SAAK,aAAa,gCAAgC,CAAC,SAAS,WAAW;AACvE,QAAI,QAAQ,SAAS;AACnB,WAAK,aAAa,gCAAgC,CAAC,YAAY,QAAQ,OAAO;AAAA,IAChF;AACA,QAAI,QAAQ,YAAY;AACtB,eAAS,IAAI,GAAG,IAAI,QAAQ,WAAW,QAAQ,KAAK;AAClD,cAAM,KAAK,QAAQ,WAAW,CAAC;AAC/B,aAAK,aAAa,2BAA2B,CAAC,OAAO,GAAG,MAAM,EAAE;AAChE,aAAK,aAAa,2BAA2B,CAAC,SAAS,GAAG,UAAU,QAAQ,EAAE;AAC9E,aAAK,aAAa,2BAA2B,CAAC,cAAc,GAAG,UAAU,aAAa,EAAE;AAAA,MAC1F;AAAA,IACF;AACA,QAAI,QAAQ,CAAC,EAAE,eAAe;AAC5B,WAAK,aAAa,8BAA8B,QAAQ,CAAC,EAAE,aAAa;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,QAAQ,UAAU;AACxB,MAAI,OAAO;AACT,QAAI,MAAM,iBAAiB,KAAM,MAAK,aAAa,mCAAmC,MAAM,aAAa;AACzG,QAAI,MAAM,qBAAqB,KAAM,MAAK,aAAa,uCAAuC,MAAM,iBAAiB;AACrH,QAAI,MAAM,gBAAgB,KAAM,MAAK,aAAa,kCAAkC,MAAM,YAAY;AACtG,QAAI,MAAM,uBAAuB,iBAAiB,MAAM;AACtD,WAAK,aAAa,uCAAuC,MAAM,sBAAsB,aAAa;AAAA,IACpG;AACA,QAAI,MAAM,2BAA2B,oBAAoB,MAAM;AAC7D,WAAK,aAAa,sCAAsC,MAAM,0BAA0B,gBAAgB;AAAA,IAC1G;AAAA,EACF;AAEA,MAAI,UAAU,MAAO,MAAK,aAAa,2BAA2B,SAAS,KAAK;AAEhF,OAAK,UAAU,EAAE,MAAM,eAAe,GAAG,CAAC;AAC1C,OAAK,IAAI;AACX;AAMA,SAAS,oBAAoB,MAAY,MAAiB;AACxD,MAAI,MAAM,eAAe,KAAM,MAAK,aAAa,4BAA4B,KAAK,WAAW;AAC7F,MAAI,MAAM,SAAS,KAAM,MAAK,aAAa,sBAAsB,KAAK,KAAK;AAC3E,MAAI,MAAM,cAAc,KAAM,MAAK,aAAa,2BAA2B,KAAK,UAAU;AAC1F,MAAI,MAAM,qBAAqB,KAAM,MAAK,aAAa,kCAAkC,KAAK,iBAAiB;AAC/G,MAAI,MAAM,oBAAoB,KAAM,MAAK,aAAa,iCAAiC,KAAK,gBAAgB;AAC5G,MAAI,MAAM,KAAM,MAAK,aAAa,+BAA+B,cAAc,KAAK,IAAI,CAAC;AAC3F;AAEA,SAAS,cAAc,OAAwB;AAC7C,MAAI;AACF,WAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,MAAY,KAAoB;AACnD,MAAI,eAAe,OAAO;AACxB,SAAK,UAAU,EAAE,MAAM,eAAe,OAAO,SAAS,IAAI,QAAQ,CAAC;AACnE,SAAK,gBAAgB,GAAG;AAAA,EAC1B,OAAO;AACL,SAAK,UAAU,EAAE,MAAM,eAAe,OAAO,SAAS,OAAO,GAAG,EAAE,CAAC;AAAA,EACrE;AACA,OAAK,IAAI;AACX;","names":[]}
|
|
@@ -0,0 +1,237 @@
|
|
|
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/pi-agent.ts
|
|
21
|
+
var pi_agent_exports = {};
|
|
22
|
+
__export(pi_agent_exports, {
|
|
23
|
+
piAgentHooks: () => piAgentHooks
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(pi_agent_exports);
|
|
26
|
+
var import_api = require("@opentelemetry/api");
|
|
27
|
+
var TRACER_NAME = "neatlogs.pi-agent";
|
|
28
|
+
var PATCH_FLAG = "_neatlogs_patched";
|
|
29
|
+
function piAgentHooks(agent) {
|
|
30
|
+
if (!agent || agent[PATCH_FLAG]) return agent;
|
|
31
|
+
const a = agent;
|
|
32
|
+
if (typeof a.subscribe !== "function") return agent;
|
|
33
|
+
const state = { toolSpans: /* @__PURE__ */ new Map(), inputMessages: [] };
|
|
34
|
+
const tracer = import_api.trace.getTracer(TRACER_NAME);
|
|
35
|
+
a.subscribe((event) => {
|
|
36
|
+
try {
|
|
37
|
+
handleEvent(tracer, state, event);
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
markPatched(a);
|
|
42
|
+
return agent;
|
|
43
|
+
}
|
|
44
|
+
function handleEvent(tracer, state, event) {
|
|
45
|
+
switch (event.type) {
|
|
46
|
+
case "agent_start": {
|
|
47
|
+
const span = tracer.startSpan(
|
|
48
|
+
"pi_agent.run",
|
|
49
|
+
{ attributes: { "neatlogs.span.kind": "AGENT" } },
|
|
50
|
+
import_api.context.active()
|
|
51
|
+
);
|
|
52
|
+
state.agentSpan = span;
|
|
53
|
+
state.agentCtx = import_api.trace.setSpan(import_api.context.active(), span);
|
|
54
|
+
state.inputMessages = [];
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "message_end": {
|
|
58
|
+
const msg = event.message;
|
|
59
|
+
if (!msg) return;
|
|
60
|
+
if (msg.role === "assistant") {
|
|
61
|
+
emitLlmSpan(tracer, state, msg);
|
|
62
|
+
const { text } = splitAssistantContent(msg.content);
|
|
63
|
+
if (text) state.inputMessages.push({ role: "assistant", content: text });
|
|
64
|
+
} else {
|
|
65
|
+
const role = msg.role === "toolResult" ? "tool" : String(msg.role || "user");
|
|
66
|
+
const content = messageText(msg);
|
|
67
|
+
if (content) state.inputMessages.push({ role, content });
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case "tool_execution_start": {
|
|
72
|
+
const parent = state.agentCtx ?? import_api.context.active();
|
|
73
|
+
const span = tracer.startSpan(
|
|
74
|
+
`pi_agent.tool.${event.toolName ?? "tool"}`,
|
|
75
|
+
{
|
|
76
|
+
attributes: {
|
|
77
|
+
"neatlogs.span.kind": "TOOL",
|
|
78
|
+
...event.toolName ? { "neatlogs.tool.name": String(event.toolName) } : {},
|
|
79
|
+
...event.args !== void 0 ? { "input.value": safeStringify(event.args).slice(0, 1e4) } : {}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
parent
|
|
83
|
+
);
|
|
84
|
+
if (event.toolCallId) state.toolSpans.set(event.toolCallId, span);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case "tool_execution_end": {
|
|
88
|
+
const span = event.toolCallId ? state.toolSpans.get(event.toolCallId) : void 0;
|
|
89
|
+
if (!span) return;
|
|
90
|
+
if (event.result !== void 0) {
|
|
91
|
+
span.setAttribute("output.value", safeStringify(event.result).slice(0, 1e4));
|
|
92
|
+
}
|
|
93
|
+
if (event.isError) {
|
|
94
|
+
span.setStatus({ code: import_api.SpanStatusCode.ERROR });
|
|
95
|
+
span.setAttribute("neatlogs.tool.is_error", true);
|
|
96
|
+
} else {
|
|
97
|
+
span.setStatus({ code: import_api.SpanStatusCode.OK });
|
|
98
|
+
}
|
|
99
|
+
span.end();
|
|
100
|
+
if (event.toolCallId) state.toolSpans.delete(event.toolCallId);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case "agent_end": {
|
|
104
|
+
for (const ts of state.toolSpans.values()) {
|
|
105
|
+
try {
|
|
106
|
+
ts.end();
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
state.toolSpans.clear();
|
|
111
|
+
if (state.agentSpan) {
|
|
112
|
+
const firstUser = state.inputMessages.find((m) => m.role === "user");
|
|
113
|
+
if (firstUser) state.agentSpan.setAttribute("input.value", firstUser.content.slice(0, 1e4));
|
|
114
|
+
const finalText = lastAssistantText(event.messages);
|
|
115
|
+
if (finalText) state.agentSpan.setAttribute("output.value", finalText.slice(0, 1e4));
|
|
116
|
+
state.agentSpan.setStatus({ code: import_api.SpanStatusCode.OK });
|
|
117
|
+
state.agentSpan.end();
|
|
118
|
+
state.agentSpan = void 0;
|
|
119
|
+
state.agentCtx = void 0;
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function emitLlmSpan(tracer, state, msg) {
|
|
128
|
+
const attrs = { "neatlogs.span.kind": "LLM" };
|
|
129
|
+
if (msg.model) attrs["neatlogs.llm.model_name"] = String(msg.model);
|
|
130
|
+
if (msg.provider) attrs["neatlogs.llm.provider"] = String(msg.provider);
|
|
131
|
+
if (msg.stopReason) attrs["neatlogs.llm.stop_reason"] = String(msg.stopReason);
|
|
132
|
+
const inMsgs = state.inputMessages;
|
|
133
|
+
if (inMsgs.length) {
|
|
134
|
+
inMsgs.forEach((m, i) => {
|
|
135
|
+
attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;
|
|
136
|
+
attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content.slice(0, 1e4);
|
|
137
|
+
});
|
|
138
|
+
attrs["neatlogs.llm.input"] = safeStringify({ messages: inMsgs }).slice(0, 2e4);
|
|
139
|
+
attrs["input.value"] = safeStringify({ messages: inMsgs }).slice(0, 1e4);
|
|
140
|
+
}
|
|
141
|
+
const { text, toolCalls } = splitAssistantContent(msg.content);
|
|
142
|
+
const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.arguments)})`).join("\n");
|
|
143
|
+
if (outText || toolCalls.length) {
|
|
144
|
+
attrs["neatlogs.llm.output_messages.0.role"] = "assistant";
|
|
145
|
+
attrs["neatlogs.llm.output_messages.0.content"] = (outText || "").slice(0, 1e4);
|
|
146
|
+
const outBlob = { role: "assistant", content: outText || "" };
|
|
147
|
+
if (toolCalls.length) {
|
|
148
|
+
outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));
|
|
149
|
+
toolCalls.forEach((tc, j) => {
|
|
150
|
+
if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;
|
|
151
|
+
if (tc.arguments !== void 0)
|
|
152
|
+
attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.arguments);
|
|
153
|
+
if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
attrs["neatlogs.llm.output"] = safeStringify(outBlob).slice(0, 2e4);
|
|
157
|
+
attrs["output.value"] = (outText || "").slice(0, 1e4);
|
|
158
|
+
}
|
|
159
|
+
const usage = msg.usage;
|
|
160
|
+
if (usage) {
|
|
161
|
+
if (usage.input != null) attrs["neatlogs.llm.token_count.prompt"] = usage.input;
|
|
162
|
+
if (usage.output != null) attrs["neatlogs.llm.token_count.completion"] = usage.output;
|
|
163
|
+
const total = usage.totalTokens ?? (usage.input ?? 0) + (usage.output ?? 0);
|
|
164
|
+
if (total) attrs["neatlogs.llm.token_count.total"] = total;
|
|
165
|
+
if (usage.cacheRead) attrs["neatlogs.llm.token_count.cache_read"] = usage.cacheRead;
|
|
166
|
+
if (usage.cacheWrite) attrs["neatlogs.llm.token_count.cache_write"] = usage.cacheWrite;
|
|
167
|
+
}
|
|
168
|
+
const parent = state.agentCtx ?? import_api.context.active();
|
|
169
|
+
const span = tracer.startSpan(
|
|
170
|
+
`pi_agent.llm.${msg.model || "model"}`,
|
|
171
|
+
{ attributes: attrs },
|
|
172
|
+
parent
|
|
173
|
+
);
|
|
174
|
+
span.setStatus({ code: import_api.SpanStatusCode.OK });
|
|
175
|
+
span.end();
|
|
176
|
+
}
|
|
177
|
+
function splitAssistantContent(content) {
|
|
178
|
+
const texts = [];
|
|
179
|
+
const toolCalls = [];
|
|
180
|
+
if (Array.isArray(content)) {
|
|
181
|
+
for (const block of content) {
|
|
182
|
+
if (!block || typeof block !== "object") continue;
|
|
183
|
+
if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
|
|
184
|
+
else if (block.type === "toolCall")
|
|
185
|
+
toolCalls.push(block);
|
|
186
|
+
}
|
|
187
|
+
} else if (typeof content === "string") {
|
|
188
|
+
texts.push(content);
|
|
189
|
+
}
|
|
190
|
+
return { text: texts.join(""), toolCalls };
|
|
191
|
+
}
|
|
192
|
+
function messageText(msg) {
|
|
193
|
+
if (!msg) return "";
|
|
194
|
+
const c = msg.content;
|
|
195
|
+
if (typeof c === "string") return c;
|
|
196
|
+
if (!Array.isArray(c)) return "";
|
|
197
|
+
const parts = [];
|
|
198
|
+
for (const block of c) {
|
|
199
|
+
if (typeof block === "string") parts.push(block);
|
|
200
|
+
else if (block && typeof block === "object") {
|
|
201
|
+
if (typeof block.text === "string") parts.push(block.text);
|
|
202
|
+
else if (block.type === "toolCall") parts.push(`${block.name ?? "tool"}(${safeStringify(block.arguments)})`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return parts.join("");
|
|
206
|
+
}
|
|
207
|
+
function lastAssistantText(messages) {
|
|
208
|
+
if (!Array.isArray(messages)) return "";
|
|
209
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
210
|
+
const m = messages[i];
|
|
211
|
+
if (m && m.role === "assistant") {
|
|
212
|
+
const { text } = splitAssistantContent(m.content);
|
|
213
|
+
if (text) return text;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return "";
|
|
217
|
+
}
|
|
218
|
+
function markPatched(e) {
|
|
219
|
+
try {
|
|
220
|
+
Object.defineProperty(e, PATCH_FLAG, { value: true, enumerable: false, configurable: true });
|
|
221
|
+
} catch {
|
|
222
|
+
e[PATCH_FLAG] = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function safeStringify(value) {
|
|
226
|
+
if (typeof value === "string") return value;
|
|
227
|
+
try {
|
|
228
|
+
return JSON.stringify(value) ?? "";
|
|
229
|
+
} catch {
|
|
230
|
+
return "";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
234
|
+
0 && (module.exports = {
|
|
235
|
+
piAgentHooks
|
|
236
|
+
});
|
|
237
|
+
//# sourceMappingURL=pi-agent.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pi-agent.ts"],"sourcesContent":["/**\n * Neatlogs Pi Agent integration.\n *\n * Usage:\n * import { init } from 'neatlogs';\n * import { piAgentHooks } from 'neatlogs/pi-agent';\n * import { Agent } from '@mariozechner/pi-agent-core';\n *\n * await init({ apiKey, workflowName });\n * const agent = piAgentHooks(new Agent({ initialState: { systemPrompt, model } }));\n * await agent.prompt('Hello');\n *\n * Pi Agent's `Agent` exposes a first-class `subscribe(listener)` API and emits\n * AgentEvents for its run lifecycle (message_*, tool_execution_*, turn_*,\n * agent_*). It does NOT emit its own OpenTelemetry spans — so we LISTEN to those\n * events (no monkey-patching) and translate them into neatlogs OTel spans:\n *\n * AGENT agent run (agent_start → agent_end)\n * ↳ LLM assistant message (each assistant message_end)\n * ↳ TOOL tool call (tool_execution_start → tool_execution_end)\n *\n * The AGENT span is opened as the active span so the LLM/TOOL children nest under\n * it (and under any user @span / trace() block active when prompt() is called).\n */\n\nimport {\n trace,\n context as otelContext,\n SpanStatusCode,\n type Span,\n type Context,\n} from '@opentelemetry/api';\n\nconst TRACER_NAME = 'neatlogs.pi-agent';\nconst PATCH_FLAG = '_neatlogs_patched';\n\n// Minimal structural types for the Pi Agent event surface (we duck-type — no\n// hard dependency on the pi-agent-core package).\ninterface PiUsage {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n totalTokens?: number;\n}\ninterface PiToolCall {\n type: 'toolCall';\n id?: string;\n name?: string;\n arguments?: Record<string, unknown>;\n}\ninterface PiAssistantMessage {\n role: 'assistant';\n content?: Array<{ type: string; text?: string; name?: string; arguments?: unknown }>;\n model?: string;\n provider?: string;\n usage?: PiUsage;\n stopReason?: string;\n}\ninterface PiAgentEvent {\n type: string;\n message?: any;\n messages?: any[];\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n result?: unknown;\n isError?: boolean;\n}\n\ninterface PerAgentState {\n agentSpan?: Span;\n agentCtx?: Context;\n toolSpans: Map<string, Span>;\n /** Running conversation (system/user/tool turns) to use as LLM-span input.\n * Pi Agent's assistant message_end carries only the response, not the prompt. */\n inputMessages: Array<{ role: string; content: string }>;\n}\n\n/**\n * Subscribe neatlogs tracing to a Pi Agent instance. Returns the same agent\n * (marked so re-subscribing is a no-op). Idempotent per agent.\n */\nexport function piAgentHooks<T extends object>(agent: T): T {\n if (!agent || (agent as any)[PATCH_FLAG]) return agent;\n const a = agent as any;\n if (typeof a.subscribe !== 'function') return agent; // not a Pi Agent — leave alone\n\n const state: PerAgentState = { toolSpans: new Map(), inputMessages: [] };\n const tracer = trace.getTracer(TRACER_NAME);\n\n a.subscribe((event: PiAgentEvent) => {\n try {\n handleEvent(tracer, state, event);\n } catch {\n // never let tracing break the agent run\n }\n });\n\n markPatched(a);\n return agent;\n}\n\nfunction handleEvent(\n tracer: ReturnType<typeof trace.getTracer>,\n state: PerAgentState,\n event: PiAgentEvent,\n): void {\n switch (event.type) {\n case 'agent_start': {\n // Open the AGENT (run) span as the active span so children nest under it.\n const span = tracer.startSpan(\n 'pi_agent.run',\n { attributes: { 'neatlogs.span.kind': 'AGENT' } },\n otelContext.active(),\n );\n state.agentSpan = span;\n state.agentCtx = trace.setSpan(otelContext.active(), span);\n state.inputMessages = [];\n break;\n }\n\n case 'message_end': {\n const msg = event.message as any;\n if (!msg) return;\n if (msg.role === 'assistant') {\n // Assistant message = the LLM response. Emit an LLM span using the\n // accumulated conversation as input, then record the assistant turn too.\n emitLlmSpan(tracer, state, msg as PiAssistantMessage);\n const { text } = splitAssistantContent(msg.content);\n if (text) state.inputMessages.push({ role: 'assistant', content: text });\n } else {\n // user / toolResult turns — accumulate as input context for later LLM spans.\n const role = msg.role === 'toolResult' ? 'tool' : String(msg.role || 'user');\n const content = messageText(msg);\n if (content) state.inputMessages.push({ role, content });\n }\n break;\n }\n\n case 'tool_execution_start': {\n const parent = state.agentCtx ?? otelContext.active();\n const span = tracer.startSpan(\n `pi_agent.tool.${event.toolName ?? 'tool'}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n ...(event.toolName ? { 'neatlogs.tool.name': String(event.toolName) } : {}),\n ...(event.args !== undefined\n ? { 'input.value': safeStringify(event.args).slice(0, 10000) }\n : {}),\n },\n },\n parent,\n );\n if (event.toolCallId) state.toolSpans.set(event.toolCallId, span);\n break;\n }\n\n case 'tool_execution_end': {\n const span = event.toolCallId ? state.toolSpans.get(event.toolCallId) : undefined;\n if (!span) return;\n if (event.result !== undefined) {\n span.setAttribute('output.value', safeStringify(event.result).slice(0, 10000));\n }\n if (event.isError) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.setAttribute('neatlogs.tool.is_error', true);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n if (event.toolCallId) state.toolSpans.delete(event.toolCallId);\n break;\n }\n\n case 'agent_end': {\n // Close any tool spans that never received an end event, then the agent span.\n for (const ts of state.toolSpans.values()) {\n try {\n ts.end();\n } catch {\n /* ignore */\n }\n }\n state.toolSpans.clear();\n if (state.agentSpan) {\n // Agent input = the first user message of the run; output = final answer.\n const firstUser = state.inputMessages.find((m) => m.role === 'user');\n if (firstUser) state.agentSpan.setAttribute('input.value', firstUser.content.slice(0, 10000));\n const finalText = lastAssistantText(event.messages);\n if (finalText) state.agentSpan.setAttribute('output.value', finalText.slice(0, 10000));\n state.agentSpan.setStatus({ code: SpanStatusCode.OK });\n state.agentSpan.end();\n state.agentSpan = undefined;\n state.agentCtx = undefined;\n }\n break;\n }\n\n default:\n break;\n }\n}\n\nfunction emitLlmSpan(\n tracer: ReturnType<typeof trace.getTracer>,\n state: PerAgentState,\n msg: PiAssistantMessage,\n): void {\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'LLM' };\n if (msg.model) attrs['neatlogs.llm.model_name'] = String(msg.model);\n if (msg.provider) attrs['neatlogs.llm.provider'] = String(msg.provider);\n if (msg.stopReason) attrs['neatlogs.llm.stop_reason'] = String(msg.stopReason);\n\n // Input = the conversation accumulated up to this assistant turn (system +\n // user + prior assistant/tool messages). Pi Agent's message_end doesn't carry\n // the prompt, so we reconstruct it from the running inputMessages list.\n const inMsgs = state.inputMessages;\n if (inMsgs.length) {\n inMsgs.forEach((m, i) => {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;\n attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content.slice(0, 10000);\n });\n attrs['neatlogs.llm.input'] = safeStringify({ messages: inMsgs }).slice(0, 20000);\n attrs['input.value'] = safeStringify({ messages: inMsgs }).slice(0, 10000);\n }\n\n const { text, toolCalls } = splitAssistantContent(msg.content);\n // Output: text if present, else a readable tool-call summary so the span isn't blank.\n const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.arguments)})`).join('\\n');\n if (outText || toolCalls.length) {\n attrs['neatlogs.llm.output_messages.0.role'] = 'assistant';\n attrs['neatlogs.llm.output_messages.0.content'] = (outText || '').slice(0, 10000);\n const outBlob: Record<string, unknown> = { role: 'assistant', content: outText || '' };\n if (toolCalls.length) {\n outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));\n toolCalls.forEach((tc, j) => {\n if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;\n if (tc.arguments !== undefined)\n attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.arguments);\n if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);\n });\n }\n attrs['neatlogs.llm.output'] = safeStringify(outBlob).slice(0, 20000);\n attrs['output.value'] = (outText || '').slice(0, 10000);\n }\n\n const usage = msg.usage;\n if (usage) {\n if (usage.input != null) attrs['neatlogs.llm.token_count.prompt'] = usage.input;\n if (usage.output != null) attrs['neatlogs.llm.token_count.completion'] = usage.output;\n const total = usage.totalTokens ?? ((usage.input ?? 0) + (usage.output ?? 0));\n if (total) attrs['neatlogs.llm.token_count.total'] = total;\n if (usage.cacheRead) attrs['neatlogs.llm.token_count.cache_read'] = usage.cacheRead;\n if (usage.cacheWrite) attrs['neatlogs.llm.token_count.cache_write'] = usage.cacheWrite;\n }\n\n const parent = state.agentCtx ?? otelContext.active();\n const span = tracer.startSpan(\n `pi_agent.llm.${msg.model || 'model'}`,\n { attributes: attrs },\n parent,\n );\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Content helpers\n// ---------------------------------------------------------------------------\n\nfunction splitAssistantContent(\n content: PiAssistantMessage['content'],\n): { text: string; toolCalls: PiToolCall[] } {\n const texts: string[] = [];\n const toolCalls: PiToolCall[] = [];\n if (Array.isArray(content)) {\n for (const block of content) {\n if (!block || typeof block !== 'object') continue;\n if (block.type === 'text' && typeof block.text === 'string') texts.push(block.text);\n else if (block.type === 'toolCall')\n toolCalls.push(block as unknown as PiToolCall);\n // thinking blocks intentionally omitted from the main output text\n }\n } else if (typeof content === 'string') {\n texts.push(content);\n }\n return { text: texts.join(''), toolCalls };\n}\n\n/** Flatten any message's content (string or block array) to readable text. */\nfunction messageText(msg: any): string {\n if (!msg) return '';\n const c = msg.content;\n if (typeof c === 'string') return c;\n if (!Array.isArray(c)) return '';\n const parts: string[] = [];\n for (const block of c) {\n if (typeof block === 'string') parts.push(block);\n else if (block && typeof block === 'object') {\n if (typeof block.text === 'string') parts.push(block.text);\n else if (block.type === 'toolCall') parts.push(`${block.name ?? 'tool'}(${safeStringify(block.arguments)})`);\n }\n }\n return parts.join('');\n}\n\nfunction lastAssistantText(messages: any[] | undefined): string {\n if (!Array.isArray(messages)) return '';\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m && m.role === 'assistant') {\n const { text } = splitAssistantContent(m.content);\n if (text) return text;\n }\n }\n return '';\n}\n\nfunction markPatched(e: any): void {\n try {\n Object.defineProperty(e, PATCH_FLAG, { value: true, enumerable: false, configurable: true });\n } catch {\n e[PATCH_FLAG] = true;\n }\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value) ?? '';\n } catch {\n return '';\n }\n}\n"],"mappings":"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","names":["otelContext"]}
|