neatlogs 1.0.6 → 1.0.8

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.
Files changed (57) hide show
  1. package/dist/azure-openai.cjs +344 -0
  2. package/dist/azure-openai.cjs.map +1 -0
  3. package/dist/azure-openai.d.ts +30 -0
  4. package/dist/azure-openai.mjs +318 -0
  5. package/dist/azure-openai.mjs.map +1 -0
  6. package/dist/bedrock.cjs +540 -0
  7. package/dist/bedrock.cjs.map +1 -0
  8. package/dist/bedrock.d.ts +29 -0
  9. package/dist/bedrock.mjs +514 -0
  10. package/dist/bedrock.mjs.map +1 -0
  11. package/dist/browser.cjs +121 -0
  12. package/dist/browser.cjs.map +1 -0
  13. package/dist/browser.d.ts +143 -0
  14. package/dist/browser.mjs +96 -0
  15. package/dist/browser.mjs.map +1 -0
  16. package/dist/claude-agent-sdk.cjs +406 -0
  17. package/dist/claude-agent-sdk.cjs.map +1 -0
  18. package/dist/claude-agent-sdk.d.ts +49 -0
  19. package/dist/claude-agent-sdk.mjs +381 -0
  20. package/dist/claude-agent-sdk.mjs.map +1 -0
  21. package/dist/index.cjs +3219 -1129
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +7 -1
  24. package/dist/index.mjs +3210 -1131
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/langchain.cjs +9 -9
  27. package/dist/langchain.cjs.map +1 -1
  28. package/dist/langchain.mjs +9 -9
  29. package/dist/langchain.mjs.map +1 -1
  30. package/dist/mastra-wrap.cjs +25 -25
  31. package/dist/mastra-wrap.cjs.map +1 -1
  32. package/dist/mastra-wrap.mjs +25 -25
  33. package/dist/mastra-wrap.mjs.map +1 -1
  34. package/dist/openai-agents.cjs +6 -6
  35. package/dist/openai-agents.cjs.map +1 -1
  36. package/dist/openai-agents.mjs +6 -6
  37. package/dist/openai-agents.mjs.map +1 -1
  38. package/dist/opencode-plugin.cjs +5879 -0
  39. package/dist/opencode-plugin.cjs.map +1 -0
  40. package/dist/opencode-plugin.d.ts +38 -0
  41. package/dist/opencode-plugin.mjs +5854 -0
  42. package/dist/opencode-plugin.mjs.map +1 -0
  43. package/dist/openrouter-agent.cjs +273 -0
  44. package/dist/openrouter-agent.cjs.map +1 -0
  45. package/dist/openrouter-agent.d.ts +34 -0
  46. package/dist/openrouter-agent.mjs +247 -0
  47. package/dist/openrouter-agent.mjs.map +1 -0
  48. package/dist/pi-agent.cjs +10 -10
  49. package/dist/pi-agent.cjs.map +1 -1
  50. package/dist/pi-agent.mjs +10 -10
  51. package/dist/pi-agent.mjs.map +1 -1
  52. package/dist/vertex-ai.cjs +424 -0
  53. package/dist/vertex-ai.cjs.map +1 -0
  54. package/dist/vertex-ai.d.ts +39 -0
  55. package/dist/vertex-ai.mjs +397 -0
  56. package/dist/vertex-ai.mjs.map +1 -0
  57. package/package.json +76 -1
package/dist/index.mjs CHANGED
@@ -1371,7 +1371,7 @@ var UnifiedAttributeProcessor = class {
1371
1371
  attrs["mcp.server.version"] = info.version ?? "";
1372
1372
  }
1373
1373
  if ("capabilities" in output) {
1374
- attrs["mcp.server.capabilities"] = JSON.stringify(output.capabilities).slice(0, 2e3);
1374
+ attrs["mcp.server.capabilities"] = JSON.stringify(output.capabilities);
1375
1375
  }
1376
1376
  }
1377
1377
  } catch {
@@ -1977,7 +1977,7 @@ var UnifiedAttributeProcessor = class {
1977
1977
  if (typeof r !== "string" || r.toLowerCase() !== role) continue;
1978
1978
  const c = unified[`${prefix}.${i}.content`];
1979
1979
  if (typeof c === "string" && c.toLowerCase().includes("thought:")) {
1980
- texts.push(c.slice(0, 2e4));
1980
+ texts.push(c);
1981
1981
  }
1982
1982
  }
1983
1983
  return texts;
@@ -4907,6 +4907,7 @@ var INSTRUMENTATION_REGISTRY = {
4907
4907
  tags: {
4908
4908
  llm: [
4909
4909
  "azure_ai_inference",
4910
+ "azure_openai",
4910
4911
  "openai",
4911
4912
  "anthropic",
4912
4913
  "cohere",
@@ -4955,7 +4956,10 @@ var INSTRUMENTATION_REGISTRY = {
4955
4956
  "smolagents",
4956
4957
  "strands",
4957
4958
  "pipecat",
4958
- "ai_sdk"
4959
+ "ai_sdk",
4960
+ "claude_agent_sdk",
4961
+ "openrouter_agent",
4962
+ "opencode"
4959
4963
  ],
4960
4964
  tool: ["langchain", "llamaindex", "haystack", "mcp"],
4961
4965
  http: ["requests", "httpx", "urllib3", "aiohttp"],
@@ -5000,7 +5004,15 @@ var INSTRUMENTATION_REGISTRY = {
5000
5004
  bedrock: {
5001
5005
  openinference: "@arizeai/openinference-instrumentation-bedrock",
5002
5006
  openllmetry: null,
5003
- neatlogs: null,
5007
+ // The wrapper is opt-in per call site (wrapBedrock); this entry keeps
5008
+ // scope detection and tagging consistent.
5009
+ neatlogs: "neatlogs/bedrock",
5010
+ default_span_kind: "LLM"
5011
+ },
5012
+ azure_openai: {
5013
+ openinference: null,
5014
+ openllmetry: null,
5015
+ neatlogs: "neatlogs/azure-openai",
5004
5016
  default_span_kind: "LLM"
5005
5017
  },
5006
5018
  groq: {
@@ -5018,7 +5030,7 @@ var INSTRUMENTATION_REGISTRY = {
5018
5030
  vertexai: {
5019
5031
  openinference: null,
5020
5032
  openllmetry: null,
5021
- neatlogs: null,
5033
+ neatlogs: "neatlogs/vertex-ai",
5022
5034
  default_span_kind: "LLM"
5023
5035
  },
5024
5036
  google_generativeai: {
@@ -5291,7 +5303,20 @@ var INSTRUMENTATION_REGISTRY = {
5291
5303
  claude_agent_sdk: {
5292
5304
  openinference: "@arizeai/openinference-instrumentation-claude-agent-sdk",
5293
5305
  openllmetry: null,
5294
- neatlogs: null,
5306
+ // Opt-in wrapper (wrapClaudeAgentSDK); entry kept for scope detection/tagging.
5307
+ neatlogs: "neatlogs/claude-agent-sdk",
5308
+ default_span_kind: "AGENT"
5309
+ },
5310
+ openrouter_agent: {
5311
+ openinference: null,
5312
+ openllmetry: null,
5313
+ neatlogs: "neatlogs/openrouter-agent",
5314
+ default_span_kind: "AGENT"
5315
+ },
5316
+ opencode: {
5317
+ openinference: null,
5318
+ openllmetry: null,
5319
+ neatlogs: "neatlogs/opencode",
5295
5320
  default_span_kind: "AGENT"
5296
5321
  }
5297
5322
  }
@@ -5800,7 +5825,7 @@ async function removeTag(name, tag) {
5800
5825
  }
5801
5826
 
5802
5827
  // src/version.ts
5803
- var __version__ = "1.0.6";
5828
+ var __version__ = "1.0.8";
5804
5829
 
5805
5830
  // src/init.ts
5806
5831
  var logger13 = getLogger();
@@ -7087,238 +7112,302 @@ function recordError2(span2, err) {
7087
7112
  span2.end();
7088
7113
  }
7089
7114
 
7090
- // src/langchain.ts
7115
+ // src/azure-openai.ts
7091
7116
  import { trace as trace8, context as otelContext6, SpanStatusCode as SpanStatusCode7 } from "@opentelemetry/api";
7092
- var TRACER_NAME5 = "neatlogs.langchain";
7093
- function langchainHandler(opts) {
7094
- return new NeatlogsCallbackHandler(opts);
7117
+ var TRACER_NAME5 = "neatlogs.azure_openai";
7118
+ var PROVIDER = "azure";
7119
+ var SYSTEM = "azure";
7120
+ function wrapAzureOpenAI(client) {
7121
+ return wrapNamespace3(client, []);
7095
7122
  }
7096
- var NeatlogsCallbackHandler = class {
7097
- name = "neatlogs";
7098
- _spans = /* @__PURE__ */ new Map();
7099
- _workflowName;
7100
- constructor(opts) {
7101
- this._workflowName = opts?.workflowName;
7102
- }
7103
- // --- Chain/Graph callbacks ---
7104
- async handleChainStart(serialized, inputs, runId, parentRunId, tags, metadata) {
7105
- const tracer = trace8.getTracer(TRACER_NAME5);
7106
- const name = serialized?.name ?? serialized?.id?.at(-1) ?? "chain";
7107
- const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
7108
- const parentCtx = parentSpan ? trace8.setSpan(otelContext6.active(), parentSpan) : otelContext6.active();
7109
- const attrs = {
7110
- "neatlogs.span.kind": "CHAIN",
7111
- "neatlogs.chain.name": name
7112
- };
7113
- if (this._workflowName) attrs["neatlogs.workflow.name"] = this._workflowName;
7114
- if (inputs) attrs["input.value"] = safeStringify5(inputs).slice(0, 1e4);
7115
- if (tags?.length) attrs["neatlogs.tags"] = tags.join(",");
7116
- const span2 = tracer.startSpan(`langchain.chain.${name}`, { attributes: attrs }, parentCtx);
7117
- this._spans.set(runId, span2);
7118
- }
7119
- async handleChainEnd(outputs, runId) {
7120
- const span2 = this._spans.get(runId);
7121
- if (!span2) return;
7122
- if (outputs) span2.setAttribute("output.value", safeStringify5(outputs).slice(0, 1e4));
7123
- span2.setStatus({ code: SpanStatusCode7.OK });
7124
- span2.end();
7125
- this._spans.delete(runId);
7126
- }
7127
- async handleChainError(error, runId) {
7128
- const span2 = this._spans.get(runId);
7129
- if (!span2) return;
7130
- span2.setStatus({ code: SpanStatusCode7.ERROR, message: error.message });
7131
- span2.recordException(error);
7132
- span2.end();
7133
- this._spans.delete(runId);
7134
- }
7135
- // --- LLM callbacks ---
7136
- async handleLLMStart(serialized, prompts, runId, parentRunId, extraParams) {
7123
+ function traceTool3(name, fn) {
7124
+ return async function tracedTool(args) {
7137
7125
  const tracer = trace8.getTracer(TRACER_NAME5);
7138
- const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
7139
- const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
7140
- const parentCtx = parentSpan ? trace8.setSpan(otelContext6.active(), parentSpan) : otelContext6.active();
7141
- const attrs = {
7142
- "neatlogs.span.kind": "LLM",
7143
- "neatlogs.llm.provider": detectProvider(model),
7144
- "neatlogs.llm.model_name": model
7145
- };
7146
- for (let i = 0; i < prompts.length; i++) {
7147
- attrs[`neatlogs.llm.input_messages.${i}.role`] = "user";
7148
- attrs[`neatlogs.llm.input_messages.${i}.content`] = prompts[i].slice(0, 1e4);
7149
- }
7150
- if (extraParams?.invocation_params) {
7151
- const p = extraParams.invocation_params;
7152
- if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
7153
- if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
7154
- if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
7126
+ return tracer.startActiveSpan(
7127
+ `tool.${name}`,
7128
+ {
7129
+ attributes: {
7130
+ "neatlogs.span.kind": "TOOL",
7131
+ "neatlogs.tool.name": name,
7132
+ "input.value": safeStringify5(args)
7133
+ }
7134
+ },
7135
+ otelContext6.active(),
7136
+ async (span2) => {
7137
+ try {
7138
+ const result = await fn(args);
7139
+ span2.setAttribute("output.value", safeStringify5(result));
7140
+ span2.setStatus({ code: SpanStatusCode7.OK });
7141
+ return result;
7142
+ } catch (err) {
7143
+ recordError3(span2, err);
7144
+ throw err;
7145
+ } finally {
7146
+ span2.end();
7147
+ }
7148
+ }
7149
+ );
7150
+ };
7151
+ }
7152
+ function wrapNamespace3(target, path4) {
7153
+ return new Proxy(target, {
7154
+ get(obj, prop, receiver) {
7155
+ const value = Reflect.get(obj, prop, receiver);
7156
+ if (typeof prop === "symbol" || String(prop).startsWith("_")) return value;
7157
+ const currentPath = [...path4, String(prop)];
7158
+ const pathStr = currentPath.join(".");
7159
+ if (pathStr === "chat.completions.create" && typeof value === "function") {
7160
+ return tracedChatCompletionsCreate2(value.bind(obj));
7161
+ }
7162
+ if (pathStr === "responses.create" && typeof value === "function") {
7163
+ return tracedResponsesCreate2(value.bind(obj));
7164
+ }
7165
+ if (value && typeof value === "object" && !Array.isArray(value) && isNamespace3(currentPath)) {
7166
+ return wrapNamespace3(value, currentPath);
7167
+ }
7168
+ return value;
7155
7169
  }
7156
- const span2 = tracer.startSpan("langchain.llm", { attributes: attrs }, parentCtx);
7157
- this._spans.set(runId, span2);
7158
- }
7159
- async handleChatModelStart(serialized, messages, runId, parentRunId, extraParams) {
7170
+ });
7171
+ }
7172
+ function isNamespace3(path4) {
7173
+ if (path4.length > 3) return false;
7174
+ const key = path4[path4.length - 1];
7175
+ return ["chat", "completions", "responses", "beta"].includes(key);
7176
+ }
7177
+ function tracedChatCompletionsCreate2(original) {
7178
+ return function(opts, ...rest) {
7160
7179
  const tracer = trace8.getTracer(TRACER_NAME5);
7161
- const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
7162
- const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
7163
- const parentCtx = parentSpan ? trace8.setSpan(otelContext6.active(), parentSpan) : otelContext6.active();
7164
- const attrs = {
7165
- "neatlogs.span.kind": "LLM",
7166
- "neatlogs.llm.provider": detectProvider(model),
7167
- "neatlogs.llm.model_name": model
7168
- };
7169
- const flatMessages = messages.flat();
7170
- for (let i = 0; i < flatMessages.length; i++) {
7171
- const msg = flatMessages[i];
7172
- const role = msg?.role ?? msg?._getType?.() ?? msg?.constructor?.name ?? "unknown";
7173
- const content = typeof msg?.content === "string" ? msg.content : safeStringify5(msg?.content);
7174
- attrs[`neatlogs.llm.input_messages.${i}.role`] = mapRole(role);
7175
- attrs[`neatlogs.llm.input_messages.${i}.content`] = content.slice(0, 1e4);
7180
+ const model = opts?.model ?? "";
7181
+ const messages = opts?.messages ?? [];
7182
+ const isStream = opts?.stream === true;
7183
+ const span2 = tracer.startSpan("azure_openai.chat.completions.create", {
7184
+ attributes: {
7185
+ "neatlogs.span.kind": "LLM",
7186
+ "neatlogs.llm.provider": PROVIDER,
7187
+ "neatlogs.llm.system": SYSTEM,
7188
+ "neatlogs.llm.model_name": model,
7189
+ "neatlogs.llm.is_streaming": isStream
7190
+ }
7191
+ }, otelContext6.active());
7192
+ for (let i = 0; i < messages.length; i++) {
7193
+ const msg = messages[i];
7194
+ span2.setAttribute(`neatlogs.llm.input_messages.${i}.role`, msg.role ?? "");
7195
+ if (typeof msg.content === "string") {
7196
+ span2.setAttribute(`neatlogs.llm.input_messages.${i}.content`, msg.content);
7197
+ } else if (msg.content) {
7198
+ span2.setAttribute(`neatlogs.llm.input_messages.${i}.content`, safeStringify5(msg.content));
7199
+ }
7200
+ if (msg.tool_call_id) {
7201
+ span2.setAttribute(`neatlogs.llm.input_messages.${i}.tool_call_id`, msg.tool_call_id);
7202
+ }
7176
7203
  }
7177
- if (extraParams?.invocation_params) {
7178
- const p = extraParams.invocation_params;
7179
- if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
7180
- if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
7181
- if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
7204
+ if (opts?.tools) {
7205
+ for (let i = 0; i < opts.tools.length; i++) {
7206
+ const fn = opts.tools[i]?.function ?? {};
7207
+ span2.setAttribute(`neatlogs.llm.tools.${i}.name`, fn.name ?? "");
7208
+ if (fn.description) span2.setAttribute(`neatlogs.llm.tools.${i}.description`, fn.description);
7209
+ if (fn.parameters) span2.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify5(fn.parameters));
7210
+ }
7182
7211
  }
7183
- const span2 = tracer.startSpan("langchain.chat_model", { attributes: attrs }, parentCtx);
7184
- this._spans.set(runId, span2);
7185
- }
7186
- async handleLLMEnd(output, runId) {
7187
- const span2 = this._spans.get(runId);
7188
- if (!span2) return;
7189
- const generations = output?.generations ?? [];
7190
- for (let i = 0; i < generations.length; i++) {
7191
- const gen = generations[i];
7192
- if (!Array.isArray(gen)) continue;
7193
- for (let j = 0; j < gen.length; j++) {
7194
- const msg = gen[j]?.message ?? gen[j];
7195
- const content = msg?.content ?? msg?.text ?? "";
7196
- span2.setAttribute(`neatlogs.llm.output_messages.${i}.role`, "assistant");
7197
- span2.setAttribute(`neatlogs.llm.output_messages.${i}.content`, String(content).slice(0, 1e4));
7198
- const toolCalls = msg?.tool_calls ?? msg?.additional_kwargs?.tool_calls;
7199
- if (toolCalls && Array.isArray(toolCalls)) {
7200
- for (let k = 0; k < toolCalls.length; k++) {
7201
- const tc = toolCalls[k];
7202
- span2.setAttribute(`neatlogs.llm.tool_calls.${k}.id`, tc.id ?? "");
7203
- span2.setAttribute(`neatlogs.llm.tool_calls.${k}.name`, tc.name ?? tc.function?.name ?? "");
7204
- span2.setAttribute(`neatlogs.llm.tool_calls.${k}.arguments`, tc.args ? safeStringify5(tc.args) : tc.function?.arguments ?? "");
7205
- }
7206
- }
7212
+ setInvocationParams3(span2, opts);
7213
+ if (isStream) {
7214
+ opts = { ...opts };
7215
+ const streamOpts = opts.stream_options ?? {};
7216
+ if (!streamOpts.include_usage) {
7217
+ opts.stream_options = { ...streamOpts, include_usage: true };
7207
7218
  }
7208
7219
  }
7209
- const usage = output?.llmOutput?.tokenUsage ?? output?.llmOutput?.usage;
7210
- if (usage) {
7211
- if (usage.promptTokens != null || usage.prompt_tokens != null) {
7212
- span2.setAttribute("neatlogs.llm.token_count.prompt", usage.promptTokens ?? usage.prompt_tokens);
7220
+ const ctx = trace8.setSpan(otelContext6.active(), span2);
7221
+ const promise = otelContext6.with(ctx, () => original(opts, ...rest));
7222
+ return promise.then(
7223
+ (response) => {
7224
+ if (isStream) {
7225
+ return wrapAsyncIterableStream2(response, span2);
7226
+ }
7227
+ finalizeChatResponse2(span2, response);
7228
+ return response;
7229
+ },
7230
+ (err) => {
7231
+ recordError3(span2, err);
7232
+ throw err;
7213
7233
  }
7214
- if (usage.completionTokens != null || usage.completion_tokens != null) {
7215
- span2.setAttribute("neatlogs.llm.token_count.completion", usage.completionTokens ?? usage.completion_tokens);
7234
+ );
7235
+ };
7236
+ }
7237
+ function tracedResponsesCreate2(original) {
7238
+ return function(opts, ...rest) {
7239
+ const tracer = trace8.getTracer(TRACER_NAME5);
7240
+ const model = opts?.model ?? "";
7241
+ const span2 = tracer.startSpan("azure_openai.responses.create", {
7242
+ attributes: {
7243
+ "neatlogs.span.kind": "LLM",
7244
+ "neatlogs.llm.provider": PROVIDER,
7245
+ "neatlogs.llm.system": SYSTEM,
7246
+ "neatlogs.llm.model_name": model,
7247
+ "input.value": safeStringify5(opts?.input ?? "")
7216
7248
  }
7217
- if (usage.totalTokens != null || usage.total_tokens != null) {
7218
- span2.setAttribute("neatlogs.llm.token_count.total", usage.totalTokens ?? usage.total_tokens);
7249
+ }, otelContext6.active());
7250
+ const ctx = trace8.setSpan(otelContext6.active(), span2);
7251
+ const promise = otelContext6.with(ctx, () => original(opts, ...rest));
7252
+ return promise.then(
7253
+ (response) => {
7254
+ if (response?.output_text) {
7255
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7256
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", response.output_text);
7257
+ }
7258
+ if (response?.model) span2.setAttribute("neatlogs.llm.model_name", response.model);
7259
+ if (response?.usage) {
7260
+ if (response.usage.input_tokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", response.usage.input_tokens);
7261
+ if (response.usage.output_tokens != null) span2.setAttribute("neatlogs.llm.token_count.completion", response.usage.output_tokens);
7262
+ }
7263
+ span2.setStatus({ code: SpanStatusCode7.OK });
7264
+ span2.end();
7265
+ return response;
7266
+ },
7267
+ (err) => {
7268
+ recordError3(span2, err);
7269
+ throw err;
7219
7270
  }
7220
- }
7271
+ );
7272
+ };
7273
+ }
7274
+ function wrapAsyncIterableStream2(stream, span2) {
7275
+ const chunks = [];
7276
+ const originalAsyncIterator = stream?.[Symbol.asyncIterator]?.bind(stream);
7277
+ if (!originalAsyncIterator) {
7221
7278
  span2.setStatus({ code: SpanStatusCode7.OK });
7222
7279
  span2.end();
7223
- this._spans.delete(runId);
7224
- }
7225
- async handleLLMNewToken(_token, _idx, _runId) {
7226
- }
7227
- async handleLLMError(error, runId) {
7228
- const span2 = this._spans.get(runId);
7229
- if (!span2) return;
7230
- span2.setStatus({ code: SpanStatusCode7.ERROR, message: error.message });
7231
- span2.recordException(error);
7232
- span2.end();
7233
- this._spans.delete(runId);
7280
+ return stream;
7234
7281
  }
7235
- // --- Tool callbacks ---
7236
- async handleToolStart(serialized, input, runId, parentRunId, _tags, _metadata, runName, toolCallId) {
7237
- const tracer = trace8.getTracer(TRACER_NAME5);
7238
- const name = serialized?.name || runName || (Array.isArray(serialized?.id) ? serialized.id[serialized.id.length - 1] : void 0) || "tool";
7239
- const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
7240
- const parentCtx = parentSpan ? trace8.setSpan(otelContext6.active(), parentSpan) : otelContext6.active();
7241
- const attrs = {
7242
- "neatlogs.span.kind": "TOOL",
7243
- "neatlogs.tool.name": name,
7244
- "input.value": String(input).slice(0, 1e4)
7245
- };
7246
- if (toolCallId) attrs["neatlogs.tool_call.id"] = toolCallId;
7247
- const span2 = tracer.startSpan(`langchain.tool.${name}`, { attributes: attrs }, parentCtx);
7248
- this._spans.set(runId, span2);
7282
+ const wrapped = Object.create(Object.getPrototypeOf(stream));
7283
+ Object.assign(wrapped, stream);
7284
+ wrapped[Symbol.asyncIterator] = function() {
7285
+ const iterator = originalAsyncIterator();
7286
+ return {
7287
+ async next() {
7288
+ try {
7289
+ const result = await iterator.next();
7290
+ if (result.done) {
7291
+ finalizeStreamChunks2(span2, chunks);
7292
+ return result;
7293
+ }
7294
+ chunks.push(result.value);
7295
+ return result;
7296
+ } catch (err) {
7297
+ recordError3(span2, err);
7298
+ throw err;
7299
+ }
7300
+ },
7301
+ async return(value) {
7302
+ finalizeStreamChunks2(span2, chunks);
7303
+ return iterator.return?.(value) ?? { done: true, value: void 0 };
7304
+ },
7305
+ async throw(err) {
7306
+ recordError3(span2, err);
7307
+ return iterator.throw?.(err) ?? { done: true, value: void 0 };
7308
+ }
7309
+ };
7310
+ };
7311
+ return wrapped;
7312
+ }
7313
+ function finalizeStreamChunks2(span2, chunks) {
7314
+ const textParts = [];
7315
+ const toolCallsAcc = {};
7316
+ let finishReason = "";
7317
+ let model = "";
7318
+ let usage = null;
7319
+ for (const chunk of chunks) {
7320
+ if (!chunk?.choices?.length) {
7321
+ if (chunk?.usage) usage = chunk.usage;
7322
+ continue;
7323
+ }
7324
+ const choice = chunk.choices[0];
7325
+ const delta = choice?.delta;
7326
+ if (delta?.content) textParts.push(delta.content);
7327
+ if (delta?.tool_calls) {
7328
+ for (const tc of delta.tool_calls) {
7329
+ const idx = tc.index ?? 0;
7330
+ if (!toolCallsAcc[idx]) toolCallsAcc[idx] = { id: "", name: "", arguments: "" };
7331
+ if (tc.id) toolCallsAcc[idx].id = tc.id;
7332
+ if (tc.function?.name) toolCallsAcc[idx].name = tc.function.name;
7333
+ if (tc.function?.arguments) toolCallsAcc[idx].arguments += tc.function.arguments;
7334
+ }
7335
+ }
7336
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
7337
+ if (chunk?.model) model = chunk.model;
7249
7338
  }
7250
- async handleToolEnd(output, runId) {
7251
- const span2 = this._spans.get(runId);
7252
- if (!span2) return;
7253
- span2.setAttribute("output.value", String(output).slice(0, 1e4));
7254
- span2.setStatus({ code: SpanStatusCode7.OK });
7255
- span2.end();
7256
- this._spans.delete(runId);
7339
+ const fullText = textParts.join("");
7340
+ if (fullText) {
7341
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7342
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", fullText);
7257
7343
  }
7258
- async handleToolError(error, runId) {
7259
- const span2 = this._spans.get(runId);
7260
- if (!span2) return;
7261
- span2.setStatus({ code: SpanStatusCode7.ERROR, message: error.message });
7262
- span2.recordException(error);
7263
- span2.end();
7264
- this._spans.delete(runId);
7344
+ let j = 0;
7345
+ for (const tc of Object.values(toolCallsAcc)) {
7346
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id);
7347
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.name);
7348
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, tc.arguments);
7349
+ j++;
7265
7350
  }
7266
- // --- Retriever callbacks ---
7267
- async handleRetrieverStart(serialized, query, runId, parentRunId) {
7268
- const tracer = trace8.getTracer(TRACER_NAME5);
7269
- const name = serialized?.name ?? "retriever";
7270
- const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
7271
- const parentCtx = parentSpan ? trace8.setSpan(otelContext6.active(), parentSpan) : otelContext6.active();
7272
- const attrs = {
7273
- "neatlogs.span.kind": "RETRIEVER",
7274
- "neatlogs.retriever.name": name,
7275
- "input.value": query.slice(0, 1e4)
7276
- };
7277
- const span2 = tracer.startSpan(`langchain.retriever.${name}`, { attributes: attrs }, parentCtx);
7278
- this._spans.set(runId, span2);
7351
+ if (model) span2.setAttribute("neatlogs.llm.model_name", model);
7352
+ if (finishReason) span2.setAttribute("neatlogs.llm.finish_reason", finishReason);
7353
+ if (usage) {
7354
+ if (usage.prompt_tokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", usage.prompt_tokens);
7355
+ if (usage.completion_tokens != null) span2.setAttribute("neatlogs.llm.token_count.completion", usage.completion_tokens);
7356
+ if (usage.total_tokens != null) span2.setAttribute("neatlogs.llm.token_count.total", usage.total_tokens);
7357
+ if (usage.prompt_tokens_details?.cached_tokens != null) {
7358
+ span2.setAttribute("neatlogs.llm.token_count.cache_read", usage.prompt_tokens_details.cached_tokens);
7359
+ }
7360
+ if (usage.completion_tokens_details?.reasoning_tokens != null) {
7361
+ span2.setAttribute("neatlogs.llm.token_count.reasoning", usage.completion_tokens_details.reasoning_tokens);
7362
+ }
7279
7363
  }
7280
- async handleRetrieverEnd(documents, runId) {
7281
- const span2 = this._spans.get(runId);
7282
- if (!span2) return;
7283
- if (documents?.length) {
7284
- span2.setAttribute("neatlogs.retriever.document_count", documents.length);
7285
- for (let i = 0; i < Math.min(documents.length, 10); i++) {
7286
- const doc = documents[i];
7287
- if (doc?.pageContent) {
7288
- span2.setAttribute(`neatlogs.retriever.documents.${i}.content`, doc.pageContent.slice(0, 2e3));
7289
- }
7364
+ span2.setStatus({ code: SpanStatusCode7.OK });
7365
+ span2.end();
7366
+ }
7367
+ function finalizeChatResponse2(span2, response) {
7368
+ const choices = response?.choices ?? [];
7369
+ for (let i = 0; i < choices.length; i++) {
7370
+ const message = choices[i]?.message;
7371
+ if (!message) continue;
7372
+ span2.setAttribute(`neatlogs.llm.output_messages.${i}.role`, "assistant");
7373
+ if (message.content) {
7374
+ span2.setAttribute(`neatlogs.llm.output_messages.${i}.content`, message.content);
7375
+ }
7376
+ if (message.tool_calls) {
7377
+ for (let j = 0; j < message.tool_calls.length; j++) {
7378
+ const tc = message.tool_calls[j];
7379
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id ?? "");
7380
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.function?.name ?? "");
7381
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, tc.function?.arguments ?? "");
7290
7382
  }
7291
7383
  }
7292
- span2.setStatus({ code: SpanStatusCode7.OK });
7293
- span2.end();
7294
- this._spans.delete(runId);
7384
+ if (choices[i].finish_reason) {
7385
+ span2.setAttribute("neatlogs.llm.finish_reason", choices[i].finish_reason);
7386
+ }
7295
7387
  }
7296
- async handleRetrieverError(error, runId) {
7297
- const span2 = this._spans.get(runId);
7298
- if (!span2) return;
7299
- span2.setStatus({ code: SpanStatusCode7.ERROR, message: error.message });
7300
- span2.recordException(error);
7301
- span2.end();
7302
- this._spans.delete(runId);
7388
+ const usage = response?.usage;
7389
+ if (usage) {
7390
+ if (usage.prompt_tokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", usage.prompt_tokens);
7391
+ if (usage.completion_tokens != null) span2.setAttribute("neatlogs.llm.token_count.completion", usage.completion_tokens);
7392
+ if (usage.total_tokens != null) span2.setAttribute("neatlogs.llm.token_count.total", usage.total_tokens);
7393
+ if (usage.prompt_tokens_details?.cached_tokens != null) {
7394
+ span2.setAttribute("neatlogs.llm.token_count.cache_read", usage.prompt_tokens_details.cached_tokens);
7395
+ }
7396
+ if (usage.completion_tokens_details?.reasoning_tokens != null) {
7397
+ span2.setAttribute("neatlogs.llm.token_count.reasoning", usage.completion_tokens_details.reasoning_tokens);
7398
+ }
7303
7399
  }
7304
- };
7305
- function detectProvider(model) {
7306
- const m = model.toLowerCase();
7307
- if (m.includes("gpt") || m.includes("o1") || m.includes("o3") || m.includes("o4")) return "openai";
7308
- if (m.includes("claude")) return "anthropic";
7309
- if (m.includes("gemini")) return "google";
7310
- if (m.includes("command")) return "cohere";
7311
- if (m.includes("llama") || m.includes("mixtral")) return "meta";
7312
- return "unknown";
7400
+ if (response?.model) span2.setAttribute("neatlogs.llm.model_name", response.model);
7401
+ span2.setStatus({ code: SpanStatusCode7.OK });
7402
+ span2.end();
7313
7403
  }
7314
- function mapRole(role) {
7315
- const r = role.toLowerCase();
7316
- if (r === "human" || r === "humanmessage") return "user";
7317
- if (r === "ai" || r === "aimessage") return "assistant";
7318
- if (r === "system" || r === "systemmessage") return "system";
7319
- if (r === "function" || r === "functionmessage") return "function";
7320
- if (r === "tool" || r === "toolmessage") return "tool";
7321
- return role;
7404
+ function setInvocationParams3(span2, opts) {
7405
+ if (opts?.temperature != null) span2.setAttribute("neatlogs.llm.temperature", opts.temperature);
7406
+ if (opts?.top_p != null) span2.setAttribute("neatlogs.llm.top_p", opts.top_p);
7407
+ if (opts?.max_tokens != null) span2.setAttribute("neatlogs.llm.max_tokens", opts.max_tokens);
7408
+ if (opts?.frequency_penalty != null) span2.setAttribute("neatlogs.llm.frequency_penalty", opts.frequency_penalty);
7409
+ if (opts?.presence_penalty != null) span2.setAttribute("neatlogs.llm.presence_penalty", opts.presence_penalty);
7410
+ if (opts?.stop) span2.setAttribute("neatlogs.llm.stop_sequences", safeStringify5(opts.stop));
7322
7411
  }
7323
7412
  function safeStringify5(value) {
7324
7413
  try {
@@ -7327,325 +7416,901 @@ function safeStringify5(value) {
7327
7416
  return "";
7328
7417
  }
7329
7418
  }
7419
+ function recordError3(span2, err) {
7420
+ if (err instanceof Error) {
7421
+ span2.setStatus({ code: SpanStatusCode7.ERROR, message: err.message });
7422
+ span2.recordException(err);
7423
+ } else {
7424
+ span2.setStatus({ code: SpanStatusCode7.ERROR, message: String(err) });
7425
+ }
7426
+ span2.end();
7427
+ }
7330
7428
 
7331
- // src/strands.ts
7332
- var PATCH_FLAG = "_neatlogs_patched";
7333
- function strandsHooks(agent) {
7334
- installEventHookFromAgent(agent);
7335
- if (agent && typeof agent === "object") {
7336
- try {
7337
- Object.defineProperty(agent, PATCH_FLAG, {
7338
- value: true,
7339
- enumerable: false,
7340
- configurable: true
7341
- });
7342
- } catch {
7343
- agent[PATCH_FLAG] = true;
7429
+ // src/vertex-ai.ts
7430
+ import { trace as trace9, context as otelContext7, SpanStatusCode as SpanStatusCode8 } from "@opentelemetry/api";
7431
+ var TRACER_NAME6 = "neatlogs.vertex_ai";
7432
+ var PROVIDER2 = "vertex_ai";
7433
+ var SYSTEM2 = "vertexai";
7434
+ function wrapVertexAI(client) {
7435
+ return wrapNamespace4(client, []);
7436
+ }
7437
+ function traceTool4(name, fn) {
7438
+ return async function tracedTool(args) {
7439
+ const tracer = trace9.getTracer(TRACER_NAME6);
7440
+ return tracer.startActiveSpan(
7441
+ `tool.${name}`,
7442
+ {
7443
+ attributes: {
7444
+ "neatlogs.span.kind": "TOOL",
7445
+ "neatlogs.tool.name": name,
7446
+ "input.value": safeStringify6(args)
7447
+ }
7448
+ },
7449
+ otelContext7.active(),
7450
+ async (span2) => {
7451
+ try {
7452
+ const result = await fn(args);
7453
+ span2.setAttribute("output.value", safeStringify6(result));
7454
+ span2.setStatus({ code: SpanStatusCode8.OK });
7455
+ return result;
7456
+ } catch (err) {
7457
+ recordError4(span2, err);
7458
+ throw err;
7459
+ } finally {
7460
+ span2.end();
7461
+ }
7462
+ }
7463
+ );
7464
+ };
7465
+ }
7466
+ function wrapNamespace4(target, path4) {
7467
+ return new Proxy(target, {
7468
+ get(obj, prop, receiver) {
7469
+ const value = Reflect.get(obj, prop, receiver);
7470
+ if (typeof prop === "symbol" || String(prop).startsWith("_")) return value;
7471
+ const currentPath = [...path4, String(prop)];
7472
+ const pathStr = currentPath.join(".");
7473
+ if (pathStr === "models.generateContent" && typeof value === "function") {
7474
+ return tracedGenerateContent(value.bind(obj), false);
7475
+ }
7476
+ if (pathStr === "models.generateContentStream" && typeof value === "function") {
7477
+ return tracedGenerateContent(value.bind(obj), true);
7478
+ }
7479
+ if (pathStr === "models.embedContent" && typeof value === "function") {
7480
+ return tracedEmbedContent(value.bind(obj));
7481
+ }
7482
+ if (pathStr === "models.countTokens" && typeof value === "function") {
7483
+ return tracedCountTokens(value.bind(obj));
7484
+ }
7485
+ if (value && typeof value === "object" && !Array.isArray(value) && isNamespace4(currentPath)) {
7486
+ return wrapNamespace4(value, currentPath);
7487
+ }
7488
+ return value;
7344
7489
  }
7490
+ });
7491
+ }
7492
+ function isNamespace4(path4) {
7493
+ if (path4.length > 2) return false;
7494
+ return ["models", "chats"].includes(path4[path4.length - 1]);
7495
+ }
7496
+ function wrapVertexAIChat(chat) {
7497
+ const c = chat;
7498
+ if (!c || c._neatlogsVertexPatched) return chat;
7499
+ if (typeof c.sendMessage === "function") {
7500
+ const orig = c.sendMessage.bind(c);
7501
+ c.sendMessage = (params, ...rest) => tracedChatSend(orig, c, params, rest, false);
7345
7502
  }
7346
- return agent;
7503
+ if (typeof c.sendMessageStream === "function") {
7504
+ const orig = c.sendMessageStream.bind(c);
7505
+ c.sendMessageStream = (params, ...rest) => tracedChatSend(orig, c, params, rest, true);
7506
+ }
7507
+ try {
7508
+ Object.defineProperty(c, "_neatlogsVertexPatched", { value: true, enumerable: false, configurable: true });
7509
+ } catch {
7510
+ c._neatlogsVertexPatched = true;
7511
+ }
7512
+ return chat;
7347
7513
  }
7348
- function installEventHookFromAgent(agent) {
7349
- const tracer = agent?._tracer;
7350
- const proto = tracer ? Object.getPrototypeOf(tracer) : void 0;
7351
- if (!proto || typeof proto._addEvent !== "function") return;
7352
- if (proto._addEvent[PATCH_FLAG]) return;
7353
- const orig = proto._addEvent;
7354
- function patchedAddEvent(span2, eventName, eventAttributes) {
7355
- const result = orig.call(this, span2, eventName, eventAttributes);
7356
- try {
7357
- enrichSpanFromEvent(span2, eventName, eventAttributes || {});
7358
- } catch {
7514
+ function tracedChatSend(original, chat, params, rest, isStream) {
7515
+ const tracer = trace9.getTracer(TRACER_NAME6);
7516
+ const model = chat?.model ?? chat?.modelVersion ?? "";
7517
+ const span2 = tracer.startSpan("vertex_ai.chat.send_message", {
7518
+ attributes: {
7519
+ "neatlogs.span.kind": "LLM",
7520
+ "neatlogs.llm.provider": PROVIDER2,
7521
+ "neatlogs.llm.system": SYSTEM2,
7522
+ "neatlogs.llm.model_name": model,
7523
+ "neatlogs.llm.is_streaming": isStream
7524
+ }
7525
+ }, otelContext7.active());
7526
+ const message = params?.message ?? params;
7527
+ span2.setAttribute("neatlogs.llm.input_messages.0.role", "user");
7528
+ span2.setAttribute(
7529
+ "neatlogs.llm.input_messages.0.content",
7530
+ typeof message === "string" ? message : safeStringify6(message)
7531
+ );
7532
+ const ctx = trace9.setSpan(otelContext7.active(), span2);
7533
+ const result = otelContext7.with(ctx, () => original(params, ...rest));
7534
+ return Promise.resolve(result).then(
7535
+ (response) => {
7536
+ if (isStream) return wrapStream(response, span2);
7537
+ finalizeResponse(span2, response);
7538
+ return response;
7539
+ },
7540
+ (err) => {
7541
+ recordError4(span2, err);
7542
+ throw err;
7359
7543
  }
7360
- return result;
7361
- }
7362
- patchedAddEvent[PATCH_FLAG] = true;
7363
- proto._addEvent = patchedAddEvent;
7544
+ );
7364
7545
  }
7365
- function enrichSpanFromEvent(span2, eventName, attrs) {
7366
- if (!span2 || typeof span2.setAttribute !== "function") return;
7367
- if (typeof span2.isRecording === "function" && !span2.isRecording()) return;
7368
- const isTool = readSpanOp(span2) === "execute_tool";
7369
- if (eventName.startsWith("gen_ai.") && eventName.endsWith(".message")) {
7370
- const role = eventName.slice("gen_ai.".length, -".message".length);
7371
- const content = flattenStrandsContent(attrs.content);
7372
- if (content) {
7373
- if (isTool) {
7374
- span2.setAttribute("input.value", content);
7375
- span2.setAttribute("neatlogs.tool.input", content);
7376
- } else {
7377
- appendInputMessage(span2, role, content);
7546
+ function tracedEmbedContent(original) {
7547
+ return function(opts, ...rest) {
7548
+ const tracer = trace9.getTracer(TRACER_NAME6);
7549
+ const span2 = tracer.startSpan("vertex_ai.models.embed_content", {
7550
+ attributes: {
7551
+ "neatlogs.span.kind": "EMBEDDING",
7552
+ "neatlogs.llm.provider": PROVIDER2,
7553
+ "neatlogs.embedding.model_name": opts?.model ?? "",
7554
+ "neatlogs.embedding.text": safeStringify6(opts?.contents ?? "")
7555
+ }
7556
+ }, otelContext7.active());
7557
+ const ctx = trace9.setSpan(otelContext7.active(), span2);
7558
+ const result = otelContext7.with(ctx, () => original(opts, ...rest));
7559
+ return Promise.resolve(result).then(
7560
+ (response) => {
7561
+ const embeddings = response?.embeddings;
7562
+ if (Array.isArray(embeddings)) {
7563
+ span2.setAttribute("neatlogs.embedding.count", embeddings.length);
7564
+ const vals = embeddings[0]?.values;
7565
+ if (Array.isArray(vals)) span2.setAttribute("neatlogs.embedding.dimensions", vals.length);
7566
+ }
7567
+ span2.setStatus({ code: SpanStatusCode8.OK });
7568
+ span2.end();
7569
+ return response;
7570
+ },
7571
+ (err) => {
7572
+ recordError4(span2, err);
7573
+ throw err;
7574
+ }
7575
+ );
7576
+ };
7577
+ }
7578
+ function tracedCountTokens(original) {
7579
+ return function(opts, ...rest) {
7580
+ const tracer = trace9.getTracer(TRACER_NAME6);
7581
+ const span2 = tracer.startSpan("vertex_ai.models.count_tokens", {
7582
+ attributes: {
7583
+ "neatlogs.span.kind": "LLM",
7584
+ "neatlogs.llm.provider": PROVIDER2,
7585
+ "neatlogs.llm.task": "count_tokens",
7586
+ "neatlogs.llm.model_name": opts?.model ?? ""
7587
+ }
7588
+ }, otelContext7.active());
7589
+ const ctx = trace9.setSpan(otelContext7.active(), span2);
7590
+ const result = otelContext7.with(ctx, () => original(opts, ...rest));
7591
+ return Promise.resolve(result).then(
7592
+ (response) => {
7593
+ if (response?.totalTokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", response.totalTokens);
7594
+ span2.setStatus({ code: SpanStatusCode8.OK });
7595
+ span2.end();
7596
+ return response;
7597
+ },
7598
+ (err) => {
7599
+ recordError4(span2, err);
7600
+ throw err;
7601
+ }
7602
+ );
7603
+ };
7604
+ }
7605
+ function tracedGenerateContent(original, isStream) {
7606
+ return function(opts, ...rest) {
7607
+ const tracer = trace9.getTracer(TRACER_NAME6);
7608
+ const model = opts?.model ?? "";
7609
+ const span2 = tracer.startSpan("vertex_ai.models.generate_content", {
7610
+ attributes: {
7611
+ "neatlogs.span.kind": "LLM",
7612
+ "neatlogs.llm.provider": PROVIDER2,
7613
+ "neatlogs.llm.system": SYSTEM2,
7614
+ "neatlogs.llm.model_name": model,
7615
+ "neatlogs.llm.is_streaming": isStream
7616
+ }
7617
+ }, otelContext7.active());
7618
+ setInputAttributes(span2, opts);
7619
+ const ctx = trace9.setSpan(otelContext7.active(), span2);
7620
+ const result = otelContext7.with(ctx, () => original(opts, ...rest));
7621
+ return Promise.resolve(result).then(
7622
+ (response) => {
7623
+ if (isStream) {
7624
+ return wrapStream(response, span2);
7625
+ }
7626
+ finalizeResponse(span2, response);
7627
+ return response;
7628
+ },
7629
+ (err) => {
7630
+ recordError4(span2, err);
7631
+ throw err;
7632
+ }
7633
+ );
7634
+ };
7635
+ }
7636
+ function setInputAttributes(span2, opts) {
7637
+ let idx = 0;
7638
+ const config = opts?.config;
7639
+ const systemInstruction = config?.systemInstruction ?? config?.system_instruction;
7640
+ if (systemInstruction) {
7641
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "system");
7642
+ span2.setAttribute(
7643
+ `neatlogs.llm.input_messages.${idx}.content`,
7644
+ typeof systemInstruction === "string" ? systemInstruction : safeStringify6(systemInstruction)
7645
+ );
7646
+ idx++;
7647
+ }
7648
+ const contents = opts?.contents;
7649
+ if (typeof contents === "string") {
7650
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
7651
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, contents);
7652
+ } else if (Array.isArray(contents)) {
7653
+ for (const item of contents) {
7654
+ if (typeof item === "string") {
7655
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
7656
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, item);
7657
+ idx++;
7658
+ } else if (item && typeof item === "object") {
7659
+ const role = item.role ?? "user";
7660
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, role);
7661
+ const parts = item.parts ?? [];
7662
+ const textParts = [];
7663
+ for (const part of parts) {
7664
+ if (typeof part === "string") textParts.push(part);
7665
+ else if (part?.text) textParts.push(part.text);
7666
+ }
7667
+ span2.setAttribute(
7668
+ `neatlogs.llm.input_messages.${idx}.content`,
7669
+ textParts.length ? textParts.join("\n") : safeStringify6(parts)
7670
+ );
7671
+ idx++;
7378
7672
  }
7379
7673
  }
7380
- return;
7674
+ } else if (contents) {
7675
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
7676
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, safeStringify6(contents));
7381
7677
  }
7382
- if (eventName === "gen_ai.choice") {
7383
- const out = flattenStrandsContent(attrs.message);
7384
- if (out) setOutput(span2, isTool, out);
7385
- return;
7678
+ const tools = config?.tools;
7679
+ if (Array.isArray(tools)) {
7680
+ let t = 0;
7681
+ for (const tool of tools) {
7682
+ const decls = tool?.functionDeclarations ?? tool?.function_declarations ?? [];
7683
+ for (const fn of decls) {
7684
+ span2.setAttribute(`neatlogs.llm.tools.${t}.name`, fn?.name ?? "");
7685
+ if (fn?.description) span2.setAttribute(`neatlogs.llm.tools.${t}.description`, fn.description);
7686
+ if (fn?.parameters) span2.setAttribute(`neatlogs.llm.tools.${t}.input_schema`, safeStringify6(fn.parameters));
7687
+ t++;
7688
+ }
7689
+ }
7386
7690
  }
7387
- if (eventName === "gen_ai.client.inference.operation.details") {
7388
- const out = flattenStrandsContent(attrs["gen_ai.output.messages"]);
7389
- if (out) setOutput(span2, isTool, out);
7691
+ if (config) {
7692
+ if (config.temperature != null) span2.setAttribute("neatlogs.llm.temperature", config.temperature);
7693
+ if (config.topP != null) span2.setAttribute("neatlogs.llm.top_p", config.topP);
7694
+ if (config.topK != null) span2.setAttribute("neatlogs.llm.top_k", config.topK);
7695
+ const maxTokens = config.maxOutputTokens ?? config.max_output_tokens;
7696
+ if (maxTokens != null) span2.setAttribute("neatlogs.llm.max_tokens", maxTokens);
7697
+ const params = {};
7698
+ if (config.temperature != null) params.temperature = config.temperature;
7699
+ if (config.topP != null) params.top_p = config.topP;
7700
+ if (config.topK != null) params.top_k = config.topK;
7701
+ if (maxTokens != null) params.max_tokens = maxTokens;
7702
+ if (config.frequencyPenalty != null) params.frequency_penalty = config.frequencyPenalty;
7703
+ if (config.presencePenalty != null) params.presence_penalty = config.presencePenalty;
7704
+ if (Object.keys(params).length > 0) {
7705
+ span2.setAttribute("neatlogs.llm.invocation_parameters", JSON.stringify(params));
7706
+ }
7390
7707
  }
7391
7708
  }
7392
- function setOutput(span2, isTool, out) {
7393
- span2.setAttribute("output.value", out);
7394
- if (isTool) {
7395
- span2.setAttribute("neatlogs.tool.output", out);
7396
- } else {
7709
+ function wrapStream(stream, span2) {
7710
+ const chunks = [];
7711
+ const originalAsyncIterator = stream?.[Symbol.asyncIterator]?.bind(stream);
7712
+ if (!originalAsyncIterator) {
7713
+ finalizeStreamChunks3(span2, chunks);
7714
+ return stream;
7715
+ }
7716
+ const wrapped = Object.create(Object.getPrototypeOf(stream));
7717
+ Object.assign(wrapped, stream);
7718
+ wrapped[Symbol.asyncIterator] = function() {
7719
+ const iterator = originalAsyncIterator();
7720
+ return {
7721
+ async next() {
7722
+ try {
7723
+ const result = await iterator.next();
7724
+ if (result.done) {
7725
+ finalizeStreamChunks3(span2, chunks);
7726
+ return result;
7727
+ }
7728
+ chunks.push(result.value);
7729
+ return result;
7730
+ } catch (err) {
7731
+ recordError4(span2, err);
7732
+ throw err;
7733
+ }
7734
+ },
7735
+ async return(value) {
7736
+ finalizeStreamChunks3(span2, chunks);
7737
+ return iterator.return?.(value) ?? { done: true, value: void 0 };
7738
+ },
7739
+ async throw(err) {
7740
+ recordError4(span2, err);
7741
+ return iterator.throw?.(err) ?? { done: true, value: void 0 };
7742
+ }
7743
+ };
7744
+ };
7745
+ return wrapped;
7746
+ }
7747
+ function finalizeStreamChunks3(span2, chunks) {
7748
+ const textParts = [];
7749
+ let finishReason = "";
7750
+ let usage = null;
7751
+ for (const chunk of chunks) {
7752
+ for (const candidate of chunk?.candidates ?? []) {
7753
+ for (const part of candidate?.content?.parts ?? []) {
7754
+ if (part?.text && !part?.thought) textParts.push(part.text);
7755
+ }
7756
+ if (candidate?.finishReason) finishReason = candidate.finishReason;
7757
+ }
7758
+ if (chunk?.usageMetadata) usage = chunk.usageMetadata;
7759
+ }
7760
+ const fullText = textParts.join("");
7761
+ if (fullText) {
7397
7762
  span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7398
- span2.setAttribute("neatlogs.llm.output_messages.0.content", out);
7399
- span2.setAttribute("neatlogs.llm.output", safeStringify6({ role: "assistant", content: out }));
7763
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", fullText);
7400
7764
  }
7765
+ if (finishReason) span2.setAttribute("neatlogs.llm.finish_reason", String(finishReason));
7766
+ setUsage(span2, usage);
7767
+ span2.setStatus({ code: SpanStatusCode8.OK });
7768
+ span2.end();
7401
7769
  }
7402
- function appendInputMessage(span2, role, content) {
7403
- const idx = span2.__neatlogs_in_idx ?? 0;
7404
- span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, role);
7405
- span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, content);
7406
- span2.__neatlogs_in_idx = idx + 1;
7407
- const existing = span2.__neatlogs_in_msgs ?? [];
7408
- existing.push({ role, content });
7409
- span2.__neatlogs_in_msgs = existing;
7410
- const blob = safeStringify6({ messages: existing });
7411
- span2.setAttribute("input.value", blob);
7412
- span2.setAttribute("neatlogs.llm.input", blob);
7770
+ function finalizeResponse(span2, response) {
7771
+ const textParts = [];
7772
+ let toolIdx = 0;
7773
+ for (const candidate of response?.candidates ?? []) {
7774
+ for (const part of candidate?.content?.parts ?? []) {
7775
+ if (part?.text && !part?.thought) {
7776
+ textParts.push(part.text);
7777
+ } else if (part?.thought && part?.text) {
7778
+ span2.setAttribute("neatlogs.llm.output_messages.0.thinking", part.text);
7779
+ } else if (part?.functionCall) {
7780
+ const fc = part.functionCall;
7781
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.name`, fc?.name ?? "");
7782
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.arguments`, safeStringify6(fc?.args ?? {}));
7783
+ toolIdx++;
7784
+ }
7785
+ }
7786
+ if (candidate?.finishReason) span2.setAttribute("neatlogs.llm.finish_reason", String(candidate.finishReason));
7787
+ }
7788
+ if (textParts.length) {
7789
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7790
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", textParts.join(""));
7791
+ }
7792
+ setUsage(span2, response?.usageMetadata);
7793
+ span2.setStatus({ code: SpanStatusCode8.OK });
7794
+ span2.end();
7413
7795
  }
7414
- function readSpanOp(span2) {
7796
+ function setUsage(span2, usage) {
7797
+ if (!usage) return;
7798
+ if (usage.promptTokenCount != null) span2.setAttribute("neatlogs.llm.token_count.prompt", usage.promptTokenCount);
7799
+ if (usage.candidatesTokenCount != null) span2.setAttribute("neatlogs.llm.token_count.completion", usage.candidatesTokenCount);
7800
+ if (usage.totalTokenCount != null) span2.setAttribute("neatlogs.llm.token_count.total", usage.totalTokenCount);
7801
+ if (usage.cachedContentTokenCount != null) span2.setAttribute("neatlogs.llm.token_count.cache_read", usage.cachedContentTokenCount);
7802
+ if (usage.thoughtsTokenCount != null) span2.setAttribute("neatlogs.llm.token_count.reasoning", usage.thoughtsTokenCount);
7803
+ }
7804
+ function safeStringify6(value) {
7415
7805
  try {
7416
- const a = span2.attributes;
7417
- if (!a) return "";
7418
- const v = typeof a.get === "function" ? a.get("gen_ai.operation.name") : a["gen_ai.operation.name"];
7419
- return typeof v === "string" ? v.toLowerCase() : "";
7806
+ return typeof value === "string" ? value : JSON.stringify(value);
7420
7807
  } catch {
7421
7808
  return "";
7422
7809
  }
7423
7810
  }
7424
- function flattenStrandsContent(content) {
7425
- if (content == null) return "";
7426
- let val = content;
7427
- if (typeof val === "string") {
7428
- const s = val.trim();
7429
- if (!(s.startsWith("[") || s.startsWith("{"))) return val;
7430
- try {
7431
- val = JSON.parse(s);
7432
- } catch {
7433
- return val;
7811
+ function recordError4(span2, err) {
7812
+ if (err instanceof Error) {
7813
+ span2.setStatus({ code: SpanStatusCode8.ERROR, message: err.message });
7814
+ span2.recordException(err);
7815
+ } else {
7816
+ span2.setStatus({ code: SpanStatusCode8.ERROR, message: String(err) });
7817
+ }
7818
+ span2.end();
7819
+ }
7820
+
7821
+ // src/bedrock.ts
7822
+ import { trace as trace10, context as otelContext8, SpanStatusCode as SpanStatusCode9 } from "@opentelemetry/api";
7823
+ var TRACER_NAME7 = "neatlogs.bedrock";
7824
+ var PROVIDER3 = "bedrock";
7825
+ function traceTool5(name, fn) {
7826
+ return async function tracedTool(args) {
7827
+ const tracer = trace10.getTracer(TRACER_NAME7);
7828
+ return tracer.startActiveSpan(
7829
+ `tool.${name}`,
7830
+ {
7831
+ attributes: {
7832
+ "neatlogs.span.kind": "TOOL",
7833
+ "neatlogs.tool.name": name,
7834
+ "input.value": safeStringify7(args)
7835
+ }
7836
+ },
7837
+ otelContext8.active(),
7838
+ async (span2) => {
7839
+ try {
7840
+ const result = await fn(args);
7841
+ span2.setAttribute("output.value", safeStringify7(result));
7842
+ span2.setStatus({ code: SpanStatusCode9.OK });
7843
+ return result;
7844
+ } catch (err) {
7845
+ recordError5(span2, err);
7846
+ throw err;
7847
+ } finally {
7848
+ span2.end();
7849
+ }
7850
+ }
7851
+ );
7852
+ };
7853
+ }
7854
+ function wrapBedrock(client) {
7855
+ const c = client;
7856
+ if (c._neatlogsBedrockPatched) return client;
7857
+ if (typeof c.send !== "function") return client;
7858
+ const originalSend = c.send.bind(c);
7859
+ c.send = function(command, ...rest) {
7860
+ const name = command?.constructor?.name ?? "";
7861
+ const input = command?.input ?? {};
7862
+ if (name === "ConverseCommand") {
7863
+ return tracedConverse(originalSend, command, input, rest, false);
7864
+ }
7865
+ if (name === "ConverseStreamCommand") {
7866
+ return tracedConverse(originalSend, command, input, rest, true);
7434
7867
  }
7868
+ if (name === "InvokeModelCommand") {
7869
+ return tracedInvokeModel(originalSend, command, input, rest, false);
7870
+ }
7871
+ if (name === "InvokeModelWithResponseStreamCommand") {
7872
+ return tracedInvokeModel(originalSend, command, input, rest, true);
7873
+ }
7874
+ return originalSend(command, ...rest);
7875
+ };
7876
+ c._neatlogsBedrockPatched = true;
7877
+ return client;
7878
+ }
7879
+ function vendorFromModel(modelId) {
7880
+ let tail = String(modelId ?? "").split("/").pop() ?? "";
7881
+ for (const prefix of ["us.", "eu.", "apac.", "us-gov."]) {
7882
+ if (tail.startsWith(prefix)) tail = tail.slice(prefix.length);
7435
7883
  }
7436
- return flattenBlocks(val);
7884
+ const vendor = tail.includes(".") ? tail.split(".")[0] : "";
7885
+ return vendor || "bedrock";
7437
7886
  }
7438
- function flattenBlocks(val) {
7439
- const items = Array.isArray(val) ? val : [val];
7440
- const out = [];
7441
- for (const item of items) {
7442
- if (typeof item === "string") {
7443
- out.push(item);
7444
- continue;
7887
+ function startSpan(name, modelId, isStream) {
7888
+ return trace10.getTracer(TRACER_NAME7).startSpan(name, {
7889
+ attributes: {
7890
+ "neatlogs.span.kind": "LLM",
7891
+ "neatlogs.llm.provider": PROVIDER3,
7892
+ "neatlogs.llm.system": vendorFromModel(modelId),
7893
+ "neatlogs.llm.model_name": String(modelId ?? ""),
7894
+ "neatlogs.llm.is_streaming": isStream
7895
+ }
7896
+ }, otelContext8.active());
7897
+ }
7898
+ function tracedConverse(originalSend, command, input, rest, isStream) {
7899
+ const span2 = startSpan(
7900
+ isStream ? "bedrock.converse_stream" : "bedrock.converse",
7901
+ input?.modelId,
7902
+ isStream
7903
+ );
7904
+ setConverseInput(span2, input);
7905
+ const ctx = trace10.setSpan(otelContext8.active(), span2);
7906
+ const promise = otelContext8.with(ctx, () => originalSend(command, ...rest));
7907
+ return promise.then(
7908
+ (response) => {
7909
+ if (isStream) {
7910
+ return wrapConverseStream(response, span2);
7911
+ }
7912
+ finalizeConverse(span2, response);
7913
+ return response;
7914
+ },
7915
+ (err) => {
7916
+ recordError5(span2, err);
7917
+ throw err;
7445
7918
  }
7446
- if (!item || typeof item !== "object") {
7447
- if (item != null) out.push(String(item));
7448
- continue;
7919
+ );
7920
+ }
7921
+ function setConverseInput(span2, input) {
7922
+ let idx = 0;
7923
+ if (Array.isArray(input?.system)) {
7924
+ const text = input.system.map((b) => b?.text ?? "").join(" ").trim();
7925
+ if (text) {
7926
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "system");
7927
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, text);
7928
+ idx++;
7449
7929
  }
7450
- const o = item;
7451
- if (typeof o.text === "string") out.push(o.text);
7452
- else if (typeof o.content === "string") out.push(o.content);
7453
- else if (o.toolUse) out.push(`${o.toolUse.name ?? "tool"}(${safeStringify6(o.toolUse.input ?? {})})`);
7454
- else if (o.toolResult) out.push(flattenBlocks(o.toolResult.content ?? o.toolResult));
7455
- else if (Array.isArray(o.parts)) out.push(flattenBlocks(o.parts));
7456
- else if (Array.isArray(o.content)) out.push(flattenBlocks(o.content));
7457
- else out.push(safeStringify6(o));
7458
7930
  }
7459
- return out.filter(Boolean).join("\n");
7931
+ for (const msg of input?.messages ?? []) {
7932
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, msg?.role ?? "user");
7933
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, converseBlocksToText(msg?.content));
7934
+ idx++;
7935
+ }
7936
+ const cfg = input?.inferenceConfig ?? {};
7937
+ if (cfg.temperature != null) span2.setAttribute("neatlogs.llm.temperature", cfg.temperature);
7938
+ if (cfg.topP != null) span2.setAttribute("neatlogs.llm.top_p", cfg.topP);
7939
+ if (cfg.maxTokens != null) span2.setAttribute("neatlogs.llm.max_tokens", cfg.maxTokens);
7940
+ const tools = input?.toolConfig?.tools ?? [];
7941
+ for (let i = 0; i < tools.length; i++) {
7942
+ const spec = tools[i]?.toolSpec ?? {};
7943
+ if (spec.name) span2.setAttribute(`neatlogs.llm.tools.${i}.name`, spec.name);
7944
+ if (spec.description) span2.setAttribute(`neatlogs.llm.tools.${i}.description`, spec.description);
7945
+ if (spec.inputSchema) span2.setAttribute(`neatlogs.llm.tools.${i}.input_schema`, safeStringify7(spec.inputSchema));
7946
+ }
7460
7947
  }
7461
- function safeStringify6(value) {
7462
- if (typeof value === "string") return value;
7463
- try {
7464
- return JSON.stringify(value) ?? "";
7465
- } catch {
7466
- return "";
7948
+ function converseBlocksToText(content) {
7949
+ if (typeof content === "string") return content;
7950
+ if (!Array.isArray(content)) return safeStringify7(content);
7951
+ const parts = [];
7952
+ for (const block of content) {
7953
+ if (block && typeof block === "object") {
7954
+ if ("text" in block) parts.push(String(block.text));
7955
+ else if ("toolResult" in block) parts.push(safeStringify7(block.toolResult));
7956
+ else if ("toolUse" in block) parts.push(safeStringify7(block.toolUse));
7957
+ else parts.push(safeStringify7(block));
7958
+ } else {
7959
+ parts.push(String(block));
7960
+ }
7467
7961
  }
7962
+ return parts.join("\n");
7468
7963
  }
7469
-
7470
- // src/openai-agents.ts
7471
- import { trace as trace9, context as otelContext7, SpanStatusCode as SpanStatusCode8 } from "@opentelemetry/api";
7472
- var TRACER_NAME6 = "neatlogs.openai_agents";
7473
- function openaiAgentsProcessor() {
7474
- return new NeatlogsTraceProcessor();
7964
+ function finalizeConverse(span2, response) {
7965
+ const content = response?.output?.message?.content ?? [];
7966
+ const textParts = [];
7967
+ let toolIdx = 0;
7968
+ for (const block of content) {
7969
+ if (!block || typeof block !== "object") continue;
7970
+ if ("text" in block) {
7971
+ textParts.push(String(block.text));
7972
+ } else if ("toolUse" in block) {
7973
+ const tu = block.toolUse;
7974
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.id`, String(tu?.toolUseId ?? ""));
7975
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.name`, String(tu?.name ?? ""));
7976
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.arguments`, safeStringify7(tu?.input ?? {}));
7977
+ toolIdx++;
7978
+ }
7979
+ }
7980
+ if (textParts.length) {
7981
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7982
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", textParts.join(""));
7983
+ }
7984
+ if (response?.stopReason) span2.setAttribute("neatlogs.llm.finish_reason", String(response.stopReason));
7985
+ setConverseUsage(span2, response?.usage);
7986
+ span2.setStatus({ code: SpanStatusCode9.OK });
7987
+ span2.end();
7475
7988
  }
7476
- var NeatlogsTraceProcessor = class {
7477
- _spans = /* @__PURE__ */ new Map();
7478
- _startTimes = /* @__PURE__ */ new Map();
7479
- // The @openai/agents SDK passes a Trace object: { traceId, name, groupId, metadata }.
7480
- onTraceStart(traceData) {
7481
- const tracer = trace9.getTracer(TRACER_NAME6);
7482
- const attrs = { "neatlogs.span.kind": "WORKFLOW" };
7483
- const workflowName = traceData?.name ?? traceData?.workflow_name;
7484
- if (workflowName) attrs["neatlogs.workflow.name"] = workflowName;
7485
- const traceId = traceData?.traceId ?? traceData?.trace_id;
7486
- if (traceId) attrs["neatlogs.agent.trace_id"] = String(traceId);
7487
- const span2 = tracer.startSpan("openai_agents.trace", { attributes: attrs }, otelContext7.active());
7488
- const key = String(traceId ?? `trace_${Date.now()}`);
7489
- this._spans.set(key, span2);
7490
- this._startTimes.set(key, Date.now());
7491
- }
7492
- onTraceEnd(traceData) {
7493
- const key = String(traceData?.traceId ?? traceData?.trace_id ?? "");
7494
- const span2 = this._spans.get(key);
7495
- if (!span2) return;
7496
- const startTime = this._startTimes.get(key);
7497
- if (startTime) {
7498
- span2.setAttribute("neatlogs.metrics.duration_ms", Date.now() - startTime);
7499
- }
7500
- span2.setStatus({ code: SpanStatusCode8.OK });
7989
+ function setConverseUsage(span2, usage) {
7990
+ if (!usage) return;
7991
+ if (usage.inputTokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", usage.inputTokens);
7992
+ if (usage.outputTokens != null) span2.setAttribute("neatlogs.llm.token_count.completion", usage.outputTokens);
7993
+ if (usage.totalTokens != null) span2.setAttribute("neatlogs.llm.token_count.total", usage.totalTokens);
7994
+ if (usage.cacheReadInputTokens != null) span2.setAttribute("neatlogs.llm.token_count.cache_read", usage.cacheReadInputTokens);
7995
+ if (usage.cacheWriteInputTokens != null) span2.setAttribute("neatlogs.llm.token_count.cache_write", usage.cacheWriteInputTokens);
7996
+ }
7997
+ function wrapConverseStream(response, span2) {
7998
+ const stream = response?.stream;
7999
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
8000
+ span2.setStatus({ code: SpanStatusCode9.OK });
7501
8001
  span2.end();
7502
- this._spans.delete(key);
7503
- this._startTimes.delete(key);
8002
+ return response;
7504
8003
  }
7505
- // The SDK passes a Span object: { type, spanId, traceId, parentId?, spanData: {...} }.
7506
- // The meaningful payload (type, name, input, output, usage, ...) lives in `spanData`.
7507
- onSpanStart(span2) {
7508
- const tracer = trace9.getTracer(TRACER_NAME6);
7509
- const data = span2?.spanData ?? span2 ?? {};
7510
- const spanType = data?.type ?? data?.span_type ?? span2?.type ?? "";
7511
- const spanId = String(span2?.spanId ?? span2?.span_id ?? data?.span_id ?? `span_${Date.now()}`);
7512
- const parentKey = String(span2?.parentId ?? span2?.traceId ?? span2?.trace_id ?? "");
7513
- const parentSpan = this._spans.get(parentKey);
7514
- const parentCtx = parentSpan ? trace9.setSpan(otelContext7.active(), parentSpan) : otelContext7.active();
7515
- let otelSpan;
7516
- if (spanType === "agent" || spanType === "agent_run") {
7517
- const agentName = data?.name ?? data?.agent_name ?? "agent";
7518
- const attrs = {
7519
- "neatlogs.span.kind": "AGENT",
7520
- "neatlogs.agent.name": agentName
7521
- };
7522
- if (Array.isArray(data?.tools) && data.tools.length) {
7523
- attrs["neatlogs.agent.available_tools"] = data.tools.join(",");
7524
- }
7525
- otelSpan = tracer.startSpan(`openai_agents.agent.${agentName}`, { attributes: attrs }, parentCtx);
7526
- } else if (spanType === "response" || spanType === "generation" || spanType === "llm") {
7527
- const attrs = {
7528
- "neatlogs.span.kind": "LLM",
7529
- "neatlogs.llm.provider": "openai"
7530
- };
7531
- const model = data?.model;
7532
- if (model) attrs["neatlogs.llm.model_name"] = model;
7533
- const inputMsgs = data?.input ?? data?.messages;
7534
- if (inputMsgs && Array.isArray(inputMsgs)) {
7535
- for (let i = 0; i < inputMsgs.length; i++) {
7536
- const msg = inputMsgs[i];
7537
- const role = typeof msg === "object" ? msg.role ?? "" : "";
7538
- const content = typeof msg === "object" ? msg.content ?? "" : String(msg);
7539
- if (role) attrs[`neatlogs.llm.input_messages.${i}.role`] = role;
7540
- if (content) attrs[`neatlogs.llm.input_messages.${i}.content`] = (typeof content === "string" ? content : safeStringify7(content)).slice(0, 1e4);
8004
+ const originalIterator = stream[Symbol.asyncIterator].bind(stream);
8005
+ const textParts = [];
8006
+ const toolCalls = {};
8007
+ let finishReason = "";
8008
+ let usage = null;
8009
+ const wrappedStream = {
8010
+ [Symbol.asyncIterator]() {
8011
+ const iterator = originalIterator();
8012
+ return {
8013
+ async next() {
8014
+ try {
8015
+ const result = await iterator.next();
8016
+ if (result.done) {
8017
+ finalizeConverseStream(span2, textParts, toolCalls, finishReason, usage);
8018
+ return result;
8019
+ }
8020
+ const ev = result.value;
8021
+ const delta = ev?.contentBlockDelta?.delta;
8022
+ if (delta?.text) textParts.push(delta.text);
8023
+ if (delta?.toolUse?.input) {
8024
+ const idx = ev.contentBlockDelta.contentBlockIndex ?? 0;
8025
+ toolCalls[idx] = toolCalls[idx] ?? { id: "", name: "", arguments: "" };
8026
+ toolCalls[idx].arguments += delta.toolUse.input;
8027
+ }
8028
+ const startBlk = ev?.contentBlockStart?.start?.toolUse;
8029
+ if (startBlk) {
8030
+ const idx = ev.contentBlockStart.contentBlockIndex ?? 0;
8031
+ toolCalls[idx] = toolCalls[idx] ?? { id: "", name: "", arguments: "" };
8032
+ toolCalls[idx].name = startBlk.name ?? "";
8033
+ toolCalls[idx].id = startBlk.toolUseId ?? "";
8034
+ }
8035
+ if (ev?.messageStop?.stopReason) finishReason = ev.messageStop.stopReason;
8036
+ if (ev?.metadata?.usage) usage = ev.metadata.usage;
8037
+ return result;
8038
+ } catch (err) {
8039
+ recordError5(span2, err);
8040
+ throw err;
8041
+ }
8042
+ },
8043
+ async return(value) {
8044
+ finalizeConverseStream(span2, textParts, toolCalls, finishReason, usage);
8045
+ return iterator.return?.(value) ?? { done: true, value: void 0 };
7541
8046
  }
7542
- }
7543
- otelSpan = tracer.startSpan("openai_agents.generation", { attributes: attrs }, parentCtx);
7544
- } else if (spanType === "function" || spanType === "tool" || spanType === "tool_call") {
7545
- const toolName = data?.name ?? data?.function_name ?? "tool";
7546
- const attrs = {
7547
- "neatlogs.span.kind": "TOOL",
7548
- "neatlogs.tool.name": toolName
7549
8047
  };
7550
- const toolInput = data?.input ?? data?.arguments;
7551
- if (toolInput !== void 0) {
7552
- attrs["input.value"] = (typeof toolInput === "string" ? toolInput : safeStringify7(toolInput)).slice(0, 1e4);
8048
+ }
8049
+ };
8050
+ response.stream = wrappedStream;
8051
+ return response;
8052
+ }
8053
+ function finalizeConverseStream(span2, textParts, toolCalls, finishReason, usage) {
8054
+ const full = textParts.join("");
8055
+ if (full) {
8056
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8057
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", full);
8058
+ }
8059
+ let j = 0;
8060
+ for (const tc of Object.values(toolCalls)) {
8061
+ if (tc.id) span2.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc.id);
8062
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc.name);
8063
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, tc.arguments);
8064
+ j++;
8065
+ }
8066
+ if (finishReason) span2.setAttribute("neatlogs.llm.finish_reason", String(finishReason));
8067
+ setConverseUsage(span2, usage);
8068
+ span2.setStatus({ code: SpanStatusCode9.OK });
8069
+ span2.end();
8070
+ }
8071
+ function isEmbeddingModel(modelId) {
8072
+ return String(modelId ?? "").toLowerCase().includes("embed");
8073
+ }
8074
+ function tracedInvokeModel(originalSend, command, input, rest, isStream) {
8075
+ const vendor = vendorFromModel(input?.modelId);
8076
+ const isEmbedding = !isStream && isEmbeddingModel(input?.modelId);
8077
+ const bodyIn = decodeBody(input?.body);
8078
+ let span2;
8079
+ if (isEmbedding) {
8080
+ span2 = trace10.getTracer(TRACER_NAME7).startSpan("bedrock.invoke_model", {
8081
+ attributes: {
8082
+ "neatlogs.span.kind": "EMBEDDING",
8083
+ "neatlogs.llm.provider": PROVIDER3,
8084
+ "neatlogs.embedding.model_name": String(input?.modelId ?? "")
7553
8085
  }
7554
- otelSpan = tracer.startSpan(`openai_agents.tool.${toolName}`, { attributes: attrs }, parentCtx);
7555
- } else if (spanType === "handoff") {
7556
- const attrs = { "neatlogs.span.kind": "AGENT" };
7557
- if (data?.from_agent) attrs["neatlogs.agent.handoff_from"] = String(data.from_agent);
7558
- if (data?.to_agent) attrs["neatlogs.agent.name"] = String(data.to_agent);
7559
- otelSpan = tracer.startSpan("openai_agents.handoff", { attributes: attrs }, parentCtx);
7560
- } else {
7561
- const attrs = { "neatlogs.span.kind": "CHAIN" };
7562
- otelSpan = tracer.startSpan(`openai_agents.${spanType || "span"}`, { attributes: attrs }, parentCtx);
8086
+ }, otelContext8.active());
8087
+ const text = bodyIn?.inputText ?? bodyIn?.texts ?? bodyIn?.input_text;
8088
+ if (text) {
8089
+ span2.setAttribute("neatlogs.embedding.text", typeof text === "string" ? text : safeStringify7(text));
7563
8090
  }
7564
- this._spans.set(spanId, otelSpan);
7565
- this._startTimes.set(spanId, Date.now());
8091
+ } else {
8092
+ span2 = startSpan(
8093
+ isStream ? "bedrock.invoke_model_with_response_stream" : "bedrock.invoke_model",
8094
+ input?.modelId,
8095
+ isStream
8096
+ );
8097
+ setInvokeInput(span2, vendor, bodyIn);
7566
8098
  }
7567
- onSpanEnd(span2) {
7568
- const data = span2?.spanData ?? span2 ?? {};
7569
- const spanId = String(span2?.spanId ?? span2?.span_id ?? data?.span_id ?? "");
7570
- const otelSpan = this._spans.get(spanId);
7571
- if (!otelSpan) return;
7572
- const spanType = data?.type ?? data?.span_type ?? span2?.type ?? "";
7573
- const startTime = this._startTimes.get(spanId);
7574
- if (spanType === "response" || spanType === "generation" || spanType === "llm") {
7575
- const resp = data?._response ?? data?.response ?? {};
7576
- const model = data?.model ?? resp?.model;
7577
- if (model) otelSpan.setAttribute("neatlogs.llm.model_name", model);
7578
- const outputItems = data?.output ?? resp?.output;
7579
- if (Array.isArray(outputItems)) {
7580
- const text = outputItems.filter((o) => o?.type === "message" || o?.role === "assistant").flatMap((o) => Array.isArray(o.content) ? o.content : [o.content]).map((c) => typeof c === "string" ? c : c?.text ?? "").join("");
7581
- if (text) {
7582
- otelSpan.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7583
- otelSpan.setAttribute("neatlogs.llm.output_messages.0.content", text.slice(0, 1e4));
7584
- }
7585
- const toolCalls = outputItems.filter((o) => o?.type === "function_call");
7586
- for (let i = 0; i < toolCalls.length; i++) {
7587
- const tc = toolCalls[i];
7588
- otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.name`, tc.name ?? "");
7589
- otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.arguments`, typeof tc.arguments === "string" ? tc.arguments : safeStringify7(tc.arguments ?? {}));
7590
- if (tc.callId ?? tc.id) otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.id`, tc.callId ?? tc.id);
7591
- }
7592
- } else if (outputItems?.content) {
7593
- otelSpan.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7594
- const c = outputItems.content;
7595
- otelSpan.setAttribute("neatlogs.llm.output_messages.0.content", (typeof c === "string" ? c : safeStringify7(c)).slice(0, 1e4));
8099
+ const ctx = trace10.setSpan(otelContext8.active(), span2);
8100
+ const promise = otelContext8.with(ctx, () => originalSend(command, ...rest));
8101
+ return promise.then(
8102
+ (response) => {
8103
+ if (isStream) {
8104
+ return wrapInvokeStream(response, span2, vendor);
7596
8105
  }
7597
- const usage = data?.usage ?? resp?.usage;
7598
- if (usage) {
7599
- const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens;
7600
- const outputTokens = usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens;
7601
- const totalTokens = usage.total_tokens ?? usage.totalTokens;
7602
- if (inputTokens != null) otelSpan.setAttribute("neatlogs.llm.token_count.prompt", inputTokens);
7603
- if (outputTokens != null) otelSpan.setAttribute("neatlogs.llm.token_count.completion", outputTokens);
7604
- if (totalTokens != null) otelSpan.setAttribute("neatlogs.llm.token_count.total", totalTokens);
7605
- else if (inputTokens != null && outputTokens != null) {
7606
- otelSpan.setAttribute("neatlogs.llm.token_count.total", inputTokens + outputTokens);
8106
+ try {
8107
+ if (isEmbedding) {
8108
+ finalizeInvokeEmbedding(span2, decodeBody(response?.body));
8109
+ } else {
8110
+ finalizeInvoke(span2, vendor, decodeBody(response?.body));
7607
8111
  }
8112
+ } catch {
8113
+ span2.setStatus({ code: SpanStatusCode9.OK });
8114
+ span2.end();
7608
8115
  }
7609
- } else if (spanType === "function" || spanType === "tool" || spanType === "tool_call") {
7610
- const output = data?.output ?? data?.result;
7611
- if (output != null) {
7612
- otelSpan.setAttribute("output.value", (typeof output === "string" ? output : safeStringify7(output)).slice(0, 1e4));
7613
- }
7614
- } else if (spanType === "agent" || spanType === "agent_run") {
7615
- const output = data?.output;
7616
- if (output != null) {
7617
- otelSpan.setAttribute("output.value", (typeof output === "string" ? output : safeStringify7(output)).slice(0, 1e4));
7618
- }
7619
- }
7620
- const error = data?.error ?? span2?.error;
7621
- if (error) {
7622
- if (error instanceof Error) {
7623
- otelSpan.setStatus({ code: SpanStatusCode8.ERROR, message: error.message });
7624
- otelSpan.recordException(error);
7625
- } else {
7626
- otelSpan.setStatus({ code: SpanStatusCode8.ERROR, message: String(error) });
7627
- }
7628
- } else {
7629
- otelSpan.setStatus({ code: SpanStatusCode8.OK });
8116
+ return response;
8117
+ },
8118
+ (err) => {
8119
+ recordError5(span2, err);
8120
+ throw err;
7630
8121
  }
7631
- if (startTime) {
7632
- otelSpan.setAttribute("neatlogs.llm.metrics.duration_ms", Date.now() - startTime);
8122
+ );
8123
+ }
8124
+ function finalizeInvokeEmbedding(span2, body) {
8125
+ const emb = body?.embedding;
8126
+ const embs = body?.embeddings;
8127
+ if (Array.isArray(emb)) {
8128
+ span2.setAttribute("neatlogs.embedding.count", 1);
8129
+ span2.setAttribute("neatlogs.embedding.dimensions", emb.length);
8130
+ } else if (Array.isArray(embs) && embs.length) {
8131
+ span2.setAttribute("neatlogs.embedding.count", embs.length);
8132
+ if (Array.isArray(embs[0])) span2.setAttribute("neatlogs.embedding.dimensions", embs[0].length);
8133
+ }
8134
+ if (body?.inputTextTokenCount != null) {
8135
+ span2.setAttribute("neatlogs.llm.token_count.prompt", body.inputTextTokenCount);
8136
+ span2.setAttribute("neatlogs.embedding.token_count", body.inputTextTokenCount);
8137
+ }
8138
+ span2.setStatus({ code: SpanStatusCode9.OK });
8139
+ span2.end();
8140
+ }
8141
+ function decodeBody(body) {
8142
+ try {
8143
+ if (body == null) return {};
8144
+ if (typeof body === "string") return JSON.parse(body);
8145
+ if (body instanceof Uint8Array) return JSON.parse(new TextDecoder().decode(body));
8146
+ if (typeof body === "object" && typeof body.byteLength === "number") {
8147
+ return JSON.parse(new TextDecoder().decode(body));
7633
8148
  }
7634
- otelSpan.end();
7635
- this._spans.delete(spanId);
7636
- this._startTimes.delete(spanId);
8149
+ if (typeof body === "object") return body;
8150
+ } catch {
7637
8151
  }
7638
- shutdown() {
7639
- for (const [, span2] of this._spans) {
7640
- span2.setStatus({ code: SpanStatusCode8.ERROR, message: "Processor shutdown before span completed" });
7641
- span2.end();
8152
+ return {};
8153
+ }
8154
+ function setInvokeInput(span2, vendor, body) {
8155
+ let idx = 0;
8156
+ if (body?.system) {
8157
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "system");
8158
+ span2.setAttribute(
8159
+ `neatlogs.llm.input_messages.${idx}.content`,
8160
+ typeof body.system === "string" ? body.system : safeStringify7(body.system)
8161
+ );
8162
+ idx++;
8163
+ }
8164
+ if (Array.isArray(body?.messages)) {
8165
+ for (const msg of body.messages) {
8166
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, msg?.role ?? "user");
8167
+ span2.setAttribute(
8168
+ `neatlogs.llm.input_messages.${idx}.content`,
8169
+ typeof msg?.content === "string" ? msg.content : safeStringify7(msg?.content)
8170
+ );
8171
+ idx++;
8172
+ }
8173
+ } else if (body?.prompt) {
8174
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
8175
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, String(body.prompt));
8176
+ } else if (body?.inputText) {
8177
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, "user");
8178
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, String(body.inputText));
8179
+ }
8180
+ if (body?.temperature != null) span2.setAttribute("neatlogs.llm.temperature", body.temperature);
8181
+ if (body?.top_p != null) span2.setAttribute("neatlogs.llm.top_p", body.top_p);
8182
+ const maxTokens = body?.max_tokens ?? body?.maxTokens ?? body?.max_tokens_to_sample;
8183
+ if (maxTokens != null) span2.setAttribute("neatlogs.llm.max_tokens", maxTokens);
8184
+ const cfg = body?.textGenerationConfig;
8185
+ if (cfg) {
8186
+ if (cfg.temperature != null) span2.setAttribute("neatlogs.llm.temperature", cfg.temperature);
8187
+ if (cfg.topP != null) span2.setAttribute("neatlogs.llm.top_p", cfg.topP);
8188
+ if (cfg.maxTokenCount != null) span2.setAttribute("neatlogs.llm.max_tokens", cfg.maxTokenCount);
8189
+ }
8190
+ }
8191
+ function finalizeInvoke(span2, vendor, body) {
8192
+ let text;
8193
+ let promptTokens;
8194
+ let completionTokens;
8195
+ let finishReason;
8196
+ if (vendor === "anthropic") {
8197
+ if (Array.isArray(body?.content)) {
8198
+ text = body.content.filter((b) => b?.type === "text").map((b) => b.text).join("");
8199
+ let toolIdx = 0;
8200
+ for (const b of body.content) {
8201
+ if (b?.type === "tool_use") {
8202
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.id`, String(b.id ?? ""));
8203
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.name`, String(b.name ?? ""));
8204
+ span2.setAttribute(`neatlogs.llm.tool_calls.${toolIdx}.arguments`, safeStringify7(b.input ?? {}));
8205
+ toolIdx++;
8206
+ }
8207
+ }
8208
+ } else if (body?.completion != null) {
8209
+ text = body.completion;
8210
+ }
8211
+ promptTokens = body?.usage?.input_tokens;
8212
+ completionTokens = body?.usage?.output_tokens;
8213
+ finishReason = body?.stop_reason;
8214
+ } else if (vendor === "amazon") {
8215
+ const r = Array.isArray(body?.results) ? body.results[0] : void 0;
8216
+ if (r) {
8217
+ text = r.outputText;
8218
+ completionTokens = r.tokenCount;
8219
+ finishReason = r.completionReason;
8220
+ }
8221
+ promptTokens = body?.inputTextTokenCount;
8222
+ } else if (vendor === "meta") {
8223
+ text = body?.generation;
8224
+ promptTokens = body?.prompt_token_count;
8225
+ completionTokens = body?.generation_token_count;
8226
+ finishReason = body?.stop_reason;
8227
+ } else if (vendor === "cohere") {
8228
+ const g = Array.isArray(body?.generations) ? body.generations[0] : void 0;
8229
+ if (g) {
8230
+ text = g.text;
8231
+ finishReason = g.finish_reason;
7642
8232
  }
7643
- this._spans.clear();
7644
- this._startTimes.clear();
8233
+ } else {
8234
+ text = body?.generation ?? body?.completion ?? body?.outputText;
7645
8235
  }
7646
- forceFlush() {
8236
+ if (text) {
8237
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8238
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", String(text));
7647
8239
  }
7648
- };
8240
+ if (promptTokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", promptTokens);
8241
+ if (completionTokens != null) span2.setAttribute("neatlogs.llm.token_count.completion", completionTokens);
8242
+ if (promptTokens != null && completionTokens != null) {
8243
+ span2.setAttribute("neatlogs.llm.token_count.total", promptTokens + completionTokens);
8244
+ }
8245
+ if (finishReason) span2.setAttribute("neatlogs.llm.finish_reason", String(finishReason));
8246
+ span2.setStatus({ code: SpanStatusCode9.OK });
8247
+ span2.end();
8248
+ }
8249
+ function wrapInvokeStream(response, span2, vendor) {
8250
+ const body = response?.body;
8251
+ if (!body || typeof body[Symbol.asyncIterator] !== "function") {
8252
+ span2.setStatus({ code: SpanStatusCode9.OK });
8253
+ span2.end();
8254
+ return response;
8255
+ }
8256
+ const originalIterator = body[Symbol.asyncIterator].bind(body);
8257
+ const textParts = [];
8258
+ let promptTokens;
8259
+ let completionTokens;
8260
+ let finishReason;
8261
+ const finalize = () => {
8262
+ const full = textParts.join("");
8263
+ if (full) {
8264
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8265
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", full);
8266
+ }
8267
+ if (promptTokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", promptTokens);
8268
+ if (completionTokens != null) span2.setAttribute("neatlogs.llm.token_count.completion", completionTokens);
8269
+ if (finishReason) span2.setAttribute("neatlogs.llm.finish_reason", String(finishReason));
8270
+ span2.setStatus({ code: SpanStatusCode9.OK });
8271
+ span2.end();
8272
+ };
8273
+ const wrappedBody = {
8274
+ [Symbol.asyncIterator]() {
8275
+ const iterator = originalIterator();
8276
+ return {
8277
+ async next() {
8278
+ try {
8279
+ const result = await iterator.next();
8280
+ if (result.done) {
8281
+ finalize();
8282
+ return result;
8283
+ }
8284
+ const data = decodeBody(result.value?.chunk?.bytes);
8285
+ if (data?.type === "content_block_delta") {
8286
+ textParts.push(data?.delta?.text ?? "");
8287
+ } else if (data?.type === "message_delta") {
8288
+ if (data?.delta?.stop_reason) finishReason = data.delta.stop_reason;
8289
+ if (data?.usage?.output_tokens != null) completionTokens = data.usage.output_tokens;
8290
+ }
8291
+ if (data?.outputText) textParts.push(data.outputText);
8292
+ if (data?.generation) textParts.push(data.generation);
8293
+ const metrics2 = data?.["amazon-bedrock-invocationMetrics"];
8294
+ if (metrics2) {
8295
+ promptTokens = metrics2.inputTokenCount ?? promptTokens;
8296
+ completionTokens = metrics2.outputTokenCount ?? completionTokens;
8297
+ }
8298
+ return result;
8299
+ } catch (err) {
8300
+ recordError5(span2, err);
8301
+ throw err;
8302
+ }
8303
+ },
8304
+ async return(value) {
8305
+ finalize();
8306
+ return iterator.return?.(value) ?? { done: true, value: void 0 };
8307
+ }
8308
+ };
8309
+ }
8310
+ };
8311
+ response.body = wrappedBody;
8312
+ return response;
8313
+ }
7649
8314
  function safeStringify7(value) {
7650
8315
  try {
7651
8316
  return typeof value === "string" ? value : JSON.stringify(value);
@@ -7653,525 +8318,1691 @@ function safeStringify7(value) {
7653
8318
  return "";
7654
8319
  }
7655
8320
  }
7656
-
7657
- // src/mastra-wrap.ts
7658
- import { trace as trace10, context as otelContext8, SpanStatusCode as SpanStatusCode9 } from "@opentelemetry/api";
7659
- var TRACER_NAME7 = "neatlogs.mastra";
7660
- var PATCH_FLAG2 = "_neatlogs_patched";
7661
- function wrapMastra(entity) {
7662
- if (!entity || entity[PATCH_FLAG2]) return entity;
7663
- const e = entity;
7664
- const className = entity.constructor?.name ?? "";
7665
- if (isRootMastra(e)) {
7666
- const proxied = wrapRootMastra(e);
7667
- markPatched(proxied);
7668
- return proxied;
8321
+ function recordError5(span2, err) {
8322
+ if (err instanceof Error) {
8323
+ span2.setStatus({ code: SpanStatusCode9.ERROR, message: err.message });
8324
+ span2.recordException(err);
8325
+ } else {
8326
+ span2.setStatus({ code: SpanStatusCode9.ERROR, message: String(err) });
7669
8327
  }
7670
- if (isAgent(e, className)) {
7671
- patchAgent(e);
7672
- } else if (isWorkflow(e, className)) {
7673
- patchWorkflow(e);
7674
- } else if (isVector(e, className)) {
7675
- patchVector(e);
7676
- } else if (isMemory(e, className)) {
7677
- patchMemory(e);
7678
- } else if (isDocument(e, className)) {
7679
- patchDocument(e);
8328
+ span2.end();
8329
+ }
8330
+
8331
+ // src/langchain.ts
8332
+ import { trace as trace11, context as otelContext9, SpanStatusCode as SpanStatusCode10 } from "@opentelemetry/api";
8333
+ var TRACER_NAME8 = "neatlogs.langchain";
8334
+ function langchainHandler(opts) {
8335
+ return new NeatlogsCallbackHandler(opts);
8336
+ }
8337
+ var NeatlogsCallbackHandler = class {
8338
+ name = "neatlogs";
8339
+ _spans = /* @__PURE__ */ new Map();
8340
+ _workflowName;
8341
+ constructor(opts) {
8342
+ this._workflowName = opts?.workflowName;
8343
+ }
8344
+ // --- Chain/Graph callbacks ---
8345
+ async handleChainStart(serialized, inputs, runId, parentRunId, tags, metadata) {
8346
+ const tracer = trace11.getTracer(TRACER_NAME8);
8347
+ const name = serialized?.name ?? serialized?.id?.at(-1) ?? "chain";
8348
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
8349
+ const parentCtx = parentSpan ? trace11.setSpan(otelContext9.active(), parentSpan) : otelContext9.active();
8350
+ const attrs = {
8351
+ "neatlogs.span.kind": "CHAIN",
8352
+ "neatlogs.chain.name": name
8353
+ };
8354
+ if (this._workflowName) attrs["neatlogs.workflow.name"] = this._workflowName;
8355
+ if (inputs) attrs["input.value"] = safeStringify8(inputs);
8356
+ if (tags?.length) attrs["neatlogs.tags"] = tags.join(",");
8357
+ const span2 = tracer.startSpan(`langchain.chain.${name}`, { attributes: attrs }, parentCtx);
8358
+ this._spans.set(runId, span2);
8359
+ }
8360
+ async handleChainEnd(outputs, runId) {
8361
+ const span2 = this._spans.get(runId);
8362
+ if (!span2) return;
8363
+ if (outputs) span2.setAttribute("output.value", safeStringify8(outputs));
8364
+ span2.setStatus({ code: SpanStatusCode10.OK });
8365
+ span2.end();
8366
+ this._spans.delete(runId);
8367
+ }
8368
+ async handleChainError(error, runId) {
8369
+ const span2 = this._spans.get(runId);
8370
+ if (!span2) return;
8371
+ span2.setStatus({ code: SpanStatusCode10.ERROR, message: error.message });
8372
+ span2.recordException(error);
8373
+ span2.end();
8374
+ this._spans.delete(runId);
8375
+ }
8376
+ // --- LLM callbacks ---
8377
+ async handleLLMStart(serialized, prompts, runId, parentRunId, extraParams) {
8378
+ const tracer = trace11.getTracer(TRACER_NAME8);
8379
+ const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
8380
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
8381
+ const parentCtx = parentSpan ? trace11.setSpan(otelContext9.active(), parentSpan) : otelContext9.active();
8382
+ const attrs = {
8383
+ "neatlogs.span.kind": "LLM",
8384
+ "neatlogs.llm.provider": detectProvider(model),
8385
+ "neatlogs.llm.model_name": model
8386
+ };
8387
+ for (let i = 0; i < prompts.length; i++) {
8388
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = "user";
8389
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = prompts[i];
8390
+ }
8391
+ if (extraParams?.invocation_params) {
8392
+ const p = extraParams.invocation_params;
8393
+ if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
8394
+ if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
8395
+ if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
8396
+ }
8397
+ const span2 = tracer.startSpan("langchain.llm", { attributes: attrs }, parentCtx);
8398
+ this._spans.set(runId, span2);
8399
+ }
8400
+ async handleChatModelStart(serialized, messages, runId, parentRunId, extraParams) {
8401
+ const tracer = trace11.getTracer(TRACER_NAME8);
8402
+ const model = serialized?.kwargs?.model_name ?? serialized?.kwargs?.model ?? serialized?.name ?? "";
8403
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
8404
+ const parentCtx = parentSpan ? trace11.setSpan(otelContext9.active(), parentSpan) : otelContext9.active();
8405
+ const attrs = {
8406
+ "neatlogs.span.kind": "LLM",
8407
+ "neatlogs.llm.provider": detectProvider(model),
8408
+ "neatlogs.llm.model_name": model
8409
+ };
8410
+ const flatMessages = messages.flat();
8411
+ for (let i = 0; i < flatMessages.length; i++) {
8412
+ const msg = flatMessages[i];
8413
+ const role = msg?.role ?? msg?._getType?.() ?? msg?.constructor?.name ?? "unknown";
8414
+ const content = typeof msg?.content === "string" ? msg.content : safeStringify8(msg?.content);
8415
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = mapRole(role);
8416
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = content;
8417
+ }
8418
+ if (extraParams?.invocation_params) {
8419
+ const p = extraParams.invocation_params;
8420
+ if (p.temperature != null) attrs["neatlogs.llm.temperature"] = p.temperature;
8421
+ if (p.max_tokens != null) attrs["neatlogs.llm.max_tokens"] = p.max_tokens;
8422
+ if (p.model_name) attrs["neatlogs.llm.model_name"] = p.model_name;
8423
+ }
8424
+ const span2 = tracer.startSpan("langchain.chat_model", { attributes: attrs }, parentCtx);
8425
+ this._spans.set(runId, span2);
8426
+ }
8427
+ async handleLLMEnd(output, runId) {
8428
+ const span2 = this._spans.get(runId);
8429
+ if (!span2) return;
8430
+ const generations = output?.generations ?? [];
8431
+ for (let i = 0; i < generations.length; i++) {
8432
+ const gen = generations[i];
8433
+ if (!Array.isArray(gen)) continue;
8434
+ for (let j = 0; j < gen.length; j++) {
8435
+ const msg = gen[j]?.message ?? gen[j];
8436
+ const content = msg?.content ?? msg?.text ?? "";
8437
+ span2.setAttribute(`neatlogs.llm.output_messages.${i}.role`, "assistant");
8438
+ span2.setAttribute(`neatlogs.llm.output_messages.${i}.content`, String(content));
8439
+ const toolCalls = msg?.tool_calls ?? msg?.additional_kwargs?.tool_calls;
8440
+ if (toolCalls && Array.isArray(toolCalls)) {
8441
+ for (let k = 0; k < toolCalls.length; k++) {
8442
+ const tc = toolCalls[k];
8443
+ span2.setAttribute(`neatlogs.llm.tool_calls.${k}.id`, tc.id ?? "");
8444
+ span2.setAttribute(`neatlogs.llm.tool_calls.${k}.name`, tc.name ?? tc.function?.name ?? "");
8445
+ span2.setAttribute(`neatlogs.llm.tool_calls.${k}.arguments`, tc.args ? safeStringify8(tc.args) : tc.function?.arguments ?? "");
8446
+ }
8447
+ }
8448
+ }
8449
+ }
8450
+ const usage = output?.llmOutput?.tokenUsage ?? output?.llmOutput?.usage;
8451
+ if (usage) {
8452
+ if (usage.promptTokens != null || usage.prompt_tokens != null) {
8453
+ span2.setAttribute("neatlogs.llm.token_count.prompt", usage.promptTokens ?? usage.prompt_tokens);
8454
+ }
8455
+ if (usage.completionTokens != null || usage.completion_tokens != null) {
8456
+ span2.setAttribute("neatlogs.llm.token_count.completion", usage.completionTokens ?? usage.completion_tokens);
8457
+ }
8458
+ if (usage.totalTokens != null || usage.total_tokens != null) {
8459
+ span2.setAttribute("neatlogs.llm.token_count.total", usage.totalTokens ?? usage.total_tokens);
8460
+ }
8461
+ }
8462
+ span2.setStatus({ code: SpanStatusCode10.OK });
8463
+ span2.end();
8464
+ this._spans.delete(runId);
8465
+ }
8466
+ async handleLLMNewToken(_token, _idx, _runId) {
8467
+ }
8468
+ async handleLLMError(error, runId) {
8469
+ const span2 = this._spans.get(runId);
8470
+ if (!span2) return;
8471
+ span2.setStatus({ code: SpanStatusCode10.ERROR, message: error.message });
8472
+ span2.recordException(error);
8473
+ span2.end();
8474
+ this._spans.delete(runId);
8475
+ }
8476
+ // --- Tool callbacks ---
8477
+ async handleToolStart(serialized, input, runId, parentRunId, _tags, _metadata, runName, toolCallId) {
8478
+ const tracer = trace11.getTracer(TRACER_NAME8);
8479
+ const name = serialized?.name || runName || (Array.isArray(serialized?.id) ? serialized.id[serialized.id.length - 1] : void 0) || "tool";
8480
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
8481
+ const parentCtx = parentSpan ? trace11.setSpan(otelContext9.active(), parentSpan) : otelContext9.active();
8482
+ const attrs = {
8483
+ "neatlogs.span.kind": "TOOL",
8484
+ "neatlogs.tool.name": name,
8485
+ "input.value": String(input)
8486
+ };
8487
+ if (toolCallId) attrs["neatlogs.tool_call.id"] = toolCallId;
8488
+ const span2 = tracer.startSpan(`langchain.tool.${name}`, { attributes: attrs }, parentCtx);
8489
+ this._spans.set(runId, span2);
8490
+ }
8491
+ async handleToolEnd(output, runId) {
8492
+ const span2 = this._spans.get(runId);
8493
+ if (!span2) return;
8494
+ span2.setAttribute("output.value", String(output));
8495
+ span2.setStatus({ code: SpanStatusCode10.OK });
8496
+ span2.end();
8497
+ this._spans.delete(runId);
8498
+ }
8499
+ async handleToolError(error, runId) {
8500
+ const span2 = this._spans.get(runId);
8501
+ if (!span2) return;
8502
+ span2.setStatus({ code: SpanStatusCode10.ERROR, message: error.message });
8503
+ span2.recordException(error);
8504
+ span2.end();
8505
+ this._spans.delete(runId);
8506
+ }
8507
+ // --- Retriever callbacks ---
8508
+ async handleRetrieverStart(serialized, query, runId, parentRunId) {
8509
+ const tracer = trace11.getTracer(TRACER_NAME8);
8510
+ const name = serialized?.name ?? "retriever";
8511
+ const parentSpan = parentRunId ? this._spans.get(parentRunId) : void 0;
8512
+ const parentCtx = parentSpan ? trace11.setSpan(otelContext9.active(), parentSpan) : otelContext9.active();
8513
+ const attrs = {
8514
+ "neatlogs.span.kind": "RETRIEVER",
8515
+ "neatlogs.retriever.name": name,
8516
+ "input.value": query
8517
+ };
8518
+ const span2 = tracer.startSpan(`langchain.retriever.${name}`, { attributes: attrs }, parentCtx);
8519
+ this._spans.set(runId, span2);
8520
+ }
8521
+ async handleRetrieverEnd(documents, runId) {
8522
+ const span2 = this._spans.get(runId);
8523
+ if (!span2) return;
8524
+ if (documents?.length) {
8525
+ span2.setAttribute("neatlogs.retriever.document_count", documents.length);
8526
+ for (let i = 0; i < Math.min(documents.length, 10); i++) {
8527
+ const doc = documents[i];
8528
+ if (doc?.pageContent) {
8529
+ span2.setAttribute(`neatlogs.retriever.documents.${i}.content`, doc.pageContent);
8530
+ }
8531
+ }
8532
+ }
8533
+ span2.setStatus({ code: SpanStatusCode10.OK });
8534
+ span2.end();
8535
+ this._spans.delete(runId);
8536
+ }
8537
+ async handleRetrieverError(error, runId) {
8538
+ const span2 = this._spans.get(runId);
8539
+ if (!span2) return;
8540
+ span2.setStatus({ code: SpanStatusCode10.ERROR, message: error.message });
8541
+ span2.recordException(error);
8542
+ span2.end();
8543
+ this._spans.delete(runId);
8544
+ }
8545
+ };
8546
+ function detectProvider(model) {
8547
+ const m = model.toLowerCase();
8548
+ if (m.includes("gpt") || m.includes("o1") || m.includes("o3") || m.includes("o4")) return "openai";
8549
+ if (m.includes("claude")) return "anthropic";
8550
+ if (m.includes("gemini")) return "google";
8551
+ if (m.includes("command")) return "cohere";
8552
+ if (m.includes("llama") || m.includes("mixtral")) return "meta";
8553
+ return "unknown";
8554
+ }
8555
+ function mapRole(role) {
8556
+ const r = role.toLowerCase();
8557
+ if (r === "human" || r === "humanmessage") return "user";
8558
+ if (r === "ai" || r === "aimessage") return "assistant";
8559
+ if (r === "system" || r === "systemmessage") return "system";
8560
+ if (r === "function" || r === "functionmessage") return "function";
8561
+ if (r === "tool" || r === "toolmessage") return "tool";
8562
+ return role;
8563
+ }
8564
+ function safeStringify8(value) {
8565
+ try {
8566
+ return typeof value === "string" ? value : JSON.stringify(value);
8567
+ } catch {
8568
+ return "";
8569
+ }
8570
+ }
8571
+
8572
+ // src/strands.ts
8573
+ var PATCH_FLAG = "_neatlogs_patched";
8574
+ function strandsHooks(agent) {
8575
+ installEventHookFromAgent(agent);
8576
+ if (agent && typeof agent === "object") {
8577
+ try {
8578
+ Object.defineProperty(agent, PATCH_FLAG, {
8579
+ value: true,
8580
+ enumerable: false,
8581
+ configurable: true
8582
+ });
8583
+ } catch {
8584
+ agent[PATCH_FLAG] = true;
8585
+ }
8586
+ }
8587
+ return agent;
8588
+ }
8589
+ function installEventHookFromAgent(agent) {
8590
+ const tracer = agent?._tracer;
8591
+ const proto = tracer ? Object.getPrototypeOf(tracer) : void 0;
8592
+ if (!proto || typeof proto._addEvent !== "function") return;
8593
+ if (proto._addEvent[PATCH_FLAG]) return;
8594
+ const orig = proto._addEvent;
8595
+ function patchedAddEvent(span2, eventName, eventAttributes) {
8596
+ const result = orig.call(this, span2, eventName, eventAttributes);
8597
+ try {
8598
+ enrichSpanFromEvent(span2, eventName, eventAttributes || {});
8599
+ } catch {
8600
+ }
8601
+ return result;
8602
+ }
8603
+ patchedAddEvent[PATCH_FLAG] = true;
8604
+ proto._addEvent = patchedAddEvent;
8605
+ }
8606
+ function enrichSpanFromEvent(span2, eventName, attrs) {
8607
+ if (!span2 || typeof span2.setAttribute !== "function") return;
8608
+ if (typeof span2.isRecording === "function" && !span2.isRecording()) return;
8609
+ const isTool = readSpanOp(span2) === "execute_tool";
8610
+ if (eventName.startsWith("gen_ai.") && eventName.endsWith(".message")) {
8611
+ const role = eventName.slice("gen_ai.".length, -".message".length);
8612
+ const content = flattenStrandsContent(attrs.content);
8613
+ if (content) {
8614
+ if (isTool) {
8615
+ span2.setAttribute("input.value", content);
8616
+ span2.setAttribute("neatlogs.tool.input", content);
8617
+ } else {
8618
+ appendInputMessage(span2, role, content);
8619
+ }
8620
+ }
8621
+ return;
8622
+ }
8623
+ if (eventName === "gen_ai.choice") {
8624
+ const out = flattenStrandsContent(attrs.message);
8625
+ if (out) setOutput(span2, isTool, out);
8626
+ return;
8627
+ }
8628
+ if (eventName === "gen_ai.client.inference.operation.details") {
8629
+ const out = flattenStrandsContent(attrs["gen_ai.output.messages"]);
8630
+ if (out) setOutput(span2, isTool, out);
8631
+ }
8632
+ }
8633
+ function setOutput(span2, isTool, out) {
8634
+ span2.setAttribute("output.value", out);
8635
+ if (isTool) {
8636
+ span2.setAttribute("neatlogs.tool.output", out);
8637
+ } else {
8638
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8639
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", out);
8640
+ span2.setAttribute("neatlogs.llm.output", safeStringify9({ role: "assistant", content: out }));
8641
+ }
8642
+ }
8643
+ function appendInputMessage(span2, role, content) {
8644
+ const idx = span2.__neatlogs_in_idx ?? 0;
8645
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.role`, role);
8646
+ span2.setAttribute(`neatlogs.llm.input_messages.${idx}.content`, content);
8647
+ span2.__neatlogs_in_idx = idx + 1;
8648
+ const existing = span2.__neatlogs_in_msgs ?? [];
8649
+ existing.push({ role, content });
8650
+ span2.__neatlogs_in_msgs = existing;
8651
+ const blob = safeStringify9({ messages: existing });
8652
+ span2.setAttribute("input.value", blob);
8653
+ span2.setAttribute("neatlogs.llm.input", blob);
8654
+ }
8655
+ function readSpanOp(span2) {
8656
+ try {
8657
+ const a = span2.attributes;
8658
+ if (!a) return "";
8659
+ const v = typeof a.get === "function" ? a.get("gen_ai.operation.name") : a["gen_ai.operation.name"];
8660
+ return typeof v === "string" ? v.toLowerCase() : "";
8661
+ } catch {
8662
+ return "";
8663
+ }
8664
+ }
8665
+ function flattenStrandsContent(content) {
8666
+ if (content == null) return "";
8667
+ let val = content;
8668
+ if (typeof val === "string") {
8669
+ const s = val.trim();
8670
+ if (!(s.startsWith("[") || s.startsWith("{"))) return val;
8671
+ try {
8672
+ val = JSON.parse(s);
8673
+ } catch {
8674
+ return val;
8675
+ }
8676
+ }
8677
+ return flattenBlocks(val);
8678
+ }
8679
+ function flattenBlocks(val) {
8680
+ const items = Array.isArray(val) ? val : [val];
8681
+ const out = [];
8682
+ for (const item of items) {
8683
+ if (typeof item === "string") {
8684
+ out.push(item);
8685
+ continue;
8686
+ }
8687
+ if (!item || typeof item !== "object") {
8688
+ if (item != null) out.push(String(item));
8689
+ continue;
8690
+ }
8691
+ const o = item;
8692
+ if (typeof o.text === "string") out.push(o.text);
8693
+ else if (typeof o.content === "string") out.push(o.content);
8694
+ else if (o.toolUse) out.push(`${o.toolUse.name ?? "tool"}(${safeStringify9(o.toolUse.input ?? {})})`);
8695
+ else if (o.toolResult) out.push(flattenBlocks(o.toolResult.content ?? o.toolResult));
8696
+ else if (Array.isArray(o.parts)) out.push(flattenBlocks(o.parts));
8697
+ else if (Array.isArray(o.content)) out.push(flattenBlocks(o.content));
8698
+ else out.push(safeStringify9(o));
8699
+ }
8700
+ return out.filter(Boolean).join("\n");
8701
+ }
8702
+ function safeStringify9(value) {
8703
+ if (typeof value === "string") return value;
8704
+ try {
8705
+ return JSON.stringify(value) ?? "";
8706
+ } catch {
8707
+ return "";
8708
+ }
8709
+ }
8710
+
8711
+ // src/openai-agents.ts
8712
+ import { trace as trace12, context as otelContext10, SpanStatusCode as SpanStatusCode11 } from "@opentelemetry/api";
8713
+ var TRACER_NAME9 = "neatlogs.openai_agents";
8714
+ function openaiAgentsProcessor() {
8715
+ return new NeatlogsTraceProcessor();
8716
+ }
8717
+ var NeatlogsTraceProcessor = class {
8718
+ _spans = /* @__PURE__ */ new Map();
8719
+ _startTimes = /* @__PURE__ */ new Map();
8720
+ // The @openai/agents SDK passes a Trace object: { traceId, name, groupId, metadata }.
8721
+ onTraceStart(traceData) {
8722
+ const tracer = trace12.getTracer(TRACER_NAME9);
8723
+ const attrs = { "neatlogs.span.kind": "WORKFLOW" };
8724
+ const workflowName = traceData?.name ?? traceData?.workflow_name;
8725
+ if (workflowName) attrs["neatlogs.workflow.name"] = workflowName;
8726
+ const traceId = traceData?.traceId ?? traceData?.trace_id;
8727
+ if (traceId) attrs["neatlogs.agent.trace_id"] = String(traceId);
8728
+ const span2 = tracer.startSpan("openai_agents.trace", { attributes: attrs }, otelContext10.active());
8729
+ const key = String(traceId ?? `trace_${Date.now()}`);
8730
+ this._spans.set(key, span2);
8731
+ this._startTimes.set(key, Date.now());
8732
+ }
8733
+ onTraceEnd(traceData) {
8734
+ const key = String(traceData?.traceId ?? traceData?.trace_id ?? "");
8735
+ const span2 = this._spans.get(key);
8736
+ if (!span2) return;
8737
+ const startTime = this._startTimes.get(key);
8738
+ if (startTime) {
8739
+ span2.setAttribute("neatlogs.metrics.duration_ms", Date.now() - startTime);
8740
+ }
8741
+ span2.setStatus({ code: SpanStatusCode11.OK });
8742
+ span2.end();
8743
+ this._spans.delete(key);
8744
+ this._startTimes.delete(key);
8745
+ }
8746
+ // The SDK passes a Span object: { type, spanId, traceId, parentId?, spanData: {...} }.
8747
+ // The meaningful payload (type, name, input, output, usage, ...) lives in `spanData`.
8748
+ onSpanStart(span2) {
8749
+ const tracer = trace12.getTracer(TRACER_NAME9);
8750
+ const data = span2?.spanData ?? span2 ?? {};
8751
+ const spanType = data?.type ?? data?.span_type ?? span2?.type ?? "";
8752
+ const spanId = String(span2?.spanId ?? span2?.span_id ?? data?.span_id ?? `span_${Date.now()}`);
8753
+ const parentKey = String(span2?.parentId ?? span2?.traceId ?? span2?.trace_id ?? "");
8754
+ const parentSpan = this._spans.get(parentKey);
8755
+ const parentCtx = parentSpan ? trace12.setSpan(otelContext10.active(), parentSpan) : otelContext10.active();
8756
+ let otelSpan;
8757
+ if (spanType === "agent" || spanType === "agent_run") {
8758
+ const agentName = data?.name ?? data?.agent_name ?? "agent";
8759
+ const attrs = {
8760
+ "neatlogs.span.kind": "AGENT",
8761
+ "neatlogs.agent.name": agentName
8762
+ };
8763
+ if (Array.isArray(data?.tools) && data.tools.length) {
8764
+ attrs["neatlogs.agent.available_tools"] = data.tools.join(",");
8765
+ }
8766
+ otelSpan = tracer.startSpan(`openai_agents.agent.${agentName}`, { attributes: attrs }, parentCtx);
8767
+ } else if (spanType === "response" || spanType === "generation" || spanType === "llm") {
8768
+ const attrs = {
8769
+ "neatlogs.span.kind": "LLM",
8770
+ "neatlogs.llm.provider": "openai"
8771
+ };
8772
+ const model = data?.model;
8773
+ if (model) attrs["neatlogs.llm.model_name"] = model;
8774
+ const inputMsgs = data?.input ?? data?.messages;
8775
+ if (inputMsgs && Array.isArray(inputMsgs)) {
8776
+ for (let i = 0; i < inputMsgs.length; i++) {
8777
+ const msg = inputMsgs[i];
8778
+ const role = typeof msg === "object" ? msg.role ?? "" : "";
8779
+ const content = typeof msg === "object" ? msg.content ?? "" : String(msg);
8780
+ if (role) attrs[`neatlogs.llm.input_messages.${i}.role`] = role;
8781
+ if (content) attrs[`neatlogs.llm.input_messages.${i}.content`] = typeof content === "string" ? content : safeStringify10(content);
8782
+ }
8783
+ }
8784
+ otelSpan = tracer.startSpan("openai_agents.generation", { attributes: attrs }, parentCtx);
8785
+ } else if (spanType === "function" || spanType === "tool" || spanType === "tool_call") {
8786
+ const toolName = data?.name ?? data?.function_name ?? "tool";
8787
+ const attrs = {
8788
+ "neatlogs.span.kind": "TOOL",
8789
+ "neatlogs.tool.name": toolName
8790
+ };
8791
+ const toolInput = data?.input ?? data?.arguments;
8792
+ if (toolInput !== void 0) {
8793
+ attrs["input.value"] = typeof toolInput === "string" ? toolInput : safeStringify10(toolInput);
8794
+ }
8795
+ otelSpan = tracer.startSpan(`openai_agents.tool.${toolName}`, { attributes: attrs }, parentCtx);
8796
+ } else if (spanType === "handoff") {
8797
+ const attrs = { "neatlogs.span.kind": "AGENT" };
8798
+ if (data?.from_agent) attrs["neatlogs.agent.handoff_from"] = String(data.from_agent);
8799
+ if (data?.to_agent) attrs["neatlogs.agent.name"] = String(data.to_agent);
8800
+ otelSpan = tracer.startSpan("openai_agents.handoff", { attributes: attrs }, parentCtx);
8801
+ } else {
8802
+ const attrs = { "neatlogs.span.kind": "CHAIN" };
8803
+ otelSpan = tracer.startSpan(`openai_agents.${spanType || "span"}`, { attributes: attrs }, parentCtx);
8804
+ }
8805
+ this._spans.set(spanId, otelSpan);
8806
+ this._startTimes.set(spanId, Date.now());
8807
+ }
8808
+ onSpanEnd(span2) {
8809
+ const data = span2?.spanData ?? span2 ?? {};
8810
+ const spanId = String(span2?.spanId ?? span2?.span_id ?? data?.span_id ?? "");
8811
+ const otelSpan = this._spans.get(spanId);
8812
+ if (!otelSpan) return;
8813
+ const spanType = data?.type ?? data?.span_type ?? span2?.type ?? "";
8814
+ const startTime = this._startTimes.get(spanId);
8815
+ if (spanType === "response" || spanType === "generation" || spanType === "llm") {
8816
+ const resp = data?._response ?? data?.response ?? {};
8817
+ const model = data?.model ?? resp?.model;
8818
+ if (model) otelSpan.setAttribute("neatlogs.llm.model_name", model);
8819
+ const outputItems = data?.output ?? resp?.output;
8820
+ if (Array.isArray(outputItems)) {
8821
+ const text = outputItems.filter((o) => o?.type === "message" || o?.role === "assistant").flatMap((o) => Array.isArray(o.content) ? o.content : [o.content]).map((c) => typeof c === "string" ? c : c?.text ?? "").join("");
8822
+ if (text) {
8823
+ otelSpan.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8824
+ otelSpan.setAttribute("neatlogs.llm.output_messages.0.content", text);
8825
+ }
8826
+ const toolCalls = outputItems.filter((o) => o?.type === "function_call");
8827
+ for (let i = 0; i < toolCalls.length; i++) {
8828
+ const tc = toolCalls[i];
8829
+ otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.name`, tc.name ?? "");
8830
+ otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.arguments`, typeof tc.arguments === "string" ? tc.arguments : safeStringify10(tc.arguments ?? {}));
8831
+ if (tc.callId ?? tc.id) otelSpan.setAttribute(`neatlogs.llm.tool_calls.${i}.id`, tc.callId ?? tc.id);
8832
+ }
8833
+ } else if (outputItems?.content) {
8834
+ otelSpan.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8835
+ const c = outputItems.content;
8836
+ otelSpan.setAttribute("neatlogs.llm.output_messages.0.content", typeof c === "string" ? c : safeStringify10(c));
8837
+ }
8838
+ const usage = data?.usage ?? resp?.usage;
8839
+ if (usage) {
8840
+ const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens;
8841
+ const outputTokens = usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens;
8842
+ const totalTokens = usage.total_tokens ?? usage.totalTokens;
8843
+ if (inputTokens != null) otelSpan.setAttribute("neatlogs.llm.token_count.prompt", inputTokens);
8844
+ if (outputTokens != null) otelSpan.setAttribute("neatlogs.llm.token_count.completion", outputTokens);
8845
+ if (totalTokens != null) otelSpan.setAttribute("neatlogs.llm.token_count.total", totalTokens);
8846
+ else if (inputTokens != null && outputTokens != null) {
8847
+ otelSpan.setAttribute("neatlogs.llm.token_count.total", inputTokens + outputTokens);
8848
+ }
8849
+ }
8850
+ } else if (spanType === "function" || spanType === "tool" || spanType === "tool_call") {
8851
+ const output = data?.output ?? data?.result;
8852
+ if (output != null) {
8853
+ otelSpan.setAttribute("output.value", typeof output === "string" ? output : safeStringify10(output));
8854
+ }
8855
+ } else if (spanType === "agent" || spanType === "agent_run") {
8856
+ const output = data?.output;
8857
+ if (output != null) {
8858
+ otelSpan.setAttribute("output.value", typeof output === "string" ? output : safeStringify10(output));
8859
+ }
8860
+ }
8861
+ const error = data?.error ?? span2?.error;
8862
+ if (error) {
8863
+ if (error instanceof Error) {
8864
+ otelSpan.setStatus({ code: SpanStatusCode11.ERROR, message: error.message });
8865
+ otelSpan.recordException(error);
8866
+ } else {
8867
+ otelSpan.setStatus({ code: SpanStatusCode11.ERROR, message: String(error) });
8868
+ }
8869
+ } else {
8870
+ otelSpan.setStatus({ code: SpanStatusCode11.OK });
8871
+ }
8872
+ if (startTime) {
8873
+ otelSpan.setAttribute("neatlogs.llm.metrics.duration_ms", Date.now() - startTime);
8874
+ }
8875
+ otelSpan.end();
8876
+ this._spans.delete(spanId);
8877
+ this._startTimes.delete(spanId);
8878
+ }
8879
+ shutdown() {
8880
+ for (const [, span2] of this._spans) {
8881
+ span2.setStatus({ code: SpanStatusCode11.ERROR, message: "Processor shutdown before span completed" });
8882
+ span2.end();
8883
+ }
8884
+ this._spans.clear();
8885
+ this._startTimes.clear();
8886
+ }
8887
+ forceFlush() {
8888
+ }
8889
+ };
8890
+ function safeStringify10(value) {
8891
+ try {
8892
+ return typeof value === "string" ? value : JSON.stringify(value);
8893
+ } catch {
8894
+ return "";
8895
+ }
8896
+ }
8897
+
8898
+ // src/mastra-wrap.ts
8899
+ import { trace as trace13, context as otelContext11, SpanStatusCode as SpanStatusCode12 } from "@opentelemetry/api";
8900
+ var TRACER_NAME10 = "neatlogs.mastra";
8901
+ var PATCH_FLAG2 = "_neatlogs_patched";
8902
+ function wrapMastra(entity) {
8903
+ if (!entity || entity[PATCH_FLAG2]) return entity;
8904
+ const e = entity;
8905
+ const className = entity.constructor?.name ?? "";
8906
+ if (isRootMastra(e)) {
8907
+ const proxied = wrapRootMastra(e);
8908
+ markPatched(proxied);
8909
+ return proxied;
8910
+ }
8911
+ if (isAgent(e, className)) {
8912
+ patchAgent(e);
8913
+ } else if (isWorkflow(e, className)) {
8914
+ patchWorkflow(e);
8915
+ } else if (isVector(e, className)) {
8916
+ patchVector(e);
8917
+ } else if (isMemory(e, className)) {
8918
+ patchMemory(e);
8919
+ } else if (isDocument(e, className)) {
8920
+ patchDocument(e);
8921
+ }
8922
+ markPatched(e);
8923
+ return entity;
8924
+ }
8925
+ function isRootMastra(e) {
8926
+ return typeof e.getAgent === "function" && typeof e.getWorkflow === "function";
8927
+ }
8928
+ function isAgent(e, className) {
8929
+ return className === "Agent" || typeof e.generate === "function" || typeof e.stream === "function";
8930
+ }
8931
+ function isWorkflow(e, className) {
8932
+ return className === "Workflow" || typeof e.createRun === "function";
8933
+ }
8934
+ function isVector(e, className) {
8935
+ return /Vector/.test(className) || typeof e.query === "function" && typeof e.upsert === "function";
8936
+ }
8937
+ function isMemory(e, className) {
8938
+ return /Memory/.test(className) || typeof e.recall === "function" && typeof e.saveMessages === "function";
8939
+ }
8940
+ function isDocument(e, className) {
8941
+ return className === "MDocument" || typeof e.chunk === "function" && typeof e.getDocs === "function";
8942
+ }
8943
+ function patchAgent(agent) {
8944
+ patchAgentMethod(agent, "generate", false);
8945
+ patchAgentMethod(agent, "stream", true);
8946
+ }
8947
+ function patchAgentMethod(agent, method, streaming) {
8948
+ if (typeof agent[method] !== "function") return;
8949
+ const orig = agent[method].bind(agent);
8950
+ agent[method] = async function tracedAgentMethod(input, opts) {
8951
+ const agentName = agent.name ?? agent.id ?? "mastra_agent";
8952
+ const model = extractModelId(agent.model);
8953
+ installAgentLlmHook(agent);
8954
+ installAgentToolHooks(agent);
8955
+ const attrs = {
8956
+ "neatlogs.span.kind": "AGENT",
8957
+ "neatlogs.agent.name": agentName,
8958
+ "input.value": toInputValue(input)
8959
+ };
8960
+ if (model) attrs["neatlogs.llm.model_name"] = model;
8961
+ if (agent.instructions && typeof agent.instructions === "string") {
8962
+ attrs["neatlogs.llm.system_prompt"] = agent.instructions;
8963
+ }
8964
+ if (streaming) attrs["neatlogs.llm.is_streaming"] = true;
8965
+ if (streaming) {
8966
+ const tracer = trace13.getTracer(TRACER_NAME10);
8967
+ const span2 = tracer.startSpan(`mastra.agent.${agentName}`, { attributes: attrs }, otelContext11.active());
8968
+ const ctx = trace13.setSpan(otelContext11.active(), span2);
8969
+ try {
8970
+ const result = await otelContext11.with(ctx, () => orig(input, opts));
8971
+ return wrapStreamingOutput(result, span2, ctx);
8972
+ } catch (err) {
8973
+ recordError6(span2, err);
8974
+ span2.end();
8975
+ throw err;
8976
+ }
8977
+ }
8978
+ return withActiveSpan(`mastra.agent.${agentName}`, attrs, async (span2) => {
8979
+ const result = await orig(input, opts);
8980
+ finalizeAgentResult(span2, result);
8981
+ return result;
8982
+ });
8983
+ };
8984
+ }
8985
+ function installAgentLlmHook(agent) {
8986
+ if (agent.__neatlogs_llm_hook || typeof agent.getLLM !== "function") return;
8987
+ agent.__neatlogs_llm_hook = true;
8988
+ const origGetLLM = agent.getLLM.bind(agent);
8989
+ agent.getLLM = function patchedGetLLM(...args) {
8990
+ const out = origGetLLM(...args);
8991
+ return Promise.resolve(out).then((llm) => {
8992
+ try {
8993
+ const model = typeof llm?.getModel === "function" ? llm.getModel() : llm;
8994
+ patchModelInPlace(model);
8995
+ } catch {
8996
+ }
8997
+ return llm;
8998
+ });
8999
+ };
9000
+ }
9001
+ function patchModelInPlace(model) {
9002
+ if (!model || model.__neatlogs_model_patched) return;
9003
+ model.__neatlogs_model_patched = true;
9004
+ const modelId = model.modelId ?? model.modelName ?? "";
9005
+ const provider = model.provider ?? "";
9006
+ for (const fn of ["doGenerate", "doStream"]) {
9007
+ if (typeof model[fn] !== "function") continue;
9008
+ const orig = model[fn].bind(model);
9009
+ const isStream = fn === "doStream";
9010
+ model[fn] = function tracedModelCall(callOpts) {
9011
+ const attrs = { "neatlogs.span.kind": "LLM" };
9012
+ if (modelId) attrs["neatlogs.llm.model_name"] = modelId;
9013
+ if (provider) attrs["neatlogs.llm.provider"] = provider;
9014
+ if (isStream) attrs["neatlogs.llm.is_streaming"] = true;
9015
+ const promptInput = callOpts?.prompt ?? callOpts?.messages;
9016
+ if (promptInput !== void 0) attrs["input.value"] = safeStringify11(promptInput);
9017
+ captureInvocationParams(attrs, callOpts);
9018
+ return withActiveSpan(`mastra.llm.${modelId || "model"}.${fn}`, attrs, async (span2) => {
9019
+ const result = await orig(callOpts);
9020
+ if (!isStream) finalizeModelResult(span2, result);
9021
+ else if (result?.usage) recordUsage(span2, result.usage);
9022
+ return result;
9023
+ });
9024
+ };
9025
+ }
9026
+ }
9027
+ function installAgentToolHooks(agent) {
9028
+ if (agent.__neatlogs_tool_hook) return;
9029
+ let tools;
9030
+ try {
9031
+ tools = typeof agent.listTools === "function" ? agent.listTools() : void 0;
9032
+ } catch {
9033
+ tools = void 0;
9034
+ }
9035
+ if (!tools || typeof tools !== "object") return;
9036
+ agent.__neatlogs_tool_hook = true;
9037
+ for (const [key, tool] of Object.entries(tools)) {
9038
+ patchToolExecute(tool, key);
9039
+ }
9040
+ }
9041
+ function patchToolExecute(tool, key) {
9042
+ if (!tool || typeof tool.execute !== "function" || tool.__neatlogs_tool_patched) return;
9043
+ tool.__neatlogs_tool_patched = true;
9044
+ const toolName = tool.id ?? key;
9045
+ const orig = tool.execute.bind(tool);
9046
+ tool.execute = function tracedToolExecute(params, options) {
9047
+ const attrs = {
9048
+ "neatlogs.span.kind": "TOOL",
9049
+ "neatlogs.tool.name": toolName,
9050
+ "input.value": safeStringify11(params)
9051
+ };
9052
+ if (tool.description) attrs["neatlogs.tool.description"] = String(tool.description);
9053
+ return withActiveSpan(`mastra.tool.${toolName}`, attrs, async (span2) => {
9054
+ const result = await orig(params, options);
9055
+ span2.setAttribute("output.value", safeStringify11(result));
9056
+ return result;
9057
+ });
9058
+ };
9059
+ }
9060
+ function patchWorkflow(workflow) {
9061
+ if (typeof workflow.createRun !== "function") return;
9062
+ const origCreateRun = workflow.createRun.bind(workflow);
9063
+ const workflowName = workflow.name ?? workflow.id ?? "mastra_workflow";
9064
+ workflow.createRun = async function tracedCreateRun(...args) {
9065
+ const run = await origCreateRun(...args);
9066
+ if (!run) return run;
9067
+ patchRunMethod(run, "start", workflowName);
9068
+ patchRunMethod(run, "resume", workflowName);
9069
+ return run;
9070
+ };
9071
+ }
9072
+ function patchRunMethod(run, method, workflowName) {
9073
+ if (typeof run[method] !== "function") return;
9074
+ const orig = run[method].bind(run);
9075
+ run[method] = async function tracedRunMethod(startOpts) {
9076
+ const attrs = {
9077
+ "neatlogs.span.kind": "WORKFLOW",
9078
+ "neatlogs.workflow.name": workflowName
9079
+ };
9080
+ if (startOpts?.inputData !== void 0) {
9081
+ attrs["input.value"] = safeStringify11(startOpts.inputData);
9082
+ }
9083
+ return withActiveSpan(`mastra.workflow.${workflowName}`, attrs, async (span2) => {
9084
+ const result = await orig(startOpts);
9085
+ if (result?.status) span2.setAttribute("neatlogs.metadata", safeStringify11({ status: result.status }));
9086
+ if (result?.result !== void 0) {
9087
+ span2.setAttribute("output.value", safeStringify11(result.result));
9088
+ }
9089
+ return result;
9090
+ });
9091
+ };
9092
+ }
9093
+ var VECTOR_READ_OPS = ["query"];
9094
+ var VECTOR_WRITE_OPS = ["upsert", "updateVector", "deleteVector", "createIndex", "deleteIndex"];
9095
+ function patchVector(vector) {
9096
+ for (const op of VECTOR_READ_OPS) patchVectorOp(vector, op, "RETRIEVER");
9097
+ for (const op of VECTOR_WRITE_OPS) patchVectorOp(vector, op, "VECTOR_STORE");
9098
+ }
9099
+ function patchVectorOp(vector, op, kind) {
9100
+ if (typeof vector[op] !== "function") return;
9101
+ const orig = vector[op].bind(vector);
9102
+ const dbName = vector.constructor?.name ?? "vector";
9103
+ vector[op] = async function tracedVectorOp(params) {
9104
+ const attrs = {
9105
+ "neatlogs.span.kind": kind,
9106
+ "neatlogs.db.system": dbName,
9107
+ "neatlogs.db.operation": op,
9108
+ "input.value": safeStringify11(params)
9109
+ };
9110
+ const indexName = params?.indexName;
9111
+ if (indexName) attrs["neatlogs.vectordb.index_name"] = String(indexName);
9112
+ if (kind === "RETRIEVER" && params?.topK != null) attrs["neatlogs.retriever.top_k"] = params.topK;
9113
+ return withActiveSpan(`mastra.vector.${op}`, attrs, async (span2) => {
9114
+ const result = await orig(params);
9115
+ span2.setAttribute("output.value", safeStringify11(result));
9116
+ return result;
9117
+ });
9118
+ };
9119
+ }
9120
+ var MEMORY_OPS = ["recall", "saveMessages", "updateWorkingMemory", "deleteMessages"];
9121
+ function patchMemory(memory) {
9122
+ for (const op of MEMORY_OPS) {
9123
+ if (typeof memory[op] !== "function") continue;
9124
+ const orig = memory[op].bind(memory);
9125
+ memory[op] = async function tracedMemoryOp(...args) {
9126
+ const attrs = {
9127
+ "neatlogs.span.kind": "CHAIN",
9128
+ "neatlogs.db.operation": op,
9129
+ "input.value": safeStringify11(args?.[0])
9130
+ };
9131
+ return withActiveSpan(`mastra.memory.${op}`, attrs, async (span2) => {
9132
+ const result = await orig(...args);
9133
+ span2.setAttribute("output.value", safeStringify11(result));
9134
+ return result;
9135
+ });
9136
+ };
9137
+ }
9138
+ }
9139
+ function patchDocument(doc) {
9140
+ if (typeof doc.chunk !== "function") return;
9141
+ const orig = doc.chunk.bind(doc);
9142
+ doc.chunk = async function tracedChunk(...args) {
9143
+ const attrs = {
9144
+ "neatlogs.span.kind": "CHAIN",
9145
+ "neatlogs.db.operation": "chunk"
9146
+ };
9147
+ return withActiveSpan("mastra.document.chunk", attrs, async (span2) => {
9148
+ const result = await orig(...args);
9149
+ if (Array.isArray(result)) span2.setAttribute("neatlogs.db.documents_count", result.length);
9150
+ return result;
9151
+ });
9152
+ };
9153
+ }
9154
+ function wrapRootMastra(mastra) {
9155
+ const AGENT_GETTERS = /* @__PURE__ */ new Set(["getAgent", "getAgentById"]);
9156
+ const WORKFLOW_GETTERS = /* @__PURE__ */ new Set(["getWorkflow", "getWorkflowById"]);
9157
+ return new Proxy(mastra, {
9158
+ get(target, prop, receiver) {
9159
+ const value = Reflect.get(target, prop, receiver);
9160
+ if (typeof value !== "function") return value;
9161
+ const name = String(prop);
9162
+ if (AGENT_GETTERS.has(name) || WORKFLOW_GETTERS.has(name)) {
9163
+ return (...args) => {
9164
+ const entity = value.apply(target, args);
9165
+ return entity && typeof entity === "object" ? wrapMastra(entity) : entity;
9166
+ };
9167
+ }
9168
+ return value.bind(target);
9169
+ }
9170
+ });
9171
+ }
9172
+ function wrapStreamingOutput(output, span2, ctx) {
9173
+ if (!output) {
9174
+ span2.setStatus({ code: SpanStatusCode12.OK });
9175
+ span2.end();
9176
+ return output;
9177
+ }
9178
+ let ended = false;
9179
+ const endOnce = (err) => {
9180
+ if (ended) return;
9181
+ ended = true;
9182
+ if (err) recordError6(span2, err);
9183
+ else span2.setStatus({ code: SpanStatusCode12.OK });
9184
+ span2.end();
9185
+ };
9186
+ if (output.text && typeof output.text.then === "function") {
9187
+ const finalize = async () => {
9188
+ try {
9189
+ const [text, usage, finishReason] = await Promise.all([
9190
+ Promise.resolve(output.text).catch(() => void 0),
9191
+ Promise.resolve(output.usage).catch(() => void 0),
9192
+ Promise.resolve(output.finishReason).catch(() => void 0)
9193
+ ]);
9194
+ if (typeof text === "string" && text) {
9195
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
9196
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", text);
9197
+ }
9198
+ if (usage) recordUsage(span2, usage);
9199
+ if (finishReason) span2.setAttribute("neatlogs.llm.stop_reason", normalizeFinishReason(finishReason));
9200
+ endOnce();
9201
+ } catch (err) {
9202
+ endOnce(err);
9203
+ }
9204
+ };
9205
+ void finalize();
9206
+ return rebindStreamContext(output, ctx);
9207
+ }
9208
+ if (output[Symbol.asyncIterator]) {
9209
+ const origIterator = output[Symbol.asyncIterator].bind(output);
9210
+ const textParts = [];
9211
+ const wrapped = Object.create(Object.getPrototypeOf(output));
9212
+ Object.assign(wrapped, output);
9213
+ wrapped[Symbol.asyncIterator] = function() {
9214
+ const iterator = origIterator();
9215
+ const finish = () => {
9216
+ if (textParts.length) {
9217
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
9218
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", textParts.join(""));
9219
+ }
9220
+ endOnce();
9221
+ };
9222
+ return {
9223
+ async next() {
9224
+ try {
9225
+ const r = await iterator.next();
9226
+ if (r.done) {
9227
+ finish();
9228
+ return r;
9229
+ }
9230
+ const chunk = r.value;
9231
+ if (typeof chunk === "string") textParts.push(chunk);
9232
+ else if (chunk?.text) textParts.push(chunk.text);
9233
+ else if (chunk?.delta) textParts.push(chunk.delta);
9234
+ return r;
9235
+ } catch (err) {
9236
+ endOnce(err);
9237
+ throw err;
9238
+ }
9239
+ },
9240
+ async return(value) {
9241
+ finish();
9242
+ return iterator.return?.(value) ?? { done: true, value: void 0 };
9243
+ },
9244
+ async throw(err) {
9245
+ endOnce(err);
9246
+ return iterator.throw?.(err) ?? { done: true, value: void 0 };
9247
+ }
9248
+ };
9249
+ };
9250
+ return wrapped;
9251
+ }
9252
+ if (typeof output.then === "function") {
9253
+ return output.then((resolved) => {
9254
+ finalizeAgentResult(span2, resolved);
9255
+ endOnce();
9256
+ return resolved;
9257
+ }).catch((err) => {
9258
+ endOnce(err);
9259
+ throw err;
9260
+ });
9261
+ }
9262
+ endOnce();
9263
+ return output;
9264
+ }
9265
+ function rebindStreamContext(output, ctx) {
9266
+ const STREAM_PROPS = /* @__PURE__ */ new Set(["textStream", "fullStream", "objectStream", "elementStream"]);
9267
+ return new Proxy(output, {
9268
+ get(target, prop, receiver) {
9269
+ const value = Reflect.get(target, prop, receiver);
9270
+ if (typeof prop === "string" && STREAM_PROPS.has(prop) && value && value[Symbol.asyncIterator]) {
9271
+ const origIterator = value[Symbol.asyncIterator].bind(value);
9272
+ return {
9273
+ [Symbol.asyncIterator]() {
9274
+ return otelContext11.with(ctx, () => origIterator());
9275
+ }
9276
+ };
9277
+ }
9278
+ if (typeof value === "function") return value.bind(target);
9279
+ return value;
9280
+ }
9281
+ });
9282
+ }
9283
+ function finalizeAgentResult(span2, result) {
9284
+ if (!result) return;
9285
+ if (result.text) {
9286
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
9287
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", String(result.text));
7680
9288
  }
7681
- markPatched(e);
7682
- return entity;
9289
+ if (Array.isArray(result.toolCalls)) {
9290
+ for (let i = 0; i < result.toolCalls.length; i++) {
9291
+ const tc = result.toolCalls[i];
9292
+ const p = tc.payload ?? tc;
9293
+ setToolCall(span2, i, p.toolName ?? p.name, p.args ?? p.arguments, p.toolCallId ?? p.id);
9294
+ }
9295
+ }
9296
+ if (result.usage) recordUsage(span2, result.usage);
9297
+ if (result.finishReason) span2.setAttribute("neatlogs.llm.stop_reason", normalizeFinishReason(result.finishReason));
9298
+ if (result.model) span2.setAttribute("neatlogs.llm.model_name", result.model);
7683
9299
  }
7684
- function isRootMastra(e) {
7685
- return typeof e.getAgent === "function" && typeof e.getWorkflow === "function";
9300
+ function finalizeModelResult(span2, result) {
9301
+ if (!result) return;
9302
+ if (typeof result.text === "string" && result.text) {
9303
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
9304
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", result.text);
9305
+ } else if (Array.isArray(result.content)) {
9306
+ const text = result.content.filter((p) => p?.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
9307
+ if (text) {
9308
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
9309
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", text);
9310
+ }
9311
+ const toolCalls = result.content.filter((p) => p?.type === "tool-call");
9312
+ for (let i = 0; i < toolCalls.length; i++) {
9313
+ const tc = toolCalls[i];
9314
+ setToolCall(span2, i, tc.toolName, tc.input ?? tc.args, tc.toolCallId);
9315
+ }
9316
+ span2.setAttribute("output.value", safeStringify11(result.content));
9317
+ }
9318
+ if (result.usage) recordUsage(span2, result.usage);
9319
+ span2.setAttribute("neatlogs.llm.stop_reason", normalizeFinishReason(result.finishReason));
7686
9320
  }
7687
- function isAgent(e, className) {
7688
- return className === "Agent" || typeof e.generate === "function" || typeof e.stream === "function";
9321
+ function normalizeFinishReason(fr) {
9322
+ if (fr == null) return "";
9323
+ if (typeof fr === "string") return fr;
9324
+ return String(fr.unified ?? fr.reason ?? fr.type ?? safeStringify11(fr));
7689
9325
  }
7690
- function isWorkflow(e, className) {
7691
- return className === "Workflow" || typeof e.createRun === "function";
9326
+ function tokenValue(v) {
9327
+ if (v == null) return void 0;
9328
+ if (typeof v === "number") return v;
9329
+ if (typeof v === "object" && typeof v.total === "number") return v.total;
9330
+ const n = Number(v);
9331
+ return Number.isFinite(n) ? n : void 0;
7692
9332
  }
7693
- function isVector(e, className) {
7694
- return /Vector/.test(className) || typeof e.query === "function" && typeof e.upsert === "function";
9333
+ function captureInvocationParams(attrs, callOpts) {
9334
+ if (!callOpts) return;
9335
+ const map = [
9336
+ ["temperature", "neatlogs.llm.temperature"],
9337
+ ["maxOutputTokens", "neatlogs.llm.max_tokens"],
9338
+ ["maxTokens", "neatlogs.llm.max_tokens"],
9339
+ ["topP", "neatlogs.llm.top_p"],
9340
+ ["topK", "neatlogs.llm.top_k"],
9341
+ ["frequencyPenalty", "neatlogs.llm.frequency_penalty"],
9342
+ ["presencePenalty", "neatlogs.llm.presence_penalty"]
9343
+ ];
9344
+ const invocation = {};
9345
+ for (const [src, target] of map) {
9346
+ const v = callOpts[src];
9347
+ if (v != null) {
9348
+ attrs[target] = v;
9349
+ invocation[src] = v;
9350
+ }
9351
+ }
9352
+ if (Array.isArray(callOpts.stopSequences) && callOpts.stopSequences.length) {
9353
+ attrs["neatlogs.llm.stop_sequences"] = safeStringify11(callOpts.stopSequences);
9354
+ invocation.stopSequences = callOpts.stopSequences;
9355
+ }
9356
+ if (Array.isArray(callOpts.tools)) {
9357
+ for (let i = 0; i < callOpts.tools.length; i++) {
9358
+ attrs[`neatlogs.llm.tools.${i}`] = safeStringify11(callOpts.tools[i]);
9359
+ }
9360
+ invocation.toolChoice = callOpts.toolChoice;
9361
+ }
9362
+ if (Object.keys(invocation).length) {
9363
+ attrs["neatlogs.llm.invocation_parameters"] = safeStringify11(invocation);
9364
+ }
7695
9365
  }
7696
- function isMemory(e, className) {
7697
- return /Memory/.test(className) || typeof e.recall === "function" && typeof e.saveMessages === "function";
9366
+ function setToolCall(span2, i, name, args, id) {
9367
+ span2.setAttribute(`neatlogs.llm.tool_calls.${i}.name`, name ?? "");
9368
+ span2.setAttribute(`neatlogs.llm.tool_calls.${i}.arguments`, safeStringify11(args ?? {}));
9369
+ if (id) span2.setAttribute(`neatlogs.llm.tool_calls.${i}.id`, id);
7698
9370
  }
7699
- function isDocument(e, className) {
7700
- return className === "MDocument" || typeof e.chunk === "function" && typeof e.getDocs === "function";
9371
+ function recordUsage(span2, usage) {
9372
+ if (!usage) return;
9373
+ const prompt = tokenValue(usage.promptTokens ?? usage.inputTokens ?? usage.input_tokens);
9374
+ const completion = tokenValue(usage.completionTokens ?? usage.outputTokens ?? usage.output_tokens);
9375
+ const total = tokenValue(usage.totalTokens);
9376
+ if (prompt != null) span2.setAttribute("neatlogs.llm.token_count.prompt", prompt);
9377
+ if (completion != null) span2.setAttribute("neatlogs.llm.token_count.completion", completion);
9378
+ if (total != null) span2.setAttribute("neatlogs.llm.token_count.total", total);
9379
+ else if (prompt != null && completion != null) {
9380
+ span2.setAttribute("neatlogs.llm.token_count.total", prompt + completion);
9381
+ }
7701
9382
  }
7702
- function patchAgent(agent) {
7703
- patchAgentMethod(agent, "generate", false);
7704
- patchAgentMethod(agent, "stream", true);
9383
+ function withActiveSpan(name, attrs, fn) {
9384
+ const tracer = trace13.getTracer(TRACER_NAME10);
9385
+ const span2 = tracer.startSpan(name, { attributes: attrs }, otelContext11.active());
9386
+ const ctx = trace13.setSpan(otelContext11.active(), span2);
9387
+ return otelContext11.with(ctx, async () => {
9388
+ try {
9389
+ const result = await fn(span2);
9390
+ span2.setStatus({ code: SpanStatusCode12.OK });
9391
+ return result;
9392
+ } catch (err) {
9393
+ recordError6(span2, err);
9394
+ throw err;
9395
+ } finally {
9396
+ span2.end();
9397
+ }
9398
+ });
7705
9399
  }
7706
- function patchAgentMethod(agent, method, streaming) {
7707
- if (typeof agent[method] !== "function") return;
7708
- const orig = agent[method].bind(agent);
7709
- agent[method] = async function tracedAgentMethod(input, opts) {
7710
- const agentName = agent.name ?? agent.id ?? "mastra_agent";
7711
- const model = extractModelId(agent.model);
7712
- installAgentLlmHook(agent);
7713
- installAgentToolHooks(agent);
7714
- const attrs = {
7715
- "neatlogs.span.kind": "AGENT",
7716
- "neatlogs.agent.name": agentName,
7717
- "input.value": toInputValue(input)
7718
- };
7719
- if (model) attrs["neatlogs.llm.model_name"] = model;
7720
- if (agent.instructions && typeof agent.instructions === "string") {
7721
- attrs["neatlogs.llm.system_prompt"] = agent.instructions.slice(0, 5e3);
9400
+ function markPatched(e) {
9401
+ try {
9402
+ Object.defineProperty(e, PATCH_FLAG2, { value: true, enumerable: false, configurable: true });
9403
+ } catch {
9404
+ e[PATCH_FLAG2] = true;
9405
+ }
9406
+ }
9407
+ function extractModelId(model) {
9408
+ if (!model) return "";
9409
+ if (typeof model === "string") return model;
9410
+ return model.modelId ?? model.name ?? "";
9411
+ }
9412
+ function toInputValue(input) {
9413
+ return typeof input === "string" ? input : safeStringify11(input);
9414
+ }
9415
+ function safeStringify11(value) {
9416
+ if (typeof value === "string") return value;
9417
+ try {
9418
+ return JSON.stringify(value) ?? "";
9419
+ } catch {
9420
+ return "";
9421
+ }
9422
+ }
9423
+ function recordError6(span2, err) {
9424
+ if (err instanceof Error) {
9425
+ span2.setStatus({ code: SpanStatusCode12.ERROR, message: err.message });
9426
+ span2.recordException(err);
9427
+ } else {
9428
+ span2.setStatus({ code: SpanStatusCode12.ERROR, message: String(err) });
9429
+ }
9430
+ }
9431
+
9432
+ // src/pi-agent.ts
9433
+ import {
9434
+ trace as trace14,
9435
+ context as otelContext12,
9436
+ SpanStatusCode as SpanStatusCode13
9437
+ } from "@opentelemetry/api";
9438
+ var TRACER_NAME11 = "neatlogs.pi-agent";
9439
+ var PATCH_FLAG3 = "_neatlogs_patched";
9440
+ function piAgentHooks(agent) {
9441
+ if (!agent || agent[PATCH_FLAG3]) return agent;
9442
+ const a = agent;
9443
+ if (typeof a.subscribe !== "function") return agent;
9444
+ const state = { toolSpans: /* @__PURE__ */ new Map(), inputMessages: [] };
9445
+ const tracer = trace14.getTracer(TRACER_NAME11);
9446
+ a.subscribe((event) => {
9447
+ try {
9448
+ handleEvent(tracer, state, event);
9449
+ } catch {
7722
9450
  }
7723
- if (streaming) attrs["neatlogs.llm.is_streaming"] = true;
7724
- if (streaming) {
7725
- const tracer = trace10.getTracer(TRACER_NAME7);
7726
- const span2 = tracer.startSpan(`mastra.agent.${agentName}`, { attributes: attrs }, otelContext8.active());
7727
- const ctx = trace10.setSpan(otelContext8.active(), span2);
7728
- try {
7729
- const result = await otelContext8.with(ctx, () => orig(input, opts));
7730
- return wrapStreamingOutput(result, span2, ctx);
7731
- } catch (err) {
7732
- recordError3(span2, err);
7733
- span2.end();
7734
- throw err;
9451
+ });
9452
+ markPatched2(a);
9453
+ return agent;
9454
+ }
9455
+ function handleEvent(tracer, state, event) {
9456
+ switch (event.type) {
9457
+ case "agent_start": {
9458
+ const span2 = tracer.startSpan(
9459
+ "pi_agent.run",
9460
+ { attributes: { "neatlogs.span.kind": "AGENT" } },
9461
+ otelContext12.active()
9462
+ );
9463
+ state.agentSpan = span2;
9464
+ state.agentCtx = trace14.setSpan(otelContext12.active(), span2);
9465
+ state.inputMessages = [];
9466
+ break;
9467
+ }
9468
+ case "message_end": {
9469
+ const msg = event.message;
9470
+ if (!msg) return;
9471
+ if (msg.role === "assistant") {
9472
+ emitLlmSpan(tracer, state, msg);
9473
+ const { text } = splitAssistantContent(msg.content);
9474
+ if (text) state.inputMessages.push({ role: "assistant", content: text });
9475
+ } else {
9476
+ const role = msg.role === "toolResult" ? "tool" : String(msg.role || "user");
9477
+ const content = messageText(msg);
9478
+ if (content) state.inputMessages.push({ role, content });
7735
9479
  }
9480
+ break;
7736
9481
  }
7737
- return withActiveSpan(`mastra.agent.${agentName}`, attrs, async (span2) => {
7738
- const result = await orig(input, opts);
7739
- finalizeAgentResult(span2, result);
7740
- return result;
7741
- });
7742
- };
7743
- }
7744
- function installAgentLlmHook(agent) {
7745
- if (agent.__neatlogs_llm_hook || typeof agent.getLLM !== "function") return;
7746
- agent.__neatlogs_llm_hook = true;
7747
- const origGetLLM = agent.getLLM.bind(agent);
7748
- agent.getLLM = function patchedGetLLM(...args) {
7749
- const out = origGetLLM(...args);
7750
- return Promise.resolve(out).then((llm) => {
7751
- try {
7752
- const model = typeof llm?.getModel === "function" ? llm.getModel() : llm;
7753
- patchModelInPlace(model);
7754
- } catch {
9482
+ case "tool_execution_start": {
9483
+ const parent = state.agentCtx ?? otelContext12.active();
9484
+ const span2 = tracer.startSpan(
9485
+ `pi_agent.tool.${event.toolName ?? "tool"}`,
9486
+ {
9487
+ attributes: {
9488
+ "neatlogs.span.kind": "TOOL",
9489
+ ...event.toolName ? { "neatlogs.tool.name": String(event.toolName) } : {},
9490
+ ...event.args !== void 0 ? { "input.value": safeStringify12(event.args) } : {}
9491
+ }
9492
+ },
9493
+ parent
9494
+ );
9495
+ if (event.toolCallId) state.toolSpans.set(event.toolCallId, span2);
9496
+ break;
9497
+ }
9498
+ case "tool_execution_end": {
9499
+ const span2 = event.toolCallId ? state.toolSpans.get(event.toolCallId) : void 0;
9500
+ if (!span2) return;
9501
+ if (event.result !== void 0) {
9502
+ span2.setAttribute("output.value", safeStringify12(event.result));
9503
+ }
9504
+ if (event.isError) {
9505
+ span2.setStatus({ code: SpanStatusCode13.ERROR });
9506
+ span2.setAttribute("neatlogs.tool.is_error", true);
9507
+ } else {
9508
+ span2.setStatus({ code: SpanStatusCode13.OK });
9509
+ }
9510
+ span2.end();
9511
+ if (event.toolCallId) state.toolSpans.delete(event.toolCallId);
9512
+ break;
9513
+ }
9514
+ case "agent_end": {
9515
+ for (const ts of state.toolSpans.values()) {
9516
+ try {
9517
+ ts.end();
9518
+ } catch {
9519
+ }
9520
+ }
9521
+ state.toolSpans.clear();
9522
+ if (state.agentSpan) {
9523
+ const firstUser = state.inputMessages.find((m) => m.role === "user");
9524
+ if (firstUser) state.agentSpan.setAttribute("input.value", firstUser.content);
9525
+ const finalText = lastAssistantText(event.messages);
9526
+ if (finalText) state.agentSpan.setAttribute("output.value", finalText);
9527
+ state.agentSpan.setStatus({ code: SpanStatusCode13.OK });
9528
+ state.agentSpan.end();
9529
+ state.agentSpan = void 0;
9530
+ state.agentCtx = void 0;
7755
9531
  }
7756
- return llm;
7757
- });
7758
- };
7759
- }
7760
- function patchModelInPlace(model) {
7761
- if (!model || model.__neatlogs_model_patched) return;
7762
- model.__neatlogs_model_patched = true;
7763
- const modelId = model.modelId ?? model.modelName ?? "";
7764
- const provider = model.provider ?? "";
7765
- for (const fn of ["doGenerate", "doStream"]) {
7766
- if (typeof model[fn] !== "function") continue;
7767
- const orig = model[fn].bind(model);
7768
- const isStream = fn === "doStream";
7769
- model[fn] = function tracedModelCall(callOpts) {
7770
- const attrs = { "neatlogs.span.kind": "LLM" };
7771
- if (modelId) attrs["neatlogs.llm.model_name"] = modelId;
7772
- if (provider) attrs["neatlogs.llm.provider"] = provider;
7773
- if (isStream) attrs["neatlogs.llm.is_streaming"] = true;
7774
- const promptInput = callOpts?.prompt ?? callOpts?.messages;
7775
- if (promptInput !== void 0) attrs["input.value"] = safeStringify8(promptInput).slice(0, 1e4);
7776
- captureInvocationParams(attrs, callOpts);
7777
- return withActiveSpan(`mastra.llm.${modelId || "model"}.${fn}`, attrs, async (span2) => {
7778
- const result = await orig(callOpts);
7779
- if (!isStream) finalizeModelResult(span2, result);
7780
- else if (result?.usage) recordUsage(span2, result.usage);
7781
- return result;
7782
- });
7783
- };
9532
+ break;
9533
+ }
9534
+ default:
9535
+ break;
7784
9536
  }
7785
9537
  }
7786
- function installAgentToolHooks(agent) {
7787
- if (agent.__neatlogs_tool_hook) return;
7788
- let tools;
7789
- try {
7790
- tools = typeof agent.listTools === "function" ? agent.listTools() : void 0;
7791
- } catch {
7792
- tools = void 0;
9538
+ function emitLlmSpan(tracer, state, msg) {
9539
+ const attrs = { "neatlogs.span.kind": "LLM" };
9540
+ if (msg.model) attrs["neatlogs.llm.model_name"] = String(msg.model);
9541
+ if (msg.provider) attrs["neatlogs.llm.provider"] = String(msg.provider);
9542
+ if (msg.stopReason) attrs["neatlogs.llm.stop_reason"] = String(msg.stopReason);
9543
+ const inMsgs = state.inputMessages;
9544
+ if (inMsgs.length) {
9545
+ inMsgs.forEach((m, i) => {
9546
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;
9547
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;
9548
+ });
9549
+ attrs["neatlogs.llm.input"] = safeStringify12({ messages: inMsgs });
9550
+ attrs["input.value"] = safeStringify12({ messages: inMsgs });
7793
9551
  }
7794
- if (!tools || typeof tools !== "object") return;
7795
- agent.__neatlogs_tool_hook = true;
7796
- for (const [key, tool] of Object.entries(tools)) {
7797
- patchToolExecute(tool, key);
9552
+ const { text, toolCalls } = splitAssistantContent(msg.content);
9553
+ const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify12(tc.arguments)})`).join("\n");
9554
+ if (outText || toolCalls.length) {
9555
+ attrs["neatlogs.llm.output_messages.0.role"] = "assistant";
9556
+ attrs["neatlogs.llm.output_messages.0.content"] = outText || "";
9557
+ const outBlob = { role: "assistant", content: outText || "" };
9558
+ if (toolCalls.length) {
9559
+ outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));
9560
+ toolCalls.forEach((tc, j) => {
9561
+ if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;
9562
+ if (tc.arguments !== void 0)
9563
+ attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify12(tc.arguments);
9564
+ if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);
9565
+ });
9566
+ }
9567
+ attrs["neatlogs.llm.output"] = safeStringify12(outBlob);
9568
+ attrs["output.value"] = outText || "";
7798
9569
  }
9570
+ const usage = msg.usage;
9571
+ if (usage) {
9572
+ if (usage.input != null) attrs["neatlogs.llm.token_count.prompt"] = usage.input;
9573
+ if (usage.output != null) attrs["neatlogs.llm.token_count.completion"] = usage.output;
9574
+ const total = usage.totalTokens ?? (usage.input ?? 0) + (usage.output ?? 0);
9575
+ if (total) attrs["neatlogs.llm.token_count.total"] = total;
9576
+ if (usage.cacheRead) attrs["neatlogs.llm.token_count.cache_read"] = usage.cacheRead;
9577
+ if (usage.cacheWrite) attrs["neatlogs.llm.token_count.cache_write"] = usage.cacheWrite;
9578
+ }
9579
+ const parent = state.agentCtx ?? otelContext12.active();
9580
+ const span2 = tracer.startSpan(
9581
+ `pi_agent.llm.${msg.model || "model"}`,
9582
+ { attributes: attrs },
9583
+ parent
9584
+ );
9585
+ span2.setStatus({ code: SpanStatusCode13.OK });
9586
+ span2.end();
7799
9587
  }
7800
- function patchToolExecute(tool, key) {
7801
- if (!tool || typeof tool.execute !== "function" || tool.__neatlogs_tool_patched) return;
7802
- tool.__neatlogs_tool_patched = true;
7803
- const toolName = tool.id ?? key;
7804
- const orig = tool.execute.bind(tool);
7805
- tool.execute = function tracedToolExecute(params, options) {
7806
- const attrs = {
7807
- "neatlogs.span.kind": "TOOL",
7808
- "neatlogs.tool.name": toolName,
7809
- "input.value": safeStringify8(params).slice(0, 1e4)
7810
- };
7811
- if (tool.description) attrs["neatlogs.tool.description"] = String(tool.description).slice(0, 2e3);
7812
- return withActiveSpan(`mastra.tool.${toolName}`, attrs, async (span2) => {
7813
- const result = await orig(params, options);
7814
- span2.setAttribute("output.value", safeStringify8(result).slice(0, 1e4));
7815
- return result;
7816
- });
7817
- };
7818
- }
7819
- function patchWorkflow(workflow) {
7820
- if (typeof workflow.createRun !== "function") return;
7821
- const origCreateRun = workflow.createRun.bind(workflow);
7822
- const workflowName = workflow.name ?? workflow.id ?? "mastra_workflow";
7823
- workflow.createRun = async function tracedCreateRun(...args) {
7824
- const run = await origCreateRun(...args);
7825
- if (!run) return run;
7826
- patchRunMethod(run, "start", workflowName);
7827
- patchRunMethod(run, "resume", workflowName);
7828
- return run;
7829
- };
9588
+ function splitAssistantContent(content) {
9589
+ const texts = [];
9590
+ const toolCalls = [];
9591
+ if (Array.isArray(content)) {
9592
+ for (const block of content) {
9593
+ if (!block || typeof block !== "object") continue;
9594
+ if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
9595
+ else if (block.type === "toolCall")
9596
+ toolCalls.push(block);
9597
+ }
9598
+ } else if (typeof content === "string") {
9599
+ texts.push(content);
9600
+ }
9601
+ return { text: texts.join(""), toolCalls };
7830
9602
  }
7831
- function patchRunMethod(run, method, workflowName) {
7832
- if (typeof run[method] !== "function") return;
7833
- const orig = run[method].bind(run);
7834
- run[method] = async function tracedRunMethod(startOpts) {
7835
- const attrs = {
7836
- "neatlogs.span.kind": "WORKFLOW",
7837
- "neatlogs.workflow.name": workflowName
7838
- };
7839
- if (startOpts?.inputData !== void 0) {
7840
- attrs["input.value"] = safeStringify8(startOpts.inputData).slice(0, 1e4);
9603
+ function messageText(msg) {
9604
+ if (!msg) return "";
9605
+ const c = msg.content;
9606
+ if (typeof c === "string") return c;
9607
+ if (!Array.isArray(c)) return "";
9608
+ const parts = [];
9609
+ for (const block of c) {
9610
+ if (typeof block === "string") parts.push(block);
9611
+ else if (block && typeof block === "object") {
9612
+ if (typeof block.text === "string") parts.push(block.text);
9613
+ else if (block.type === "toolCall") parts.push(`${block.name ?? "tool"}(${safeStringify12(block.arguments)})`);
7841
9614
  }
7842
- return withActiveSpan(`mastra.workflow.${workflowName}`, attrs, async (span2) => {
7843
- const result = await orig(startOpts);
7844
- if (result?.status) span2.setAttribute("neatlogs.metadata", safeStringify8({ status: result.status }));
7845
- if (result?.result !== void 0) {
7846
- span2.setAttribute("output.value", safeStringify8(result.result).slice(0, 1e4));
7847
- }
7848
- return result;
7849
- });
7850
- };
9615
+ }
9616
+ return parts.join("");
7851
9617
  }
7852
- var VECTOR_READ_OPS = ["query"];
7853
- var VECTOR_WRITE_OPS = ["upsert", "updateVector", "deleteVector", "createIndex", "deleteIndex"];
7854
- function patchVector(vector) {
7855
- for (const op of VECTOR_READ_OPS) patchVectorOp(vector, op, "RETRIEVER");
7856
- for (const op of VECTOR_WRITE_OPS) patchVectorOp(vector, op, "VECTOR_STORE");
9618
+ function lastAssistantText(messages) {
9619
+ if (!Array.isArray(messages)) return "";
9620
+ for (let i = messages.length - 1; i >= 0; i--) {
9621
+ const m = messages[i];
9622
+ if (m && m.role === "assistant") {
9623
+ const { text } = splitAssistantContent(m.content);
9624
+ if (text) return text;
9625
+ }
9626
+ }
9627
+ return "";
7857
9628
  }
7858
- function patchVectorOp(vector, op, kind) {
7859
- if (typeof vector[op] !== "function") return;
7860
- const orig = vector[op].bind(vector);
7861
- const dbName = vector.constructor?.name ?? "vector";
7862
- vector[op] = async function tracedVectorOp(params) {
7863
- const attrs = {
7864
- "neatlogs.span.kind": kind,
7865
- "neatlogs.db.system": dbName,
7866
- "neatlogs.db.operation": op,
7867
- "input.value": safeStringify8(params).slice(0, 1e4)
7868
- };
7869
- const indexName = params?.indexName;
7870
- if (indexName) attrs["neatlogs.vectordb.index_name"] = String(indexName);
7871
- if (kind === "RETRIEVER" && params?.topK != null) attrs["neatlogs.retriever.top_k"] = params.topK;
7872
- return withActiveSpan(`mastra.vector.${op}`, attrs, async (span2) => {
7873
- const result = await orig(params);
7874
- span2.setAttribute("output.value", safeStringify8(result).slice(0, 1e4));
7875
- return result;
7876
- });
7877
- };
9629
+ function markPatched2(e) {
9630
+ try {
9631
+ Object.defineProperty(e, PATCH_FLAG3, { value: true, enumerable: false, configurable: true });
9632
+ } catch {
9633
+ e[PATCH_FLAG3] = true;
9634
+ }
7878
9635
  }
7879
- var MEMORY_OPS = ["recall", "saveMessages", "updateWorkingMemory", "deleteMessages"];
7880
- function patchMemory(memory) {
7881
- for (const op of MEMORY_OPS) {
7882
- if (typeof memory[op] !== "function") continue;
7883
- const orig = memory[op].bind(memory);
7884
- memory[op] = async function tracedMemoryOp(...args) {
7885
- const attrs = {
7886
- "neatlogs.span.kind": "CHAIN",
7887
- "neatlogs.db.operation": op,
7888
- "input.value": safeStringify8(args?.[0]).slice(0, 1e4)
7889
- };
7890
- return withActiveSpan(`mastra.memory.${op}`, attrs, async (span2) => {
7891
- const result = await orig(...args);
7892
- span2.setAttribute("output.value", safeStringify8(result).slice(0, 8e3));
7893
- return result;
7894
- });
7895
- };
9636
+ function safeStringify12(value) {
9637
+ if (typeof value === "string") return value;
9638
+ try {
9639
+ return JSON.stringify(value) ?? "";
9640
+ } catch {
9641
+ return "";
7896
9642
  }
7897
9643
  }
7898
- function patchDocument(doc) {
7899
- if (typeof doc.chunk !== "function") return;
7900
- const orig = doc.chunk.bind(doc);
7901
- doc.chunk = async function tracedChunk(...args) {
7902
- const attrs = {
7903
- "neatlogs.span.kind": "CHAIN",
7904
- "neatlogs.db.operation": "chunk"
7905
- };
7906
- return withActiveSpan("mastra.document.chunk", attrs, async (span2) => {
7907
- const result = await orig(...args);
7908
- if (Array.isArray(result)) span2.setAttribute("neatlogs.db.documents_count", result.length);
7909
- return result;
9644
+
9645
+ // src/claude-agent-sdk.ts
9646
+ import { trace as trace15, context as otelContext13, SpanStatusCode as SpanStatusCode14 } from "@opentelemetry/api";
9647
+ var TRACER_NAME12 = "neatlogs.claude_agent_sdk";
9648
+ var ROOT_SCOPE = "__root__";
9649
+ function wrapClaudeAgentSDK(sdk, options = {}) {
9650
+ if (!sdk || typeof sdk.query !== "function") return sdk;
9651
+ if (sdk._neatlogsWrapped) return sdk;
9652
+ const wrapped = { ...sdk };
9653
+ wrapped.query = wrapQuery(sdk.query.bind(sdk), options);
9654
+ try {
9655
+ Object.defineProperty(wrapped, "_neatlogsWrapped", {
9656
+ value: true,
9657
+ enumerable: false,
9658
+ configurable: true
7910
9659
  });
7911
- };
9660
+ } catch {
9661
+ wrapped._neatlogsWrapped = true;
9662
+ }
9663
+ return wrapped;
7912
9664
  }
7913
- function wrapRootMastra(mastra) {
7914
- const AGENT_GETTERS = /* @__PURE__ */ new Set(["getAgent", "getAgentById"]);
7915
- const WORKFLOW_GETTERS = /* @__PURE__ */ new Set(["getWorkflow", "getWorkflowById"]);
7916
- return new Proxy(mastra, {
7917
- get(target, prop, receiver) {
7918
- const value = Reflect.get(target, prop, receiver);
7919
- if (typeof value !== "function") return value;
7920
- const name = String(prop);
7921
- if (AGENT_GETTERS.has(name) || WORKFLOW_GETTERS.has(name)) {
7922
- return (...args) => {
7923
- const entity = value.apply(target, args);
7924
- return entity && typeof entity === "object" ? wrapMastra(entity) : entity;
7925
- };
7926
- }
7927
- return value.bind(target);
7928
- }
7929
- });
9665
+ function wrapQuery(original, options) {
9666
+ return function(params, ...rest) {
9667
+ const tracer = trace15.getTracer(TRACER_NAME12);
9668
+ const workflowName = options.workflowName ?? "claude_agent.query";
9669
+ const promptRef = { text: "" };
9670
+ const agentSpan = tracer.startSpan(
9671
+ "claude_agent.query",
9672
+ { attributes: { "neatlogs.span.kind": "AGENT", "neatlogs.workflow.name": workflowName } },
9673
+ otelContext13.active()
9674
+ );
9675
+ const promptText = extractPromptText(params?.prompt);
9676
+ if (promptText) {
9677
+ promptRef.text = promptText;
9678
+ agentSpan.setAttribute("input.value", promptText);
9679
+ } else if (params && isAsyncIterable(params.prompt)) {
9680
+ params = { ...params, prompt: tapPromptStream(params.prompt, promptRef) };
9681
+ }
9682
+ const agentCtx = trace15.setSpan(otelContext13.active(), agentSpan);
9683
+ const queryObj = otelContext13.with(agentCtx, () => original(params, ...rest));
9684
+ return instrumentQueryIterable(queryObj, agentSpan, agentCtx, tracer, promptRef);
9685
+ };
7930
9686
  }
7931
- function wrapStreamingOutput(output, span2, ctx) {
7932
- if (!output) {
7933
- span2.setStatus({ code: SpanStatusCode9.OK });
7934
- span2.end();
7935
- return output;
7936
- }
7937
- let ended = false;
7938
- const endOnce = (err) => {
7939
- if (ended) return;
7940
- ended = true;
7941
- if (err) recordError3(span2, err);
7942
- else span2.setStatus({ code: SpanStatusCode9.OK });
7943
- span2.end();
9687
+ function isAsyncIterable(v) {
9688
+ return Boolean(v) && typeof v[Symbol.asyncIterator] === "function";
9689
+ }
9690
+ async function* tapPromptStream(prompt, ref) {
9691
+ for await (const message of prompt) {
9692
+ if (!ref.text) {
9693
+ const text = userMessageText(message);
9694
+ if (text) ref.text = text;
9695
+ }
9696
+ yield message;
9697
+ }
9698
+ }
9699
+ function instrumentQueryIterable(queryObj, agentSpan, agentCtx, tracer, promptRef) {
9700
+ const originalAsyncIterator = queryObj?.[Symbol.asyncIterator]?.bind(queryObj);
9701
+ if (!originalAsyncIterator) {
9702
+ agentSpan.setStatus({ code: SpanStatusCode14.OK });
9703
+ agentSpan.end();
9704
+ return queryObj;
9705
+ }
9706
+ const rootScope = {
9707
+ span: agentSpan,
9708
+ ctx: agentCtx,
9709
+ inputMessages: promptRef.text ? [{ role: "user", content: promptRef.text }] : [],
9710
+ assistantBuffer: null,
9711
+ finalText: "",
9712
+ inputCaptured: Boolean(promptRef.text)
7944
9713
  };
7945
- if (output.text && typeof output.text.then === "function") {
7946
- const finalize = async () => {
9714
+ const state = {
9715
+ toolSpans: /* @__PURE__ */ new Map(),
9716
+ scopes: /* @__PURE__ */ new Map([[ROOT_SCOPE, rootScope]]),
9717
+ finished: false,
9718
+ promptRef
9719
+ };
9720
+ const finalizeAgent = (status, err) => {
9721
+ if (state.finished) return;
9722
+ state.finished = true;
9723
+ for (const scope of state.scopes.values()) flushAssistantTurn(tracer, scope, state);
9724
+ for (const ts of state.toolSpans.values()) {
7947
9725
  try {
7948
- const [text, usage, finishReason] = await Promise.all([
7949
- Promise.resolve(output.text).catch(() => void 0),
7950
- Promise.resolve(output.usage).catch(() => void 0),
7951
- Promise.resolve(output.finishReason).catch(() => void 0)
7952
- ]);
7953
- if (typeof text === "string" && text) {
7954
- span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7955
- span2.setAttribute("neatlogs.llm.output_messages.0.content", text.slice(0, 1e4));
7956
- }
7957
- if (usage) recordUsage(span2, usage);
7958
- if (finishReason) span2.setAttribute("neatlogs.llm.stop_reason", normalizeFinishReason(finishReason));
7959
- endOnce();
7960
- } catch (err) {
7961
- endOnce(err);
9726
+ ts.end();
9727
+ } catch {
7962
9728
  }
7963
- };
7964
- void finalize();
7965
- return rebindStreamContext(output, ctx);
7966
- }
7967
- if (output[Symbol.asyncIterator]) {
7968
- const origIterator = output[Symbol.asyncIterator].bind(output);
7969
- const textParts = [];
7970
- const wrapped = Object.create(Object.getPrototypeOf(output));
7971
- Object.assign(wrapped, output);
7972
- wrapped[Symbol.asyncIterator] = function() {
7973
- const iterator = origIterator();
7974
- const finish = () => {
7975
- if (textParts.length) {
7976
- span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
7977
- span2.setAttribute("neatlogs.llm.output_messages.0.content", textParts.join("").slice(0, 1e4));
7978
- }
7979
- endOnce();
7980
- };
7981
- return {
7982
- async next() {
9729
+ }
9730
+ state.toolSpans.clear();
9731
+ for (const [key, scope] of state.scopes) {
9732
+ if (key === ROOT_SCOPE) continue;
9733
+ closeScope(scope, "ok");
9734
+ }
9735
+ if (rootScope.finalText) agentSpan.setAttribute("output.value", rootScope.finalText);
9736
+ if (status === "error") {
9737
+ recordError7(agentSpan, err);
9738
+ } else {
9739
+ agentSpan.setStatus({ code: SpanStatusCode14.OK });
9740
+ agentSpan.end();
9741
+ }
9742
+ };
9743
+ const wrapped = Object.create(Object.getPrototypeOf(queryObj));
9744
+ Object.assign(wrapped, queryObj);
9745
+ wrapped[Symbol.asyncIterator] = function() {
9746
+ const iterator = originalAsyncIterator();
9747
+ return {
9748
+ async next() {
9749
+ try {
9750
+ const result = await otelContext13.with(agentCtx, () => iterator.next());
9751
+ if (result.done) {
9752
+ finalizeAgent("ok");
9753
+ return result;
9754
+ }
7983
9755
  try {
7984
- const r = await iterator.next();
7985
- if (r.done) {
7986
- finish();
7987
- return r;
7988
- }
7989
- const chunk = r.value;
7990
- if (typeof chunk === "string") textParts.push(chunk);
7991
- else if (chunk?.text) textParts.push(chunk.text);
7992
- else if (chunk?.delta) textParts.push(chunk.delta);
7993
- return r;
7994
- } catch (err) {
7995
- endOnce(err);
7996
- throw err;
9756
+ handleMessage(tracer, state, result.value, finalizeAgent);
9757
+ } catch {
7997
9758
  }
7998
- },
7999
- async return(value) {
8000
- finish();
8001
- return iterator.return?.(value) ?? { done: true, value: void 0 };
8002
- },
8003
- async throw(err) {
8004
- endOnce(err);
8005
- return iterator.throw?.(err) ?? { done: true, value: void 0 };
9759
+ return result;
9760
+ } catch (err) {
9761
+ finalizeAgent("error", err);
9762
+ throw err;
8006
9763
  }
8007
- };
9764
+ },
9765
+ async return(value) {
9766
+ finalizeAgent("ok");
9767
+ return iterator.return?.(value) ?? { done: true, value: void 0 };
9768
+ },
9769
+ async throw(err) {
9770
+ finalizeAgent("error", err);
9771
+ if (iterator.throw) return iterator.throw(err);
9772
+ throw err;
9773
+ }
8008
9774
  };
8009
- return wrapped;
8010
- }
8011
- if (typeof output.then === "function") {
8012
- return output.then((resolved) => {
8013
- finalizeAgentResult(span2, resolved);
8014
- endOnce();
8015
- return resolved;
8016
- }).catch((err) => {
8017
- endOnce(err);
8018
- throw err;
8019
- });
9775
+ };
9776
+ return wrapped;
9777
+ }
9778
+ function closeScope(scope, status) {
9779
+ try {
9780
+ if (scope.finalText) scope.span.setAttribute("output.value", scope.finalText);
9781
+ scope.span.setStatus({ code: status === "ok" ? SpanStatusCode14.OK : SpanStatusCode14.ERROR });
9782
+ scope.span.end();
9783
+ } catch {
8020
9784
  }
8021
- endOnce();
8022
- return output;
8023
9785
  }
8024
- function rebindStreamContext(output, ctx) {
8025
- const STREAM_PROPS = /* @__PURE__ */ new Set(["textStream", "fullStream", "objectStream", "elementStream"]);
8026
- return new Proxy(output, {
8027
- get(target, prop, receiver) {
8028
- const value = Reflect.get(target, prop, receiver);
8029
- if (typeof prop === "string" && STREAM_PROPS.has(prop) && value && value[Symbol.asyncIterator]) {
8030
- const origIterator = value[Symbol.asyncIterator].bind(value);
8031
- return {
8032
- [Symbol.asyncIterator]() {
8033
- return otelContext8.with(ctx, () => origIterator());
8034
- }
8035
- };
9786
+ function getScope(tracer, state, msg) {
9787
+ const parentId = msg?.parent_tool_use_id ?? null;
9788
+ if (!parentId) return state.scopes.get(ROOT_SCOPE);
9789
+ const existing = state.scopes.get(parentId);
9790
+ if (existing) return existing;
9791
+ const root = state.scopes.get(ROOT_SCOPE);
9792
+ if (!state.toolSpans.has(parentId) && root.assistantBuffer) {
9793
+ flushAssistantTurn(tracer, root, state);
9794
+ }
9795
+ const parentToolSpan = state.toolSpans.get(parentId);
9796
+ const parentCtx = parentToolSpan ? trace15.setSpan(otelContext13.active(), parentToolSpan) : state.scopes.get(ROOT_SCOPE).ctx;
9797
+ const subType = msg?.subagent_type ? String(msg.subagent_type) : "subagent";
9798
+ const attrs = {
9799
+ "neatlogs.span.kind": "AGENT",
9800
+ "neatlogs.agent.name": subType
9801
+ };
9802
+ if (msg?.task_description) attrs["input.value"] = String(msg.task_description);
9803
+ const span2 = tracer.startSpan(`claude_agent.subagent.${subType}`, { attributes: attrs }, parentCtx);
9804
+ const scope = {
9805
+ span: span2,
9806
+ ctx: trace15.setSpan(otelContext13.active(), span2),
9807
+ inputMessages: msg?.task_description ? [{ role: "user", content: String(msg.task_description) }] : [],
9808
+ assistantBuffer: null,
9809
+ finalText: "",
9810
+ inputCaptured: Boolean(msg?.task_description)
9811
+ };
9812
+ state.scopes.set(parentId, scope);
9813
+ return scope;
9814
+ }
9815
+ function handleMessage(tracer, state, msg, finalizeAgent) {
9816
+ if (!msg || typeof msg !== "object") return;
9817
+ const rootScope = state.scopes.get(ROOT_SCOPE);
9818
+ if (!rootScope.inputCaptured && state.promptRef.text) {
9819
+ rootScope.inputCaptured = true;
9820
+ rootScope.span.setAttribute("input.value", state.promptRef.text);
9821
+ }
9822
+ switch (msg.type) {
9823
+ case "system": {
9824
+ if (msg.session_id) {
9825
+ state.sessionId = msg.session_id;
9826
+ rootScope.span.setAttribute("neatlogs.conversation.id", String(msg.session_id));
9827
+ }
9828
+ if (msg.model) {
9829
+ state.model = msg.model;
9830
+ rootScope.span.setAttribute("neatlogs.agent.model", String(msg.model));
8036
9831
  }
8037
- if (typeof value === "function") return value.bind(target);
8038
- return value;
9832
+ break;
8039
9833
  }
8040
- });
8041
- }
8042
- function finalizeAgentResult(span2, result) {
8043
- if (!result) return;
8044
- if (result.text) {
8045
- span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8046
- span2.setAttribute("neatlogs.llm.output_messages.0.content", String(result.text).slice(0, 1e4));
8047
- }
8048
- if (Array.isArray(result.toolCalls)) {
8049
- for (let i = 0; i < result.toolCalls.length; i++) {
8050
- const tc = result.toolCalls[i];
8051
- const p = tc.payload ?? tc;
8052
- setToolCall(span2, i, p.toolName ?? p.name, p.args ?? p.arguments, p.toolCallId ?? p.id);
9834
+ case "user": {
9835
+ const scope = getScope(tracer, state, msg);
9836
+ flushAssistantTurn(tracer, scope, state);
9837
+ const userText = userMessageText(msg);
9838
+ if (userText) {
9839
+ if (!scope.inputCaptured) {
9840
+ scope.inputCaptured = true;
9841
+ scope.span.setAttribute("input.value", userText);
9842
+ }
9843
+ scope.inputMessages.push({ role: "user", content: userText });
9844
+ }
9845
+ closeToolSpansFromUser(state, scope, msg);
9846
+ break;
8053
9847
  }
8054
- }
8055
- if (result.usage) recordUsage(span2, result.usage);
8056
- if (result.finishReason) span2.setAttribute("neatlogs.llm.stop_reason", normalizeFinishReason(result.finishReason));
8057
- if (result.model) span2.setAttribute("neatlogs.llm.model_name", result.model);
8058
- }
8059
- function finalizeModelResult(span2, result) {
8060
- if (!result) return;
8061
- if (typeof result.text === "string" && result.text) {
8062
- span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8063
- span2.setAttribute("neatlogs.llm.output_messages.0.content", result.text.slice(0, 1e4));
8064
- } else if (Array.isArray(result.content)) {
8065
- const text = result.content.filter((p) => p?.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
8066
- if (text) {
8067
- span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
8068
- span2.setAttribute("neatlogs.llm.output_messages.0.content", text.slice(0, 1e4));
9848
+ case "assistant": {
9849
+ const scope = getScope(tracer, state, msg);
9850
+ bufferAssistantMessage(scope, msg);
9851
+ break;
8069
9852
  }
8070
- const toolCalls = result.content.filter((p) => p?.type === "tool-call");
8071
- for (let i = 0; i < toolCalls.length; i++) {
8072
- const tc = toolCalls[i];
8073
- setToolCall(span2, i, tc.toolName, tc.input ?? tc.args, tc.toolCallId);
9853
+ case "result": {
9854
+ flushAssistantTurn(tracer, rootScope, state);
9855
+ const text = typeof msg.result === "string" ? msg.result : "";
9856
+ if (text) rootScope.finalText = text;
9857
+ if (msg.session_id && !state.sessionId) {
9858
+ rootScope.span.setAttribute("neatlogs.conversation.id", String(msg.session_id));
9859
+ }
9860
+ const usage = msg.usage;
9861
+ if (usage) setUsage2(rootScope.span, usage);
9862
+ if (msg.total_cost_usd != null) rootScope.span.setAttribute("neatlogs.agent.cost_usd", msg.total_cost_usd);
9863
+ if (msg.num_turns != null) rootScope.span.setAttribute("neatlogs.agent.num_turns", msg.num_turns);
9864
+ if (msg.is_error) rootScope.span.setAttribute("neatlogs.agent.is_error", true);
9865
+ finalizeAgent(msg.is_error ? "error" : "ok", msg.is_error ? new Error(String(text || "agent run failed")) : void 0);
9866
+ break;
8074
9867
  }
8075
- span2.setAttribute("output.value", safeStringify8(result.content).slice(0, 1e4));
9868
+ default:
9869
+ break;
8076
9870
  }
8077
- if (result.usage) recordUsage(span2, result.usage);
8078
- span2.setAttribute("neatlogs.llm.stop_reason", normalizeFinishReason(result.finishReason));
8079
- }
8080
- function normalizeFinishReason(fr) {
8081
- if (fr == null) return "";
8082
- if (typeof fr === "string") return fr;
8083
- return String(fr.unified ?? fr.reason ?? fr.type ?? safeStringify8(fr));
8084
- }
8085
- function tokenValue(v) {
8086
- if (v == null) return void 0;
8087
- if (typeof v === "number") return v;
8088
- if (typeof v === "object" && typeof v.total === "number") return v.total;
8089
- const n = Number(v);
8090
- return Number.isFinite(n) ? n : void 0;
8091
9871
  }
8092
- function captureInvocationParams(attrs, callOpts) {
8093
- if (!callOpts) return;
8094
- const map = [
8095
- ["temperature", "neatlogs.llm.temperature"],
8096
- ["maxOutputTokens", "neatlogs.llm.max_tokens"],
8097
- ["maxTokens", "neatlogs.llm.max_tokens"],
8098
- ["topP", "neatlogs.llm.top_p"],
8099
- ["topK", "neatlogs.llm.top_k"],
8100
- ["frequencyPenalty", "neatlogs.llm.frequency_penalty"],
8101
- ["presencePenalty", "neatlogs.llm.presence_penalty"]
8102
- ];
8103
- const invocation = {};
8104
- for (const [src, target] of map) {
8105
- const v = callOpts[src];
8106
- if (v != null) {
8107
- attrs[target] = v;
8108
- invocation[src] = v;
9872
+ function bufferAssistantMessage(scope, msg) {
9873
+ const message = msg.message ?? msg;
9874
+ const content = message?.content ?? [];
9875
+ if (!scope.assistantBuffer) {
9876
+ scope.assistantBuffer = { textParts: [], thinkingParts: [], toolCalls: [], usage: void 0 };
9877
+ }
9878
+ const buf = scope.assistantBuffer;
9879
+ if (message?.model) buf.model = message.model;
9880
+ if (message?.stop_reason) buf.stopReason = message.stop_reason;
9881
+ if (message?.usage) buf.usage = message.usage;
9882
+ for (const block of Array.isArray(content) ? content : []) {
9883
+ if (!block || typeof block !== "object") continue;
9884
+ if (block.type === "text" && typeof block.text === "string") buf.textParts.push(block.text);
9885
+ else if (block.type === "thinking" && typeof block.thinking === "string") buf.thinkingParts.push(block.thinking);
9886
+ else if (block.type === "tool_use") {
9887
+ buf.toolCalls.push({ id: block.id ?? "", name: block.name ?? "", input: block.input });
8109
9888
  }
8110
9889
  }
8111
- if (Array.isArray(callOpts.stopSequences) && callOpts.stopSequences.length) {
8112
- attrs["neatlogs.llm.stop_sequences"] = safeStringify8(callOpts.stopSequences);
8113
- invocation.stopSequences = callOpts.stopSequences;
9890
+ }
9891
+ function flushAssistantTurn(tracer, scope, state) {
9892
+ const buf = scope.assistantBuffer;
9893
+ if (!buf) return;
9894
+ scope.assistantBuffer = null;
9895
+ const model = buf.model ?? state.model ?? "";
9896
+ const attrs = {
9897
+ "neatlogs.span.kind": "LLM",
9898
+ "neatlogs.llm.provider": "anthropic",
9899
+ "neatlogs.llm.system": "anthropic"
9900
+ };
9901
+ if (model) attrs["neatlogs.llm.model_name"] = String(model);
9902
+ if (scope.inputMessages.length === 0 && state.promptRef.text) {
9903
+ scope.inputMessages.push({ role: "user", content: state.promptRef.text });
8114
9904
  }
8115
- if (Array.isArray(callOpts.tools)) {
8116
- for (let i = 0; i < callOpts.tools.length; i++) {
8117
- attrs[`neatlogs.llm.tools.${i}`] = safeStringify8(callOpts.tools[i]).slice(0, 4e3);
8118
- }
8119
- invocation.toolChoice = callOpts.toolChoice;
9905
+ scope.inputMessages.forEach((m, i) => {
9906
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;
9907
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;
9908
+ });
9909
+ if (scope.inputMessages.length) {
9910
+ attrs["input.value"] = safeStringify13({ messages: scope.inputMessages });
8120
9911
  }
8121
- if (Object.keys(invocation).length) {
8122
- attrs["neatlogs.llm.invocation_parameters"] = safeStringify8(invocation).slice(0, 4e3);
9912
+ const outText = buf.textParts.join("");
9913
+ const outValue = outText || buf.toolCalls.map((tc) => `${tc.name}(${safeStringify13(tc.input ?? {})})`).join("\n");
9914
+ if (outValue) {
9915
+ attrs["neatlogs.llm.output_messages.0.role"] = "assistant";
9916
+ attrs["neatlogs.llm.output_messages.0.content"] = outValue;
9917
+ attrs["output.value"] = outValue;
8123
9918
  }
8124
- }
8125
- function setToolCall(span2, i, name, args, id) {
8126
- span2.setAttribute(`neatlogs.llm.tool_calls.${i}.name`, name ?? "");
8127
- span2.setAttribute(`neatlogs.llm.tool_calls.${i}.arguments`, safeStringify8(args ?? {}));
8128
- if (id) span2.setAttribute(`neatlogs.llm.tool_calls.${i}.id`, id);
8129
- }
8130
- function recordUsage(span2, usage) {
8131
- if (!usage) return;
8132
- const prompt = tokenValue(usage.promptTokens ?? usage.inputTokens ?? usage.input_tokens);
8133
- const completion = tokenValue(usage.completionTokens ?? usage.outputTokens ?? usage.output_tokens);
8134
- const total = tokenValue(usage.totalTokens);
8135
- if (prompt != null) span2.setAttribute("neatlogs.llm.token_count.prompt", prompt);
8136
- if (completion != null) span2.setAttribute("neatlogs.llm.token_count.completion", completion);
8137
- if (total != null) span2.setAttribute("neatlogs.llm.token_count.total", total);
8138
- else if (prompt != null && completion != null) {
8139
- span2.setAttribute("neatlogs.llm.token_count.total", prompt + completion);
9919
+ if (buf.thinkingParts.length) {
9920
+ attrs["neatlogs.llm.output_messages.0.thinking"] = buf.thinkingParts.join("");
8140
9921
  }
8141
- }
8142
- function withActiveSpan(name, attrs, fn) {
8143
- const tracer = trace10.getTracer(TRACER_NAME7);
8144
- const span2 = tracer.startSpan(name, { attributes: attrs }, otelContext8.active());
8145
- const ctx = trace10.setSpan(otelContext8.active(), span2);
8146
- return otelContext8.with(ctx, async () => {
8147
- try {
8148
- const result = await fn(span2);
8149
- span2.setStatus({ code: SpanStatusCode9.OK });
8150
- return result;
8151
- } catch (err) {
8152
- recordError3(span2, err);
8153
- throw err;
8154
- } finally {
8155
- span2.end();
8156
- }
9922
+ buf.toolCalls.forEach((tc, j) => {
9923
+ attrs[`neatlogs.llm.tool_calls.${j}.id`] = tc.id;
9924
+ attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;
9925
+ attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify13(tc.input ?? {});
8157
9926
  });
9927
+ if (buf.stopReason) attrs["neatlogs.llm.finish_reason"] = String(buf.stopReason);
9928
+ const span2 = tracer.startSpan(`claude_agent.llm.${model || "model"}`, { attributes: attrs }, scope.ctx);
9929
+ if (buf.usage) setUsage2(span2, buf.usage);
9930
+ span2.setStatus({ code: SpanStatusCode14.OK });
9931
+ span2.end();
9932
+ if (outText) scope.finalText = outText;
9933
+ const turnParts = [];
9934
+ if (outText) turnParts.push(outText);
9935
+ for (const tc of buf.toolCalls) turnParts.push(`[tool_call ${tc.name} ${safeStringify13(tc.input ?? {})}]`);
9936
+ if (turnParts.length) scope.inputMessages.push({ role: "assistant", content: turnParts.join("\n") });
9937
+ for (const tc of buf.toolCalls) {
9938
+ const toolSpan = tracer.startSpan(
9939
+ `claude_agent.tool.${tc.name || "tool"}`,
9940
+ {
9941
+ attributes: {
9942
+ "neatlogs.span.kind": "TOOL",
9943
+ "neatlogs.tool.name": String(tc.name ?? ""),
9944
+ ...tc.id ? { "neatlogs.tool_call.id": String(tc.id) } : {},
9945
+ "input.value": safeStringify13(tc.input ?? {})
9946
+ }
9947
+ },
9948
+ scope.ctx
9949
+ );
9950
+ if (tc.id) state.toolSpans.set(tc.id, toolSpan);
9951
+ }
9952
+ }
9953
+ function closeToolSpansFromUser(state, scope, msg) {
9954
+ const content = (msg.message ?? msg)?.content ?? [];
9955
+ for (const block of Array.isArray(content) ? content : []) {
9956
+ if (block?.type !== "tool_result") continue;
9957
+ const id = block.tool_use_id ?? "";
9958
+ const out = block.content;
9959
+ const outText = typeof out === "string" ? out : safeStringify13(out);
9960
+ if (outText) scope.inputMessages.push({ role: "tool", content: outText });
9961
+ const span2 = state.toolSpans.get(id);
9962
+ if (!span2) continue;
9963
+ span2.setAttribute("output.value", outText);
9964
+ if (block.is_error) {
9965
+ span2.setStatus({ code: SpanStatusCode14.ERROR });
9966
+ span2.setAttribute("neatlogs.tool.is_error", true);
9967
+ } else {
9968
+ span2.setStatus({ code: SpanStatusCode14.OK });
9969
+ }
9970
+ span2.end();
9971
+ state.toolSpans.delete(id);
9972
+ }
8158
9973
  }
8159
- function markPatched(e) {
8160
- try {
8161
- Object.defineProperty(e, PATCH_FLAG2, { value: true, enumerable: false, configurable: true });
8162
- } catch {
8163
- e[PATCH_FLAG2] = true;
9974
+ function userMessageText(msg) {
9975
+ const content = (msg.message ?? msg)?.content;
9976
+ if (typeof content === "string") return content;
9977
+ if (!Array.isArray(content)) return "";
9978
+ const parts = [];
9979
+ for (const block of content) {
9980
+ if (typeof block === "string") parts.push(block);
9981
+ else if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string") {
9982
+ parts.push(block.text);
9983
+ }
8164
9984
  }
9985
+ return parts.join("\n");
8165
9986
  }
8166
- function extractModelId(model) {
8167
- if (!model) return "";
8168
- if (typeof model === "string") return model;
8169
- return model.modelId ?? model.name ?? "";
9987
+ function setUsage2(span2, usage) {
9988
+ if (!usage) return;
9989
+ if (usage.input_tokens != null) span2.setAttribute("neatlogs.llm.token_count.prompt", usage.input_tokens);
9990
+ if (usage.output_tokens != null) span2.setAttribute("neatlogs.llm.token_count.completion", usage.output_tokens);
9991
+ if (usage.input_tokens != null && usage.output_tokens != null) {
9992
+ span2.setAttribute("neatlogs.llm.token_count.total", usage.input_tokens + usage.output_tokens);
9993
+ }
9994
+ if (usage.cache_read_input_tokens != null) {
9995
+ span2.setAttribute("neatlogs.llm.token_count.cache_read", usage.cache_read_input_tokens);
9996
+ }
9997
+ if (usage.cache_creation_input_tokens != null) {
9998
+ span2.setAttribute("neatlogs.llm.token_count.cache_write", usage.cache_creation_input_tokens);
9999
+ }
8170
10000
  }
8171
- function toInputValue(input) {
8172
- return (typeof input === "string" ? input : safeStringify8(input)).slice(0, 1e4);
10001
+ function extractPromptText(prompt) {
10002
+ if (typeof prompt === "string") return prompt;
10003
+ return "";
8173
10004
  }
8174
- function safeStringify8(value) {
10005
+ function safeStringify13(value) {
8175
10006
  if (typeof value === "string") return value;
8176
10007
  try {
8177
10008
  return JSON.stringify(value) ?? "";
@@ -8179,220 +10010,457 @@ function safeStringify8(value) {
8179
10010
  return "";
8180
10011
  }
8181
10012
  }
8182
- function recordError3(span2, err) {
10013
+ function recordError7(span2, err) {
8183
10014
  if (err instanceof Error) {
8184
- span2.setStatus({ code: SpanStatusCode9.ERROR, message: err.message });
10015
+ span2.setStatus({ code: SpanStatusCode14.ERROR, message: err.message });
8185
10016
  span2.recordException(err);
8186
10017
  } else {
8187
- span2.setStatus({ code: SpanStatusCode9.ERROR, message: String(err) });
10018
+ span2.setStatus({ code: SpanStatusCode14.ERROR, message: String(err) });
8188
10019
  }
10020
+ span2.end();
8189
10021
  }
8190
10022
 
8191
- // src/pi-agent.ts
8192
- import {
8193
- trace as trace11,
8194
- context as otelContext9,
8195
- SpanStatusCode as SpanStatusCode10
8196
- } from "@opentelemetry/api";
8197
- var TRACER_NAME8 = "neatlogs.pi-agent";
8198
- var PATCH_FLAG3 = "_neatlogs_patched";
8199
- function piAgentHooks(agent) {
8200
- if (!agent || agent[PATCH_FLAG3]) return agent;
8201
- const a = agent;
8202
- if (typeof a.subscribe !== "function") return agent;
8203
- const state = { toolSpans: /* @__PURE__ */ new Map(), inputMessages: [] };
8204
- const tracer = trace11.getTracer(TRACER_NAME8);
8205
- a.subscribe((event) => {
8206
- try {
8207
- handleEvent(tracer, state, event);
8208
- } catch {
10023
+ // src/openrouter-agent.ts
10024
+ import { trace as trace16, context as otelContext14, SpanStatusCode as SpanStatusCode15 } from "@opentelemetry/api";
10025
+ var TRACER_NAME13 = "neatlogs.openrouter_agent";
10026
+ var PROVIDER4 = "openrouter";
10027
+ function wrapOpenRouterAgent(client) {
10028
+ const c = client;
10029
+ if (!c || c._neatlogsWrapped) return client;
10030
+ return new Proxy(client, {
10031
+ get(obj, prop, receiver) {
10032
+ const value = Reflect.get(obj, prop, receiver);
10033
+ if (prop === "callModel" && typeof value === "function") {
10034
+ return tracedCallModel(value.bind(obj));
10035
+ }
10036
+ return value;
8209
10037
  }
8210
10038
  });
8211
- markPatched2(a);
8212
- return agent;
8213
10039
  }
8214
- function handleEvent(tracer, state, event) {
8215
- switch (event.type) {
8216
- case "agent_start": {
8217
- const span2 = tracer.startSpan(
8218
- "pi_agent.run",
8219
- { attributes: { "neatlogs.span.kind": "AGENT" } },
8220
- otelContext9.active()
8221
- );
8222
- state.agentSpan = span2;
8223
- state.agentCtx = trace11.setSpan(otelContext9.active(), span2);
8224
- state.inputMessages = [];
8225
- break;
8226
- }
8227
- case "message_end": {
8228
- const msg = event.message;
8229
- if (!msg) return;
8230
- if (msg.role === "assistant") {
8231
- emitLlmSpan(tracer, state, msg);
8232
- const { text } = splitAssistantContent(msg.content);
8233
- if (text) state.inputMessages.push({ role: "assistant", content: text });
8234
- } else {
8235
- const role = msg.role === "toolResult" ? "tool" : String(msg.role || "user");
8236
- const content = messageText(msg);
8237
- if (content) state.inputMessages.push({ role, content });
8238
- }
8239
- break;
10040
+ function wrapCallModel(callModel) {
10041
+ return function(clientArg, opts, ...rest) {
10042
+ const span2 = startLlmSpan(opts);
10043
+ const ctx = trace16.setSpan(otelContext14.active(), span2);
10044
+ const result = otelContext14.with(ctx, () => callModel.call(this, clientArg, opts, ...rest));
10045
+ return instrumentModelResult(result, span2);
10046
+ };
10047
+ }
10048
+ function tracedCallModel(original) {
10049
+ return function(opts, ...rest) {
10050
+ const span2 = startLlmSpan(opts);
10051
+ const ctx = trace16.setSpan(otelContext14.active(), span2);
10052
+ const result = otelContext14.with(ctx, () => original(opts, ...rest));
10053
+ return instrumentModelResult(result, span2);
10054
+ };
10055
+ }
10056
+ function startLlmSpan(opts) {
10057
+ const tracer = trace16.getTracer(TRACER_NAME13);
10058
+ const model = opts?.model ?? "";
10059
+ const span2 = tracer.startSpan("openrouter.call_model", {
10060
+ attributes: {
10061
+ "neatlogs.span.kind": "LLM",
10062
+ "neatlogs.llm.provider": PROVIDER4,
10063
+ "neatlogs.llm.system": PROVIDER4,
10064
+ "neatlogs.llm.model_name": model
8240
10065
  }
8241
- case "tool_execution_start": {
8242
- const parent = state.agentCtx ?? otelContext9.active();
8243
- const span2 = tracer.startSpan(
8244
- `pi_agent.tool.${event.toolName ?? "tool"}`,
8245
- {
8246
- attributes: {
8247
- "neatlogs.span.kind": "TOOL",
8248
- ...event.toolName ? { "neatlogs.tool.name": String(event.toolName) } : {},
8249
- ...event.args !== void 0 ? { "input.value": safeStringify9(event.args).slice(0, 1e4) } : {}
8250
- }
8251
- },
8252
- parent
10066
+ }, otelContext14.active());
10067
+ const messages = Array.isArray(opts?.messages) ? opts.messages : Array.isArray(opts?.input) ? opts.input : [];
10068
+ if (messages.length) {
10069
+ messages.forEach((msg, i) => {
10070
+ span2.setAttribute(`neatlogs.llm.input_messages.${i}.role`, msg?.role ?? "user");
10071
+ const content = msg?.content;
10072
+ span2.setAttribute(
10073
+ `neatlogs.llm.input_messages.${i}.content`,
10074
+ typeof content === "string" ? content : safeStringify14(content)
8253
10075
  );
8254
- if (event.toolCallId) state.toolSpans.set(event.toolCallId, span2);
8255
- break;
8256
- }
8257
- case "tool_execution_end": {
8258
- const span2 = event.toolCallId ? state.toolSpans.get(event.toolCallId) : void 0;
8259
- if (!span2) return;
8260
- if (event.result !== void 0) {
8261
- span2.setAttribute("output.value", safeStringify9(event.result).slice(0, 1e4));
8262
- }
8263
- if (event.isError) {
8264
- span2.setStatus({ code: SpanStatusCode10.ERROR });
8265
- span2.setAttribute("neatlogs.tool.is_error", true);
8266
- } else {
8267
- span2.setStatus({ code: SpanStatusCode10.OK });
8268
- }
10076
+ });
10077
+ span2.setAttribute("input.value", safeStringify14({ messages }));
10078
+ } else if (typeof opts?.input === "string") {
10079
+ span2.setAttribute("neatlogs.llm.input_messages.0.role", "user");
10080
+ span2.setAttribute("neatlogs.llm.input_messages.0.content", opts.input);
10081
+ span2.setAttribute("input.value", opts.input);
10082
+ }
10083
+ if (typeof opts?.instructions === "string" && opts.instructions) {
10084
+ span2.setAttribute("neatlogs.llm.system_prompt", opts.instructions);
10085
+ }
10086
+ if (Array.isArray(opts?.tools)) {
10087
+ for (let i = 0; i < opts.tools.length; i++) {
10088
+ const t = opts.tools[i] ?? {};
10089
+ const name = t.name ?? t.function?.name;
10090
+ if (name) span2.setAttribute(`neatlogs.llm.tools.${i}.name`, name);
10091
+ const desc = t.description ?? t.function?.description;
10092
+ if (desc) span2.setAttribute(`neatlogs.llm.tools.${i}.description`, desc);
10093
+ }
10094
+ }
10095
+ const temperature = opts?.temperature;
10096
+ const topP = opts?.top_p ?? opts?.topP;
10097
+ const maxTokens = opts?.max_tokens ?? opts?.maxTokens ?? opts?.max_output_tokens ?? opts?.maxOutputTokens;
10098
+ const frequencyPenalty = opts?.frequency_penalty ?? opts?.frequencyPenalty;
10099
+ const presencePenalty = opts?.presence_penalty ?? opts?.presencePenalty;
10100
+ const topK = opts?.top_k ?? opts?.topK;
10101
+ if (temperature != null) span2.setAttribute("neatlogs.llm.temperature", temperature);
10102
+ if (topP != null) span2.setAttribute("neatlogs.llm.top_p", topP);
10103
+ if (maxTokens != null) span2.setAttribute("neatlogs.llm.max_tokens", maxTokens);
10104
+ const params = {};
10105
+ if (temperature != null) params.temperature = temperature;
10106
+ if (topP != null) params.top_p = topP;
10107
+ if (maxTokens != null) params.max_tokens = maxTokens;
10108
+ if (frequencyPenalty != null) params.frequency_penalty = frequencyPenalty;
10109
+ if (presencePenalty != null) params.presence_penalty = presencePenalty;
10110
+ if (topK != null) params.top_k = topK;
10111
+ if (Object.keys(params).length) {
10112
+ span2.setAttribute("neatlogs.llm.invocation_parameters", JSON.stringify(params));
10113
+ }
10114
+ return span2;
10115
+ }
10116
+ function instrumentModelResult(result, span2) {
10117
+ if (!result || typeof result !== "object" && typeof result !== "function") {
10118
+ span2.setStatus({ code: SpanStatusCode15.OK });
10119
+ span2.end();
10120
+ return result;
10121
+ }
10122
+ let finalized = false;
10123
+ const finalizeFromResult = (resolved) => {
10124
+ if (finalized) return;
10125
+ finalized = true;
10126
+ try {
10127
+ finalizeLlm(span2, resolved);
10128
+ } catch {
10129
+ span2.setStatus({ code: SpanStatusCode15.OK });
8269
10130
  span2.end();
8270
- if (event.toolCallId) state.toolSpans.delete(event.toolCallId);
8271
- break;
8272
10131
  }
8273
- case "agent_end": {
8274
- for (const ts of state.toolSpans.values()) {
8275
- try {
8276
- ts.end();
8277
- } catch {
8278
- }
10132
+ };
10133
+ const finalizeError = (err) => {
10134
+ if (finalized) return;
10135
+ finalized = true;
10136
+ recordError8(span2, err);
10137
+ };
10138
+ return new Proxy(result, {
10139
+ get(obj, prop, receiver) {
10140
+ const value = Reflect.get(obj, prop, receiver);
10141
+ if (prop === "getResponse" && typeof value === "function") {
10142
+ return function(...args) {
10143
+ return Promise.resolve(value.apply(obj, args)).then(
10144
+ (resp) => {
10145
+ finalizeFromResult(resp);
10146
+ return resp;
10147
+ },
10148
+ (err) => {
10149
+ finalizeError(err);
10150
+ throw err;
10151
+ }
10152
+ );
10153
+ };
8279
10154
  }
8280
- state.toolSpans.clear();
8281
- if (state.agentSpan) {
8282
- const firstUser = state.inputMessages.find((m) => m.role === "user");
8283
- if (firstUser) state.agentSpan.setAttribute("input.value", firstUser.content.slice(0, 1e4));
8284
- const finalText = lastAssistantText(event.messages);
8285
- if (finalText) state.agentSpan.setAttribute("output.value", finalText.slice(0, 1e4));
8286
- state.agentSpan.setStatus({ code: SpanStatusCode10.OK });
8287
- state.agentSpan.end();
8288
- state.agentSpan = void 0;
8289
- state.agentCtx = void 0;
10155
+ if (prop === "getText" && typeof value === "function") {
10156
+ return function(...args) {
10157
+ return Promise.resolve(value.apply(obj, args)).then(
10158
+ async (textStr) => {
10159
+ let resp;
10160
+ if (typeof obj?.getResponse === "function") {
10161
+ try {
10162
+ resp = await obj.getResponse();
10163
+ } catch {
10164
+ resp = void 0;
10165
+ }
10166
+ }
10167
+ finalizeFromResult(resp ? { ...resp, text: textStr } : { text: textStr });
10168
+ return textStr;
10169
+ },
10170
+ (err) => {
10171
+ finalizeError(err);
10172
+ throw err;
10173
+ }
10174
+ );
10175
+ };
8290
10176
  }
8291
- break;
10177
+ if (prop === "then" && typeof value === "function") {
10178
+ return function(onFulfilled, onRejected) {
10179
+ return value.call(
10180
+ obj,
10181
+ (resolved) => {
10182
+ finalizeFromResult(typeof resolved === "string" ? { text: resolved } : resolved ?? obj);
10183
+ return onFulfilled ? onFulfilled(resolved) : resolved;
10184
+ },
10185
+ (err) => {
10186
+ finalizeError(err);
10187
+ return onRejected ? onRejected(err) : Promise.reject(err);
10188
+ }
10189
+ );
10190
+ };
10191
+ }
10192
+ return value;
8292
10193
  }
8293
- default:
8294
- break;
8295
- }
10194
+ });
8296
10195
  }
8297
- function emitLlmSpan(tracer, state, msg) {
8298
- const attrs = { "neatlogs.span.kind": "LLM" };
8299
- if (msg.model) attrs["neatlogs.llm.model_name"] = String(msg.model);
8300
- if (msg.provider) attrs["neatlogs.llm.provider"] = String(msg.provider);
8301
- if (msg.stopReason) attrs["neatlogs.llm.stop_reason"] = String(msg.stopReason);
8302
- const inMsgs = state.inputMessages;
8303
- if (inMsgs.length) {
8304
- inMsgs.forEach((m, i) => {
8305
- attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;
8306
- attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content.slice(0, 1e4);
10196
+ function setOpenResponsesUsage(span2, resp) {
10197
+ const u = resp?.usage ?? resp;
10198
+ if (!u) return;
10199
+ const input = u.inputTokens ?? u.input_tokens ?? u.prompt_tokens;
10200
+ const output = u.outputTokens ?? u.output_tokens ?? u.completion_tokens;
10201
+ const total = u.totalTokens ?? u.total_tokens;
10202
+ const cached = u.cachedTokens ?? u.cached_tokens;
10203
+ if (input != null) span2.setAttribute("neatlogs.llm.token_count.prompt", input);
10204
+ if (output != null) span2.setAttribute("neatlogs.llm.token_count.completion", output);
10205
+ if (total != null) span2.setAttribute("neatlogs.llm.token_count.total", total);
10206
+ else if (input != null && output != null) span2.setAttribute("neatlogs.llm.token_count.total", input + output);
10207
+ if (cached != null) span2.setAttribute("neatlogs.llm.token_count.cache_read", cached);
10208
+ }
10209
+ function finalizeLlm(span2, result) {
10210
+ const text = result?.text ?? result?.output_text ?? result?.content ?? extractOpenResponsesText(result) ?? result?.choices?.[0]?.message?.content ?? result?.message?.content;
10211
+ if (text) {
10212
+ span2.setAttribute("neatlogs.llm.output_messages.0.role", "assistant");
10213
+ span2.setAttribute("neatlogs.llm.output_messages.0.content", String(text));
10214
+ span2.setAttribute("output.value", String(text));
10215
+ }
10216
+ const toolCalls = result?.toolCalls ?? result?.tool_calls ?? result?.choices?.[0]?.message?.tool_calls ?? result?.message?.tool_calls;
10217
+ if (Array.isArray(toolCalls)) {
10218
+ toolCalls.forEach((tc, j) => {
10219
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.id`, tc?.id ?? "");
10220
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.name`, tc?.function?.name ?? tc?.name ?? "");
10221
+ const args = tc?.function?.arguments ?? tc?.arguments;
10222
+ span2.setAttribute(`neatlogs.llm.tool_calls.${j}.arguments`, typeof args === "string" ? args : safeStringify14(args ?? {}));
8307
10223
  });
8308
- attrs["neatlogs.llm.input"] = safeStringify9({ messages: inMsgs }).slice(0, 2e4);
8309
- attrs["input.value"] = safeStringify9({ messages: inMsgs }).slice(0, 1e4);
8310
10224
  }
8311
- const { text, toolCalls } = splitAssistantContent(msg.content);
8312
- const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify9(tc.arguments)})`).join("\n");
8313
- if (outText || toolCalls.length) {
8314
- attrs["neatlogs.llm.output_messages.0.role"] = "assistant";
8315
- attrs["neatlogs.llm.output_messages.0.content"] = (outText || "").slice(0, 1e4);
8316
- const outBlob = { role: "assistant", content: outText || "" };
8317
- if (toolCalls.length) {
8318
- outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));
8319
- toolCalls.forEach((tc, j) => {
8320
- if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;
8321
- if (tc.arguments !== void 0)
8322
- attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify9(tc.arguments);
8323
- if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);
8324
- });
10225
+ const model = result?.model ?? result?.response?.model;
10226
+ if (model) span2.setAttribute("neatlogs.llm.model_name", String(model));
10227
+ const responseId = result?.id ?? result?.response?.id;
10228
+ if (responseId) span2.setAttribute("neatlogs.llm.response_id", String(responseId));
10229
+ const finishReason = result?.finishReason ?? result?.finish_reason ?? result?.choices?.[0]?.finish_reason;
10230
+ if (finishReason) span2.setAttribute("neatlogs.llm.finish_reason", String(finishReason));
10231
+ setOpenResponsesUsage(span2, result);
10232
+ span2.setStatus({ code: SpanStatusCode15.OK });
10233
+ span2.end();
10234
+ }
10235
+ function extractOpenResponsesText(result) {
10236
+ const output = result?.output;
10237
+ if (!Array.isArray(output)) return void 0;
10238
+ const parts = [];
10239
+ for (const item of output) {
10240
+ if (item?.type === "message" && Array.isArray(item.content)) {
10241
+ for (const c of item.content) {
10242
+ if ((c?.type === "output_text" || c?.type === "text") && typeof c.text === "string") parts.push(c.text);
10243
+ }
8325
10244
  }
8326
- attrs["neatlogs.llm.output"] = safeStringify9(outBlob).slice(0, 2e4);
8327
- attrs["output.value"] = (outText || "").slice(0, 1e4);
8328
10245
  }
8329
- const usage = msg.usage;
8330
- if (usage) {
8331
- if (usage.input != null) attrs["neatlogs.llm.token_count.prompt"] = usage.input;
8332
- if (usage.output != null) attrs["neatlogs.llm.token_count.completion"] = usage.output;
8333
- const total = usage.totalTokens ?? (usage.input ?? 0) + (usage.output ?? 0);
8334
- if (total) attrs["neatlogs.llm.token_count.total"] = total;
8335
- if (usage.cacheRead) attrs["neatlogs.llm.token_count.cache_read"] = usage.cacheRead;
8336
- if (usage.cacheWrite) attrs["neatlogs.llm.token_count.cache_write"] = usage.cacheWrite;
10246
+ return parts.length ? parts.join("") : void 0;
10247
+ }
10248
+ function safeStringify14(value) {
10249
+ if (typeof value === "string") return value;
10250
+ try {
10251
+ return JSON.stringify(value) ?? "";
10252
+ } catch {
10253
+ return "";
10254
+ }
10255
+ }
10256
+ function recordError8(span2, err) {
10257
+ if (err instanceof Error) {
10258
+ span2.setStatus({ code: SpanStatusCode15.ERROR, message: err.message });
10259
+ span2.recordException(err);
10260
+ } else {
10261
+ span2.setStatus({ code: SpanStatusCode15.ERROR, message: String(err) });
8337
10262
  }
8338
- const parent = state.agentCtx ?? otelContext9.active();
8339
- const span2 = tracer.startSpan(
8340
- `pi_agent.llm.${msg.model || "model"}`,
8341
- { attributes: attrs },
8342
- parent
8343
- );
8344
- span2.setStatus({ code: SpanStatusCode10.OK });
8345
10263
  span2.end();
8346
10264
  }
8347
- function splitAssistantContent(content) {
8348
- const texts = [];
8349
- const toolCalls = [];
8350
- if (Array.isArray(content)) {
8351
- for (const block of content) {
8352
- if (!block || typeof block !== "object") continue;
8353
- if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
8354
- else if (block.type === "toolCall")
8355
- toolCalls.push(block);
8356
- }
8357
- } else if (typeof content === "string") {
8358
- texts.push(content);
10265
+
10266
+ // src/opencode-plugin.ts
10267
+ import { trace as trace17, context as otelContext15, SpanStatusCode as SpanStatusCode16 } from "@opentelemetry/api";
10268
+ var TRACER_NAME14 = "neatlogs.opencode";
10269
+ var _initialized2 = false;
10270
+ async function ensureInitialized() {
10271
+ if (_initialized2) return;
10272
+ _initialized2 = true;
10273
+ try {
10274
+ await init({
10275
+ apiKey: process.env.NEATLOGS_API_KEY,
10276
+ // The plugin bootstraps itself inside opencode's process — there is no user
10277
+ // init() to pass an endpoint, so honor NEATLOGS_ENDPOINT from the env (e.g.
10278
+ // a local backend). init() otherwise defaults to the hosted cloud.
10279
+ ...process.env.NEATLOGS_ENDPOINT ? { endpoint: process.env.NEATLOGS_ENDPOINT } : {},
10280
+ workflowName: process.env.NEATLOGS_WORKFLOW_NAME || "opencode",
10281
+ // opencode is long-running and event-driven; keep auto session off so each
10282
+ // span's conversation id is the opencode session id we set explicitly.
10283
+ autoSession: false
10284
+ });
10285
+ } catch {
8359
10286
  }
8360
- return { text: texts.join(""), toolCalls };
8361
10287
  }
8362
- function messageText(msg) {
8363
- if (!msg) return "";
8364
- const c = msg.content;
8365
- if (typeof c === "string") return c;
8366
- if (!Array.isArray(c)) return "";
8367
- const parts = [];
8368
- for (const block of c) {
8369
- if (typeof block === "string") parts.push(block);
8370
- else if (block && typeof block === "object") {
8371
- if (typeof block.text === "string") parts.push(block.text);
8372
- else if (block.type === "toolCall") parts.push(`${block.name ?? "tool"}(${safeStringify9(block.arguments)})`);
10288
+ var NeatlogsOpencodePlugin = async (_ctx) => {
10289
+ await ensureInitialized();
10290
+ const sessions = /* @__PURE__ */ new Map();
10291
+ const tracer = trace17.getTracer(TRACER_NAME14);
10292
+ const captureSystemPrompt = String(process.env.NEATLOGS_CAPTURE_SYSTEM_PROMPT || "").toLowerCase() === "true";
10293
+ function stateFor(sessionID) {
10294
+ let s = sessions.get(sessionID);
10295
+ if (!s) {
10296
+ s = { toolSpans: /* @__PURE__ */ new Map(), messageText: /* @__PURE__ */ new Map(), emitted: /* @__PURE__ */ new Set(), lastUserText: "" };
10297
+ sessions.set(sessionID, s);
8373
10298
  }
10299
+ return s;
8374
10300
  }
8375
- return parts.join("");
8376
- }
8377
- function lastAssistantText(messages) {
8378
- if (!Array.isArray(messages)) return "";
8379
- for (let i = messages.length - 1; i >= 0; i--) {
8380
- const m = messages[i];
8381
- if (m && m.role === "assistant") {
8382
- const { text } = splitAssistantContent(m.content);
8383
- if (text) return text;
10301
+ return {
10302
+ /**
10303
+ * Global event bus. opencode emits message + session lifecycle events here.
10304
+ * We use it to emit an LLM span when an assistant message completes, and to
10305
+ * track user-message text + assistant text from part updates.
10306
+ */
10307
+ event: async ({ event }) => {
10308
+ try {
10309
+ await handleEvent2(tracer, sessions, stateFor, captureSystemPrompt, event);
10310
+ } catch {
10311
+ }
10312
+ },
10313
+ /** Fired before a tool runs: open a TOOL span keyed by callID. */
10314
+ "tool.execute.before": async (input, output) => {
10315
+ try {
10316
+ const sessionID = input?.sessionID ?? "";
10317
+ const callID = input?.callID ?? input?.tool ?? "";
10318
+ const st = stateFor(sessionID);
10319
+ const span2 = tracer.startSpan(
10320
+ `opencode.tool.${input?.tool ?? "tool"}`,
10321
+ {
10322
+ attributes: {
10323
+ "neatlogs.span.kind": "TOOL",
10324
+ "neatlogs.tool.name": String(input?.tool ?? ""),
10325
+ ...sessionID ? { "neatlogs.conversation.id": String(sessionID) } : {},
10326
+ ...output?.args !== void 0 ? { "input.value": safeStringify15(output.args) } : {}
10327
+ }
10328
+ },
10329
+ otelContext15.active()
10330
+ );
10331
+ if (callID) st.toolSpans.set(String(callID), span2);
10332
+ } catch {
10333
+ }
10334
+ },
10335
+ /** Fired after a tool runs: close the matching TOOL span. */
10336
+ "tool.execute.after": async (input, output) => {
10337
+ try {
10338
+ const sessionID = input?.sessionID ?? "";
10339
+ const callID = String(input?.callID ?? input?.tool ?? "");
10340
+ const st = stateFor(sessionID);
10341
+ const span2 = st.toolSpans.get(callID);
10342
+ if (!span2) return;
10343
+ if (output?.title) span2.setAttribute("neatlogs.tool.title", String(output.title));
10344
+ const out = output?.output ?? output?.result;
10345
+ if (out !== void 0) {
10346
+ span2.setAttribute("output.value", typeof out === "string" ? out : safeStringify15(out));
10347
+ }
10348
+ if (output?.metadata !== void 0) {
10349
+ span2.setAttribute("neatlogs.tool.metadata", safeStringify15(output.metadata));
10350
+ }
10351
+ span2.setStatus({ code: SpanStatusCode16.OK });
10352
+ span2.end();
10353
+ st.toolSpans.delete(callID);
10354
+ } catch {
10355
+ }
10356
+ }
10357
+ };
10358
+ };
10359
+ function handleEvent2(tracer, sessions, stateFor, captureSystemPrompt, event) {
10360
+ const type = event?.type;
10361
+ const props = event?.properties ?? {};
10362
+ if (type === "message.part.updated" || type === "message.part.completed") {
10363
+ const part = props.part ?? props;
10364
+ const messageID = part?.messageID ?? part?.message_id;
10365
+ const sessionID = part?.sessionID ?? props.sessionID;
10366
+ if (part?.type === "text" && typeof part?.text === "string" && messageID && sessionID) {
10367
+ const st = stateFor(String(sessionID));
10368
+ st.messageText.set(String(messageID), part.text);
8384
10369
  }
10370
+ return;
8385
10371
  }
8386
- return "";
10372
+ if (type === "message.updated" || type === "message.completed") {
10373
+ const info = props.info ?? props.message ?? props;
10374
+ const sessionID = info?.sessionID ?? info?.session_id;
10375
+ if (!sessionID) return;
10376
+ const st = stateFor(String(sessionID));
10377
+ if (info?.role === "user") {
10378
+ const text = messageText2(info) || st.messageText.get(String(info?.id)) || "";
10379
+ if (text) st.lastUserText = text;
10380
+ return;
10381
+ }
10382
+ if (info?.role === "assistant") {
10383
+ const completed = info?.time?.completed ?? info?.completed;
10384
+ const id = String(info?.id ?? "");
10385
+ if (!completed || st.emitted.has(id)) return;
10386
+ st.emitted.add(id);
10387
+ emitLlmSpan2(tracer, st, captureSystemPrompt, info, String(sessionID));
10388
+ }
10389
+ return;
10390
+ }
10391
+ if (type === "session.idle" || type === "session.deleted") {
10392
+ const sessionID = props.sessionID ?? props.info?.id;
10393
+ if (!sessionID) return void 0;
10394
+ const st = sessions.get(String(sessionID));
10395
+ if (!st) return void 0;
10396
+ for (const ts of st.toolSpans.values()) {
10397
+ try {
10398
+ ts.end();
10399
+ } catch {
10400
+ }
10401
+ }
10402
+ st.toolSpans.clear();
10403
+ if (type === "session.deleted") sessions.delete(String(sessionID));
10404
+ return flush().catch(() => false);
10405
+ }
10406
+ return void 0;
8387
10407
  }
8388
- function markPatched2(e) {
8389
- try {
8390
- Object.defineProperty(e, PATCH_FLAG3, { value: true, enumerable: false, configurable: true });
8391
- } catch {
8392
- e[PATCH_FLAG3] = true;
10408
+ function emitLlmSpan2(tracer, st, captureSystemPrompt, info, sessionID) {
10409
+ const model = info?.modelID ?? info?.model ?? "";
10410
+ const provider = info?.providerID ?? info?.provider ?? "";
10411
+ const attrs = {
10412
+ "neatlogs.span.kind": "LLM",
10413
+ "neatlogs.conversation.id": sessionID
10414
+ };
10415
+ if (model) attrs["neatlogs.llm.model_name"] = String(model);
10416
+ if (provider) attrs["neatlogs.llm.provider"] = String(provider);
10417
+ if (captureSystemPrompt && info?.system) {
10418
+ const sys = Array.isArray(info.system) ? info.system.join("\n") : String(info.system);
10419
+ attrs["neatlogs.llm.input_messages.0.role"] = "system";
10420
+ attrs["neatlogs.llm.input_messages.0.content"] = sys;
10421
+ }
10422
+ let inIdx = captureSystemPrompt && info?.system ? 1 : 0;
10423
+ if (st.lastUserText) {
10424
+ attrs[`neatlogs.llm.input_messages.${inIdx}.role`] = "user";
10425
+ attrs[`neatlogs.llm.input_messages.${inIdx}.content`] = st.lastUserText;
10426
+ inIdx++;
10427
+ }
10428
+ const outText = messageText2(info) || st.messageText.get(String(info?.id)) || "";
10429
+ if (outText) {
10430
+ attrs["neatlogs.llm.output_messages.0.role"] = "assistant";
10431
+ attrs["neatlogs.llm.output_messages.0.content"] = outText;
10432
+ }
10433
+ const tokens = info?.tokens;
10434
+ if (tokens) {
10435
+ if (tokens.input != null) attrs["neatlogs.llm.token_count.prompt"] = tokens.input;
10436
+ if (tokens.output != null) attrs["neatlogs.llm.token_count.completion"] = tokens.output;
10437
+ if (tokens.input != null && tokens.output != null) {
10438
+ attrs["neatlogs.llm.token_count.total"] = tokens.input + tokens.output;
10439
+ }
10440
+ if (tokens.reasoning != null) attrs["neatlogs.llm.token_count.reasoning"] = tokens.reasoning;
10441
+ if (tokens.cache?.read != null) attrs["neatlogs.llm.token_count.cache_read"] = tokens.cache.read;
10442
+ if (tokens.cache?.write != null) attrs["neatlogs.llm.token_count.cache_write"] = tokens.cache.write;
10443
+ }
10444
+ if (info?.cost != null) attrs["neatlogs.llm.cost_usd"] = info.cost;
10445
+ const span2 = tracer.startSpan(`opencode.llm.${model || "model"}`, { attributes: attrs }, otelContext15.active());
10446
+ span2.setStatus({ code: SpanStatusCode16.OK });
10447
+ span2.end();
10448
+ if (info?.id) st.messageText.delete(String(info.id));
10449
+ }
10450
+ function messageText2(info) {
10451
+ if (!info) return "";
10452
+ if (typeof info.text === "string") return info.text;
10453
+ const parts = info.parts ?? info.content;
10454
+ if (typeof parts === "string") return parts;
10455
+ if (!Array.isArray(parts)) return "";
10456
+ const out = [];
10457
+ for (const p of parts) {
10458
+ if (typeof p === "string") out.push(p);
10459
+ else if (p && typeof p === "object" && typeof p.text === "string") out.push(p.text);
8393
10460
  }
10461
+ return out.join("");
8394
10462
  }
8395
- function safeStringify9(value) {
10463
+ function safeStringify15(value) {
8396
10464
  if (typeof value === "string") return value;
8397
10465
  try {
8398
10466
  return JSON.stringify(value) ?? "";
@@ -8451,6 +10519,7 @@ function bindTemplates(llm, systemTpl, userTpl, compiledVars) {
8451
10519
  return llmCopy;
8452
10520
  }
8453
10521
  export {
10522
+ NeatlogsOpencodePlugin,
8454
10523
  PromptApiError,
8455
10524
  PromptClient,
8456
10525
  PromptClientError,
@@ -8484,11 +10553,21 @@ export {
8484
10553
  strandsHooks,
8485
10554
  trace2 as trace,
8486
10555
  traceTool2 as traceToolAnthropic,
10556
+ traceTool3 as traceToolAzureOpenAI,
10557
+ traceTool5 as traceToolBedrock,
8487
10558
  traceTool as traceToolOpenAI,
10559
+ traceTool4 as traceToolVertexAI,
8488
10560
  updatePrompt,
8489
10561
  wrapAISDK,
8490
10562
  wrapAnthropic,
10563
+ wrapAzureOpenAI,
10564
+ wrapBedrock,
10565
+ wrapCallModel,
10566
+ wrapClaudeAgentSDK,
8491
10567
  wrapMastra,
8492
- wrapOpenAI
10568
+ wrapOpenAI,
10569
+ wrapOpenRouterAgent,
10570
+ wrapVertexAI,
10571
+ wrapVertexAIChat
8493
10572
  };
8494
10573
  //# sourceMappingURL=index.mjs.map