neatlogs 1.0.3 → 1.0.5

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.
@@ -0,0 +1,269 @@
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/langchain.ts
21
+ var langchain_exports = {};
22
+ __export(langchain_exports, {
23
+ langchainHandler: () => langchainHandler
24
+ });
25
+ module.exports = __toCommonJS(langchain_exports);
26
+ var import_api = require("@opentelemetry/api");
27
+ var TRACER_NAME = "neatlogs.langchain";
28
+ function langchainHandler(opts) {
29
+ return new NeatlogsCallbackHandler(opts);
30
+ }
31
+ var NeatlogsCallbackHandler = class {
32
+ name = "neatlogs";
33
+ _spans = /* @__PURE__ */ new Map();
34
+ _workflowName;
35
+ constructor(opts) {
36
+ this._workflowName = opts?.workflowName;
37
+ }
38
+ // --- Chain/Graph callbacks ---
39
+ async handleChainStart(serialized, inputs, runId, parentRunId, tags, metadata) {
40
+ const tracer = import_api.trace.getTracer(TRACER_NAME);
41
+ const name = serialized?.name ?? serialized?.id?.at(-1) ?? "chain";
42
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
43
+ const parentCtx = parentSpan ? import_api.trace.setSpan(import_api.context.active(), parentSpan) : import_api.context.active();
44
+ const attrs = {
45
+ "neatlogs.span.kind": "CHAIN",
46
+ "neatlogs.chain.name": name
47
+ };
48
+ if (this._workflowName) attrs["neatlogs.workflow.name"] = this._workflowName;
49
+ if (inputs) attrs["input.value"] = safeStringify(inputs).slice(0, 1e4);
50
+ if (tags?.length) attrs["neatlogs.tags"] = tags.join(",");
51
+ const span = tracer.startSpan(`langchain.chain.${name}`, { attributes: attrs }, parentCtx);
52
+ this._spans.set(runId, span);
53
+ }
54
+ async handleChainEnd(outputs, runId) {
55
+ const span = this._spans.get(runId);
56
+ if (!span) return;
57
+ if (outputs) span.setAttribute("output.value", safeStringify(outputs).slice(0, 1e4));
58
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
59
+ span.end();
60
+ this._spans.delete(runId);
61
+ }
62
+ async handleChainError(error, runId) {
63
+ const span = this._spans.get(runId);
64
+ if (!span) return;
65
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
66
+ span.recordException(error);
67
+ span.end();
68
+ this._spans.delete(runId);
69
+ }
70
+ // --- LLM callbacks ---
71
+ async handleLLMStart(serialized, prompts, runId, parentRunId, extraParams) {
72
+ const tracer = import_api.trace.getTracer(TRACER_NAME);
73
+ const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
74
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
75
+ const parentCtx = parentSpan ? import_api.trace.setSpan(import_api.context.active(), parentSpan) : import_api.context.active();
76
+ const attrs = {
77
+ "neatlogs.span.kind": "LLM",
78
+ "neatlogs.llm.provider": detectProvider(model),
79
+ "neatlogs.llm.model_name": model
80
+ };
81
+ for (let i = 0; i < prompts.length; i++) {
82
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = "user";
83
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = prompts[i].slice(0, 1e4);
84
+ }
85
+ if (extraParams?.invocation_params) {
86
+ const p = extraParams.invocation_params;
87
+ if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
88
+ if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
89
+ if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
90
+ }
91
+ const span = tracer.startSpan("langchain.llm", { attributes: attrs }, parentCtx);
92
+ this._spans.set(runId, span);
93
+ }
94
+ async handleChatModelStart(serialized, messages, runId, parentRunId, extraParams) {
95
+ const tracer = import_api.trace.getTracer(TRACER_NAME);
96
+ const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
97
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
98
+ const parentCtx = parentSpan ? import_api.trace.setSpan(import_api.context.active(), parentSpan) : import_api.context.active();
99
+ const attrs = {
100
+ "neatlogs.span.kind": "LLM",
101
+ "neatlogs.llm.provider": detectProvider(model),
102
+ "neatlogs.llm.model_name": model
103
+ };
104
+ const flatMessages = messages.flat();
105
+ for (let i = 0; i < flatMessages.length; i++) {
106
+ const msg = flatMessages[i];
107
+ const role = msg?.role ?? msg?._getType?.() ?? msg?.constructor?.name ?? "unknown";
108
+ const content = typeof msg?.content === "string" ? msg.content : safeStringify(msg?.content);
109
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = mapRole(role);
110
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = content.slice(0, 1e4);
111
+ }
112
+ if (extraParams?.invocation_params) {
113
+ const p = extraParams.invocation_params;
114
+ if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
115
+ if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
116
+ if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
117
+ }
118
+ const span = tracer.startSpan("langchain.chat_model", { attributes: attrs }, parentCtx);
119
+ this._spans.set(runId, span);
120
+ }
121
+ async handleLLMEnd(output, runId) {
122
+ const span = this._spans.get(runId);
123
+ if (!span) return;
124
+ const generations = output?.generations ?? [];
125
+ for (let i = 0; i < generations.length; i++) {
126
+ const gen = generations[i];
127
+ if (!Array.isArray(gen)) continue;
128
+ for (let j = 0; j < gen.length; j++) {
129
+ const msg = gen[j]?.message ?? gen[j];
130
+ const content = msg?.content ?? msg?.text ?? "";
131
+ span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, "assistant");
132
+ span.setAttribute(`neatlogs.llm.output_messages.${i}.content`, String(content).slice(0, 1e4));
133
+ const toolCalls = msg?.tool_calls ?? msg?.additional_kwargs?.tool_calls;
134
+ if (toolCalls && Array.isArray(toolCalls)) {
135
+ for (let k = 0; k < toolCalls.length; k++) {
136
+ const tc = toolCalls[k];
137
+ span.setAttribute(`neatlogs.llm.tool_calls.${k}.id`, tc.id ?? "");
138
+ span.setAttribute(`neatlogs.llm.tool_calls.${k}.name`, tc.name ?? tc.function?.name ?? "");
139
+ span.setAttribute(`neatlogs.llm.tool_calls.${k}.arguments`, tc.args ? safeStringify(tc.args) : tc.function?.arguments ?? "");
140
+ }
141
+ }
142
+ }
143
+ }
144
+ const usage = output?.llmOutput?.tokenUsage ?? output?.llmOutput?.usage;
145
+ if (usage) {
146
+ if (usage.promptTokens != null || usage.prompt_tokens != null) {
147
+ span.setAttribute("neatlogs.llm.token_count.prompt", usage.promptTokens ?? usage.prompt_tokens);
148
+ }
149
+ if (usage.completionTokens != null || usage.completion_tokens != null) {
150
+ span.setAttribute("neatlogs.llm.token_count.completion", usage.completionTokens ?? usage.completion_tokens);
151
+ }
152
+ if (usage.totalTokens != null || usage.total_tokens != null) {
153
+ span.setAttribute("neatlogs.llm.token_count.total", usage.totalTokens ?? usage.total_tokens);
154
+ }
155
+ }
156
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
157
+ span.end();
158
+ this._spans.delete(runId);
159
+ }
160
+ async handleLLMNewToken(_token, _idx, _runId) {
161
+ }
162
+ async handleLLMError(error, runId) {
163
+ const span = this._spans.get(runId);
164
+ if (!span) return;
165
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
166
+ span.recordException(error);
167
+ span.end();
168
+ this._spans.delete(runId);
169
+ }
170
+ // --- Tool callbacks ---
171
+ async handleToolStart(serialized, input, runId, parentRunId, _tags, _metadata, runName, toolCallId) {
172
+ const tracer = import_api.trace.getTracer(TRACER_NAME);
173
+ const name = serialized?.name || runName || (Array.isArray(serialized?.id) ? serialized.id[serialized.id.length - 1] : void 0) || "tool";
174
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
175
+ const parentCtx = parentSpan ? import_api.trace.setSpan(import_api.context.active(), parentSpan) : import_api.context.active();
176
+ const attrs = {
177
+ "neatlogs.span.kind": "TOOL",
178
+ "neatlogs.tool.name": name,
179
+ "input.value": String(input).slice(0, 1e4)
180
+ };
181
+ if (toolCallId) attrs["neatlogs.tool_call.id"] = toolCallId;
182
+ const span = tracer.startSpan(`langchain.tool.${name}`, { attributes: attrs }, parentCtx);
183
+ this._spans.set(runId, span);
184
+ }
185
+ async handleToolEnd(output, runId) {
186
+ const span = this._spans.get(runId);
187
+ if (!span) return;
188
+ span.setAttribute("output.value", String(output).slice(0, 1e4));
189
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
190
+ span.end();
191
+ this._spans.delete(runId);
192
+ }
193
+ async handleToolError(error, runId) {
194
+ const span = this._spans.get(runId);
195
+ if (!span) return;
196
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
197
+ span.recordException(error);
198
+ span.end();
199
+ this._spans.delete(runId);
200
+ }
201
+ // --- Retriever callbacks ---
202
+ async handleRetrieverStart(serialized, query, runId, parentRunId) {
203
+ const tracer = import_api.trace.getTracer(TRACER_NAME);
204
+ const name = serialized?.name ?? "retriever";
205
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
206
+ const parentCtx = parentSpan ? import_api.trace.setSpan(import_api.context.active(), parentSpan) : import_api.context.active();
207
+ const attrs = {
208
+ "neatlogs.span.kind": "RETRIEVER",
209
+ "neatlogs.retriever.name": name,
210
+ "input.value": query.slice(0, 1e4)
211
+ };
212
+ const span = tracer.startSpan(`langchain.retriever.${name}`, { attributes: attrs }, parentCtx);
213
+ this._spans.set(runId, span);
214
+ }
215
+ async handleRetrieverEnd(documents, runId) {
216
+ const span = this._spans.get(runId);
217
+ if (!span) return;
218
+ if (documents?.length) {
219
+ span.setAttribute("neatlogs.retriever.document_count", documents.length);
220
+ for (let i = 0; i < Math.min(documents.length, 10); i++) {
221
+ const doc = documents[i];
222
+ if (doc?.pageContent) {
223
+ span.setAttribute(`neatlogs.retriever.documents.${i}.content`, doc.pageContent.slice(0, 2e3));
224
+ }
225
+ }
226
+ }
227
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
228
+ span.end();
229
+ this._spans.delete(runId);
230
+ }
231
+ async handleRetrieverError(error, runId) {
232
+ const span = this._spans.get(runId);
233
+ if (!span) return;
234
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
235
+ span.recordException(error);
236
+ span.end();
237
+ this._spans.delete(runId);
238
+ }
239
+ };
240
+ function detectProvider(model) {
241
+ const m = model.toLowerCase();
242
+ if (m.includes("gpt") || m.includes("o1") || m.includes("o3") || m.includes("o4")) return "openai";
243
+ if (m.includes("claude")) return "anthropic";
244
+ if (m.includes("gemini")) return "google";
245
+ if (m.includes("command")) return "cohere";
246
+ if (m.includes("llama") || m.includes("mixtral")) return "meta";
247
+ return "unknown";
248
+ }
249
+ function mapRole(role) {
250
+ const r = role.toLowerCase();
251
+ if (r === "human" || r === "humanmessage") return "user";
252
+ if (r === "ai" || r === "aimessage") return "assistant";
253
+ if (r === "system" || r === "systemmessage") return "system";
254
+ if (r === "function" || r === "functionmessage") return "function";
255
+ if (r === "tool" || r === "toolmessage") return "tool";
256
+ return role;
257
+ }
258
+ function safeStringify(value) {
259
+ try {
260
+ return typeof value === "string" ? value : JSON.stringify(value);
261
+ } catch {
262
+ return "";
263
+ }
264
+ }
265
+ // Annotate the CommonJS export names for ESM import in node:
266
+ 0 && (module.exports = {
267
+ langchainHandler
268
+ });
269
+ //# sourceMappingURL=langchain.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/langchain.ts"],"sourcesContent":["/**\n * Neatlogs LangChain/LangGraph callback handler.\n *\n * Usage:\n * import { langchainHandler } from 'neatlogs/langchain';\n * const result = await chain.invoke(input, { callbacks: [langchainHandler()] });\n *\n * Creates spans: CHAIN (chain/graph runs), LLM (model calls), TOOL (tool invocations).\n * Works with LangChain, LangGraph, and Deep Agents (same callback interface).\n */\n\nimport { trace, context as otelContext, SpanStatusCode, type Span } from '@opentelemetry/api';\n\nconst TRACER_NAME = 'neatlogs.langchain';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface LangchainHandlerOptions {\n workflowName?: string;\n}\n\nexport function langchainHandler(opts?: LangchainHandlerOptions): any {\n return new NeatlogsCallbackHandler(opts);\n}\n\n// ---------------------------------------------------------------------------\n// Callback Handler\n// ---------------------------------------------------------------------------\n\nclass NeatlogsCallbackHandler {\n name = 'neatlogs';\n private _spans: Map<string, Span> = new Map();\n private _workflowName: string | undefined;\n\n constructor(opts?: LangchainHandlerOptions) {\n this._workflowName = opts?.workflowName;\n }\n\n // --- Chain/Graph callbacks ---\n\n async handleChainStart(\n serialized: Record<string, any>,\n inputs: Record<string, any>,\n runId: string,\n parentRunId?: string,\n tags?: string[],\n metadata?: Record<string, any>,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const name = serialized?.name ?? serialized?.id?.at(-1) ?? 'chain';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'CHAIN',\n 'neatlogs.chain.name': name,\n };\n if (this._workflowName) attrs['neatlogs.workflow.name'] = this._workflowName;\n if (inputs) attrs['input.value'] = safeStringify(inputs).slice(0, 10000);\n if (tags?.length) attrs['neatlogs.tags'] = tags.join(',');\n\n const span = tracer.startSpan(`langchain.chain.${name}`, { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleChainEnd(\n outputs: Record<string, any>,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n if (outputs) span.setAttribute('output.value', safeStringify(outputs).slice(0, 10000));\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleChainError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n\n // --- LLM callbacks ---\n\n async handleLLMStart(\n serialized: Record<string, any>,\n prompts: string[],\n runId: string,\n parentRunId?: string,\n extraParams?: Record<string, any>,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? '';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': detectProvider(model),\n 'neatlogs.llm.model_name': model,\n };\n\n for (let i = 0; i < prompts.length; i++) {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = 'user';\n attrs[`neatlogs.llm.input_messages.${i}.content`] = prompts[i].slice(0, 10000);\n }\n\n if (extraParams?.invocation_params) {\n const p = extraParams.invocation_params;\n if (p.temperature != null) attrs['neatlogs.llm.temperature'] = p.temperature;\n if (p.max_tokens != null) attrs['neatlogs.llm.max_tokens'] = p.max_tokens;\n if (p.model_name) attrs['neatlogs.llm.model_name'] = p.model_name;\n }\n\n const span = tracer.startSpan('langchain.llm', { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleChatModelStart(\n serialized: Record<string, any>,\n messages: any[][],\n runId: string,\n parentRunId?: string,\n extraParams?: Record<string, any>,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? '';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': detectProvider(model),\n 'neatlogs.llm.model_name': model,\n };\n\n const flatMessages = messages.flat();\n for (let i = 0; i < flatMessages.length; i++) {\n const msg = flatMessages[i];\n const role = msg?.role ?? msg?._getType?.() ?? msg?.constructor?.name ?? 'unknown';\n const content = typeof msg?.content === 'string' ? msg.content : safeStringify(msg?.content);\n attrs[`neatlogs.llm.input_messages.${i}.role`] = mapRole(role);\n attrs[`neatlogs.llm.input_messages.${i}.content`] = content.slice(0, 10000);\n }\n\n if (extraParams?.invocation_params) {\n const p = extraParams.invocation_params;\n if (p.temperature != null) attrs['neatlogs.llm.temperature'] = p.temperature;\n if (p.max_tokens != null) attrs['neatlogs.llm.max_tokens'] = p.max_tokens;\n if (p.model_name) attrs['neatlogs.llm.model_name'] = p.model_name;\n }\n\n const span = tracer.startSpan('langchain.chat_model', { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleLLMEnd(\n output: any,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n const generations = output?.generations ?? [];\n for (let i = 0; i < generations.length; i++) {\n const gen = generations[i];\n if (!Array.isArray(gen)) continue;\n for (let j = 0; j < gen.length; j++) {\n const msg = gen[j]?.message ?? gen[j];\n const content = msg?.content ?? msg?.text ?? '';\n span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, 'assistant');\n span.setAttribute(`neatlogs.llm.output_messages.${i}.content`, String(content).slice(0, 10000));\n\n const toolCalls = msg?.tool_calls ?? msg?.additional_kwargs?.tool_calls;\n if (toolCalls && Array.isArray(toolCalls)) {\n for (let k = 0; k < toolCalls.length; k++) {\n const tc = toolCalls[k];\n span.setAttribute(`neatlogs.llm.tool_calls.${k}.id`, tc.id ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${k}.name`, tc.name ?? tc.function?.name ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${k}.arguments`, tc.args ? safeStringify(tc.args) : (tc.function?.arguments ?? ''));\n }\n }\n }\n }\n\n const usage = output?.llmOutput?.tokenUsage ?? output?.llmOutput?.usage;\n if (usage) {\n if (usage.promptTokens != null || usage.prompt_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.prompt', usage.promptTokens ?? usage.prompt_tokens);\n }\n if (usage.completionTokens != null || usage.completion_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.completion', usage.completionTokens ?? usage.completion_tokens);\n }\n if (usage.totalTokens != null || usage.total_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.total', usage.totalTokens ?? usage.total_tokens);\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleLLMNewToken(\n _token: string,\n _idx: any,\n _runId: string,\n ): Promise<void> {}\n\n async handleLLMError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n\n // --- Tool callbacks ---\n\n async handleToolStart(\n serialized: Record<string, any>,\n input: string,\n runId: string,\n parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n runName?: string,\n toolCallId?: string,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n // LangChain 1.x: serialized.name is absent and serialized.id is the class\n // name (e.g. \"DynamicStructuredTool\"); the real tool name arrives as `runName`.\n // Prefer serialized.name (older LC), then runName, then the class id, then 'tool'.\n const name =\n serialized?.name ||\n runName ||\n (Array.isArray(serialized?.id) ? serialized.id[serialized.id.length - 1] : undefined) ||\n 'tool';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': name,\n 'input.value': String(input).slice(0, 10000),\n };\n if (toolCallId) attrs['neatlogs.tool_call.id'] = toolCallId;\n\n const span = tracer.startSpan(`langchain.tool.${name}`, { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleToolEnd(\n output: string,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setAttribute('output.value', String(output).slice(0, 10000));\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleToolError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n\n // --- Retriever callbacks ---\n\n async handleRetrieverStart(\n serialized: Record<string, any>,\n query: string,\n runId: string,\n parentRunId?: string,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const name = serialized?.name ?? 'retriever';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'RETRIEVER',\n 'neatlogs.retriever.name': name,\n 'input.value': query.slice(0, 10000),\n };\n\n const span = tracer.startSpan(`langchain.retriever.${name}`, { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleRetrieverEnd(\n documents: any[],\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n if (documents?.length) {\n span.setAttribute('neatlogs.retriever.document_count', documents.length);\n for (let i = 0; i < Math.min(documents.length, 10); i++) {\n const doc = documents[i];\n if (doc?.pageContent) {\n span.setAttribute(`neatlogs.retriever.documents.${i}.content`, doc.pageContent.slice(0, 2000));\n }\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleRetrieverError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction detectProvider(model: string): string {\n const m = model.toLowerCase();\n if (m.includes('gpt') || m.includes('o1') || m.includes('o3') || m.includes('o4')) return 'openai';\n if (m.includes('claude')) return 'anthropic';\n if (m.includes('gemini')) return 'google';\n if (m.includes('command')) return 'cohere';\n if (m.includes('llama') || m.includes('mixtral')) return 'meta';\n return 'unknown';\n}\n\nfunction mapRole(role: string): string {\n const r = role.toLowerCase();\n if (r === 'human' || r === 'humanmessage') return 'user';\n if (r === 'ai' || r === 'aimessage') return 'assistant';\n if (r === 'system' || r === 'systemmessage') return 'system';\n if (r === 'function' || r === 'functionmessage') return 'function';\n if (r === 'tool' || r === 'toolmessage') return 'tool';\n return role;\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"],"mappings":"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","names":["otelContext"]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Neatlogs LangChain/LangGraph callback handler.
3
+ *
4
+ * Usage:
5
+ * import { langchainHandler } from 'neatlogs/langchain';
6
+ * const result = await chain.invoke(input, { callbacks: [langchainHandler()] });
7
+ *
8
+ * Creates spans: CHAIN (chain/graph runs), LLM (model calls), TOOL (tool invocations).
9
+ * Works with LangChain, LangGraph, and Deep Agents (same callback interface).
10
+ */
11
+ interface LangchainHandlerOptions {
12
+ workflowName?: string;
13
+ }
14
+ declare function langchainHandler(opts?: LangchainHandlerOptions): any;
15
+
16
+ export { type LangchainHandlerOptions, langchainHandler };
@@ -0,0 +1,244 @@
1
+ // src/langchain.ts
2
+ import { trace, context as otelContext, SpanStatusCode } from "@opentelemetry/api";
3
+ var TRACER_NAME = "neatlogs.langchain";
4
+ function langchainHandler(opts) {
5
+ return new NeatlogsCallbackHandler(opts);
6
+ }
7
+ var NeatlogsCallbackHandler = class {
8
+ name = "neatlogs";
9
+ _spans = /* @__PURE__ */ new Map();
10
+ _workflowName;
11
+ constructor(opts) {
12
+ this._workflowName = opts?.workflowName;
13
+ }
14
+ // --- Chain/Graph callbacks ---
15
+ async handleChainStart(serialized, inputs, runId, parentRunId, tags, metadata) {
16
+ const tracer = trace.getTracer(TRACER_NAME);
17
+ const name = serialized?.name ?? serialized?.id?.at(-1) ?? "chain";
18
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
19
+ const parentCtx = parentSpan ? trace.setSpan(otelContext.active(), parentSpan) : otelContext.active();
20
+ const attrs = {
21
+ "neatlogs.span.kind": "CHAIN",
22
+ "neatlogs.chain.name": name
23
+ };
24
+ if (this._workflowName) attrs["neatlogs.workflow.name"] = this._workflowName;
25
+ if (inputs) attrs["input.value"] = safeStringify(inputs).slice(0, 1e4);
26
+ if (tags?.length) attrs["neatlogs.tags"] = tags.join(",");
27
+ const span = tracer.startSpan(`langchain.chain.${name}`, { attributes: attrs }, parentCtx);
28
+ this._spans.set(runId, span);
29
+ }
30
+ async handleChainEnd(outputs, runId) {
31
+ const span = this._spans.get(runId);
32
+ if (!span) return;
33
+ if (outputs) span.setAttribute("output.value", safeStringify(outputs).slice(0, 1e4));
34
+ span.setStatus({ code: SpanStatusCode.OK });
35
+ span.end();
36
+ this._spans.delete(runId);
37
+ }
38
+ async handleChainError(error, runId) {
39
+ const span = this._spans.get(runId);
40
+ if (!span) return;
41
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
42
+ span.recordException(error);
43
+ span.end();
44
+ this._spans.delete(runId);
45
+ }
46
+ // --- LLM callbacks ---
47
+ async handleLLMStart(serialized, prompts, runId, parentRunId, extraParams) {
48
+ const tracer = trace.getTracer(TRACER_NAME);
49
+ const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
50
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
51
+ const parentCtx = parentSpan ? trace.setSpan(otelContext.active(), parentSpan) : otelContext.active();
52
+ const attrs = {
53
+ "neatlogs.span.kind": "LLM",
54
+ "neatlogs.llm.provider": detectProvider(model),
55
+ "neatlogs.llm.model_name": model
56
+ };
57
+ for (let i = 0; i < prompts.length; i++) {
58
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = "user";
59
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = prompts[i].slice(0, 1e4);
60
+ }
61
+ if (extraParams?.invocation_params) {
62
+ const p = extraParams.invocation_params;
63
+ if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
64
+ if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
65
+ if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
66
+ }
67
+ const span = tracer.startSpan("langchain.llm", { attributes: attrs }, parentCtx);
68
+ this._spans.set(runId, span);
69
+ }
70
+ async handleChatModelStart(serialized, messages, runId, parentRunId, extraParams) {
71
+ const tracer = trace.getTracer(TRACER_NAME);
72
+ const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
73
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
74
+ const parentCtx = parentSpan ? trace.setSpan(otelContext.active(), parentSpan) : otelContext.active();
75
+ const attrs = {
76
+ "neatlogs.span.kind": "LLM",
77
+ "neatlogs.llm.provider": detectProvider(model),
78
+ "neatlogs.llm.model_name": model
79
+ };
80
+ const flatMessages = messages.flat();
81
+ for (let i = 0; i < flatMessages.length; i++) {
82
+ const msg = flatMessages[i];
83
+ const role = msg?.role ?? msg?._getType?.() ?? msg?.constructor?.name ?? "unknown";
84
+ const content = typeof msg?.content === "string" ? msg.content : safeStringify(msg?.content);
85
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = mapRole(role);
86
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = content.slice(0, 1e4);
87
+ }
88
+ if (extraParams?.invocation_params) {
89
+ const p = extraParams.invocation_params;
90
+ if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
91
+ if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
92
+ if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
93
+ }
94
+ const span = tracer.startSpan("langchain.chat_model", { attributes: attrs }, parentCtx);
95
+ this._spans.set(runId, span);
96
+ }
97
+ async handleLLMEnd(output, runId) {
98
+ const span = this._spans.get(runId);
99
+ if (!span) return;
100
+ const generations = output?.generations ?? [];
101
+ for (let i = 0; i < generations.length; i++) {
102
+ const gen = generations[i];
103
+ if (!Array.isArray(gen)) continue;
104
+ for (let j = 0; j < gen.length; j++) {
105
+ const msg = gen[j]?.message ?? gen[j];
106
+ const content = msg?.content ?? msg?.text ?? "";
107
+ span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, "assistant");
108
+ span.setAttribute(`neatlogs.llm.output_messages.${i}.content`, String(content).slice(0, 1e4));
109
+ const toolCalls = msg?.tool_calls ?? msg?.additional_kwargs?.tool_calls;
110
+ if (toolCalls && Array.isArray(toolCalls)) {
111
+ for (let k = 0; k < toolCalls.length; k++) {
112
+ const tc = toolCalls[k];
113
+ span.setAttribute(`neatlogs.llm.tool_calls.${k}.id`, tc.id ?? "");
114
+ span.setAttribute(`neatlogs.llm.tool_calls.${k}.name`, tc.name ?? tc.function?.name ?? "");
115
+ span.setAttribute(`neatlogs.llm.tool_calls.${k}.arguments`, tc.args ? safeStringify(tc.args) : tc.function?.arguments ?? "");
116
+ }
117
+ }
118
+ }
119
+ }
120
+ const usage = output?.llmOutput?.tokenUsage ?? output?.llmOutput?.usage;
121
+ if (usage) {
122
+ if (usage.promptTokens != null || usage.prompt_tokens != null) {
123
+ span.setAttribute("neatlogs.llm.token_count.prompt", usage.promptTokens ?? usage.prompt_tokens);
124
+ }
125
+ if (usage.completionTokens != null || usage.completion_tokens != null) {
126
+ span.setAttribute("neatlogs.llm.token_count.completion", usage.completionTokens ?? usage.completion_tokens);
127
+ }
128
+ if (usage.totalTokens != null || usage.total_tokens != null) {
129
+ span.setAttribute("neatlogs.llm.token_count.total", usage.totalTokens ?? usage.total_tokens);
130
+ }
131
+ }
132
+ span.setStatus({ code: SpanStatusCode.OK });
133
+ span.end();
134
+ this._spans.delete(runId);
135
+ }
136
+ async handleLLMNewToken(_token, _idx, _runId) {
137
+ }
138
+ async handleLLMError(error, runId) {
139
+ const span = this._spans.get(runId);
140
+ if (!span) return;
141
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
142
+ span.recordException(error);
143
+ span.end();
144
+ this._spans.delete(runId);
145
+ }
146
+ // --- Tool callbacks ---
147
+ async handleToolStart(serialized, input, runId, parentRunId, _tags, _metadata, runName, toolCallId) {
148
+ const tracer = trace.getTracer(TRACER_NAME);
149
+ const name = serialized?.name || runName || (Array.isArray(serialized?.id) ? serialized.id[serialized.id.length - 1] : void 0) || "tool";
150
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
151
+ const parentCtx = parentSpan ? trace.setSpan(otelContext.active(), parentSpan) : otelContext.active();
152
+ const attrs = {
153
+ "neatlogs.span.kind": "TOOL",
154
+ "neatlogs.tool.name": name,
155
+ "input.value": String(input).slice(0, 1e4)
156
+ };
157
+ if (toolCallId) attrs["neatlogs.tool_call.id"] = toolCallId;
158
+ const span = tracer.startSpan(`langchain.tool.${name}`, { attributes: attrs }, parentCtx);
159
+ this._spans.set(runId, span);
160
+ }
161
+ async handleToolEnd(output, runId) {
162
+ const span = this._spans.get(runId);
163
+ if (!span) return;
164
+ span.setAttribute("output.value", String(output).slice(0, 1e4));
165
+ span.setStatus({ code: SpanStatusCode.OK });
166
+ span.end();
167
+ this._spans.delete(runId);
168
+ }
169
+ async handleToolError(error, runId) {
170
+ const span = this._spans.get(runId);
171
+ if (!span) return;
172
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
173
+ span.recordException(error);
174
+ span.end();
175
+ this._spans.delete(runId);
176
+ }
177
+ // --- Retriever callbacks ---
178
+ async handleRetrieverStart(serialized, query, runId, parentRunId) {
179
+ const tracer = trace.getTracer(TRACER_NAME);
180
+ const name = serialized?.name ?? "retriever";
181
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
182
+ const parentCtx = parentSpan ? trace.setSpan(otelContext.active(), parentSpan) : otelContext.active();
183
+ const attrs = {
184
+ "neatlogs.span.kind": "RETRIEVER",
185
+ "neatlogs.retriever.name": name,
186
+ "input.value": query.slice(0, 1e4)
187
+ };
188
+ const span = tracer.startSpan(`langchain.retriever.${name}`, { attributes: attrs }, parentCtx);
189
+ this._spans.set(runId, span);
190
+ }
191
+ async handleRetrieverEnd(documents, runId) {
192
+ const span = this._spans.get(runId);
193
+ if (!span) return;
194
+ if (documents?.length) {
195
+ span.setAttribute("neatlogs.retriever.document_count", documents.length);
196
+ for (let i = 0; i < Math.min(documents.length, 10); i++) {
197
+ const doc = documents[i];
198
+ if (doc?.pageContent) {
199
+ span.setAttribute(`neatlogs.retriever.documents.${i}.content`, doc.pageContent.slice(0, 2e3));
200
+ }
201
+ }
202
+ }
203
+ span.setStatus({ code: SpanStatusCode.OK });
204
+ span.end();
205
+ this._spans.delete(runId);
206
+ }
207
+ async handleRetrieverError(error, runId) {
208
+ const span = this._spans.get(runId);
209
+ if (!span) return;
210
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
211
+ span.recordException(error);
212
+ span.end();
213
+ this._spans.delete(runId);
214
+ }
215
+ };
216
+ function detectProvider(model) {
217
+ const m = model.toLowerCase();
218
+ if (m.includes("gpt") || m.includes("o1") || m.includes("o3") || m.includes("o4")) return "openai";
219
+ if (m.includes("claude")) return "anthropic";
220
+ if (m.includes("gemini")) return "google";
221
+ if (m.includes("command")) return "cohere";
222
+ if (m.includes("llama") || m.includes("mixtral")) return "meta";
223
+ return "unknown";
224
+ }
225
+ function mapRole(role) {
226
+ const r = role.toLowerCase();
227
+ if (r === "human" || r === "humanmessage") return "user";
228
+ if (r === "ai" || r === "aimessage") return "assistant";
229
+ if (r === "system" || r === "systemmessage") return "system";
230
+ if (r === "function" || r === "functionmessage") return "function";
231
+ if (r === "tool" || r === "toolmessage") return "tool";
232
+ return role;
233
+ }
234
+ function safeStringify(value) {
235
+ try {
236
+ return typeof value === "string" ? value : JSON.stringify(value);
237
+ } catch {
238
+ return "";
239
+ }
240
+ }
241
+ export {
242
+ langchainHandler
243
+ };
244
+ //# sourceMappingURL=langchain.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/langchain.ts"],"sourcesContent":["/**\n * Neatlogs LangChain/LangGraph callback handler.\n *\n * Usage:\n * import { langchainHandler } from 'neatlogs/langchain';\n * const result = await chain.invoke(input, { callbacks: [langchainHandler()] });\n *\n * Creates spans: CHAIN (chain/graph runs), LLM (model calls), TOOL (tool invocations).\n * Works with LangChain, LangGraph, and Deep Agents (same callback interface).\n */\n\nimport { trace, context as otelContext, SpanStatusCode, type Span } from '@opentelemetry/api';\n\nconst TRACER_NAME = 'neatlogs.langchain';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface LangchainHandlerOptions {\n workflowName?: string;\n}\n\nexport function langchainHandler(opts?: LangchainHandlerOptions): any {\n return new NeatlogsCallbackHandler(opts);\n}\n\n// ---------------------------------------------------------------------------\n// Callback Handler\n// ---------------------------------------------------------------------------\n\nclass NeatlogsCallbackHandler {\n name = 'neatlogs';\n private _spans: Map<string, Span> = new Map();\n private _workflowName: string | undefined;\n\n constructor(opts?: LangchainHandlerOptions) {\n this._workflowName = opts?.workflowName;\n }\n\n // --- Chain/Graph callbacks ---\n\n async handleChainStart(\n serialized: Record<string, any>,\n inputs: Record<string, any>,\n runId: string,\n parentRunId?: string,\n tags?: string[],\n metadata?: Record<string, any>,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const name = serialized?.name ?? serialized?.id?.at(-1) ?? 'chain';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'CHAIN',\n 'neatlogs.chain.name': name,\n };\n if (this._workflowName) attrs['neatlogs.workflow.name'] = this._workflowName;\n if (inputs) attrs['input.value'] = safeStringify(inputs).slice(0, 10000);\n if (tags?.length) attrs['neatlogs.tags'] = tags.join(',');\n\n const span = tracer.startSpan(`langchain.chain.${name}`, { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleChainEnd(\n outputs: Record<string, any>,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n if (outputs) span.setAttribute('output.value', safeStringify(outputs).slice(0, 10000));\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleChainError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n\n // --- LLM callbacks ---\n\n async handleLLMStart(\n serialized: Record<string, any>,\n prompts: string[],\n runId: string,\n parentRunId?: string,\n extraParams?: Record<string, any>,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? '';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': detectProvider(model),\n 'neatlogs.llm.model_name': model,\n };\n\n for (let i = 0; i < prompts.length; i++) {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = 'user';\n attrs[`neatlogs.llm.input_messages.${i}.content`] = prompts[i].slice(0, 10000);\n }\n\n if (extraParams?.invocation_params) {\n const p = extraParams.invocation_params;\n if (p.temperature != null) attrs['neatlogs.llm.temperature'] = p.temperature;\n if (p.max_tokens != null) attrs['neatlogs.llm.max_tokens'] = p.max_tokens;\n if (p.model_name) attrs['neatlogs.llm.model_name'] = p.model_name;\n }\n\n const span = tracer.startSpan('langchain.llm', { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleChatModelStart(\n serialized: Record<string, any>,\n messages: any[][],\n runId: string,\n parentRunId?: string,\n extraParams?: Record<string, any>,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? '';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'LLM',\n 'neatlogs.llm.provider': detectProvider(model),\n 'neatlogs.llm.model_name': model,\n };\n\n const flatMessages = messages.flat();\n for (let i = 0; i < flatMessages.length; i++) {\n const msg = flatMessages[i];\n const role = msg?.role ?? msg?._getType?.() ?? msg?.constructor?.name ?? 'unknown';\n const content = typeof msg?.content === 'string' ? msg.content : safeStringify(msg?.content);\n attrs[`neatlogs.llm.input_messages.${i}.role`] = mapRole(role);\n attrs[`neatlogs.llm.input_messages.${i}.content`] = content.slice(0, 10000);\n }\n\n if (extraParams?.invocation_params) {\n const p = extraParams.invocation_params;\n if (p.temperature != null) attrs['neatlogs.llm.temperature'] = p.temperature;\n if (p.max_tokens != null) attrs['neatlogs.llm.max_tokens'] = p.max_tokens;\n if (p.model_name) attrs['neatlogs.llm.model_name'] = p.model_name;\n }\n\n const span = tracer.startSpan('langchain.chat_model', { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleLLMEnd(\n output: any,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n const generations = output?.generations ?? [];\n for (let i = 0; i < generations.length; i++) {\n const gen = generations[i];\n if (!Array.isArray(gen)) continue;\n for (let j = 0; j < gen.length; j++) {\n const msg = gen[j]?.message ?? gen[j];\n const content = msg?.content ?? msg?.text ?? '';\n span.setAttribute(`neatlogs.llm.output_messages.${i}.role`, 'assistant');\n span.setAttribute(`neatlogs.llm.output_messages.${i}.content`, String(content).slice(0, 10000));\n\n const toolCalls = msg?.tool_calls ?? msg?.additional_kwargs?.tool_calls;\n if (toolCalls && Array.isArray(toolCalls)) {\n for (let k = 0; k < toolCalls.length; k++) {\n const tc = toolCalls[k];\n span.setAttribute(`neatlogs.llm.tool_calls.${k}.id`, tc.id ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${k}.name`, tc.name ?? tc.function?.name ?? '');\n span.setAttribute(`neatlogs.llm.tool_calls.${k}.arguments`, tc.args ? safeStringify(tc.args) : (tc.function?.arguments ?? ''));\n }\n }\n }\n }\n\n const usage = output?.llmOutput?.tokenUsage ?? output?.llmOutput?.usage;\n if (usage) {\n if (usage.promptTokens != null || usage.prompt_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.prompt', usage.promptTokens ?? usage.prompt_tokens);\n }\n if (usage.completionTokens != null || usage.completion_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.completion', usage.completionTokens ?? usage.completion_tokens);\n }\n if (usage.totalTokens != null || usage.total_tokens != null) {\n span.setAttribute('neatlogs.llm.token_count.total', usage.totalTokens ?? usage.total_tokens);\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleLLMNewToken(\n _token: string,\n _idx: any,\n _runId: string,\n ): Promise<void> {}\n\n async handleLLMError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n\n // --- Tool callbacks ---\n\n async handleToolStart(\n serialized: Record<string, any>,\n input: string,\n runId: string,\n parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n runName?: string,\n toolCallId?: string,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n // LangChain 1.x: serialized.name is absent and serialized.id is the class\n // name (e.g. \"DynamicStructuredTool\"); the real tool name arrives as `runName`.\n // Prefer serialized.name (older LC), then runName, then the class id, then 'tool'.\n const name =\n serialized?.name ||\n runName ||\n (Array.isArray(serialized?.id) ? serialized.id[serialized.id.length - 1] : undefined) ||\n 'tool';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'TOOL',\n 'neatlogs.tool.name': name,\n 'input.value': String(input).slice(0, 10000),\n };\n if (toolCallId) attrs['neatlogs.tool_call.id'] = toolCallId;\n\n const span = tracer.startSpan(`langchain.tool.${name}`, { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleToolEnd(\n output: string,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setAttribute('output.value', String(output).slice(0, 10000));\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleToolError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n\n // --- Retriever callbacks ---\n\n async handleRetrieverStart(\n serialized: Record<string, any>,\n query: string,\n runId: string,\n parentRunId?: string,\n ): Promise<void> {\n const tracer = trace.getTracer(TRACER_NAME);\n const name = serialized?.name ?? 'retriever';\n\n const parentSpan = parentRunId ? this._spans.get(parentRunId) : undefined;\n const parentCtx = parentSpan\n ? trace.setSpan(otelContext.active(), parentSpan)\n : otelContext.active();\n\n const attrs: Record<string, any> = {\n 'neatlogs.span.kind': 'RETRIEVER',\n 'neatlogs.retriever.name': name,\n 'input.value': query.slice(0, 10000),\n };\n\n const span = tracer.startSpan(`langchain.retriever.${name}`, { attributes: attrs }, parentCtx);\n this._spans.set(runId, span);\n }\n\n async handleRetrieverEnd(\n documents: any[],\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n if (documents?.length) {\n span.setAttribute('neatlogs.retriever.document_count', documents.length);\n for (let i = 0; i < Math.min(documents.length, 10); i++) {\n const doc = documents[i];\n if (doc?.pageContent) {\n span.setAttribute(`neatlogs.retriever.documents.${i}.content`, doc.pageContent.slice(0, 2000));\n }\n }\n }\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n this._spans.delete(runId);\n }\n\n async handleRetrieverError(\n error: Error,\n runId: string,\n ): Promise<void> {\n const span = this._spans.get(runId);\n if (!span) return;\n\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n this._spans.delete(runId);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction detectProvider(model: string): string {\n const m = model.toLowerCase();\n if (m.includes('gpt') || m.includes('o1') || m.includes('o3') || m.includes('o4')) return 'openai';\n if (m.includes('claude')) return 'anthropic';\n if (m.includes('gemini')) return 'google';\n if (m.includes('command')) return 'cohere';\n if (m.includes('llama') || m.includes('mixtral')) return 'meta';\n return 'unknown';\n}\n\nfunction mapRole(role: string): string {\n const r = role.toLowerCase();\n if (r === 'human' || r === 'humanmessage') return 'user';\n if (r === 'ai' || r === 'aimessage') return 'assistant';\n if (r === 'system' || r === 'systemmessage') return 'system';\n if (r === 'function' || r === 'functionmessage') return 'function';\n if (r === 'tool' || r === 'toolmessage') return 'tool';\n return role;\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"],"mappings":"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","names":[]}