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