neatlogs 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  UserPromptTemplate: () => UserPromptTemplate,
41
41
  __version__: () => __version__,
42
42
  bindTemplates: () => bindTemplates,
43
+ createAITelemetry: () => createAITelemetry,
43
44
  createPrompt: () => createPrompt,
44
45
  deletePrompt: () => deletePrompt,
45
46
  fetchPrompt: () => fetchPrompt,
@@ -57,7 +58,8 @@ __export(index_exports, {
57
58
  shutdown: () => shutdown,
58
59
  span: () => span,
59
60
  trace: () => trace2,
60
- updatePrompt: () => updatePrompt
61
+ updatePrompt: () => updatePrompt,
62
+ wrapAISDK: () => wrapAISDK
61
63
  });
62
64
  module.exports = __toCommonJS(index_exports);
63
65
 
@@ -132,6 +134,9 @@ var SCOPE_PATTERNS = {
132
134
  // Neatlogs custom instrumentations
133
135
  "@neatlogs/instrumentation-google-genai": { provider: "google", framework: "google_genai" },
134
136
  "@neatlogs/instrumentation-mastra": { framework: "mastra" },
137
+ "@neatlogs/instrumentation-ai-sdk": { framework: "ai_sdk" },
138
+ // Vercel AI SDK native scope (the `ai` package emits this directly)
139
+ ai: { framework: "ai_sdk" },
135
140
  // OpenInference instrumentations (npm @arizeai scope names)
136
141
  "@arizeai/openinference-instrumentation-openai": { provider: "openai", framework: "openai" },
137
142
  "@arizeai/openinference-instrumentation-anthropic": { provider: "anthropic", framework: "anthropic" },
@@ -219,6 +224,8 @@ function parseInstrumentationScope(scopeName) {
219
224
  result.framework = "haystack";
220
225
  } else if (scopeLower.includes("dspy")) {
221
226
  result.framework = "dspy";
227
+ } else if (scopeLower.includes("vercel-ai") || scopeLower.includes("ai-sdk")) {
228
+ result.framework = "ai_sdk";
222
229
  }
223
230
  if (scopeLower.includes("openai")) {
224
231
  result.provider = "openai";
@@ -1231,6 +1238,7 @@ var UnifiedAttributeProcessor = class {
1231
1238
  this.addCrewaiTokenUsageFallback(attrs);
1232
1239
  this.addReasoningTokensFromOutputValue(attrs);
1233
1240
  this.addCrewaiKickoffTelemetry(attrs);
1241
+ this.extractVercelAiSdkAttrs(attrs);
1234
1242
  this.extractToolCalls(attrs);
1235
1243
  for (const [k, v] of Object.entries(attrs)) {
1236
1244
  const m = INPUT_MSG_TOOL_RE.exec(k);
@@ -1591,6 +1599,167 @@ var UnifiedAttributeProcessor = class {
1591
1599
  attrs["llm.invocation_parameters"] = JSON.stringify(existing);
1592
1600
  }
1593
1601
  }
1602
+ // ── Vercel AI SDK extraction ───────────────────────
1603
+ /**
1604
+ * The Vercel AI SDK emits its own `ai.*` namespace alongside `gen_ai.*`. Map
1605
+ * the AI-SDK-specific keys onto the canonical `llm.*` / `gen_ai.*` / `tool.*`
1606
+ * keys that the existing pipeline already understands. Span-kind inference
1607
+ * runs first so downstream `applyNamespaceMapping` resolves it correctly.
1608
+ */
1609
+ extractVercelAiSdkAttrs(attrs) {
1610
+ const spanName = attrs["_span_name"] ?? "";
1611
+ const isAiSdkSpan = spanName.startsWith("ai.") || "ai.model.id" in attrs || "ai.toolCall.name" in attrs;
1612
+ if (!isAiSdkSpan) return;
1613
+ if (!("openinference.span.kind" in attrs)) {
1614
+ if (spanName === "ai.toolCall") {
1615
+ attrs["openinference.span.kind"] = "TOOL";
1616
+ } else if (spanName.startsWith("ai.embed")) {
1617
+ attrs["openinference.span.kind"] = "EMBEDDING";
1618
+ } else if (spanName.startsWith("ai.rerank")) {
1619
+ attrs["openinference.span.kind"] = "RERANKER";
1620
+ } else if (spanName.endsWith(".doStream") || spanName.endsWith(".doGenerate") || spanName.endsWith(".doRerank") || spanName.endsWith(".doEmbed")) {
1621
+ if (spanName.includes("embed")) {
1622
+ attrs["openinference.span.kind"] = "EMBEDDING";
1623
+ } else if (spanName.includes("rerank")) {
1624
+ attrs["openinference.span.kind"] = "RERANKER";
1625
+ } else {
1626
+ attrs["openinference.span.kind"] = "LLM";
1627
+ }
1628
+ } else if (spanName === "ai.generateText" || spanName === "ai.streamText" || spanName === "ai.generateObject" || spanName === "ai.streamObject") {
1629
+ attrs["openinference.span.kind"] = "CHAIN";
1630
+ }
1631
+ }
1632
+ if ("ai.model.id" in attrs && !("llm.model_name" in attrs)) {
1633
+ attrs["llm.model_name"] = attrs["ai.model.id"];
1634
+ }
1635
+ if ("ai.model.provider" in attrs && !("llm.provider" in attrs)) {
1636
+ const raw = String(attrs["ai.model.provider"]);
1637
+ attrs["llm.provider"] = raw.split(".")[0];
1638
+ }
1639
+ if ("ai.usage.promptTokens" in attrs && !("llm.token_count.prompt" in attrs)) {
1640
+ attrs["llm.token_count.prompt"] = attrs["ai.usage.promptTokens"];
1641
+ }
1642
+ if ("ai.usage.completionTokens" in attrs && !("llm.token_count.completion" in attrs)) {
1643
+ attrs["llm.token_count.completion"] = attrs["ai.usage.completionTokens"];
1644
+ }
1645
+ if ("ai.usage.totalTokens" in attrs && !("llm.token_count.total" in attrs)) {
1646
+ attrs["llm.token_count.total"] = attrs["ai.usage.totalTokens"];
1647
+ }
1648
+ const settingMap = {
1649
+ "ai.settings.temperature": "gen_ai.request.temperature",
1650
+ "ai.settings.maxTokens": "gen_ai.request.max_tokens",
1651
+ "ai.settings.topP": "gen_ai.request.top_p",
1652
+ "ai.settings.topK": "gen_ai.request.top_k",
1653
+ "ai.settings.frequencyPenalty": "gen_ai.request.frequency_penalty",
1654
+ "ai.settings.presencePenalty": "gen_ai.request.presence_penalty",
1655
+ "ai.settings.stopSequences": "gen_ai.request.stop_sequences"
1656
+ };
1657
+ for (const [src, tgt] of Object.entries(settingMap)) {
1658
+ if (src in attrs && !(tgt in attrs)) {
1659
+ attrs[tgt] = attrs[src];
1660
+ }
1661
+ }
1662
+ if (!("llm.invocation_parameters" in attrs)) {
1663
+ const params = {};
1664
+ if ("gen_ai.request.temperature" in attrs) params.temperature = attrs["gen_ai.request.temperature"];
1665
+ if ("gen_ai.request.max_tokens" in attrs) params.max_tokens = attrs["gen_ai.request.max_tokens"];
1666
+ if ("gen_ai.request.top_p" in attrs) params.top_p = attrs["gen_ai.request.top_p"];
1667
+ if ("gen_ai.request.top_k" in attrs) params.top_k = attrs["gen_ai.request.top_k"];
1668
+ if ("gen_ai.request.frequency_penalty" in attrs) params.frequency_penalty = attrs["gen_ai.request.frequency_penalty"];
1669
+ if ("gen_ai.request.presence_penalty" in attrs) params.presence_penalty = attrs["gen_ai.request.presence_penalty"];
1670
+ if ("gen_ai.request.stop_sequences" in attrs) params.stop_sequences = attrs["gen_ai.request.stop_sequences"];
1671
+ if (Object.keys(params).length > 0) {
1672
+ attrs["llm.invocation_parameters"] = JSON.stringify(params);
1673
+ }
1674
+ }
1675
+ if ("ai.operationId" in attrs && !("gen_ai.operation.name" in attrs)) {
1676
+ attrs["gen_ai.operation.name"] = attrs["ai.operationId"];
1677
+ }
1678
+ if ("ai.response.text" in attrs && !("llm.output_messages.0.message.content" in attrs)) {
1679
+ attrs["llm.output_messages.0.message.role"] = "assistant";
1680
+ attrs["llm.output_messages.0.message.content"] = attrs["ai.response.text"];
1681
+ }
1682
+ if ("ai.response.finishReason" in attrs && !("llm.response.finish_reason" in attrs)) {
1683
+ attrs["llm.response.finish_reason"] = attrs["ai.response.finishReason"];
1684
+ }
1685
+ if ("ai.response.id" in attrs && !("gen_ai.response.id" in attrs)) {
1686
+ attrs["gen_ai.response.id"] = attrs["ai.response.id"];
1687
+ }
1688
+ const rawMessages = attrs["ai.prompt.messages"];
1689
+ if (typeof rawMessages === "string") {
1690
+ try {
1691
+ const parsed = JSON.parse(rawMessages);
1692
+ if (Array.isArray(parsed)) {
1693
+ parsed.forEach((msg, i) => {
1694
+ if (msg && typeof msg === "object") {
1695
+ if (typeof msg.role === "string") {
1696
+ attrs[`llm.input_messages.${i}.message.role`] = msg.role;
1697
+ }
1698
+ if (typeof msg.content === "string") {
1699
+ attrs[`llm.input_messages.${i}.message.content`] = msg.content;
1700
+ } else if (msg.content !== void 0) {
1701
+ attrs[`llm.input_messages.${i}.message.content`] = JSON.stringify(msg.content);
1702
+ }
1703
+ }
1704
+ });
1705
+ }
1706
+ } catch {
1707
+ }
1708
+ }
1709
+ const rawToolCalls = attrs["ai.response.toolCalls"];
1710
+ if (typeof rawToolCalls === "string") {
1711
+ try {
1712
+ const parsed = JSON.parse(rawToolCalls);
1713
+ if (Array.isArray(parsed)) {
1714
+ parsed.forEach((tc, i) => {
1715
+ if (tc && typeof tc === "object") {
1716
+ if (tc.toolName !== void 0) {
1717
+ attrs[`llm.output_messages.0.message.tool_calls.${i}.tool_call.function.name`] = tc.toolName;
1718
+ }
1719
+ if (tc.args !== void 0) {
1720
+ const argStr = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
1721
+ attrs[`llm.output_messages.0.message.tool_calls.${i}.tool_call.function.arguments`] = argStr;
1722
+ }
1723
+ if (tc.toolCallId !== void 0) {
1724
+ attrs[`llm.output_messages.0.message.tool_calls.${i}.tool_call.id`] = tc.toolCallId;
1725
+ }
1726
+ }
1727
+ });
1728
+ }
1729
+ } catch {
1730
+ }
1731
+ }
1732
+ if ("ai.value" in attrs && typeof attrs["ai.value"] === "string") {
1733
+ try {
1734
+ attrs["ai.value"] = JSON.parse(attrs["ai.value"]);
1735
+ } catch {
1736
+ }
1737
+ }
1738
+ if ("ai.values" in attrs && Array.isArray(attrs["ai.values"])) {
1739
+ attrs["ai.values"] = JSON.stringify(attrs["ai.values"].map((v) => {
1740
+ if (typeof v === "string") {
1741
+ try {
1742
+ return JSON.parse(v);
1743
+ } catch {
1744
+ }
1745
+ }
1746
+ return v;
1747
+ }));
1748
+ }
1749
+ if (spanName === "ai.toolCall") {
1750
+ if ("ai.toolCall.name" in attrs && !("tool.name" in attrs)) {
1751
+ attrs["tool.name"] = attrs["ai.toolCall.name"];
1752
+ }
1753
+ if ("ai.toolCall.args" in attrs && !("input.value" in attrs)) {
1754
+ const raw = attrs["ai.toolCall.args"];
1755
+ attrs["input.value"] = typeof raw === "string" ? raw : JSON.stringify(raw);
1756
+ }
1757
+ if ("ai.toolCall.result" in attrs && !("output.value" in attrs)) {
1758
+ const raw = attrs["ai.toolCall.result"];
1759
+ attrs["output.value"] = typeof raw === "string" ? raw : JSON.stringify(raw);
1760
+ }
1761
+ }
1762
+ }
1594
1763
  // ── CrewAI-specific ─────────────────────────────────
1595
1764
  addCrewaiTokenUsageFallback(attrs) {
1596
1765
  const usage = attrs["neatlogs.crew.token_usage"];
@@ -1766,8 +1935,9 @@ var UnifiedAttributeProcessor = class {
1766
1935
  if (oiKind) {
1767
1936
  unified["neatlogs.span.kind"] = String(oiKind).toLowerCase();
1768
1937
  } else {
1938
+ const scopeName = attrs["neatlogs.instrumentation.name"] ?? "";
1769
1939
  const spanName = attrs["_span_name"] ?? "";
1770
- if (spanName) {
1940
+ if (spanName && scopeName !== "next.js") {
1771
1941
  const inferred = inferSpanKindFromName(spanName).toLowerCase();
1772
1942
  unified["neatlogs.span.kind"] = inferred;
1773
1943
  }
@@ -1776,7 +1946,8 @@ var UnifiedAttributeProcessor = class {
1776
1946
  const llmRequestType = (attrs["llm.request.type"] ?? "").toLowerCase();
1777
1947
  const genAiOperation = (attrs["gen_ai.operation.name"] ?? "").toLowerCase();
1778
1948
  const spanNameLower = (attrs["_span_name"] ?? "").toLowerCase();
1779
- if (llmRequestType === "rerank" || genAiOperation === "rerank" || spanNameLower.includes("rerank")) {
1949
+ const hasExplicitKind = "openinference.span.kind" in attrs || "traceloop.span.kind" in attrs;
1950
+ if (!hasExplicitKind && (llmRequestType === "rerank" || genAiOperation === "rerank" || spanNameLower.includes("rerank"))) {
1780
1951
  unified["neatlogs.span.kind"] = "reranker";
1781
1952
  }
1782
1953
  const spanKind = (attrs["neatlogs.span.kind"] ?? attrs["openinference.span.kind"] ?? "").toLowerCase();
@@ -1955,7 +2126,7 @@ var UnifiedAttributeProcessor = class {
1955
2126
  filterEmbeddingVectors(attrs) {
1956
2127
  const filtered = {};
1957
2128
  for (const [key, value] of Object.entries(attrs)) {
1958
- if (key.includes(".embedding.vector") || key.includes(".embeddings.")) {
2129
+ if (key.includes(".embedding.vector") || key.includes(".embeddings.") || key === "ai.embeddings" || key === "ai.embedding") {
1959
2130
  if (this.debug) {
1960
2131
  logger2.debug(`[FILTER] Dropped embedding vector key: ${key}`);
1961
2132
  }
@@ -1982,7 +2153,7 @@ var UnifiedAttributeProcessor = class {
1982
2153
  * Check if the raw span kind indicates CLIENT (OTel SpanKind.CLIENT = 3).
1983
2154
  */
1984
2155
  isHttpLikeSpanKind(kind) {
1985
- if (typeof kind === "number") return kind === 2;
2156
+ if (typeof kind === "number") return kind === 3;
1986
2157
  return String(kind).toUpperCase() === "CLIENT";
1987
2158
  }
1988
2159
  };
@@ -2613,21 +2784,24 @@ var attribute_mapping_default = {
2613
2784
  total: {
2614
2785
  sources: [
2615
2786
  "llm.token_count.total",
2616
- "llm.usage.total_tokens"
2787
+ "llm.usage.total_tokens",
2788
+ "ai.usage.totalTokens"
2617
2789
  ],
2618
2790
  target: "neatlogs.llm.token_count.total"
2619
2791
  },
2620
2792
  prompt: {
2621
2793
  sources: [
2622
2794
  "llm.token_count.prompt",
2623
- "gen_ai.usage.input_tokens"
2795
+ "gen_ai.usage.input_tokens",
2796
+ "ai.usage.promptTokens"
2624
2797
  ],
2625
2798
  target: "neatlogs.llm.token_count.prompt"
2626
2799
  },
2627
2800
  completion: {
2628
2801
  sources: [
2629
2802
  "llm.token_count.completion",
2630
- "gen_ai.usage.output_tokens"
2803
+ "gen_ai.usage.output_tokens",
2804
+ "ai.usage.completionTokens"
2631
2805
  ],
2632
2806
  target: "neatlogs.llm.token_count.completion"
2633
2807
  },
@@ -3218,24 +3392,62 @@ var attribute_mapping_default = {
3218
3392
  },
3219
3393
  reranker: {
3220
3394
  description: "Reranker model attributes",
3395
+ applies_to: ["reranker"],
3221
3396
  model_name: {
3222
3397
  sources: [
3223
- "reranker.model_name"
3398
+ "reranker.model_name",
3399
+ "ai.model.id"
3224
3400
  ],
3225
3401
  target: "neatlogs.reranker.model_name"
3226
3402
  },
3403
+ provider: {
3404
+ sources: [
3405
+ "ai.model.provider"
3406
+ ],
3407
+ target: "neatlogs.reranker.provider",
3408
+ description: "Vercel AI SDK reranker provider (e.g. gateway, cohere)"
3409
+ },
3227
3410
  query: {
3228
3411
  sources: [
3229
- "reranker.query"
3412
+ "reranker.query",
3413
+ "ai.rerank.query"
3230
3414
  ],
3231
3415
  target: "neatlogs.reranker.query"
3232
3416
  },
3417
+ documents: {
3418
+ sources: [
3419
+ "ai.documents"
3420
+ ],
3421
+ target: "neatlogs.reranker.input_documents",
3422
+ description: "Vercel AI SDK rerank input documents array"
3423
+ },
3424
+ ranking: {
3425
+ sources: [
3426
+ "ai.ranking"
3427
+ ],
3428
+ target: "neatlogs.reranker.output_documents",
3429
+ description: "Vercel AI SDK rerank output ranking array"
3430
+ },
3431
+ ranking_type: {
3432
+ sources: [
3433
+ "ai.ranking.type"
3434
+ ],
3435
+ target: "neatlogs.reranker.ranking_type",
3436
+ description: "Vercel AI SDK rerank type (e.g. text)"
3437
+ },
3233
3438
  top_k: {
3234
3439
  sources: [
3235
3440
  "reranker.top_k"
3236
3441
  ],
3237
3442
  target: "neatlogs.reranker.top_k"
3238
3443
  },
3444
+ max_retries: {
3445
+ sources: [
3446
+ "ai.settings.maxRetries"
3447
+ ],
3448
+ target: "neatlogs.reranker.max_retries",
3449
+ description: "Vercel AI SDK max retries setting"
3450
+ },
3239
3451
  input_documents: {
3240
3452
  sources: {
3241
3453
  id: [
@@ -3273,9 +3485,19 @@ var attribute_mapping_default = {
3273
3485
  },
3274
3486
  embedding: {
3275
3487
  description: "Embedding model attributes",
3488
+ applies_to: ["embedding"],
3489
+ input: {
3490
+ sources: [
3491
+ "ai.value",
3492
+ "ai.values"
3493
+ ],
3494
+ target: "neatlogs.embedding.input",
3495
+ description: "Vercel AI SDK embed/embedMany input value(s)"
3496
+ },
3276
3497
  model_name: {
3277
3498
  sources: [
3278
- "embedding.model_name"
3499
+ "embedding.model_name",
3500
+ "ai.model.id"
3279
3501
  ],
3280
3502
  target: "neatlogs.embedding.model_name"
3281
3503
  },
@@ -3293,7 +3515,8 @@ var attribute_mapping_default = {
3293
3515
  },
3294
3516
  token_count: {
3295
3517
  sources: [
3296
- "embedding.token_count"
3518
+ "embedding.token_count",
3519
+ "ai.usage.tokens"
3297
3520
  ],
3298
3521
  target: "neatlogs.embedding.token_count"
3299
3522
  },
@@ -3748,10 +3971,13 @@ var AttributeMapper = class {
3748
3971
  mapNestedConfig(config, attributes, spanKind) {
3749
3972
  const mapped = {};
3750
3973
  for (const [key, value] of Object.entries(config)) {
3751
- if (["description", "sources", "target", "indexed", "priority", "values"].includes(key)) {
3974
+ if (["description", "sources", "target", "indexed", "priority", "values", "applies_to"].includes(key)) {
3752
3975
  continue;
3753
3976
  }
3754
3977
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3978
+ if (Array.isArray(value.applies_to) && spanKind && !value.applies_to.includes(spanKind)) {
3979
+ continue;
3980
+ }
3755
3981
  if ("sources" in value) {
3756
3982
  if (value.indexed) {
3757
3983
  const target = value.target ?? "";
@@ -3790,6 +4016,9 @@ var AttributeMapper = class {
3790
4016
  for (const [sectionName, sectionConfig] of Object.entries(this.mappings)) {
3791
4017
  if (sectionName === "span_kind" || sectionName === "keep_as_is" || sectionName === "ignore") continue;
3792
4018
  if (typeof sectionConfig === "object" && sectionConfig !== null) {
4019
+ if (Array.isArray(sectionConfig.applies_to) && spanKind && !sectionConfig.applies_to.includes(spanKind)) {
4020
+ continue;
4021
+ }
3793
4022
  if ("mappings" in sectionConfig) {
3794
4023
  const nestedMapped = this.mapNestedConfig(
3795
4024
  sectionConfig.mappings,
@@ -3798,8 +4027,11 @@ var AttributeMapper = class {
3798
4027
  );
3799
4028
  Object.assign(mapped, nestedMapped);
3800
4029
  for (const [subKey, subVal] of Object.entries(sectionConfig)) {
3801
- if (subKey === "mappings" || subKey === "description") continue;
4030
+ if (subKey === "mappings" || subKey === "description" || subKey === "applies_to") continue;
3802
4031
  if (typeof subVal === "object" && subVal !== null && !Array.isArray(subVal)) {
4032
+ if (Array.isArray(subVal.applies_to) && spanKind && !subVal.applies_to.includes(spanKind)) {
4033
+ continue;
4034
+ }
3803
4035
  if ("sources" in subVal) {
3804
4036
  if (subVal.indexed) {
3805
4037
  const target = subVal.target ?? "";
@@ -4179,7 +4411,7 @@ var NeatlogsSpanProcessor = class {
4179
4411
  for (const key of Object.keys(unifiedAttrs)) {
4180
4412
  if (key.includes("input_messages") || key.includes("output_messages") || key.includes("gen_ai.prompt") || key.includes("gen_ai.completion") || key.includes(".content")) {
4181
4413
  keysToRemove.push(key);
4182
- } else if (skipOutput && (key === "neatlogs.embedding.input" || key === "neatlogs.embedding.output")) {
4414
+ } else if (key === "neatlogs.embedding.output" || skipOutput && key === "neatlogs.embedding.input") {
4183
4415
  keysToRemove.push(key);
4184
4416
  }
4185
4417
  }
@@ -4482,7 +4714,7 @@ var FilteringExporter = class {
4482
4714
  _delegate;
4483
4715
  export(spans, resultCallback) {
4484
4716
  const filtered = spans.filter(
4485
- (s) => !s.attributes["neatlogs.dropped"]
4717
+ (s) => !s.attributes["neatlogs.dropped"] && s.instrumentationLibrary.name !== "next.js"
4486
4718
  );
4487
4719
  if (filtered.length === 0) {
4488
4720
  resultCallback({ code: 0 });
@@ -4738,7 +4970,8 @@ var INSTRUMENTATION_REGISTRY = {
4738
4970
  "huggingface_hub",
4739
4971
  "litellm",
4740
4972
  "google_genai",
4741
- "portkey"
4973
+ "portkey",
4974
+ "ai_sdk"
4742
4975
  ],
4743
4976
  embedding: ["openai", "cohere", "huggingface", "vertexai", "mistralai", "ollama"],
4744
4977
  retrieval: [
@@ -4767,7 +5000,8 @@ var INSTRUMENTATION_REGISTRY = {
4767
5000
  "pydantic_ai",
4768
5001
  "smolagents",
4769
5002
  "strands",
4770
- "pipecat"
5003
+ "pipecat",
5004
+ "ai_sdk"
4771
5005
  ],
4772
5006
  tool: ["langchain", "llamaindex", "haystack", "mcp"],
4773
5007
  http: ["requests", "httpx", "urllib3", "aiohttp"],
@@ -4780,6 +5014,15 @@ var INSTRUMENTATION_REGISTRY = {
4780
5014
  neatlogs: null,
4781
5015
  default_span_kind: "LLM"
4782
5016
  },
5017
+ ai_sdk: {
5018
+ openinference: null,
5019
+ openllmetry: null,
5020
+ // The wrapper is opt-in per call site; init({ instrumentations: ['ai_sdk'] })
5021
+ // is a no-op. This registry entry exists so scope detection and tagging stay
5022
+ // consistent with other LLM/agent libraries.
5023
+ neatlogs: "@neatlogs/instrumentation-ai-sdk",
5024
+ default_span_kind: "LLM"
5025
+ },
4783
5026
  openai: {
4784
5027
  openinference: "@arizeai/openinference-instrumentation-openai",
4785
5028
  openllmetry: null,
@@ -5107,63 +5350,14 @@ function getLibraryInfo(library) {
5107
5350
  var logger12 = getLogger();
5108
5351
  var InstrumentationManager = class {
5109
5352
  provider;
5110
- debug;
5111
- excludedUrls;
5112
5353
  _instrumented = [];
5113
5354
  constructor(options) {
5114
5355
  this.provider = options.provider;
5115
- this.debug = options.debug ?? false;
5116
- this.excludedUrls = options.excludedUrls ?? [];
5117
5356
  }
5118
5357
  /** Get list of successfully instrumented libraries. */
5119
5358
  get instrumented() {
5120
5359
  return [...this._instrumented];
5121
5360
  }
5122
- /**
5123
- * Instrument HTTP (fetch/undici) for W3C traceparent context propagation.
5124
- * Always called by init().
5125
- */
5126
- async instrumentHttp() {
5127
- const excludedUrls = this.excludedUrls;
5128
- const ignoreOutgoingRequestHook = excludedUrls.length > 0 ? (request) => {
5129
- try {
5130
- const url = typeof request === "string" ? request : request?.href ?? request?.path ?? "";
5131
- return excludedUrls.some((excluded) => url.includes(excluded));
5132
- } catch {
5133
- return false;
5134
- }
5135
- } : void 0;
5136
- try {
5137
- const { HttpInstrumentation } = await import("@opentelemetry/instrumentation-http");
5138
- const httpInstr = new HttpInstrumentation({
5139
- ...ignoreOutgoingRequestHook ? { ignoreOutgoingRequestHook } : {}
5140
- });
5141
- httpInstr.setTracerProvider(this.provider);
5142
- httpInstr.enable();
5143
- logger12.debug("Instrumented http/https for context propagation");
5144
- } catch {
5145
- logger12.debug("http instrumentation not available \u2014 skipping");
5146
- }
5147
- try {
5148
- const { UndiciInstrumentation } = await import("@opentelemetry/instrumentation-undici");
5149
- const ignoreUndiciHook = excludedUrls.length > 0 ? (request) => {
5150
- try {
5151
- const url = `${request?.origin ?? ""}${request?.path ?? ""}`;
5152
- return excludedUrls.some((excluded) => url.includes(excluded));
5153
- } catch {
5154
- return false;
5155
- }
5156
- } : void 0;
5157
- const undiciInstr = new UndiciInstrumentation({
5158
- ...ignoreUndiciHook ? { ignoreRequestHook: ignoreUndiciHook } : {}
5159
- });
5160
- undiciInstr.setTracerProvider(this.provider);
5161
- undiciInstr.enable();
5162
- logger12.debug("Instrumented undici for context propagation");
5163
- } catch {
5164
- logger12.debug("undici instrumentation not available \u2014 skipping");
5165
- }
5166
- }
5167
5361
  /**
5168
5362
  * Instrument the specified libraries.
5169
5363
  * Priority: neatlogs custom > OpenInference > skip
@@ -5832,10 +6026,8 @@ async function init(options = {}) {
5832
6026
  }
5833
6027
  const manager = new InstrumentationManager({
5834
6028
  provider,
5835
- debug: options.debug,
5836
- excludedUrls: [baseUrl, endpoint].filter(Boolean)
6029
+ debug: options.debug
5837
6030
  });
5838
- await manager.instrumentHttp();
5839
6031
  if (options.instrumentations?.length) {
5840
6032
  await manager.instrument(options.instrumentations);
5841
6033
  if (options.debug) {
@@ -6126,6 +6318,166 @@ async function getMastraObservability() {
6126
6318
  return _cached;
6127
6319
  }
6128
6320
 
6321
+ // src/ai-sdk.ts
6322
+ var import_api8 = require("@opentelemetry/api");
6323
+ var TRACER_NAME2 = "neatlogs.ai-sdk";
6324
+ function createAITelemetry(opts = {}) {
6325
+ const userMeta = opts.metadata ?? {};
6326
+ return {
6327
+ isEnabled: true,
6328
+ recordInputs: true,
6329
+ recordOutputs: true,
6330
+ tracer: import_api8.trace.getTracer(TRACER_NAME2),
6331
+ metadata: { ...userMeta, neatlogsWrapped: true }
6332
+ };
6333
+ }
6334
+ function safeStringify2(value) {
6335
+ try {
6336
+ return JSON.stringify(value);
6337
+ } catch {
6338
+ return "";
6339
+ }
6340
+ }
6341
+ function setInputValue(span2, opts) {
6342
+ if (opts && ("prompt" in opts || "messages" in opts)) {
6343
+ const input = opts.messages ?? opts.prompt;
6344
+ const stringified2 = safeStringify2(input);
6345
+ if (stringified2) {
6346
+ span2.setAttribute("input.value", stringified2);
6347
+ }
6348
+ return;
6349
+ }
6350
+ const stringified = safeStringify2(opts);
6351
+ if (stringified) {
6352
+ span2.setAttribute("input.value", stringified);
6353
+ }
6354
+ }
6355
+ function setOutputValue(span2, result) {
6356
+ if (result && typeof result === "object") {
6357
+ const r = result;
6358
+ if ("text" in r && "finishReason" in r) {
6359
+ const text = String(r.text ?? "");
6360
+ if (text) {
6361
+ span2.setAttribute("output.value", text);
6362
+ }
6363
+ if (r.finishReason) {
6364
+ span2.setAttribute("gen_ai.finish_reason", String(r.finishReason));
6365
+ }
6366
+ return;
6367
+ }
6368
+ }
6369
+ const stringified = safeStringify2(result);
6370
+ if (stringified) {
6371
+ span2.setAttribute("output.value", stringified);
6372
+ }
6373
+ }
6374
+ var WRAPPED_FUNCTIONS = [
6375
+ "generateText",
6376
+ "streamText",
6377
+ "generateObject",
6378
+ "streamObject",
6379
+ "embed",
6380
+ "embedMany",
6381
+ "rerank"
6382
+ ];
6383
+ function wrapAISDK(aiModule) {
6384
+ const wrapped = { ...aiModule };
6385
+ for (const name of WRAPPED_FUNCTIONS) {
6386
+ const original = aiModule[name];
6387
+ if (typeof original !== "function") continue;
6388
+ if (name === "streamText" || name === "streamObject") {
6389
+ wrapped[name] = createSyncWrapper(name, original);
6390
+ } else {
6391
+ wrapped[name] = createAsyncWrapper(name, original);
6392
+ }
6393
+ }
6394
+ return wrapped;
6395
+ }
6396
+ function rootSpanKind(name) {
6397
+ if (name === "embed" || name === "embedMany" || name === "rerank") return "CHAIN";
6398
+ return "WORKFLOW";
6399
+ }
6400
+ function getParentContext() {
6401
+ const activeSpan = import_api8.trace.getActiveSpan();
6402
+ if (activeSpan) {
6403
+ const instrScope = activeSpan.instrumentationLibrary?.name ?? "";
6404
+ if (instrScope === "next.js") {
6405
+ return import_api8.ROOT_CONTEXT;
6406
+ }
6407
+ }
6408
+ return import_api8.context.active();
6409
+ }
6410
+ function createAsyncWrapper(name, original) {
6411
+ return async function wrappedAsyncFn(opts) {
6412
+ const tracer = import_api8.trace.getTracer(TRACER_NAME2);
6413
+ return tracer.startActiveSpan(
6414
+ `ai.${name}`,
6415
+ { attributes: { "openinference.span.kind": rootSpanKind(name) } },
6416
+ getParentContext(),
6417
+ async (span2) => {
6418
+ try {
6419
+ const isEmbedOrRerank = name === "embed" || name === "embedMany" || name === "rerank";
6420
+ if (!isEmbedOrRerank) {
6421
+ setInputValue(span2, opts);
6422
+ }
6423
+ if (name === "rerank" && opts?.query) {
6424
+ span2.setAttribute("ai.rerank.query", String(opts.query));
6425
+ }
6426
+ const merged = mergeTelemetry(opts);
6427
+ const result = await original(merged);
6428
+ if (!isEmbedOrRerank) {
6429
+ setOutputValue(span2, result);
6430
+ }
6431
+ return result;
6432
+ } catch (err) {
6433
+ recordSpanError(span2, err);
6434
+ throw err;
6435
+ } finally {
6436
+ span2.end();
6437
+ }
6438
+ }
6439
+ );
6440
+ };
6441
+ }
6442
+ function createSyncWrapper(name, original) {
6443
+ return function wrappedSyncFn(opts) {
6444
+ const tracer = import_api8.trace.getTracer(TRACER_NAME2);
6445
+ return tracer.startActiveSpan(`ai.${name}`, { attributes: { "openinference.span.kind": rootSpanKind(name) } }, getParentContext(), (span2) => {
6446
+ try {
6447
+ setInputValue(span2, opts);
6448
+ const merged = mergeTelemetry(opts);
6449
+ const result = original(merged);
6450
+ return result;
6451
+ } catch (err) {
6452
+ recordSpanError(span2, err);
6453
+ throw err;
6454
+ } finally {
6455
+ span2.end();
6456
+ }
6457
+ });
6458
+ };
6459
+ }
6460
+ function mergeTelemetry(opts) {
6461
+ const baseTelemetry = createAITelemetry({
6462
+ metadata: opts?.experimental_telemetry?.metadata
6463
+ });
6464
+ return {
6465
+ ...opts,
6466
+ experimental_telemetry: {
6467
+ ...opts?.experimental_telemetry,
6468
+ ...baseTelemetry
6469
+ }
6470
+ };
6471
+ }
6472
+ function recordSpanError(span2, err) {
6473
+ if (err instanceof Error) {
6474
+ span2.setStatus({ code: import_api8.SpanStatusCode.ERROR, message: err.message });
6475
+ span2.recordException(err);
6476
+ } else {
6477
+ span2.setStatus({ code: import_api8.SpanStatusCode.ERROR, message: String(err) });
6478
+ }
6479
+ }
6480
+
6129
6481
  // src/core/llm-binder.ts
6130
6482
  var logger15 = getLogger();
6131
6483
  function bindTemplates(llm, systemTpl, userTpl, compiledVars) {
@@ -6187,6 +6539,7 @@ function bindTemplates(llm, systemTpl, userTpl, compiledVars) {
6187
6539
  UserPromptTemplate,
6188
6540
  __version__,
6189
6541
  bindTemplates,
6542
+ createAITelemetry,
6190
6543
  createPrompt,
6191
6544
  deletePrompt,
6192
6545
  fetchPrompt,
@@ -6204,6 +6557,7 @@ function bindTemplates(llm, systemTpl, userTpl, compiledVars) {
6204
6557
  shutdown,
6205
6558
  span,
6206
6559
  trace,
6207
- updatePrompt
6560
+ updatePrompt,
6561
+ wrapAISDK
6208
6562
  });
6209
6563
  //# sourceMappingURL=index.cjs.map