neatlogs 1.0.2 → 1.0.4
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/README.md +28 -0
- package/dist/ai-sdk.cjs +190 -0
- package/dist/ai-sdk.cjs.map +1 -0
- package/dist/ai-sdk.d.ts +39 -0
- package/dist/ai-sdk.mjs +164 -0
- package/dist/ai-sdk.mjs.map +1 -0
- package/dist/index.cjs +426 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.mjs +423 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +32 -9
- package/dist/index.d.mts +0 -531
package/dist/index.mjs
CHANGED
|
@@ -74,6 +74,9 @@ var SCOPE_PATTERNS = {
|
|
|
74
74
|
// Neatlogs custom instrumentations
|
|
75
75
|
"@neatlogs/instrumentation-google-genai": { provider: "google", framework: "google_genai" },
|
|
76
76
|
"@neatlogs/instrumentation-mastra": { framework: "mastra" },
|
|
77
|
+
"@neatlogs/instrumentation-ai-sdk": { framework: "ai_sdk" },
|
|
78
|
+
// Vercel AI SDK native scope (the `ai` package emits this directly)
|
|
79
|
+
ai: { framework: "ai_sdk" },
|
|
77
80
|
// OpenInference instrumentations (npm @arizeai scope names)
|
|
78
81
|
"@arizeai/openinference-instrumentation-openai": { provider: "openai", framework: "openai" },
|
|
79
82
|
"@arizeai/openinference-instrumentation-anthropic": { provider: "anthropic", framework: "anthropic" },
|
|
@@ -161,6 +164,8 @@ function parseInstrumentationScope(scopeName) {
|
|
|
161
164
|
result.framework = "haystack";
|
|
162
165
|
} else if (scopeLower.includes("dspy")) {
|
|
163
166
|
result.framework = "dspy";
|
|
167
|
+
} else if (scopeLower.includes("vercel-ai") || scopeLower.includes("ai-sdk")) {
|
|
168
|
+
result.framework = "ai_sdk";
|
|
164
169
|
}
|
|
165
170
|
if (scopeLower.includes("openai")) {
|
|
166
171
|
result.provider = "openai";
|
|
@@ -1173,6 +1178,7 @@ var UnifiedAttributeProcessor = class {
|
|
|
1173
1178
|
this.addCrewaiTokenUsageFallback(attrs);
|
|
1174
1179
|
this.addReasoningTokensFromOutputValue(attrs);
|
|
1175
1180
|
this.addCrewaiKickoffTelemetry(attrs);
|
|
1181
|
+
this.extractVercelAiSdkAttrs(attrs);
|
|
1176
1182
|
this.extractToolCalls(attrs);
|
|
1177
1183
|
for (const [k, v] of Object.entries(attrs)) {
|
|
1178
1184
|
const m = INPUT_MSG_TOOL_RE.exec(k);
|
|
@@ -1533,6 +1539,167 @@ var UnifiedAttributeProcessor = class {
|
|
|
1533
1539
|
attrs["llm.invocation_parameters"] = JSON.stringify(existing);
|
|
1534
1540
|
}
|
|
1535
1541
|
}
|
|
1542
|
+
// ── Vercel AI SDK extraction ───────────────────────
|
|
1543
|
+
/**
|
|
1544
|
+
* The Vercel AI SDK emits its own `ai.*` namespace alongside `gen_ai.*`. Map
|
|
1545
|
+
* the AI-SDK-specific keys onto the canonical `llm.*` / `gen_ai.*` / `tool.*`
|
|
1546
|
+
* keys that the existing pipeline already understands. Span-kind inference
|
|
1547
|
+
* runs first so downstream `applyNamespaceMapping` resolves it correctly.
|
|
1548
|
+
*/
|
|
1549
|
+
extractVercelAiSdkAttrs(attrs) {
|
|
1550
|
+
const spanName = attrs["_span_name"] ?? "";
|
|
1551
|
+
const isAiSdkSpan = spanName.startsWith("ai.") || "ai.model.id" in attrs || "ai.toolCall.name" in attrs;
|
|
1552
|
+
if (!isAiSdkSpan) return;
|
|
1553
|
+
if (!("openinference.span.kind" in attrs)) {
|
|
1554
|
+
if (spanName === "ai.toolCall") {
|
|
1555
|
+
attrs["openinference.span.kind"] = "TOOL";
|
|
1556
|
+
} else if (spanName.startsWith("ai.embed")) {
|
|
1557
|
+
attrs["openinference.span.kind"] = "EMBEDDING";
|
|
1558
|
+
} else if (spanName.startsWith("ai.rerank")) {
|
|
1559
|
+
attrs["openinference.span.kind"] = "RERANKER";
|
|
1560
|
+
} else if (spanName.endsWith(".doStream") || spanName.endsWith(".doGenerate") || spanName.endsWith(".doRerank") || spanName.endsWith(".doEmbed")) {
|
|
1561
|
+
if (spanName.includes("embed")) {
|
|
1562
|
+
attrs["openinference.span.kind"] = "EMBEDDING";
|
|
1563
|
+
} else if (spanName.includes("rerank")) {
|
|
1564
|
+
attrs["openinference.span.kind"] = "RERANKER";
|
|
1565
|
+
} else {
|
|
1566
|
+
attrs["openinference.span.kind"] = "LLM";
|
|
1567
|
+
}
|
|
1568
|
+
} else if (spanName === "ai.generateText" || spanName === "ai.streamText" || spanName === "ai.generateObject" || spanName === "ai.streamObject") {
|
|
1569
|
+
attrs["openinference.span.kind"] = "CHAIN";
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
if ("ai.model.id" in attrs && !("llm.model_name" in attrs)) {
|
|
1573
|
+
attrs["llm.model_name"] = attrs["ai.model.id"];
|
|
1574
|
+
}
|
|
1575
|
+
if ("ai.model.provider" in attrs && !("llm.provider" in attrs)) {
|
|
1576
|
+
const raw = String(attrs["ai.model.provider"]);
|
|
1577
|
+
attrs["llm.provider"] = raw.split(".")[0];
|
|
1578
|
+
}
|
|
1579
|
+
if ("ai.usage.promptTokens" in attrs && !("llm.token_count.prompt" in attrs)) {
|
|
1580
|
+
attrs["llm.token_count.prompt"] = attrs["ai.usage.promptTokens"];
|
|
1581
|
+
}
|
|
1582
|
+
if ("ai.usage.completionTokens" in attrs && !("llm.token_count.completion" in attrs)) {
|
|
1583
|
+
attrs["llm.token_count.completion"] = attrs["ai.usage.completionTokens"];
|
|
1584
|
+
}
|
|
1585
|
+
if ("ai.usage.totalTokens" in attrs && !("llm.token_count.total" in attrs)) {
|
|
1586
|
+
attrs["llm.token_count.total"] = attrs["ai.usage.totalTokens"];
|
|
1587
|
+
}
|
|
1588
|
+
const settingMap = {
|
|
1589
|
+
"ai.settings.temperature": "gen_ai.request.temperature",
|
|
1590
|
+
"ai.settings.maxTokens": "gen_ai.request.max_tokens",
|
|
1591
|
+
"ai.settings.topP": "gen_ai.request.top_p",
|
|
1592
|
+
"ai.settings.topK": "gen_ai.request.top_k",
|
|
1593
|
+
"ai.settings.frequencyPenalty": "gen_ai.request.frequency_penalty",
|
|
1594
|
+
"ai.settings.presencePenalty": "gen_ai.request.presence_penalty",
|
|
1595
|
+
"ai.settings.stopSequences": "gen_ai.request.stop_sequences"
|
|
1596
|
+
};
|
|
1597
|
+
for (const [src, tgt] of Object.entries(settingMap)) {
|
|
1598
|
+
if (src in attrs && !(tgt in attrs)) {
|
|
1599
|
+
attrs[tgt] = attrs[src];
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
if (!("llm.invocation_parameters" in attrs)) {
|
|
1603
|
+
const params = {};
|
|
1604
|
+
if ("gen_ai.request.temperature" in attrs) params.temperature = attrs["gen_ai.request.temperature"];
|
|
1605
|
+
if ("gen_ai.request.max_tokens" in attrs) params.max_tokens = attrs["gen_ai.request.max_tokens"];
|
|
1606
|
+
if ("gen_ai.request.top_p" in attrs) params.top_p = attrs["gen_ai.request.top_p"];
|
|
1607
|
+
if ("gen_ai.request.top_k" in attrs) params.top_k = attrs["gen_ai.request.top_k"];
|
|
1608
|
+
if ("gen_ai.request.frequency_penalty" in attrs) params.frequency_penalty = attrs["gen_ai.request.frequency_penalty"];
|
|
1609
|
+
if ("gen_ai.request.presence_penalty" in attrs) params.presence_penalty = attrs["gen_ai.request.presence_penalty"];
|
|
1610
|
+
if ("gen_ai.request.stop_sequences" in attrs) params.stop_sequences = attrs["gen_ai.request.stop_sequences"];
|
|
1611
|
+
if (Object.keys(params).length > 0) {
|
|
1612
|
+
attrs["llm.invocation_parameters"] = JSON.stringify(params);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
if ("ai.operationId" in attrs && !("gen_ai.operation.name" in attrs)) {
|
|
1616
|
+
attrs["gen_ai.operation.name"] = attrs["ai.operationId"];
|
|
1617
|
+
}
|
|
1618
|
+
if ("ai.response.text" in attrs && !("llm.output_messages.0.message.content" in attrs)) {
|
|
1619
|
+
attrs["llm.output_messages.0.message.role"] = "assistant";
|
|
1620
|
+
attrs["llm.output_messages.0.message.content"] = attrs["ai.response.text"];
|
|
1621
|
+
}
|
|
1622
|
+
if ("ai.response.finishReason" in attrs && !("llm.response.finish_reason" in attrs)) {
|
|
1623
|
+
attrs["llm.response.finish_reason"] = attrs["ai.response.finishReason"];
|
|
1624
|
+
}
|
|
1625
|
+
if ("ai.response.id" in attrs && !("gen_ai.response.id" in attrs)) {
|
|
1626
|
+
attrs["gen_ai.response.id"] = attrs["ai.response.id"];
|
|
1627
|
+
}
|
|
1628
|
+
const rawMessages = attrs["ai.prompt.messages"];
|
|
1629
|
+
if (typeof rawMessages === "string") {
|
|
1630
|
+
try {
|
|
1631
|
+
const parsed = JSON.parse(rawMessages);
|
|
1632
|
+
if (Array.isArray(parsed)) {
|
|
1633
|
+
parsed.forEach((msg, i) => {
|
|
1634
|
+
if (msg && typeof msg === "object") {
|
|
1635
|
+
if (typeof msg.role === "string") {
|
|
1636
|
+
attrs[`llm.input_messages.${i}.message.role`] = msg.role;
|
|
1637
|
+
}
|
|
1638
|
+
if (typeof msg.content === "string") {
|
|
1639
|
+
attrs[`llm.input_messages.${i}.message.content`] = msg.content;
|
|
1640
|
+
} else if (msg.content !== void 0) {
|
|
1641
|
+
attrs[`llm.input_messages.${i}.message.content`] = JSON.stringify(msg.content);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
} catch {
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
const rawToolCalls = attrs["ai.response.toolCalls"];
|
|
1650
|
+
if (typeof rawToolCalls === "string") {
|
|
1651
|
+
try {
|
|
1652
|
+
const parsed = JSON.parse(rawToolCalls);
|
|
1653
|
+
if (Array.isArray(parsed)) {
|
|
1654
|
+
parsed.forEach((tc, i) => {
|
|
1655
|
+
if (tc && typeof tc === "object") {
|
|
1656
|
+
if (tc.toolName !== void 0) {
|
|
1657
|
+
attrs[`llm.output_messages.0.message.tool_calls.${i}.tool_call.function.name`] = tc.toolName;
|
|
1658
|
+
}
|
|
1659
|
+
if (tc.args !== void 0) {
|
|
1660
|
+
const argStr = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
1661
|
+
attrs[`llm.output_messages.0.message.tool_calls.${i}.tool_call.function.arguments`] = argStr;
|
|
1662
|
+
}
|
|
1663
|
+
if (tc.toolCallId !== void 0) {
|
|
1664
|
+
attrs[`llm.output_messages.0.message.tool_calls.${i}.tool_call.id`] = tc.toolCallId;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
} catch {
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
if ("ai.value" in attrs && typeof attrs["ai.value"] === "string") {
|
|
1673
|
+
try {
|
|
1674
|
+
attrs["ai.value"] = JSON.parse(attrs["ai.value"]);
|
|
1675
|
+
} catch {
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if ("ai.values" in attrs && Array.isArray(attrs["ai.values"])) {
|
|
1679
|
+
attrs["ai.values"] = JSON.stringify(attrs["ai.values"].map((v) => {
|
|
1680
|
+
if (typeof v === "string") {
|
|
1681
|
+
try {
|
|
1682
|
+
return JSON.parse(v);
|
|
1683
|
+
} catch {
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
return v;
|
|
1687
|
+
}));
|
|
1688
|
+
}
|
|
1689
|
+
if (spanName === "ai.toolCall") {
|
|
1690
|
+
if ("ai.toolCall.name" in attrs && !("tool.name" in attrs)) {
|
|
1691
|
+
attrs["tool.name"] = attrs["ai.toolCall.name"];
|
|
1692
|
+
}
|
|
1693
|
+
if ("ai.toolCall.args" in attrs && !("input.value" in attrs)) {
|
|
1694
|
+
const raw = attrs["ai.toolCall.args"];
|
|
1695
|
+
attrs["input.value"] = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
1696
|
+
}
|
|
1697
|
+
if ("ai.toolCall.result" in attrs && !("output.value" in attrs)) {
|
|
1698
|
+
const raw = attrs["ai.toolCall.result"];
|
|
1699
|
+
attrs["output.value"] = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1536
1703
|
// ── CrewAI-specific ─────────────────────────────────
|
|
1537
1704
|
addCrewaiTokenUsageFallback(attrs) {
|
|
1538
1705
|
const usage = attrs["neatlogs.crew.token_usage"];
|
|
@@ -1708,8 +1875,9 @@ var UnifiedAttributeProcessor = class {
|
|
|
1708
1875
|
if (oiKind) {
|
|
1709
1876
|
unified["neatlogs.span.kind"] = String(oiKind).toLowerCase();
|
|
1710
1877
|
} else {
|
|
1878
|
+
const scopeName = attrs["neatlogs.instrumentation.name"] ?? "";
|
|
1711
1879
|
const spanName = attrs["_span_name"] ?? "";
|
|
1712
|
-
if (spanName) {
|
|
1880
|
+
if (spanName && scopeName !== "next.js") {
|
|
1713
1881
|
const inferred = inferSpanKindFromName(spanName).toLowerCase();
|
|
1714
1882
|
unified["neatlogs.span.kind"] = inferred;
|
|
1715
1883
|
}
|
|
@@ -1718,7 +1886,8 @@ var UnifiedAttributeProcessor = class {
|
|
|
1718
1886
|
const llmRequestType = (attrs["llm.request.type"] ?? "").toLowerCase();
|
|
1719
1887
|
const genAiOperation = (attrs["gen_ai.operation.name"] ?? "").toLowerCase();
|
|
1720
1888
|
const spanNameLower = (attrs["_span_name"] ?? "").toLowerCase();
|
|
1721
|
-
|
|
1889
|
+
const hasExplicitKind = "openinference.span.kind" in attrs || "traceloop.span.kind" in attrs;
|
|
1890
|
+
if (!hasExplicitKind && (llmRequestType === "rerank" || genAiOperation === "rerank" || spanNameLower.includes("rerank"))) {
|
|
1722
1891
|
unified["neatlogs.span.kind"] = "reranker";
|
|
1723
1892
|
}
|
|
1724
1893
|
const spanKind = (attrs["neatlogs.span.kind"] ?? attrs["openinference.span.kind"] ?? "").toLowerCase();
|
|
@@ -1897,7 +2066,7 @@ var UnifiedAttributeProcessor = class {
|
|
|
1897
2066
|
filterEmbeddingVectors(attrs) {
|
|
1898
2067
|
const filtered = {};
|
|
1899
2068
|
for (const [key, value] of Object.entries(attrs)) {
|
|
1900
|
-
if (key.includes(".embedding.vector") || key.includes(".embeddings.")) {
|
|
2069
|
+
if (key.includes(".embedding.vector") || key.includes(".embeddings.") || key === "ai.embeddings" || key === "ai.embedding") {
|
|
1901
2070
|
if (this.debug) {
|
|
1902
2071
|
logger2.debug(`[FILTER] Dropped embedding vector key: ${key}`);
|
|
1903
2072
|
}
|
|
@@ -1924,7 +2093,7 @@ var UnifiedAttributeProcessor = class {
|
|
|
1924
2093
|
* Check if the raw span kind indicates CLIENT (OTel SpanKind.CLIENT = 3).
|
|
1925
2094
|
*/
|
|
1926
2095
|
isHttpLikeSpanKind(kind) {
|
|
1927
|
-
if (typeof kind === "number") return kind ===
|
|
2096
|
+
if (typeof kind === "number") return kind === 3;
|
|
1928
2097
|
return String(kind).toUpperCase() === "CLIENT";
|
|
1929
2098
|
}
|
|
1930
2099
|
};
|
|
@@ -2561,21 +2730,24 @@ var attribute_mapping_default = {
|
|
|
2561
2730
|
total: {
|
|
2562
2731
|
sources: [
|
|
2563
2732
|
"llm.token_count.total",
|
|
2564
|
-
"llm.usage.total_tokens"
|
|
2733
|
+
"llm.usage.total_tokens",
|
|
2734
|
+
"ai.usage.totalTokens"
|
|
2565
2735
|
],
|
|
2566
2736
|
target: "neatlogs.llm.token_count.total"
|
|
2567
2737
|
},
|
|
2568
2738
|
prompt: {
|
|
2569
2739
|
sources: [
|
|
2570
2740
|
"llm.token_count.prompt",
|
|
2571
|
-
"gen_ai.usage.input_tokens"
|
|
2741
|
+
"gen_ai.usage.input_tokens",
|
|
2742
|
+
"ai.usage.promptTokens"
|
|
2572
2743
|
],
|
|
2573
2744
|
target: "neatlogs.llm.token_count.prompt"
|
|
2574
2745
|
},
|
|
2575
2746
|
completion: {
|
|
2576
2747
|
sources: [
|
|
2577
2748
|
"llm.token_count.completion",
|
|
2578
|
-
"gen_ai.usage.output_tokens"
|
|
2749
|
+
"gen_ai.usage.output_tokens",
|
|
2750
|
+
"ai.usage.completionTokens"
|
|
2579
2751
|
],
|
|
2580
2752
|
target: "neatlogs.llm.token_count.completion"
|
|
2581
2753
|
},
|
|
@@ -3166,24 +3338,62 @@ var attribute_mapping_default = {
|
|
|
3166
3338
|
},
|
|
3167
3339
|
reranker: {
|
|
3168
3340
|
description: "Reranker model attributes",
|
|
3341
|
+
applies_to: ["reranker"],
|
|
3169
3342
|
model_name: {
|
|
3170
3343
|
sources: [
|
|
3171
|
-
"reranker.model_name"
|
|
3344
|
+
"reranker.model_name",
|
|
3345
|
+
"ai.model.id"
|
|
3172
3346
|
],
|
|
3173
3347
|
target: "neatlogs.reranker.model_name"
|
|
3174
3348
|
},
|
|
3349
|
+
provider: {
|
|
3350
|
+
sources: [
|
|
3351
|
+
"ai.model.provider"
|
|
3352
|
+
],
|
|
3353
|
+
target: "neatlogs.reranker.provider",
|
|
3354
|
+
description: "Vercel AI SDK reranker provider (e.g. gateway, cohere)"
|
|
3355
|
+
},
|
|
3175
3356
|
query: {
|
|
3176
3357
|
sources: [
|
|
3177
|
-
"reranker.query"
|
|
3358
|
+
"reranker.query",
|
|
3359
|
+
"ai.rerank.query"
|
|
3178
3360
|
],
|
|
3179
3361
|
target: "neatlogs.reranker.query"
|
|
3180
3362
|
},
|
|
3363
|
+
documents: {
|
|
3364
|
+
sources: [
|
|
3365
|
+
"ai.documents"
|
|
3366
|
+
],
|
|
3367
|
+
target: "neatlogs.reranker.input_documents",
|
|
3368
|
+
description: "Vercel AI SDK rerank input documents array"
|
|
3369
|
+
},
|
|
3370
|
+
ranking: {
|
|
3371
|
+
sources: [
|
|
3372
|
+
"ai.ranking"
|
|
3373
|
+
],
|
|
3374
|
+
target: "neatlogs.reranker.output_documents",
|
|
3375
|
+
description: "Vercel AI SDK rerank output ranking array"
|
|
3376
|
+
},
|
|
3377
|
+
ranking_type: {
|
|
3378
|
+
sources: [
|
|
3379
|
+
"ai.ranking.type"
|
|
3380
|
+
],
|
|
3381
|
+
target: "neatlogs.reranker.ranking_type",
|
|
3382
|
+
description: "Vercel AI SDK rerank type (e.g. text)"
|
|
3383
|
+
},
|
|
3181
3384
|
top_k: {
|
|
3182
3385
|
sources: [
|
|
3183
3386
|
"reranker.top_k"
|
|
3184
3387
|
],
|
|
3185
3388
|
target: "neatlogs.reranker.top_k"
|
|
3186
3389
|
},
|
|
3390
|
+
max_retries: {
|
|
3391
|
+
sources: [
|
|
3392
|
+
"ai.settings.maxRetries"
|
|
3393
|
+
],
|
|
3394
|
+
target: "neatlogs.reranker.max_retries",
|
|
3395
|
+
description: "Vercel AI SDK max retries setting"
|
|
3396
|
+
},
|
|
3187
3397
|
input_documents: {
|
|
3188
3398
|
sources: {
|
|
3189
3399
|
id: [
|
|
@@ -3221,9 +3431,19 @@ var attribute_mapping_default = {
|
|
|
3221
3431
|
},
|
|
3222
3432
|
embedding: {
|
|
3223
3433
|
description: "Embedding model attributes",
|
|
3434
|
+
applies_to: ["embedding"],
|
|
3435
|
+
input: {
|
|
3436
|
+
sources: [
|
|
3437
|
+
"ai.value",
|
|
3438
|
+
"ai.values"
|
|
3439
|
+
],
|
|
3440
|
+
target: "neatlogs.embedding.input",
|
|
3441
|
+
description: "Vercel AI SDK embed/embedMany input value(s)"
|
|
3442
|
+
},
|
|
3224
3443
|
model_name: {
|
|
3225
3444
|
sources: [
|
|
3226
|
-
"embedding.model_name"
|
|
3445
|
+
"embedding.model_name",
|
|
3446
|
+
"ai.model.id"
|
|
3227
3447
|
],
|
|
3228
3448
|
target: "neatlogs.embedding.model_name"
|
|
3229
3449
|
},
|
|
@@ -3241,7 +3461,8 @@ var attribute_mapping_default = {
|
|
|
3241
3461
|
},
|
|
3242
3462
|
token_count: {
|
|
3243
3463
|
sources: [
|
|
3244
|
-
"embedding.token_count"
|
|
3464
|
+
"embedding.token_count",
|
|
3465
|
+
"ai.usage.tokens"
|
|
3245
3466
|
],
|
|
3246
3467
|
target: "neatlogs.embedding.token_count"
|
|
3247
3468
|
},
|
|
@@ -3696,10 +3917,13 @@ var AttributeMapper = class {
|
|
|
3696
3917
|
mapNestedConfig(config, attributes, spanKind) {
|
|
3697
3918
|
const mapped = {};
|
|
3698
3919
|
for (const [key, value] of Object.entries(config)) {
|
|
3699
|
-
if (["description", "sources", "target", "indexed", "priority", "values"].includes(key)) {
|
|
3920
|
+
if (["description", "sources", "target", "indexed", "priority", "values", "applies_to"].includes(key)) {
|
|
3700
3921
|
continue;
|
|
3701
3922
|
}
|
|
3702
3923
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3924
|
+
if (Array.isArray(value.applies_to) && spanKind && !value.applies_to.includes(spanKind)) {
|
|
3925
|
+
continue;
|
|
3926
|
+
}
|
|
3703
3927
|
if ("sources" in value) {
|
|
3704
3928
|
if (value.indexed) {
|
|
3705
3929
|
const target = value.target ?? "";
|
|
@@ -3738,6 +3962,9 @@ var AttributeMapper = class {
|
|
|
3738
3962
|
for (const [sectionName, sectionConfig] of Object.entries(this.mappings)) {
|
|
3739
3963
|
if (sectionName === "span_kind" || sectionName === "keep_as_is" || sectionName === "ignore") continue;
|
|
3740
3964
|
if (typeof sectionConfig === "object" && sectionConfig !== null) {
|
|
3965
|
+
if (Array.isArray(sectionConfig.applies_to) && spanKind && !sectionConfig.applies_to.includes(spanKind)) {
|
|
3966
|
+
continue;
|
|
3967
|
+
}
|
|
3741
3968
|
if ("mappings" in sectionConfig) {
|
|
3742
3969
|
const nestedMapped = this.mapNestedConfig(
|
|
3743
3970
|
sectionConfig.mappings,
|
|
@@ -3746,8 +3973,11 @@ var AttributeMapper = class {
|
|
|
3746
3973
|
);
|
|
3747
3974
|
Object.assign(mapped, nestedMapped);
|
|
3748
3975
|
for (const [subKey, subVal] of Object.entries(sectionConfig)) {
|
|
3749
|
-
if (subKey === "mappings" || subKey === "description") continue;
|
|
3976
|
+
if (subKey === "mappings" || subKey === "description" || subKey === "applies_to") continue;
|
|
3750
3977
|
if (typeof subVal === "object" && subVal !== null && !Array.isArray(subVal)) {
|
|
3978
|
+
if (Array.isArray(subVal.applies_to) && spanKind && !subVal.applies_to.includes(spanKind)) {
|
|
3979
|
+
continue;
|
|
3980
|
+
}
|
|
3751
3981
|
if ("sources" in subVal) {
|
|
3752
3982
|
if (subVal.indexed) {
|
|
3753
3983
|
const target = subVal.target ?? "";
|
|
@@ -4127,7 +4357,7 @@ var NeatlogsSpanProcessor = class {
|
|
|
4127
4357
|
for (const key of Object.keys(unifiedAttrs)) {
|
|
4128
4358
|
if (key.includes("input_messages") || key.includes("output_messages") || key.includes("gen_ai.prompt") || key.includes("gen_ai.completion") || key.includes(".content")) {
|
|
4129
4359
|
keysToRemove.push(key);
|
|
4130
|
-
} else if (
|
|
4360
|
+
} else if (key === "neatlogs.embedding.output" || skipOutput && key === "neatlogs.embedding.input") {
|
|
4131
4361
|
keysToRemove.push(key);
|
|
4132
4362
|
}
|
|
4133
4363
|
}
|
|
@@ -4430,7 +4660,7 @@ var FilteringExporter = class {
|
|
|
4430
4660
|
_delegate;
|
|
4431
4661
|
export(spans, resultCallback) {
|
|
4432
4662
|
const filtered = spans.filter(
|
|
4433
|
-
(s) => !s.attributes["neatlogs.dropped"]
|
|
4663
|
+
(s) => !s.attributes["neatlogs.dropped"] && s.instrumentationLibrary.name !== "next.js"
|
|
4434
4664
|
);
|
|
4435
4665
|
if (filtered.length === 0) {
|
|
4436
4666
|
resultCallback({ code: 0 });
|
|
@@ -4686,7 +4916,8 @@ var INSTRUMENTATION_REGISTRY = {
|
|
|
4686
4916
|
"huggingface_hub",
|
|
4687
4917
|
"litellm",
|
|
4688
4918
|
"google_genai",
|
|
4689
|
-
"portkey"
|
|
4919
|
+
"portkey",
|
|
4920
|
+
"ai_sdk"
|
|
4690
4921
|
],
|
|
4691
4922
|
embedding: ["openai", "cohere", "huggingface", "vertexai", "mistralai", "ollama"],
|
|
4692
4923
|
retrieval: [
|
|
@@ -4715,7 +4946,8 @@ var INSTRUMENTATION_REGISTRY = {
|
|
|
4715
4946
|
"pydantic_ai",
|
|
4716
4947
|
"smolagents",
|
|
4717
4948
|
"strands",
|
|
4718
|
-
"pipecat"
|
|
4949
|
+
"pipecat",
|
|
4950
|
+
"ai_sdk"
|
|
4719
4951
|
],
|
|
4720
4952
|
tool: ["langchain", "llamaindex", "haystack", "mcp"],
|
|
4721
4953
|
http: ["requests", "httpx", "urllib3", "aiohttp"],
|
|
@@ -4728,6 +4960,15 @@ var INSTRUMENTATION_REGISTRY = {
|
|
|
4728
4960
|
neatlogs: null,
|
|
4729
4961
|
default_span_kind: "LLM"
|
|
4730
4962
|
},
|
|
4963
|
+
ai_sdk: {
|
|
4964
|
+
openinference: null,
|
|
4965
|
+
openllmetry: null,
|
|
4966
|
+
// The wrapper is opt-in per call site; init({ instrumentations: ['ai_sdk'] })
|
|
4967
|
+
// is a no-op. This registry entry exists so scope detection and tagging stay
|
|
4968
|
+
// consistent with other LLM/agent libraries.
|
|
4969
|
+
neatlogs: "@neatlogs/instrumentation-ai-sdk",
|
|
4970
|
+
default_span_kind: "LLM"
|
|
4971
|
+
},
|
|
4731
4972
|
openai: {
|
|
4732
4973
|
openinference: "@arizeai/openinference-instrumentation-openai",
|
|
4733
4974
|
openllmetry: null,
|
|
@@ -5055,63 +5296,14 @@ function getLibraryInfo(library) {
|
|
|
5055
5296
|
var logger12 = getLogger();
|
|
5056
5297
|
var InstrumentationManager = class {
|
|
5057
5298
|
provider;
|
|
5058
|
-
debug;
|
|
5059
|
-
excludedUrls;
|
|
5060
5299
|
_instrumented = [];
|
|
5061
5300
|
constructor(options) {
|
|
5062
5301
|
this.provider = options.provider;
|
|
5063
|
-
this.debug = options.debug ?? false;
|
|
5064
|
-
this.excludedUrls = options.excludedUrls ?? [];
|
|
5065
5302
|
}
|
|
5066
5303
|
/** Get list of successfully instrumented libraries. */
|
|
5067
5304
|
get instrumented() {
|
|
5068
5305
|
return [...this._instrumented];
|
|
5069
5306
|
}
|
|
5070
|
-
/**
|
|
5071
|
-
* Instrument HTTP (fetch/undici) for W3C traceparent context propagation.
|
|
5072
|
-
* Always called by init().
|
|
5073
|
-
*/
|
|
5074
|
-
async instrumentHttp() {
|
|
5075
|
-
const excludedUrls = this.excludedUrls;
|
|
5076
|
-
const ignoreOutgoingRequestHook = excludedUrls.length > 0 ? (request) => {
|
|
5077
|
-
try {
|
|
5078
|
-
const url = typeof request === "string" ? request : request?.href ?? request?.path ?? "";
|
|
5079
|
-
return excludedUrls.some((excluded) => url.includes(excluded));
|
|
5080
|
-
} catch {
|
|
5081
|
-
return false;
|
|
5082
|
-
}
|
|
5083
|
-
} : void 0;
|
|
5084
|
-
try {
|
|
5085
|
-
const { HttpInstrumentation } = await import("@opentelemetry/instrumentation-http");
|
|
5086
|
-
const httpInstr = new HttpInstrumentation({
|
|
5087
|
-
...ignoreOutgoingRequestHook ? { ignoreOutgoingRequestHook } : {}
|
|
5088
|
-
});
|
|
5089
|
-
httpInstr.setTracerProvider(this.provider);
|
|
5090
|
-
httpInstr.enable();
|
|
5091
|
-
logger12.debug("Instrumented http/https for context propagation");
|
|
5092
|
-
} catch {
|
|
5093
|
-
logger12.debug("http instrumentation not available \u2014 skipping");
|
|
5094
|
-
}
|
|
5095
|
-
try {
|
|
5096
|
-
const { UndiciInstrumentation } = await import("@opentelemetry/instrumentation-undici");
|
|
5097
|
-
const ignoreUndiciHook = excludedUrls.length > 0 ? (request) => {
|
|
5098
|
-
try {
|
|
5099
|
-
const url = `${request?.origin ?? ""}${request?.path ?? ""}`;
|
|
5100
|
-
return excludedUrls.some((excluded) => url.includes(excluded));
|
|
5101
|
-
} catch {
|
|
5102
|
-
return false;
|
|
5103
|
-
}
|
|
5104
|
-
} : void 0;
|
|
5105
|
-
const undiciInstr = new UndiciInstrumentation({
|
|
5106
|
-
...ignoreUndiciHook ? { ignoreRequestHook: ignoreUndiciHook } : {}
|
|
5107
|
-
});
|
|
5108
|
-
undiciInstr.setTracerProvider(this.provider);
|
|
5109
|
-
undiciInstr.enable();
|
|
5110
|
-
logger12.debug("Instrumented undici for context propagation");
|
|
5111
|
-
} catch {
|
|
5112
|
-
logger12.debug("undici instrumentation not available \u2014 skipping");
|
|
5113
|
-
}
|
|
5114
|
-
}
|
|
5115
5307
|
/**
|
|
5116
5308
|
* Instrument the specified libraries.
|
|
5117
5309
|
* Priority: neatlogs custom > OpenInference > skip
|
|
@@ -5665,7 +5857,7 @@ async function init(options = {}) {
|
|
|
5665
5857
|
logger13.debug(`Auto-generated session_id: ${sessionId}`);
|
|
5666
5858
|
}
|
|
5667
5859
|
}
|
|
5668
|
-
const endpoint = options.endpoint ?? "https://staging-cloud.neatlogs.com
|
|
5860
|
+
const endpoint = options.endpoint ?? "https://staging-cloud.neatlogs.com";
|
|
5669
5861
|
const baseUrl = new URL(endpoint).origin;
|
|
5670
5862
|
_setSessionConfig({
|
|
5671
5863
|
sessionId,
|
|
@@ -5780,10 +5972,8 @@ async function init(options = {}) {
|
|
|
5780
5972
|
}
|
|
5781
5973
|
const manager = new InstrumentationManager({
|
|
5782
5974
|
provider,
|
|
5783
|
-
debug: options.debug
|
|
5784
|
-
excludedUrls: [baseUrl, endpoint].filter(Boolean)
|
|
5975
|
+
debug: options.debug
|
|
5785
5976
|
});
|
|
5786
|
-
await manager.instrumentHttp();
|
|
5787
5977
|
if (options.instrumentations?.length) {
|
|
5788
5978
|
await manager.instrument(options.instrumentations);
|
|
5789
5979
|
if (options.debug) {
|
|
@@ -6074,6 +6264,166 @@ async function getMastraObservability() {
|
|
|
6074
6264
|
return _cached;
|
|
6075
6265
|
}
|
|
6076
6266
|
|
|
6267
|
+
// src/ai-sdk.ts
|
|
6268
|
+
import { trace as trace5, context as otelContext3, ROOT_CONTEXT as ROOT_CONTEXT2, SpanStatusCode as SpanStatusCode4 } from "@opentelemetry/api";
|
|
6269
|
+
var TRACER_NAME2 = "neatlogs.ai-sdk";
|
|
6270
|
+
function createAITelemetry(opts = {}) {
|
|
6271
|
+
const userMeta = opts.metadata ?? {};
|
|
6272
|
+
return {
|
|
6273
|
+
isEnabled: true,
|
|
6274
|
+
recordInputs: true,
|
|
6275
|
+
recordOutputs: true,
|
|
6276
|
+
tracer: trace5.getTracer(TRACER_NAME2),
|
|
6277
|
+
metadata: { ...userMeta, neatlogsWrapped: true }
|
|
6278
|
+
};
|
|
6279
|
+
}
|
|
6280
|
+
function safeStringify2(value) {
|
|
6281
|
+
try {
|
|
6282
|
+
return JSON.stringify(value);
|
|
6283
|
+
} catch {
|
|
6284
|
+
return "";
|
|
6285
|
+
}
|
|
6286
|
+
}
|
|
6287
|
+
function setInputValue(span2, opts) {
|
|
6288
|
+
if (opts && ("prompt" in opts || "messages" in opts)) {
|
|
6289
|
+
const input = opts.messages ?? opts.prompt;
|
|
6290
|
+
const stringified2 = safeStringify2(input);
|
|
6291
|
+
if (stringified2) {
|
|
6292
|
+
span2.setAttribute("input.value", stringified2);
|
|
6293
|
+
}
|
|
6294
|
+
return;
|
|
6295
|
+
}
|
|
6296
|
+
const stringified = safeStringify2(opts);
|
|
6297
|
+
if (stringified) {
|
|
6298
|
+
span2.setAttribute("input.value", stringified);
|
|
6299
|
+
}
|
|
6300
|
+
}
|
|
6301
|
+
function setOutputValue(span2, result) {
|
|
6302
|
+
if (result && typeof result === "object") {
|
|
6303
|
+
const r = result;
|
|
6304
|
+
if ("text" in r && "finishReason" in r) {
|
|
6305
|
+
const text = String(r.text ?? "");
|
|
6306
|
+
if (text) {
|
|
6307
|
+
span2.setAttribute("output.value", text);
|
|
6308
|
+
}
|
|
6309
|
+
if (r.finishReason) {
|
|
6310
|
+
span2.setAttribute("gen_ai.finish_reason", String(r.finishReason));
|
|
6311
|
+
}
|
|
6312
|
+
return;
|
|
6313
|
+
}
|
|
6314
|
+
}
|
|
6315
|
+
const stringified = safeStringify2(result);
|
|
6316
|
+
if (stringified) {
|
|
6317
|
+
span2.setAttribute("output.value", stringified);
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
6320
|
+
var WRAPPED_FUNCTIONS = [
|
|
6321
|
+
"generateText",
|
|
6322
|
+
"streamText",
|
|
6323
|
+
"generateObject",
|
|
6324
|
+
"streamObject",
|
|
6325
|
+
"embed",
|
|
6326
|
+
"embedMany",
|
|
6327
|
+
"rerank"
|
|
6328
|
+
];
|
|
6329
|
+
function wrapAISDK(aiModule) {
|
|
6330
|
+
const wrapped = { ...aiModule };
|
|
6331
|
+
for (const name of WRAPPED_FUNCTIONS) {
|
|
6332
|
+
const original = aiModule[name];
|
|
6333
|
+
if (typeof original !== "function") continue;
|
|
6334
|
+
if (name === "streamText" || name === "streamObject") {
|
|
6335
|
+
wrapped[name] = createSyncWrapper(name, original);
|
|
6336
|
+
} else {
|
|
6337
|
+
wrapped[name] = createAsyncWrapper(name, original);
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
return wrapped;
|
|
6341
|
+
}
|
|
6342
|
+
function rootSpanKind(name) {
|
|
6343
|
+
if (name === "embed" || name === "embedMany" || name === "rerank") return "CHAIN";
|
|
6344
|
+
return "WORKFLOW";
|
|
6345
|
+
}
|
|
6346
|
+
function getParentContext() {
|
|
6347
|
+
const activeSpan = trace5.getActiveSpan();
|
|
6348
|
+
if (activeSpan) {
|
|
6349
|
+
const instrScope = activeSpan.instrumentationLibrary?.name ?? "";
|
|
6350
|
+
if (instrScope === "next.js") {
|
|
6351
|
+
return ROOT_CONTEXT2;
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
return otelContext3.active();
|
|
6355
|
+
}
|
|
6356
|
+
function createAsyncWrapper(name, original) {
|
|
6357
|
+
return async function wrappedAsyncFn(opts) {
|
|
6358
|
+
const tracer = trace5.getTracer(TRACER_NAME2);
|
|
6359
|
+
return tracer.startActiveSpan(
|
|
6360
|
+
`ai.${name}`,
|
|
6361
|
+
{ attributes: { "openinference.span.kind": rootSpanKind(name) } },
|
|
6362
|
+
getParentContext(),
|
|
6363
|
+
async (span2) => {
|
|
6364
|
+
try {
|
|
6365
|
+
const isEmbedOrRerank = name === "embed" || name === "embedMany" || name === "rerank";
|
|
6366
|
+
if (!isEmbedOrRerank) {
|
|
6367
|
+
setInputValue(span2, opts);
|
|
6368
|
+
}
|
|
6369
|
+
if (name === "rerank" && opts?.query) {
|
|
6370
|
+
span2.setAttribute("ai.rerank.query", String(opts.query));
|
|
6371
|
+
}
|
|
6372
|
+
const merged = mergeTelemetry(opts);
|
|
6373
|
+
const result = await original(merged);
|
|
6374
|
+
if (!isEmbedOrRerank) {
|
|
6375
|
+
setOutputValue(span2, result);
|
|
6376
|
+
}
|
|
6377
|
+
return result;
|
|
6378
|
+
} catch (err) {
|
|
6379
|
+
recordSpanError(span2, err);
|
|
6380
|
+
throw err;
|
|
6381
|
+
} finally {
|
|
6382
|
+
span2.end();
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
);
|
|
6386
|
+
};
|
|
6387
|
+
}
|
|
6388
|
+
function createSyncWrapper(name, original) {
|
|
6389
|
+
return function wrappedSyncFn(opts) {
|
|
6390
|
+
const tracer = trace5.getTracer(TRACER_NAME2);
|
|
6391
|
+
return tracer.startActiveSpan(`ai.${name}`, { attributes: { "openinference.span.kind": rootSpanKind(name) } }, getParentContext(), (span2) => {
|
|
6392
|
+
try {
|
|
6393
|
+
setInputValue(span2, opts);
|
|
6394
|
+
const merged = mergeTelemetry(opts);
|
|
6395
|
+
const result = original(merged);
|
|
6396
|
+
return result;
|
|
6397
|
+
} catch (err) {
|
|
6398
|
+
recordSpanError(span2, err);
|
|
6399
|
+
throw err;
|
|
6400
|
+
} finally {
|
|
6401
|
+
span2.end();
|
|
6402
|
+
}
|
|
6403
|
+
});
|
|
6404
|
+
};
|
|
6405
|
+
}
|
|
6406
|
+
function mergeTelemetry(opts) {
|
|
6407
|
+
const baseTelemetry = createAITelemetry({
|
|
6408
|
+
metadata: opts?.experimental_telemetry?.metadata
|
|
6409
|
+
});
|
|
6410
|
+
return {
|
|
6411
|
+
...opts,
|
|
6412
|
+
experimental_telemetry: {
|
|
6413
|
+
...opts?.experimental_telemetry,
|
|
6414
|
+
...baseTelemetry
|
|
6415
|
+
}
|
|
6416
|
+
};
|
|
6417
|
+
}
|
|
6418
|
+
function recordSpanError(span2, err) {
|
|
6419
|
+
if (err instanceof Error) {
|
|
6420
|
+
span2.setStatus({ code: SpanStatusCode4.ERROR, message: err.message });
|
|
6421
|
+
span2.recordException(err);
|
|
6422
|
+
} else {
|
|
6423
|
+
span2.setStatus({ code: SpanStatusCode4.ERROR, message: String(err) });
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
|
|
6077
6427
|
// src/core/llm-binder.ts
|
|
6078
6428
|
var logger15 = getLogger();
|
|
6079
6429
|
function bindTemplates(llm, systemTpl, userTpl, compiledVars) {
|
|
@@ -6134,6 +6484,7 @@ export {
|
|
|
6134
6484
|
UserPromptTemplate,
|
|
6135
6485
|
__version__,
|
|
6136
6486
|
bindTemplates,
|
|
6487
|
+
createAITelemetry,
|
|
6137
6488
|
createPrompt,
|
|
6138
6489
|
deletePrompt,
|
|
6139
6490
|
fetchPrompt,
|
|
@@ -6151,6 +6502,7 @@ export {
|
|
|
6151
6502
|
shutdown,
|
|
6152
6503
|
span,
|
|
6153
6504
|
trace2 as trace,
|
|
6154
|
-
updatePrompt
|
|
6505
|
+
updatePrompt,
|
|
6506
|
+
wrapAISDK
|
|
6155
6507
|
};
|
|
6156
6508
|
//# sourceMappingURL=index.mjs.map
|