@agent-inspect/mcp-server 4.4.0 → 5.0.0
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 +2192 -2189
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2192 -2189
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import os from 'os';
|
|
|
7
7
|
import 'nanoid';
|
|
8
8
|
import 'chalk';
|
|
9
9
|
import 'fs';
|
|
10
|
+
import 'url';
|
|
10
11
|
|
|
11
12
|
// packages/mcp-server/src/index.ts
|
|
12
13
|
|
|
@@ -1524,7 +1525,7 @@ function summarize(findings, diagnostics) {
|
|
|
1524
1525
|
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
1525
1526
|
};
|
|
1526
1527
|
}
|
|
1527
|
-
function eventEvidence(event,
|
|
1528
|
+
function eventEvidence(event, path12) {
|
|
1528
1529
|
return {
|
|
1529
1530
|
runId: event.runId,
|
|
1530
1531
|
eventId: event.eventId,
|
|
@@ -1635,1059 +1636,492 @@ function runTraceChecks(input, options = {}) {
|
|
|
1635
1636
|
};
|
|
1636
1637
|
}
|
|
1637
1638
|
|
|
1638
|
-
// packages/core/src/
|
|
1639
|
-
function
|
|
1640
|
-
|
|
1641
|
-
if ("outputPreview" in meta) return meta.outputPreview;
|
|
1642
|
-
if ("resultPreview" in meta) return meta.resultPreview;
|
|
1643
|
-
return void 0;
|
|
1639
|
+
// packages/core/src/persisted/token-usage.ts
|
|
1640
|
+
function isRecord5(value) {
|
|
1641
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1644
1642
|
}
|
|
1645
|
-
function
|
|
1646
|
-
|
|
1647
|
-
return s;
|
|
1643
|
+
function nonNegativeFinite(value) {
|
|
1644
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1648
1645
|
}
|
|
1649
|
-
function
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
const
|
|
1655
|
-
const
|
|
1656
|
-
const
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
if (lastCompleted === void 0) runStatus = "running";
|
|
1660
|
-
else runStatus = lastCompleted.status;
|
|
1661
|
-
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
1662
|
-
const steps = /* @__PURE__ */ new Map();
|
|
1663
|
-
let order = 0;
|
|
1664
|
-
for (const e of events) {
|
|
1665
|
-
if (e.event !== "step_started") continue;
|
|
1666
|
-
const s = e;
|
|
1667
|
-
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
1668
|
-
steps.set(s.stepId, {
|
|
1669
|
-
id: s.stepId,
|
|
1670
|
-
parentId: s.parentId,
|
|
1671
|
-
name: s.name,
|
|
1672
|
-
type: s.type,
|
|
1673
|
-
order: order++,
|
|
1674
|
-
timestamp: s.timestamp,
|
|
1675
|
-
metadata: meta
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
for (const e of events) {
|
|
1679
|
-
if (e.event !== "step_completed") continue;
|
|
1680
|
-
const acc = steps.get(e.stepId);
|
|
1681
|
-
if (!acc) continue;
|
|
1682
|
-
acc.status = e.status;
|
|
1683
|
-
acc.durationMs = e.durationMs;
|
|
1684
|
-
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
1685
|
-
const extra = e;
|
|
1686
|
-
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
1687
|
-
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
1691
|
-
for (const acc of steps.values()) {
|
|
1692
|
-
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
1693
|
-
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
1694
|
-
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
1695
|
-
}
|
|
1696
|
-
const outputPreview = extractOutputPreview(meta);
|
|
1697
|
-
const sc = {
|
|
1698
|
-
id: acc.id,
|
|
1699
|
-
name: acc.name,
|
|
1700
|
-
type: acc.type,
|
|
1701
|
-
status: mapStepStatus(acc.status),
|
|
1702
|
-
durationMs: acc.durationMs,
|
|
1703
|
-
error: acc.errorMsg,
|
|
1704
|
-
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
1705
|
-
outputPreview,
|
|
1706
|
-
children: []
|
|
1707
|
-
};
|
|
1708
|
-
nodes.set(acc.id, sc);
|
|
1709
|
-
}
|
|
1710
|
-
const roots = [];
|
|
1711
|
-
const sortByOrder = (a, b) => {
|
|
1712
|
-
const oa = steps.get(a.id)?.order ?? 0;
|
|
1713
|
-
const ob = steps.get(b.id)?.order ?? 0;
|
|
1714
|
-
return oa - ob;
|
|
1715
|
-
};
|
|
1716
|
-
for (const acc of steps.values()) {
|
|
1717
|
-
const node = nodes.get(acc.id);
|
|
1718
|
-
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
1719
|
-
nodes.get(acc.parentId).children.push(node);
|
|
1720
|
-
} else {
|
|
1721
|
-
roots.push(node);
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
roots.sort(sortByOrder);
|
|
1725
|
-
for (const n of nodes.values()) {
|
|
1726
|
-
n.children.sort(sortByOrder);
|
|
1646
|
+
function normalizeTokenUsage(value) {
|
|
1647
|
+
if (!isRecord5(value)) return void 0;
|
|
1648
|
+
const input = nonNegativeFinite(value.input);
|
|
1649
|
+
const output = nonNegativeFinite(value.output);
|
|
1650
|
+
const suppliedTotal = nonNegativeFinite(value.total);
|
|
1651
|
+
const cached = nonNegativeFinite(value.cached);
|
|
1652
|
+
const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
|
|
1653
|
+
const total = suppliedTotal ?? derivedTotal;
|
|
1654
|
+
if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
|
|
1655
|
+
return void 0;
|
|
1727
1656
|
}
|
|
1728
1657
|
return {
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
steps: roots
|
|
1658
|
+
...input !== void 0 ? { input } : {},
|
|
1659
|
+
...output !== void 0 ? { output } : {},
|
|
1660
|
+
...total !== void 0 ? { total } : {},
|
|
1661
|
+
...cached !== void 0 ? { cached } : {}
|
|
1734
1662
|
};
|
|
1735
1663
|
}
|
|
1736
1664
|
|
|
1737
|
-
// packages/core/src/
|
|
1738
|
-
|
|
1739
|
-
"
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1665
|
+
// packages/core/src/persisted/from-trace-event.ts
|
|
1666
|
+
function sanitizeIdPart(value) {
|
|
1667
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1668
|
+
}
|
|
1669
|
+
function nodeIdForEvent(event) {
|
|
1670
|
+
switch (event.event) {
|
|
1671
|
+
case "run_started":
|
|
1672
|
+
case "run_completed":
|
|
1673
|
+
return event.runId;
|
|
1674
|
+
case "step_started":
|
|
1675
|
+
case "step_completed":
|
|
1676
|
+
return event.stepId;
|
|
1677
|
+
case "outcome_observed":
|
|
1678
|
+
return event.outcomeId;
|
|
1679
|
+
default:
|
|
1680
|
+
return "unknown";
|
|
1751
1681
|
}
|
|
1752
|
-
return false;
|
|
1753
1682
|
}
|
|
1754
|
-
function
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1683
|
+
function createPersistedEventId(event, eventIndex) {
|
|
1684
|
+
const runId = sanitizeIdPart(event.runId);
|
|
1685
|
+
const ev = sanitizeIdPart(event.event);
|
|
1686
|
+
const node = sanitizeIdPart(nodeIdForEvent(event));
|
|
1687
|
+
return `manual:${runId}:${ev}:${node}:${eventIndex}`;
|
|
1688
|
+
}
|
|
1689
|
+
function toIsoTimestamp(ms) {
|
|
1690
|
+
if (typeof ms !== "number" || !Number.isFinite(ms)) {
|
|
1691
|
+
return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
|
|
1762
1692
|
}
|
|
1763
|
-
return
|
|
1693
|
+
return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
|
|
1764
1694
|
}
|
|
1765
|
-
function
|
|
1766
|
-
return
|
|
1695
|
+
function buildSource(options) {
|
|
1696
|
+
return {
|
|
1697
|
+
type: "manual",
|
|
1698
|
+
name: options?.sourceName ?? "trace-event",
|
|
1699
|
+
version: options?.sourceVersion ?? "0.1"
|
|
1700
|
+
};
|
|
1767
1701
|
}
|
|
1768
|
-
function
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1702
|
+
function mapStepTypeToInspectKind(type) {
|
|
1703
|
+
switch (type) {
|
|
1704
|
+
case "run":
|
|
1705
|
+
return "RUN";
|
|
1706
|
+
case "llm":
|
|
1707
|
+
return "LLM";
|
|
1708
|
+
case "tool":
|
|
1709
|
+
return "TOOL";
|
|
1710
|
+
case "decision":
|
|
1711
|
+
return "DECISION";
|
|
1712
|
+
case "logic":
|
|
1713
|
+
case "state":
|
|
1714
|
+
case "custom":
|
|
1715
|
+
return "LOGIC";
|
|
1716
|
+
default:
|
|
1717
|
+
return "LOGIC";
|
|
1775
1718
|
}
|
|
1776
|
-
return out;
|
|
1777
1719
|
}
|
|
1778
|
-
function
|
|
1779
|
-
|
|
1780
|
-
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
1720
|
+
function mapRunOrStepStatus(status) {
|
|
1721
|
+
return status === "success" ? "ok" : "error";
|
|
1781
1722
|
}
|
|
1782
|
-
function
|
|
1783
|
-
if (
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1723
|
+
function mapErrorInfo(error) {
|
|
1724
|
+
if (!error?.message) {
|
|
1725
|
+
return {};
|
|
1726
|
+
}
|
|
1727
|
+
const out = {
|
|
1728
|
+
persisted: {
|
|
1729
|
+
message: error.message,
|
|
1730
|
+
name: "Error"
|
|
1790
1731
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1732
|
+
};
|
|
1733
|
+
if (typeof error.stack === "string" && error.stack.length > 0) {
|
|
1734
|
+
out.errorStack = error.stack;
|
|
1793
1735
|
}
|
|
1794
1736
|
return out;
|
|
1795
1737
|
}
|
|
1796
|
-
function
|
|
1797
|
-
|
|
1798
|
-
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
1799
|
-
}
|
|
1800
|
-
if (Array.isArray(value)) {
|
|
1801
|
-
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
|
|
1802
|
-
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
1803
|
-
return arr;
|
|
1804
|
-
}
|
|
1805
|
-
const o = value;
|
|
1806
|
-
const inner = {};
|
|
1807
|
-
for (const k of Object.keys(o)) {
|
|
1808
|
-
if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
1809
|
-
else inner[k] = compactValue(o[k], maxLen);
|
|
1810
|
-
}
|
|
1811
|
-
return inner;
|
|
1738
|
+
function mapTokenUsageFromMetadata(metadata) {
|
|
1739
|
+
return normalizeTokenUsage(metadata?.tokens);
|
|
1812
1740
|
}
|
|
1813
|
-
function
|
|
1814
|
-
const out =
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
out
|
|
1818
|
-
if (n.children.length > 0) walk(n.children);
|
|
1741
|
+
function compactAttributes(entries) {
|
|
1742
|
+
const out = {};
|
|
1743
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
1744
|
+
if (value !== void 0) {
|
|
1745
|
+
out[key] = value;
|
|
1819
1746
|
}
|
|
1820
1747
|
}
|
|
1821
|
-
|
|
1822
|
-
return out;
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
// packages/core/src/diff/engine.ts
|
|
1826
|
-
var DEFAULT_THRESHOLD_MS = 0;
|
|
1827
|
-
function pathSeg(step, index) {
|
|
1828
|
-
return { index, name: step.name, stepId: step.id };
|
|
1748
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1829
1749
|
}
|
|
1830
|
-
function
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1750
|
+
function traceEventToPersistedInspectEvent(event, options) {
|
|
1751
|
+
const eventIndex = options?.eventIndex ?? 0;
|
|
1752
|
+
const eventId = createPersistedEventId(event, eventIndex);
|
|
1753
|
+
const source = buildSource(options);
|
|
1754
|
+
const tsMain = toIsoTimestamp(event.timestamp);
|
|
1755
|
+
switch (event.event) {
|
|
1756
|
+
case "run_started": {
|
|
1757
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
1758
|
+
const correlation = extractCorrelationMetadata(event.metadata);
|
|
1759
|
+
const attributes = compactAttributes({
|
|
1760
|
+
legacyEvent: "run_started",
|
|
1761
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
1762
|
+
correlationId: correlation?.correlationId,
|
|
1763
|
+
requestId: correlation?.requestId,
|
|
1764
|
+
decisionId: correlation?.decisionId,
|
|
1765
|
+
groupId: correlation?.groupId,
|
|
1766
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
1767
|
+
});
|
|
1768
|
+
return {
|
|
1769
|
+
schemaVersion: "0.2",
|
|
1770
|
+
eventId,
|
|
1771
|
+
runId: event.runId,
|
|
1772
|
+
kind: "RUN",
|
|
1773
|
+
name: event.name,
|
|
1774
|
+
status: "running",
|
|
1775
|
+
timestamp: tsMain.iso,
|
|
1776
|
+
startedAt: tsStart.iso,
|
|
1777
|
+
confidence: "explicit",
|
|
1778
|
+
source,
|
|
1779
|
+
attributes
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
case "run_completed": {
|
|
1783
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
1784
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
1785
|
+
const attributes = compactAttributes({
|
|
1786
|
+
legacyEvent: "run_completed",
|
|
1787
|
+
errorStack,
|
|
1788
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
1789
|
+
});
|
|
1790
|
+
return {
|
|
1791
|
+
schemaVersion: "0.2",
|
|
1792
|
+
eventId,
|
|
1793
|
+
runId: event.runId,
|
|
1794
|
+
kind: "RUN",
|
|
1795
|
+
name: "run",
|
|
1796
|
+
status: mapRunOrStepStatus(event.status),
|
|
1797
|
+
timestamp: tsMain.iso,
|
|
1798
|
+
endedAt: tsEnd.iso,
|
|
1799
|
+
durationMs: event.durationMs,
|
|
1800
|
+
confidence: "explicit",
|
|
1801
|
+
source,
|
|
1802
|
+
attributes,
|
|
1803
|
+
error
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
case "step_started": {
|
|
1807
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
1808
|
+
const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
|
|
1809
|
+
const attributes = compactAttributes({
|
|
1810
|
+
legacyEvent: "step_started",
|
|
1811
|
+
stepId: event.stepId,
|
|
1812
|
+
stepType: event.type,
|
|
1813
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
1814
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
1815
|
+
});
|
|
1816
|
+
const out = {
|
|
1817
|
+
schemaVersion: "0.2",
|
|
1818
|
+
eventId,
|
|
1819
|
+
runId: event.runId,
|
|
1820
|
+
kind: mapStepTypeToInspectKind(event.type),
|
|
1821
|
+
name: event.name,
|
|
1822
|
+
status: "running",
|
|
1823
|
+
timestamp: tsMain.iso,
|
|
1824
|
+
startedAt: tsStart.iso,
|
|
1825
|
+
confidence: "explicit",
|
|
1826
|
+
source,
|
|
1827
|
+
attributes
|
|
1828
|
+
};
|
|
1829
|
+
if (event.parentId !== void 0) {
|
|
1830
|
+
out.parentId = event.parentId;
|
|
1831
|
+
}
|
|
1832
|
+
if (tokenUsage !== void 0) {
|
|
1833
|
+
out.tokenUsage = tokenUsage;
|
|
1843
1834
|
}
|
|
1835
|
+
return out;
|
|
1844
1836
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1837
|
+
case "step_completed": {
|
|
1838
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
1839
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
1840
|
+
const attributes = compactAttributes({
|
|
1841
|
+
legacyEvent: "step_completed",
|
|
1842
|
+
stepId: event.stepId,
|
|
1843
|
+
errorStack,
|
|
1844
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
1845
|
+
});
|
|
1846
|
+
return {
|
|
1847
|
+
schemaVersion: "0.2",
|
|
1848
|
+
eventId,
|
|
1849
|
+
runId: event.runId,
|
|
1850
|
+
kind: "LOGIC",
|
|
1851
|
+
name: event.stepId,
|
|
1852
|
+
status: mapRunOrStepStatus(event.status),
|
|
1853
|
+
timestamp: tsMain.iso,
|
|
1854
|
+
endedAt: tsEnd.iso,
|
|
1855
|
+
durationMs: event.durationMs,
|
|
1856
|
+
confidence: "explicit",
|
|
1857
|
+
source,
|
|
1858
|
+
attributes,
|
|
1859
|
+
error
|
|
1860
|
+
};
|
|
1849
1861
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1862
|
+
case "outcome_observed": {
|
|
1863
|
+
const tsObserved = toIsoTimestamp(event.observedAt);
|
|
1864
|
+
const attributes = compactAttributes({
|
|
1865
|
+
legacyEvent: "outcome_observed",
|
|
1866
|
+
outcomeId: event.outcomeId,
|
|
1867
|
+
outcomeStatus: event.status,
|
|
1868
|
+
expectation: event.expectation,
|
|
1869
|
+
method: event.method,
|
|
1870
|
+
actual: event.actual,
|
|
1871
|
+
evidence: event.evidence,
|
|
1872
|
+
observedAt: tsObserved.iso,
|
|
1873
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsObserved.invalidTimestamp ? true : void 0
|
|
1874
|
+
});
|
|
1875
|
+
const out = {
|
|
1876
|
+
schemaVersion: "0.2",
|
|
1877
|
+
eventId,
|
|
1878
|
+
runId: event.runId,
|
|
1879
|
+
kind: "OUTCOME",
|
|
1880
|
+
name: event.name,
|
|
1881
|
+
status: event.status === "failed" ? "error" : "ok",
|
|
1882
|
+
timestamp: tsMain.iso,
|
|
1883
|
+
confidence: "explicit",
|
|
1884
|
+
source,
|
|
1885
|
+
attributes
|
|
1886
|
+
};
|
|
1887
|
+
if (event.parentId !== void 0) {
|
|
1888
|
+
out.parentId = event.parentId;
|
|
1889
|
+
}
|
|
1890
|
+
if (event.actual !== void 0) {
|
|
1891
|
+
out.outputSummary = event.actual;
|
|
1892
|
+
}
|
|
1893
|
+
return out;
|
|
1855
1894
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
pairs.push([void 0, R]);
|
|
1895
|
+
default: {
|
|
1896
|
+
const _exhaustive = event;
|
|
1897
|
+
throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
|
|
1860
1898
|
}
|
|
1861
1899
|
}
|
|
1862
|
-
return pairs;
|
|
1863
1900
|
}
|
|
1864
|
-
function
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
severity: "warning",
|
|
1880
|
-
message: "Step type differs",
|
|
1881
|
-
path: path8,
|
|
1882
|
-
left: L.type,
|
|
1883
|
-
right: R.type
|
|
1884
|
-
});
|
|
1885
|
-
}
|
|
1886
|
-
if ((L.status ?? "") !== (R.status ?? "")) {
|
|
1887
|
-
out.push({
|
|
1888
|
-
kind: "step-status",
|
|
1889
|
-
severity: "warning",
|
|
1890
|
-
message: "Step status differs",
|
|
1891
|
-
path: path8,
|
|
1892
|
-
left: L.status,
|
|
1893
|
-
right: R.status
|
|
1894
|
-
});
|
|
1901
|
+
function traceEventsToPersistedInspectEvents(events, options) {
|
|
1902
|
+
return events.map(
|
|
1903
|
+
(event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
|
|
1904
|
+
);
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
// packages/core/src/logs/tree-builder.ts
|
|
1908
|
+
function inc(map, key) {
|
|
1909
|
+
map[key] = (map[key] ?? 0) + 1;
|
|
1910
|
+
}
|
|
1911
|
+
function computeRunStatus(events) {
|
|
1912
|
+
let hasRunning = false;
|
|
1913
|
+
for (const e of events) {
|
|
1914
|
+
if (e.status === "error") return "error";
|
|
1915
|
+
if (e.status === "running") hasRunning = true;
|
|
1895
1916
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
message: "Step error message differs",
|
|
1903
|
-
path: path8,
|
|
1904
|
-
left: le || void 0,
|
|
1905
|
-
right: re || void 0
|
|
1906
|
-
});
|
|
1917
|
+
if (hasRunning) return "running";
|
|
1918
|
+
return "ok";
|
|
1919
|
+
}
|
|
1920
|
+
var TreeBuilder = class {
|
|
1921
|
+
constructor(options) {
|
|
1922
|
+
void options?.config;
|
|
1907
1923
|
}
|
|
1908
|
-
|
|
1909
|
-
const
|
|
1910
|
-
const
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
if (ld === void 0 && rd === void 0) differs = false;
|
|
1914
|
-
else if (ld === void 0 || rd === void 0) differs = true;
|
|
1915
|
-
else differs = Math.abs(ld - rd) > th;
|
|
1916
|
-
if (differs) {
|
|
1917
|
-
out.push({
|
|
1918
|
-
kind: "duration",
|
|
1919
|
-
severity: "info",
|
|
1920
|
-
message: "Step duration differs",
|
|
1921
|
-
path: path8,
|
|
1922
|
-
left: ld,
|
|
1923
|
-
right: rd
|
|
1924
|
-
});
|
|
1924
|
+
build(events) {
|
|
1925
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
1926
|
+
for (const e of events) {
|
|
1927
|
+
if (!byRun.has(e.runId)) byRun.set(e.runId, []);
|
|
1928
|
+
byRun.get(e.runId).push(e);
|
|
1925
1929
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
message: `Step only in left run: ${lch.name}`,
|
|
1964
|
-
path: buildPath([...segments, pathSeg(lch, ci)]),
|
|
1965
|
-
left: lch.id,
|
|
1966
|
-
right: void 0
|
|
1967
|
-
});
|
|
1968
|
-
} else if (rch !== void 0) {
|
|
1930
|
+
const out = [];
|
|
1931
|
+
for (const [runId, runEvents] of byRun.entries()) {
|
|
1932
|
+
const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
|
|
1933
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
1934
|
+
for (const e of sorted) {
|
|
1935
|
+
nodes.set(e.eventId, { event: e, children: [], depth: 0 });
|
|
1936
|
+
}
|
|
1937
|
+
const roots = [];
|
|
1938
|
+
for (const node of nodes.values()) {
|
|
1939
|
+
const parentId = node.event.parentId;
|
|
1940
|
+
if (parentId && nodes.has(parentId)) {
|
|
1941
|
+
nodes.get(parentId).children.push(node);
|
|
1942
|
+
} else {
|
|
1943
|
+
roots.push(node);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
const assignDepth = (n, depth) => {
|
|
1947
|
+
n.depth = depth;
|
|
1948
|
+
for (const c of n.children) assignDepth(c, depth + 1);
|
|
1949
|
+
};
|
|
1950
|
+
for (const r of roots) assignDepth(r, 0);
|
|
1951
|
+
const confidenceBreakdown = {
|
|
1952
|
+
explicit: 0,
|
|
1953
|
+
correlated: 0,
|
|
1954
|
+
heuristic: 0,
|
|
1955
|
+
unknown: 0
|
|
1956
|
+
};
|
|
1957
|
+
const kinds = {};
|
|
1958
|
+
for (const e of sorted) {
|
|
1959
|
+
inc(confidenceBreakdown, e.confidence);
|
|
1960
|
+
kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
|
|
1961
|
+
}
|
|
1962
|
+
const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
|
|
1963
|
+
const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
|
|
1964
|
+
const status = computeRunStatus(sorted);
|
|
1965
|
+
const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
|
|
1966
|
+
const name = sorted.find((e) => e.kind === "RUN")?.name;
|
|
1969
1967
|
out.push({
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1968
|
+
runId,
|
|
1969
|
+
name,
|
|
1970
|
+
status,
|
|
1971
|
+
startedAt,
|
|
1972
|
+
endedAt: status === "running" ? void 0 : endedAt,
|
|
1973
|
+
durationMs,
|
|
1974
|
+
children: roots,
|
|
1975
|
+
metadata: {
|
|
1976
|
+
totalEvents: sorted.length,
|
|
1977
|
+
confidenceBreakdown,
|
|
1978
|
+
kinds
|
|
1979
|
+
}
|
|
1976
1980
|
});
|
|
1977
1981
|
}
|
|
1978
|
-
|
|
1982
|
+
out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
|
|
1983
|
+
return out;
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
|
|
1987
|
+
// packages/core/src/persisted/to-inspect-event.ts
|
|
1988
|
+
function compactAttributes2(entries) {
|
|
1989
|
+
const out = {};
|
|
1990
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
1991
|
+
if (value !== void 0) {
|
|
1992
|
+
out[key] = value;
|
|
1993
|
+
}
|
|
1979
1994
|
}
|
|
1995
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1980
1996
|
}
|
|
1981
|
-
function
|
|
1997
|
+
function parseIsoToMs3(iso) {
|
|
1998
|
+
const parsed = Date.parse(iso);
|
|
1999
|
+
if (!Number.isFinite(parsed)) {
|
|
2000
|
+
return { ms: 0, invalidTimestamp: true };
|
|
2001
|
+
}
|
|
2002
|
+
return { ms: parsed, invalidTimestamp: false };
|
|
2003
|
+
}
|
|
2004
|
+
function mapPersistedSourceToInspect(event) {
|
|
2005
|
+
const attrs = event.attributes ?? {};
|
|
2006
|
+
const sourceName = event.source.name;
|
|
2007
|
+
if (sourceName === "pino") {
|
|
2008
|
+
return {
|
|
2009
|
+
type: "pino",
|
|
2010
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
2011
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
if (sourceName === "winston") {
|
|
2015
|
+
return {
|
|
2016
|
+
type: "winston",
|
|
2017
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
2018
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
const mapType = (t) => {
|
|
2022
|
+
switch (t) {
|
|
2023
|
+
case "manual":
|
|
2024
|
+
return "manual";
|
|
2025
|
+
case "json-log":
|
|
2026
|
+
return "json-log";
|
|
2027
|
+
case "log4js":
|
|
2028
|
+
return "log4js";
|
|
2029
|
+
case "adapter":
|
|
2030
|
+
case "ai-sdk":
|
|
2031
|
+
case "otel":
|
|
2032
|
+
return "adapter";
|
|
2033
|
+
default:
|
|
2034
|
+
return "json-log";
|
|
2035
|
+
}
|
|
2036
|
+
};
|
|
1982
2037
|
return {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
check: "all"
|
|
2038
|
+
type: mapType(event.source.type),
|
|
2039
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
2040
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
1987
2041
|
};
|
|
1988
2042
|
}
|
|
1989
|
-
function
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
const merged = mergeDiffDefaults();
|
|
1994
|
-
const opts = {
|
|
1995
|
-
ignoreDuration: merged.ignoreDuration,
|
|
1996
|
-
durationThresholdMs: merged.durationThresholdMs
|
|
1997
|
-
};
|
|
1998
|
-
const raw = [];
|
|
1999
|
-
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
2000
|
-
raw.push({
|
|
2001
|
-
kind: "run-status",
|
|
2002
|
-
severity: "warning",
|
|
2003
|
-
message: "Run completion status differs",
|
|
2004
|
-
left: left.status,
|
|
2005
|
-
right: right.status
|
|
2006
|
-
});
|
|
2043
|
+
function buildInspectAttributes(event) {
|
|
2044
|
+
const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
|
|
2045
|
+
if (event.inputSummary !== void 0) {
|
|
2046
|
+
attrs.inputSummary = event.inputSummary;
|
|
2007
2047
|
}
|
|
2008
|
-
{
|
|
2009
|
-
|
|
2010
|
-
const rd = right.durationMs;
|
|
2011
|
-
const th = merged.durationThresholdMs;
|
|
2012
|
-
let differs = false;
|
|
2013
|
-
if (ld === void 0 && rd === void 0) differs = false;
|
|
2014
|
-
else if (ld === void 0 || rd === void 0) differs = true;
|
|
2015
|
-
else differs = Math.abs(ld - rd) > th;
|
|
2016
|
-
if (differs) {
|
|
2017
|
-
raw.push({
|
|
2018
|
-
kind: "duration",
|
|
2019
|
-
severity: "info",
|
|
2020
|
-
message: "Run duration differs",
|
|
2021
|
-
left: ld,
|
|
2022
|
-
right: rd
|
|
2023
|
-
});
|
|
2024
|
-
}
|
|
2048
|
+
if (event.outputSummary !== void 0) {
|
|
2049
|
+
attrs.outputSummary = event.outputSummary;
|
|
2025
2050
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
raw.push({
|
|
2034
|
-
kind: "step-removed",
|
|
2035
|
-
severity: "warning",
|
|
2036
|
-
message: `Step only in left run: ${ls.name}`,
|
|
2037
|
-
path: buildPath([pathSeg(ls, idx)]),
|
|
2038
|
-
left: ls.id,
|
|
2039
|
-
right: void 0
|
|
2040
|
-
});
|
|
2041
|
-
idx += 1;
|
|
2042
|
-
} else if (rs !== void 0) {
|
|
2043
|
-
raw.push({
|
|
2044
|
-
kind: "step-added",
|
|
2045
|
-
severity: "warning",
|
|
2046
|
-
message: `Step only in right run: ${rs.name}`,
|
|
2047
|
-
path: buildPath([pathSeg(rs, idx)]),
|
|
2048
|
-
left: void 0,
|
|
2049
|
-
right: rs.id
|
|
2050
|
-
});
|
|
2051
|
-
idx += 1;
|
|
2051
|
+
if (event.error) {
|
|
2052
|
+
if (event.error.name !== void 0) {
|
|
2053
|
+
attrs.errorName = event.error.name;
|
|
2054
|
+
}
|
|
2055
|
+
attrs.errorMessage = event.error.message;
|
|
2056
|
+
if (event.error.code !== void 0) {
|
|
2057
|
+
attrs.errorCode = event.error.code;
|
|
2052
2058
|
}
|
|
2053
2059
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
let warnings = 0;
|
|
2057
|
-
let info = 0;
|
|
2058
|
-
for (const d of differences) {
|
|
2059
|
-
if (d.severity === "error") errors += 1;
|
|
2060
|
-
else if (d.severity === "warning") warnings += 1;
|
|
2061
|
-
else info += 1;
|
|
2060
|
+
if (event.tokenUsage) {
|
|
2061
|
+
attrs.tokens = { ...event.tokenUsage };
|
|
2062
2062
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
kind: "first-divergence",
|
|
2066
|
-
severity: firstVisible.severity,
|
|
2067
|
-
message: `First divergence: ${firstVisible.message}`,
|
|
2068
|
-
path: firstVisible.path,
|
|
2069
|
-
left: firstVisible.left,
|
|
2070
|
-
right: firstVisible.right
|
|
2071
|
-
} : void 0;
|
|
2072
|
-
const summary = {
|
|
2073
|
-
leftRunId: left.runId,
|
|
2074
|
-
rightRunId: right.runId,
|
|
2075
|
-
totalDifferences: differences.length,
|
|
2076
|
-
errors,
|
|
2077
|
-
warnings,
|
|
2078
|
-
info,
|
|
2079
|
-
firstDivergence
|
|
2080
|
-
};
|
|
2081
|
-
return { summary, differences };
|
|
2082
|
-
}
|
|
2083
|
-
|
|
2084
|
-
// packages/core/src/exporters/markdown-exporter.ts
|
|
2085
|
-
function renderTreeAscii(nodes, indent = "") {
|
|
2086
|
-
const lines = [];
|
|
2087
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
2088
|
-
const n = nodes[i];
|
|
2089
|
-
const last = i === nodes.length - 1;
|
|
2090
|
-
const branch = last ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
2091
|
-
const ev = n.event;
|
|
2092
|
-
const status = ev.status ?? "?";
|
|
2093
|
-
const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
|
|
2094
|
-
lines.push(`${indent}${branch}${escapeMarkdown(ev.name)} [${ev.kind}] ${status} (${dur})`);
|
|
2095
|
-
const nextIndent = indent + (last ? " " : "\u2502 ");
|
|
2096
|
-
if (n.children.length > 0) {
|
|
2097
|
-
const childStr = renderTreeAscii(n.children, nextIndent);
|
|
2098
|
-
if (childStr.length > 0) lines.push(childStr);
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
return lines.join("\n");
|
|
2102
|
-
}
|
|
2103
|
-
function exportMarkdown(tree, options) {
|
|
2104
|
-
const warnings = [];
|
|
2105
|
-
const includeMetadata = options?.includeMetadata ?? true;
|
|
2106
|
-
const includeAttributes = options?.includeAttributes ?? false;
|
|
2107
|
-
const includeErrors = options?.includeErrors ?? true;
|
|
2108
|
-
const maxLen = options?.maxAttributeLength ?? 500;
|
|
2109
|
-
const titleName = tree.name ?? tree.runId;
|
|
2110
|
-
const lines = [];
|
|
2111
|
-
lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
|
|
2112
|
-
lines.push("");
|
|
2113
|
-
lines.push("Generated locally by AgentInspect. Review for sensitive data before sharing.");
|
|
2114
|
-
lines.push("");
|
|
2115
|
-
if (includeMetadata) {
|
|
2116
|
-
lines.push("## Summary");
|
|
2117
|
-
lines.push("");
|
|
2118
|
-
lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
|
|
2119
|
-
if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
|
|
2120
|
-
lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
|
|
2121
|
-
lines.push(
|
|
2122
|
-
`- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
|
|
2123
|
-
);
|
|
2124
|
-
lines.push(
|
|
2125
|
-
`- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
|
|
2126
|
-
);
|
|
2127
|
-
lines.push(
|
|
2128
|
-
`- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
|
|
2129
|
-
);
|
|
2130
|
-
lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
|
|
2131
|
-
lines.push("");
|
|
2132
|
-
lines.push("### Confidence breakdown");
|
|
2133
|
-
lines.push("");
|
|
2134
|
-
lines.push("| bucket | count |");
|
|
2135
|
-
lines.push("| --- | --- |");
|
|
2136
|
-
for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
|
|
2137
|
-
const key = k;
|
|
2138
|
-
lines.push(
|
|
2139
|
-
`| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
|
|
2140
|
-
);
|
|
2141
|
-
}
|
|
2142
|
-
lines.push("");
|
|
2143
|
-
lines.push("### Kind breakdown");
|
|
2144
|
-
lines.push("");
|
|
2145
|
-
lines.push("| kind | count |");
|
|
2146
|
-
lines.push("| --- | --- |");
|
|
2147
|
-
for (const k of Object.keys(tree.metadata.kinds).sort()) {
|
|
2148
|
-
const key = k;
|
|
2149
|
-
const c = tree.metadata.kinds[key];
|
|
2150
|
-
if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
|
|
2151
|
-
}
|
|
2152
|
-
lines.push("");
|
|
2063
|
+
if (event.source.type === "ai-sdk" || event.source.type === "otel") {
|
|
2064
|
+
attrs.originalSourceType = event.source.type;
|
|
2153
2065
|
}
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
lines.push("```text");
|
|
2157
|
-
lines.push(
|
|
2158
|
-
tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
|
|
2159
|
-
);
|
|
2160
|
-
lines.push("```");
|
|
2161
|
-
lines.push("");
|
|
2162
|
-
const flat = flattenTree(tree);
|
|
2163
|
-
const errors = flat.filter((n) => n.event.status === "error");
|
|
2164
|
-
if (includeErrors && errors.length > 0) {
|
|
2165
|
-
lines.push("## Errors");
|
|
2166
|
-
lines.push("");
|
|
2167
|
-
for (const n of errors) {
|
|
2168
|
-
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
2169
|
-
n.event.attributes.error.message,
|
|
2170
|
-
maxLen
|
|
2171
|
-
) : "";
|
|
2172
|
-
lines.push(
|
|
2173
|
-
`- **${escapeMarkdown(n.event.name)}** (${escapeMarkdown(n.event.eventId)}): ${escapeMarkdown(msg || "error")}`
|
|
2174
|
-
);
|
|
2175
|
-
}
|
|
2176
|
-
lines.push("");
|
|
2066
|
+
if (event.source.name !== void 0) {
|
|
2067
|
+
attrs.sourceName = event.source.name;
|
|
2177
2068
|
}
|
|
2178
|
-
if (
|
|
2179
|
-
|
|
2180
|
-
lines.push("");
|
|
2181
|
-
for (const n of flat) {
|
|
2182
|
-
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
2183
|
-
const compact = compactAttributes(n.event.attributes, {
|
|
2184
|
-
maxLength: maxLen});
|
|
2185
|
-
lines.push(`### ${escapeMarkdown(n.event.name)}`);
|
|
2186
|
-
lines.push("");
|
|
2187
|
-
lines.push("```json");
|
|
2188
|
-
lines.push(stableJson(compact, true));
|
|
2189
|
-
lines.push("```");
|
|
2190
|
-
lines.push("");
|
|
2191
|
-
}
|
|
2192
|
-
warnings.push(
|
|
2193
|
-
"Attributes may still contain sensitive data; review exports before sharing."
|
|
2194
|
-
);
|
|
2069
|
+
if (event.source.version !== void 0) {
|
|
2070
|
+
attrs.sourceVersion = event.source.version;
|
|
2195
2071
|
}
|
|
2196
|
-
return
|
|
2197
|
-
format: "markdown",
|
|
2198
|
-
content: lines.join("\n"),
|
|
2199
|
-
contentType: "text/markdown",
|
|
2200
|
-
fileExtension: ".md",
|
|
2201
|
-
warnings
|
|
2202
|
-
};
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2205
|
-
// packages/core/src/persisted/token-usage.ts
|
|
2206
|
-
function isRecord5(value) {
|
|
2207
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2208
|
-
}
|
|
2209
|
-
function nonNegativeFinite(value) {
|
|
2210
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
2072
|
+
return attrs;
|
|
2211
2073
|
}
|
|
2212
|
-
function
|
|
2213
|
-
if (!
|
|
2214
|
-
|
|
2215
|
-
const output = nonNegativeFinite(value.output);
|
|
2216
|
-
const suppliedTotal = nonNegativeFinite(value.total);
|
|
2217
|
-
const cached = nonNegativeFinite(value.cached);
|
|
2218
|
-
const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
|
|
2219
|
-
const total = suppliedTotal ?? derivedTotal;
|
|
2220
|
-
if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
|
|
2221
|
-
return void 0;
|
|
2074
|
+
function persistedInspectEventToInspectEvent(event) {
|
|
2075
|
+
if (!isPersistedInspectEvent(event)) {
|
|
2076
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
2222
2077
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
...cached !== void 0 ? { cached } : {}
|
|
2228
|
-
};
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
|
-
// packages/core/src/persisted/from-trace-event.ts
|
|
2232
|
-
function sanitizeIdPart(value) {
|
|
2233
|
-
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2234
|
-
}
|
|
2235
|
-
function nodeIdForEvent(event) {
|
|
2236
|
-
switch (event.event) {
|
|
2237
|
-
case "run_started":
|
|
2238
|
-
case "run_completed":
|
|
2239
|
-
return event.runId;
|
|
2240
|
-
case "step_started":
|
|
2241
|
-
case "step_completed":
|
|
2242
|
-
return event.stepId;
|
|
2243
|
-
case "outcome_observed":
|
|
2244
|
-
return event.outcomeId;
|
|
2245
|
-
default:
|
|
2246
|
-
return "unknown";
|
|
2078
|
+
const ts = parseIsoToMs3(event.timestamp);
|
|
2079
|
+
const attrs = buildInspectAttributes(event);
|
|
2080
|
+
if (ts.invalidTimestamp) {
|
|
2081
|
+
attrs.invalidTimestamp = true;
|
|
2247
2082
|
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
return `manual:${runId}:${ev}:${node}:${eventIndex}`;
|
|
2254
|
-
}
|
|
2255
|
-
function toIsoTimestamp(ms) {
|
|
2256
|
-
if (typeof ms !== "number" || !Number.isFinite(ms)) {
|
|
2257
|
-
return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
|
|
2083
|
+
let status;
|
|
2084
|
+
if (event.status === "running" || event.status === "ok" || event.status === "error") {
|
|
2085
|
+
status = event.status;
|
|
2086
|
+
} else if (event.status === "unknown") {
|
|
2087
|
+
attrs.persistedStatus = "unknown";
|
|
2258
2088
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2089
|
+
const out = {
|
|
2090
|
+
eventId: event.eventId,
|
|
2091
|
+
runId: event.runId,
|
|
2092
|
+
name: event.name,
|
|
2093
|
+
kind: event.kind,
|
|
2094
|
+
timestamp: ts.ms,
|
|
2095
|
+
confidence: event.confidence,
|
|
2096
|
+
source: mapPersistedSourceToInspect(event),
|
|
2097
|
+
attributes: compactAttributes2(attrs)
|
|
2266
2098
|
};
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
switch (type) {
|
|
2270
|
-
case "run":
|
|
2271
|
-
return "RUN";
|
|
2272
|
-
case "llm":
|
|
2273
|
-
return "LLM";
|
|
2274
|
-
case "tool":
|
|
2275
|
-
return "TOOL";
|
|
2276
|
-
case "decision":
|
|
2277
|
-
return "DECISION";
|
|
2278
|
-
case "logic":
|
|
2279
|
-
case "state":
|
|
2280
|
-
case "custom":
|
|
2281
|
-
return "LOGIC";
|
|
2282
|
-
default:
|
|
2283
|
-
return "LOGIC";
|
|
2099
|
+
if (event.parentId !== void 0) {
|
|
2100
|
+
out.parentId = event.parentId;
|
|
2284
2101
|
}
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
return status === "success" ? "ok" : "error";
|
|
2288
|
-
}
|
|
2289
|
-
function mapErrorInfo(error) {
|
|
2290
|
-
if (!error?.message) {
|
|
2291
|
-
return {};
|
|
2102
|
+
if (status !== void 0) {
|
|
2103
|
+
out.status = status;
|
|
2292
2104
|
}
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
message: error.message,
|
|
2296
|
-
name: "Error"
|
|
2297
|
-
}
|
|
2298
|
-
};
|
|
2299
|
-
if (typeof error.stack === "string" && error.stack.length > 0) {
|
|
2300
|
-
out.errorStack = error.stack;
|
|
2105
|
+
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
|
|
2106
|
+
out.durationMs = event.durationMs;
|
|
2301
2107
|
}
|
|
2302
2108
|
return out;
|
|
2303
2109
|
}
|
|
2304
|
-
function
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2110
|
+
function persistedInspectEventsToInspectEvents(events, options) {
|
|
2111
|
+
const skipInvalid = options?.skipInvalid === true;
|
|
2112
|
+
const out = [];
|
|
2113
|
+
for (const event of events) {
|
|
2114
|
+
if (!isPersistedInspectEvent(event)) {
|
|
2115
|
+
if (skipInvalid) {
|
|
2116
|
+
continue;
|
|
2117
|
+
}
|
|
2118
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
2312
2119
|
}
|
|
2120
|
+
out.push(persistedInspectEventToInspectEvent(event));
|
|
2313
2121
|
}
|
|
2314
|
-
return
|
|
2315
|
-
}
|
|
2316
|
-
function traceEventToPersistedInspectEvent(event, options) {
|
|
2317
|
-
const eventIndex = options?.eventIndex ?? 0;
|
|
2318
|
-
const eventId = createPersistedEventId(event, eventIndex);
|
|
2319
|
-
const source = buildSource(options);
|
|
2320
|
-
const tsMain = toIsoTimestamp(event.timestamp);
|
|
2321
|
-
switch (event.event) {
|
|
2322
|
-
case "run_started": {
|
|
2323
|
-
const tsStart = toIsoTimestamp(event.startTime);
|
|
2324
|
-
const correlation = extractCorrelationMetadata(event.metadata);
|
|
2325
|
-
const attributes = compactAttributes2({
|
|
2326
|
-
legacyEvent: "run_started",
|
|
2327
|
-
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
2328
|
-
correlationId: correlation?.correlationId,
|
|
2329
|
-
requestId: correlation?.requestId,
|
|
2330
|
-
decisionId: correlation?.decisionId,
|
|
2331
|
-
groupId: correlation?.groupId,
|
|
2332
|
-
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
2333
|
-
});
|
|
2334
|
-
return {
|
|
2335
|
-
schemaVersion: "0.2",
|
|
2336
|
-
eventId,
|
|
2337
|
-
runId: event.runId,
|
|
2338
|
-
kind: "RUN",
|
|
2339
|
-
name: event.name,
|
|
2340
|
-
status: "running",
|
|
2341
|
-
timestamp: tsMain.iso,
|
|
2342
|
-
startedAt: tsStart.iso,
|
|
2343
|
-
confidence: "explicit",
|
|
2344
|
-
source,
|
|
2345
|
-
attributes
|
|
2346
|
-
};
|
|
2347
|
-
}
|
|
2348
|
-
case "run_completed": {
|
|
2349
|
-
const tsEnd = toIsoTimestamp(event.endTime);
|
|
2350
|
-
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
2351
|
-
const attributes = compactAttributes2({
|
|
2352
|
-
legacyEvent: "run_completed",
|
|
2353
|
-
errorStack,
|
|
2354
|
-
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
2355
|
-
});
|
|
2356
|
-
return {
|
|
2357
|
-
schemaVersion: "0.2",
|
|
2358
|
-
eventId,
|
|
2359
|
-
runId: event.runId,
|
|
2360
|
-
kind: "RUN",
|
|
2361
|
-
name: "run",
|
|
2362
|
-
status: mapRunOrStepStatus(event.status),
|
|
2363
|
-
timestamp: tsMain.iso,
|
|
2364
|
-
endedAt: tsEnd.iso,
|
|
2365
|
-
durationMs: event.durationMs,
|
|
2366
|
-
confidence: "explicit",
|
|
2367
|
-
source,
|
|
2368
|
-
attributes,
|
|
2369
|
-
error
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
case "step_started": {
|
|
2373
|
-
const tsStart = toIsoTimestamp(event.startTime);
|
|
2374
|
-
const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
|
|
2375
|
-
const attributes = compactAttributes2({
|
|
2376
|
-
legacyEvent: "step_started",
|
|
2377
|
-
stepId: event.stepId,
|
|
2378
|
-
stepType: event.type,
|
|
2379
|
-
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
2380
|
-
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
2381
|
-
});
|
|
2382
|
-
const out = {
|
|
2383
|
-
schemaVersion: "0.2",
|
|
2384
|
-
eventId,
|
|
2385
|
-
runId: event.runId,
|
|
2386
|
-
kind: mapStepTypeToInspectKind(event.type),
|
|
2387
|
-
name: event.name,
|
|
2388
|
-
status: "running",
|
|
2389
|
-
timestamp: tsMain.iso,
|
|
2390
|
-
startedAt: tsStart.iso,
|
|
2391
|
-
confidence: "explicit",
|
|
2392
|
-
source,
|
|
2393
|
-
attributes
|
|
2394
|
-
};
|
|
2395
|
-
if (event.parentId !== void 0) {
|
|
2396
|
-
out.parentId = event.parentId;
|
|
2397
|
-
}
|
|
2398
|
-
if (tokenUsage !== void 0) {
|
|
2399
|
-
out.tokenUsage = tokenUsage;
|
|
2400
|
-
}
|
|
2401
|
-
return out;
|
|
2402
|
-
}
|
|
2403
|
-
case "step_completed": {
|
|
2404
|
-
const tsEnd = toIsoTimestamp(event.endTime);
|
|
2405
|
-
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
2406
|
-
const attributes = compactAttributes2({
|
|
2407
|
-
legacyEvent: "step_completed",
|
|
2408
|
-
stepId: event.stepId,
|
|
2409
|
-
errorStack,
|
|
2410
|
-
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
2411
|
-
});
|
|
2412
|
-
return {
|
|
2413
|
-
schemaVersion: "0.2",
|
|
2414
|
-
eventId,
|
|
2415
|
-
runId: event.runId,
|
|
2416
|
-
kind: "LOGIC",
|
|
2417
|
-
name: event.stepId,
|
|
2418
|
-
status: mapRunOrStepStatus(event.status),
|
|
2419
|
-
timestamp: tsMain.iso,
|
|
2420
|
-
endedAt: tsEnd.iso,
|
|
2421
|
-
durationMs: event.durationMs,
|
|
2422
|
-
confidence: "explicit",
|
|
2423
|
-
source,
|
|
2424
|
-
attributes,
|
|
2425
|
-
error
|
|
2426
|
-
};
|
|
2427
|
-
}
|
|
2428
|
-
case "outcome_observed": {
|
|
2429
|
-
const tsObserved = toIsoTimestamp(event.observedAt);
|
|
2430
|
-
const attributes = compactAttributes2({
|
|
2431
|
-
legacyEvent: "outcome_observed",
|
|
2432
|
-
outcomeId: event.outcomeId,
|
|
2433
|
-
outcomeStatus: event.status,
|
|
2434
|
-
expectation: event.expectation,
|
|
2435
|
-
method: event.method,
|
|
2436
|
-
actual: event.actual,
|
|
2437
|
-
evidence: event.evidence,
|
|
2438
|
-
observedAt: tsObserved.iso,
|
|
2439
|
-
invalidTimestamp: tsMain.invalidTimestamp || tsObserved.invalidTimestamp ? true : void 0
|
|
2440
|
-
});
|
|
2441
|
-
const out = {
|
|
2442
|
-
schemaVersion: "0.2",
|
|
2443
|
-
eventId,
|
|
2444
|
-
runId: event.runId,
|
|
2445
|
-
kind: "OUTCOME",
|
|
2446
|
-
name: event.name,
|
|
2447
|
-
status: event.status === "failed" ? "error" : "ok",
|
|
2448
|
-
timestamp: tsMain.iso,
|
|
2449
|
-
confidence: "explicit",
|
|
2450
|
-
source,
|
|
2451
|
-
attributes
|
|
2452
|
-
};
|
|
2453
|
-
if (event.parentId !== void 0) {
|
|
2454
|
-
out.parentId = event.parentId;
|
|
2455
|
-
}
|
|
2456
|
-
if (event.actual !== void 0) {
|
|
2457
|
-
out.outputSummary = event.actual;
|
|
2458
|
-
}
|
|
2459
|
-
return out;
|
|
2460
|
-
}
|
|
2461
|
-
default: {
|
|
2462
|
-
const _exhaustive = event;
|
|
2463
|
-
throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
function traceEventsToPersistedInspectEvents(events, options) {
|
|
2468
|
-
return events.map(
|
|
2469
|
-
(event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
|
|
2470
|
-
);
|
|
2122
|
+
return out;
|
|
2471
2123
|
}
|
|
2472
2124
|
|
|
2473
|
-
// packages/core/src/persisted/to-inspect-event.ts
|
|
2474
|
-
function compactAttributes3(entries) {
|
|
2475
|
-
const out = {};
|
|
2476
|
-
for (const [key, value] of Object.entries(entries)) {
|
|
2477
|
-
if (value !== void 0) {
|
|
2478
|
-
out[key] = value;
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
2482
|
-
}
|
|
2483
|
-
function parseIsoToMs3(iso) {
|
|
2484
|
-
const parsed = Date.parse(iso);
|
|
2485
|
-
if (!Number.isFinite(parsed)) {
|
|
2486
|
-
return { ms: 0, invalidTimestamp: true };
|
|
2487
|
-
}
|
|
2488
|
-
return { ms: parsed, invalidTimestamp: false };
|
|
2489
|
-
}
|
|
2490
|
-
function mapPersistedSourceToInspect(event) {
|
|
2491
|
-
const attrs = event.attributes ?? {};
|
|
2492
|
-
const sourceName = event.source.name;
|
|
2493
|
-
if (sourceName === "pino") {
|
|
2494
|
-
return {
|
|
2495
|
-
type: "pino",
|
|
2496
|
-
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
2497
|
-
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
2498
|
-
};
|
|
2499
|
-
}
|
|
2500
|
-
if (sourceName === "winston") {
|
|
2501
|
-
return {
|
|
2502
|
-
type: "winston",
|
|
2503
|
-
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
2504
|
-
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
2505
|
-
};
|
|
2506
|
-
}
|
|
2507
|
-
const mapType = (t) => {
|
|
2508
|
-
switch (t) {
|
|
2509
|
-
case "manual":
|
|
2510
|
-
return "manual";
|
|
2511
|
-
case "json-log":
|
|
2512
|
-
return "json-log";
|
|
2513
|
-
case "log4js":
|
|
2514
|
-
return "log4js";
|
|
2515
|
-
case "adapter":
|
|
2516
|
-
case "ai-sdk":
|
|
2517
|
-
case "otel":
|
|
2518
|
-
return "adapter";
|
|
2519
|
-
default:
|
|
2520
|
-
return "json-log";
|
|
2521
|
-
}
|
|
2522
|
-
};
|
|
2523
|
-
return {
|
|
2524
|
-
type: mapType(event.source.type),
|
|
2525
|
-
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
2526
|
-
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
2527
|
-
};
|
|
2528
|
-
}
|
|
2529
|
-
function buildInspectAttributes(event) {
|
|
2530
|
-
const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
|
|
2531
|
-
if (event.inputSummary !== void 0) {
|
|
2532
|
-
attrs.inputSummary = event.inputSummary;
|
|
2533
|
-
}
|
|
2534
|
-
if (event.outputSummary !== void 0) {
|
|
2535
|
-
attrs.outputSummary = event.outputSummary;
|
|
2536
|
-
}
|
|
2537
|
-
if (event.error) {
|
|
2538
|
-
if (event.error.name !== void 0) {
|
|
2539
|
-
attrs.errorName = event.error.name;
|
|
2540
|
-
}
|
|
2541
|
-
attrs.errorMessage = event.error.message;
|
|
2542
|
-
if (event.error.code !== void 0) {
|
|
2543
|
-
attrs.errorCode = event.error.code;
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
if (event.tokenUsage) {
|
|
2547
|
-
attrs.tokens = { ...event.tokenUsage };
|
|
2548
|
-
}
|
|
2549
|
-
if (event.source.type === "ai-sdk" || event.source.type === "otel") {
|
|
2550
|
-
attrs.originalSourceType = event.source.type;
|
|
2551
|
-
}
|
|
2552
|
-
if (event.source.name !== void 0) {
|
|
2553
|
-
attrs.sourceName = event.source.name;
|
|
2554
|
-
}
|
|
2555
|
-
if (event.source.version !== void 0) {
|
|
2556
|
-
attrs.sourceVersion = event.source.version;
|
|
2557
|
-
}
|
|
2558
|
-
return attrs;
|
|
2559
|
-
}
|
|
2560
|
-
function persistedInspectEventToInspectEvent(event) {
|
|
2561
|
-
if (!isPersistedInspectEvent(event)) {
|
|
2562
|
-
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
2563
|
-
}
|
|
2564
|
-
const ts = parseIsoToMs3(event.timestamp);
|
|
2565
|
-
const attrs = buildInspectAttributes(event);
|
|
2566
|
-
if (ts.invalidTimestamp) {
|
|
2567
|
-
attrs.invalidTimestamp = true;
|
|
2568
|
-
}
|
|
2569
|
-
let status;
|
|
2570
|
-
if (event.status === "running" || event.status === "ok" || event.status === "error") {
|
|
2571
|
-
status = event.status;
|
|
2572
|
-
} else if (event.status === "unknown") {
|
|
2573
|
-
attrs.persistedStatus = "unknown";
|
|
2574
|
-
}
|
|
2575
|
-
const out = {
|
|
2576
|
-
eventId: event.eventId,
|
|
2577
|
-
runId: event.runId,
|
|
2578
|
-
name: event.name,
|
|
2579
|
-
kind: event.kind,
|
|
2580
|
-
timestamp: ts.ms,
|
|
2581
|
-
confidence: event.confidence,
|
|
2582
|
-
source: mapPersistedSourceToInspect(event),
|
|
2583
|
-
attributes: compactAttributes3(attrs)
|
|
2584
|
-
};
|
|
2585
|
-
if (event.parentId !== void 0) {
|
|
2586
|
-
out.parentId = event.parentId;
|
|
2587
|
-
}
|
|
2588
|
-
if (status !== void 0) {
|
|
2589
|
-
out.status = status;
|
|
2590
|
-
}
|
|
2591
|
-
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
|
|
2592
|
-
out.durationMs = event.durationMs;
|
|
2593
|
-
}
|
|
2594
|
-
return out;
|
|
2595
|
-
}
|
|
2596
|
-
function persistedInspectEventsToInspectEvents(events, options) {
|
|
2597
|
-
const skipInvalid = options?.skipInvalid === true;
|
|
2598
|
-
const out = [];
|
|
2599
|
-
for (const event of events) {
|
|
2600
|
-
if (!isPersistedInspectEvent(event)) {
|
|
2601
|
-
if (skipInvalid) {
|
|
2602
|
-
continue;
|
|
2603
|
-
}
|
|
2604
|
-
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
2605
|
-
}
|
|
2606
|
-
out.push(persistedInspectEventToInspectEvent(event));
|
|
2607
|
-
}
|
|
2608
|
-
return out;
|
|
2609
|
-
}
|
|
2610
|
-
|
|
2611
|
-
// packages/core/src/logs/tree-builder.ts
|
|
2612
|
-
function inc(map, key) {
|
|
2613
|
-
map[key] = (map[key] ?? 0) + 1;
|
|
2614
|
-
}
|
|
2615
|
-
function computeRunStatus(events) {
|
|
2616
|
-
let hasRunning = false;
|
|
2617
|
-
for (const e of events) {
|
|
2618
|
-
if (e.status === "error") return "error";
|
|
2619
|
-
if (e.status === "running") hasRunning = true;
|
|
2620
|
-
}
|
|
2621
|
-
if (hasRunning) return "running";
|
|
2622
|
-
return "ok";
|
|
2623
|
-
}
|
|
2624
|
-
var TreeBuilder = class {
|
|
2625
|
-
constructor(options) {
|
|
2626
|
-
void options?.config;
|
|
2627
|
-
}
|
|
2628
|
-
build(events) {
|
|
2629
|
-
const byRun = /* @__PURE__ */ new Map();
|
|
2630
|
-
for (const e of events) {
|
|
2631
|
-
if (!byRun.has(e.runId)) byRun.set(e.runId, []);
|
|
2632
|
-
byRun.get(e.runId).push(e);
|
|
2633
|
-
}
|
|
2634
|
-
const out = [];
|
|
2635
|
-
for (const [runId, runEvents] of byRun.entries()) {
|
|
2636
|
-
const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
|
|
2637
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
2638
|
-
for (const e of sorted) {
|
|
2639
|
-
nodes.set(e.eventId, { event: e, children: [], depth: 0 });
|
|
2640
|
-
}
|
|
2641
|
-
const roots = [];
|
|
2642
|
-
for (const node of nodes.values()) {
|
|
2643
|
-
const parentId = node.event.parentId;
|
|
2644
|
-
if (parentId && nodes.has(parentId)) {
|
|
2645
|
-
nodes.get(parentId).children.push(node);
|
|
2646
|
-
} else {
|
|
2647
|
-
roots.push(node);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
const assignDepth = (n, depth) => {
|
|
2651
|
-
n.depth = depth;
|
|
2652
|
-
for (const c of n.children) assignDepth(c, depth + 1);
|
|
2653
|
-
};
|
|
2654
|
-
for (const r of roots) assignDepth(r, 0);
|
|
2655
|
-
const confidenceBreakdown = {
|
|
2656
|
-
explicit: 0,
|
|
2657
|
-
correlated: 0,
|
|
2658
|
-
heuristic: 0,
|
|
2659
|
-
unknown: 0
|
|
2660
|
-
};
|
|
2661
|
-
const kinds = {};
|
|
2662
|
-
for (const e of sorted) {
|
|
2663
|
-
inc(confidenceBreakdown, e.confidence);
|
|
2664
|
-
kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
|
|
2665
|
-
}
|
|
2666
|
-
const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
|
|
2667
|
-
const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
|
|
2668
|
-
const status = computeRunStatus(sorted);
|
|
2669
|
-
const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
|
|
2670
|
-
const name = sorted.find((e) => e.kind === "RUN")?.name;
|
|
2671
|
-
out.push({
|
|
2672
|
-
runId,
|
|
2673
|
-
name,
|
|
2674
|
-
status,
|
|
2675
|
-
startedAt,
|
|
2676
|
-
endedAt: status === "running" ? void 0 : endedAt,
|
|
2677
|
-
durationMs,
|
|
2678
|
-
children: roots,
|
|
2679
|
-
metadata: {
|
|
2680
|
-
totalEvents: sorted.length,
|
|
2681
|
-
confidenceBreakdown,
|
|
2682
|
-
kinds
|
|
2683
|
-
}
|
|
2684
|
-
});
|
|
2685
|
-
}
|
|
2686
|
-
out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
|
|
2687
|
-
return out;
|
|
2688
|
-
}
|
|
2689
|
-
};
|
|
2690
|
-
|
|
2691
2125
|
// packages/core/src/persisted/tree-bridge.ts
|
|
2692
2126
|
function persistedInspectEventsToRunTrees(events, options) {
|
|
2693
2127
|
const inspectEvents = persistedInspectEventsToInspectEvents(events, {
|
|
@@ -2695,6 +2129,8 @@ function persistedInspectEventsToRunTrees(events, options) {
|
|
|
2695
2129
|
});
|
|
2696
2130
|
return new TreeBuilder().build(inspectEvents);
|
|
2697
2131
|
}
|
|
2132
|
+
|
|
2133
|
+
// packages/core/src/readers/index.ts
|
|
2698
2134
|
var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
|
|
2699
2135
|
var MIN_DETECTION_CONFIDENCE = 0.5;
|
|
2700
2136
|
var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
|
|
@@ -2767,591 +2203,1178 @@ var TraceReadError = class extends Error {
|
|
|
2767
2203
|
this.code = code;
|
|
2768
2204
|
this.warnings = warnings;
|
|
2769
2205
|
}
|
|
2770
|
-
};
|
|
2771
|
-
function normalizeCandidate(reader, candidate) {
|
|
2772
|
-
const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
|
|
2773
|
-
return {
|
|
2774
|
-
...candidate,
|
|
2775
|
-
format: candidate.format || reader.format,
|
|
2776
|
-
confidence,
|
|
2777
|
-
readerName: candidate.readerName ?? reader.name
|
|
2778
|
-
};
|
|
2206
|
+
};
|
|
2207
|
+
function normalizeCandidate(reader, candidate) {
|
|
2208
|
+
const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
|
|
2209
|
+
return {
|
|
2210
|
+
...candidate,
|
|
2211
|
+
format: candidate.format || reader.format,
|
|
2212
|
+
confidence,
|
|
2213
|
+
readerName: candidate.readerName ?? reader.name
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
function sortCandidates(candidates) {
|
|
2217
|
+
return [...candidates].sort((a, b) => {
|
|
2218
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
2219
|
+
return a.format.localeCompare(b.format);
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
function collectWarnings(candidates) {
|
|
2223
|
+
return candidates.flatMap((candidate) => candidate.warnings ?? []);
|
|
2224
|
+
}
|
|
2225
|
+
function dedupeWarnings(warnings) {
|
|
2226
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2227
|
+
const out = [];
|
|
2228
|
+
for (const warning of warnings) {
|
|
2229
|
+
const key = [
|
|
2230
|
+
warning.code,
|
|
2231
|
+
warning.message,
|
|
2232
|
+
warning.severity ?? "",
|
|
2233
|
+
warning.sourceFile ?? "",
|
|
2234
|
+
warning.line ?? "",
|
|
2235
|
+
warning.field ?? ""
|
|
2236
|
+
].join("\0");
|
|
2237
|
+
if (seen.has(key)) continue;
|
|
2238
|
+
seen.add(key);
|
|
2239
|
+
out.push(warning);
|
|
2240
|
+
}
|
|
2241
|
+
return out;
|
|
2242
|
+
}
|
|
2243
|
+
function attachSingleSourceFile(warnings, resolved) {
|
|
2244
|
+
if (resolved.sourceFiles.length !== 1) return [...warnings];
|
|
2245
|
+
const [sourceFile] = resolved.sourceFiles;
|
|
2246
|
+
return warnings.map((warning) => ({
|
|
2247
|
+
...warning,
|
|
2248
|
+
sourceFile: warning.sourceFile ?? sourceFile
|
|
2249
|
+
}));
|
|
2250
|
+
}
|
|
2251
|
+
function findReaderByFormat(format, readers) {
|
|
2252
|
+
return readers.find((reader) => reader.format === format);
|
|
2253
|
+
}
|
|
2254
|
+
async function jsonlFilesInDirectory(dirPath) {
|
|
2255
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
2256
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
2257
|
+
}
|
|
2258
|
+
async function resolveInput(input) {
|
|
2259
|
+
const cached = resolvedInputCache.get(input);
|
|
2260
|
+
if (cached) return cached;
|
|
2261
|
+
const promise = resolveInputUncached(input);
|
|
2262
|
+
resolvedInputCache.set(input, promise);
|
|
2263
|
+
return promise;
|
|
2264
|
+
}
|
|
2265
|
+
function assertInputWithinBounds(content, sourceFile) {
|
|
2266
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
2267
|
+
if (bytes <= DEFAULT_MAX_TRACE_INPUT_BYTES) return;
|
|
2268
|
+
throw new TraceReadError("unsupported_format", "Trace input exceeds the local reader size limit.", [
|
|
2269
|
+
{
|
|
2270
|
+
code: "input_too_large",
|
|
2271
|
+
message: `Trace input is ${bytes} bytes; max is ${DEFAULT_MAX_TRACE_INPUT_BYTES} bytes.`,
|
|
2272
|
+
severity: "error",
|
|
2273
|
+
...sourceFile !== void 0 ? { sourceFile } : {}
|
|
2274
|
+
}
|
|
2275
|
+
]);
|
|
2276
|
+
}
|
|
2277
|
+
async function resolveInputUncached(input) {
|
|
2278
|
+
if (input.type === "string") {
|
|
2279
|
+
assertInputWithinBounds(input.content);
|
|
2280
|
+
return { content: input.content, sourceFiles: [] };
|
|
2281
|
+
}
|
|
2282
|
+
if (input.type === "buffer") {
|
|
2283
|
+
const content = input.content.toString("utf-8");
|
|
2284
|
+
assertInputWithinBounds(content);
|
|
2285
|
+
return { content, sourceFiles: [] };
|
|
2286
|
+
}
|
|
2287
|
+
if (input.type === "file") {
|
|
2288
|
+
const content = await readFile(input.path, "utf-8");
|
|
2289
|
+
assertInputWithinBounds(content, input.path);
|
|
2290
|
+
return { content, sourceFiles: [input.path] };
|
|
2291
|
+
}
|
|
2292
|
+
if (input.type === "directory") {
|
|
2293
|
+
const files = await jsonlFilesInDirectory(input.path);
|
|
2294
|
+
const parts = await Promise.all(
|
|
2295
|
+
files.map(async (file) => (await readFile(file, "utf-8")).trimEnd())
|
|
2296
|
+
);
|
|
2297
|
+
const content = parts.filter((part) => part.trim() !== "").join("\n");
|
|
2298
|
+
assertInputWithinBounds(content, input.path);
|
|
2299
|
+
return {
|
|
2300
|
+
content,
|
|
2301
|
+
sourceFiles: files
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
return void 0;
|
|
2305
|
+
}
|
|
2306
|
+
function detectJsonlFormat(content) {
|
|
2307
|
+
let saw01 = false;
|
|
2308
|
+
let saw02 = false;
|
|
2309
|
+
let saw10 = false;
|
|
2310
|
+
let validRows = 0;
|
|
2311
|
+
let invalidJsonRows = 0;
|
|
2312
|
+
let unknownSchemaRows = 0;
|
|
2313
|
+
let firstInvalidJsonLine;
|
|
2314
|
+
let firstUnknownSchemaLine;
|
|
2315
|
+
let lineNumber = 0;
|
|
2316
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2317
|
+
lineNumber += 1;
|
|
2318
|
+
const trimmed = line.trim();
|
|
2319
|
+
if (trimmed === "") continue;
|
|
2320
|
+
let parsed;
|
|
2321
|
+
try {
|
|
2322
|
+
parsed = JSON.parse(trimmed);
|
|
2323
|
+
} catch {
|
|
2324
|
+
invalidJsonRows += 1;
|
|
2325
|
+
firstInvalidJsonLine ??= lineNumber;
|
|
2326
|
+
continue;
|
|
2327
|
+
}
|
|
2328
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
|
|
2329
|
+
const version = parsed.schemaVersion;
|
|
2330
|
+
if (version === "0.1") {
|
|
2331
|
+
saw01 = true;
|
|
2332
|
+
validRows += 1;
|
|
2333
|
+
continue;
|
|
2334
|
+
}
|
|
2335
|
+
if (version === "0.2") {
|
|
2336
|
+
saw02 = true;
|
|
2337
|
+
validRows += 1;
|
|
2338
|
+
continue;
|
|
2339
|
+
}
|
|
2340
|
+
if (version === "1.0") {
|
|
2341
|
+
saw10 = true;
|
|
2342
|
+
validRows += 1;
|
|
2343
|
+
continue;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
unknownSchemaRows += 1;
|
|
2347
|
+
firstUnknownSchemaLine ??= lineNumber;
|
|
2348
|
+
}
|
|
2349
|
+
const warnings = [];
|
|
2350
|
+
if (invalidJsonRows > 0) {
|
|
2351
|
+
warnings.push({
|
|
2352
|
+
code: "invalid_jsonl_rows",
|
|
2353
|
+
message: `Skipped ${invalidJsonRows} invalid JSONL row(s) during format detection.`,
|
|
2354
|
+
severity: "warning",
|
|
2355
|
+
...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
if (unknownSchemaRows > 0) {
|
|
2359
|
+
warnings.push({
|
|
2360
|
+
code: "unknown_schema_rows",
|
|
2361
|
+
message: `Skipped ${unknownSchemaRows} row(s) with unknown schemaVersion during format detection.`,
|
|
2362
|
+
severity: "warning",
|
|
2363
|
+
...firstUnknownSchemaLine !== void 0 ? { line: firstUnknownSchemaLine } : {}
|
|
2364
|
+
});
|
|
2365
|
+
}
|
|
2366
|
+
let format = "empty";
|
|
2367
|
+
const seenFormats = [saw01, saw02, saw10].filter(Boolean).length;
|
|
2368
|
+
if (seenFormats > 1) format = "mixed";
|
|
2369
|
+
else if (saw01) format = "0.1";
|
|
2370
|
+
else if (saw02) format = "0.2";
|
|
2371
|
+
else if (saw10) format = "1.0";
|
|
2372
|
+
return { format, validRows, warnings };
|
|
2779
2373
|
}
|
|
2780
|
-
function
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2374
|
+
function agentInspectFormatLabel(format) {
|
|
2375
|
+
switch (format) {
|
|
2376
|
+
case "0.1":
|
|
2377
|
+
return "agent-inspect-v0.1-jsonl";
|
|
2378
|
+
case "0.2":
|
|
2379
|
+
return "agent-inspect-v0.2-jsonl";
|
|
2380
|
+
case "1.0":
|
|
2381
|
+
return "agent-inspect-v1.0-jsonl";
|
|
2382
|
+
case "mixed":
|
|
2383
|
+
return "agent-inspect-mixed-jsonl";
|
|
2384
|
+
default:
|
|
2385
|
+
return "agent-inspect-jsonl";
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
function persistedEventsForParsedTrace(parsed) {
|
|
2389
|
+
if ((parsed.format === "0.2" || parsed.format === "1.0") && parsed.persisted.length > 0) {
|
|
2390
|
+
return [...parsed.persisted];
|
|
2391
|
+
}
|
|
2392
|
+
if (parsed.format === "mixed" && parsed.rows.length > 0) {
|
|
2393
|
+
return parsed.rows.map((row, index) => {
|
|
2394
|
+
if (row.format === "0.2" || row.format === "1.0") return row.event;
|
|
2395
|
+
return traceEventToPersistedInspectEvent(row.event, {
|
|
2396
|
+
eventIndex: index,
|
|
2397
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
2398
|
+
});
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
return traceEventsToPersistedInspectEvents(parsed.events, {
|
|
2402
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
2784
2403
|
});
|
|
2785
2404
|
}
|
|
2786
|
-
function
|
|
2787
|
-
return
|
|
2405
|
+
function isRecord6(value) {
|
|
2406
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2788
2407
|
}
|
|
2789
|
-
function
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
warning.severity ?? "",
|
|
2797
|
-
warning.sourceFile ?? "",
|
|
2798
|
-
warning.line ?? "",
|
|
2799
|
-
warning.field ?? ""
|
|
2800
|
-
].join("\0");
|
|
2801
|
-
if (seen.has(key)) continue;
|
|
2802
|
-
seen.add(key);
|
|
2803
|
-
out.push(warning);
|
|
2408
|
+
function isNonEmptyString3(value) {
|
|
2409
|
+
return typeof value === "string" && value.trim() !== "";
|
|
2410
|
+
}
|
|
2411
|
+
function readStringField(record, keys) {
|
|
2412
|
+
for (const key of keys) {
|
|
2413
|
+
const value = record[key];
|
|
2414
|
+
if (isNonEmptyString3(value)) return value;
|
|
2804
2415
|
}
|
|
2805
|
-
return
|
|
2416
|
+
return void 0;
|
|
2806
2417
|
}
|
|
2807
|
-
function
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
return warnings.map((warning) => ({
|
|
2811
|
-
...warning,
|
|
2812
|
-
sourceFile: warning.sourceFile ?? sourceFile
|
|
2813
|
-
}));
|
|
2418
|
+
function readRecordField(record, key) {
|
|
2419
|
+
const value = record[key];
|
|
2420
|
+
return isRecord6(value) ? value : void 0;
|
|
2814
2421
|
}
|
|
2815
|
-
function
|
|
2816
|
-
return
|
|
2422
|
+
function parseJsonDocument(content) {
|
|
2423
|
+
return JSON.parse(content);
|
|
2817
2424
|
}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2425
|
+
function looksLikeOpenInferenceSpan(value) {
|
|
2426
|
+
if (!isRecord6(value)) return false;
|
|
2427
|
+
const attributes = readRecordField(value, "attributes");
|
|
2428
|
+
return readStringField(value, ["trace_id", "traceId"]) !== void 0 && readStringField(value, ["span_id", "spanId"]) !== void 0 && (readStringField(value, ["name"]) !== void 0 || attributes?.["openinference.span.kind"] !== void 0);
|
|
2821
2429
|
}
|
|
2822
|
-
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2430
|
+
function extractOpenInferenceDocument(root) {
|
|
2431
|
+
const warnings = [];
|
|
2432
|
+
const unsupportedFields = [];
|
|
2433
|
+
if (Array.isArray(root)) {
|
|
2434
|
+
const spans = root.filter(looksLikeOpenInferenceSpan);
|
|
2435
|
+
if (spans.length === 0) return void 0;
|
|
2436
|
+
if (spans.length !== root.length) {
|
|
2437
|
+
warnings.push({
|
|
2438
|
+
code: "openinference_skipped_items",
|
|
2439
|
+
message: "Skipped non-span item(s) in OpenInference span array.",
|
|
2440
|
+
severity: "warning"
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
return {
|
|
2444
|
+
spans,
|
|
2445
|
+
confidence: 0.82,
|
|
2446
|
+
description: "OpenInference span array",
|
|
2447
|
+
warnings,
|
|
2448
|
+
unsupportedFields
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
if (!isRecord6(root)) return void 0;
|
|
2452
|
+
const rootFormat = root.format;
|
|
2453
|
+
const rootCompatibility = root.compatibility;
|
|
2454
|
+
const version = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
|
|
2455
|
+
if (Array.isArray(root.spans)) {
|
|
2456
|
+
const spans = root.spans.filter(looksLikeOpenInferenceSpan);
|
|
2457
|
+
if (spans.length === 0 && (rootFormat === "openinference" || rootCompatibility === "openinference-compatible")) {
|
|
2458
|
+
warnings.push({
|
|
2459
|
+
code: "openinference_no_valid_spans",
|
|
2460
|
+
message: "OpenInference document did not contain any valid spans.",
|
|
2461
|
+
severity: "error"
|
|
2462
|
+
});
|
|
2463
|
+
return {
|
|
2464
|
+
spans,
|
|
2465
|
+
confidence: 0.7,
|
|
2466
|
+
description: "Malformed OpenInference document",
|
|
2467
|
+
version,
|
|
2468
|
+
warnings,
|
|
2469
|
+
unsupportedFields
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
if (spans.length === 0) return void 0;
|
|
2473
|
+
if (spans.length !== root.spans.length) {
|
|
2474
|
+
warnings.push({
|
|
2475
|
+
code: "openinference_skipped_spans",
|
|
2476
|
+
message: "Skipped invalid OpenInference span item(s).",
|
|
2477
|
+
severity: "warning"
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2480
|
+
return {
|
|
2481
|
+
spans,
|
|
2482
|
+
confidence: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? 0.9 : 0.84,
|
|
2483
|
+
description: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? "OpenInference document" : "OpenInference spans document",
|
|
2484
|
+
version,
|
|
2485
|
+
warnings,
|
|
2486
|
+
unsupportedFields
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
if (Array.isArray(root.data)) {
|
|
2490
|
+
const spans = root.data.filter(looksLikeOpenInferenceSpan);
|
|
2491
|
+
if (spans.length === 0) return void 0;
|
|
2492
|
+
if (spans.length !== root.data.length) {
|
|
2493
|
+
warnings.push({
|
|
2494
|
+
code: "openinference_skipped_data_items",
|
|
2495
|
+
message: "Skipped non-span item(s) in OpenInference data array.",
|
|
2496
|
+
severity: "warning"
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
return {
|
|
2500
|
+
spans,
|
|
2501
|
+
confidence: 0.8,
|
|
2502
|
+
description: "OpenInference data document",
|
|
2503
|
+
version,
|
|
2504
|
+
warnings,
|
|
2505
|
+
unsupportedFields
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
if (looksLikeOpenInferenceSpan(root)) {
|
|
2509
|
+
return {
|
|
2510
|
+
spans: [root],
|
|
2511
|
+
confidence: 0.76,
|
|
2512
|
+
description: "OpenInference single span",
|
|
2513
|
+
version,
|
|
2514
|
+
warnings,
|
|
2515
|
+
unsupportedFields
|
|
2516
|
+
};
|
|
2517
|
+
}
|
|
2518
|
+
if (rootFormat === "openinference" || rootCompatibility === "openinference-compatible") {
|
|
2519
|
+
warnings.push({
|
|
2520
|
+
code: "openinference_missing_spans",
|
|
2521
|
+
message: "OpenInference document is missing a spans array.",
|
|
2522
|
+
severity: "error"
|
|
2523
|
+
});
|
|
2524
|
+
return {
|
|
2525
|
+
spans: [],
|
|
2526
|
+
confidence: 0.7,
|
|
2527
|
+
description: "Malformed OpenInference document",
|
|
2528
|
+
version,
|
|
2529
|
+
warnings,
|
|
2530
|
+
unsupportedFields
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
return void 0;
|
|
2534
|
+
}
|
|
2535
|
+
function parseUnixNanoToIso(value) {
|
|
2536
|
+
if (typeof value === "bigint" && value >= 0n) {
|
|
2537
|
+
return new Date(Number(value / 1000000n)).toISOString();
|
|
2538
|
+
}
|
|
2539
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
2540
|
+
return new Date(Math.floor(value / 1e6)).toISOString();
|
|
2541
|
+
}
|
|
2542
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
2543
|
+
return new Date(Number(BigInt(value) / 1000000n)).toISOString();
|
|
2544
|
+
}
|
|
2545
|
+
return void 0;
|
|
2546
|
+
}
|
|
2547
|
+
function parseIsoTime(value) {
|
|
2548
|
+
if (!isNonEmptyString3(value)) return void 0;
|
|
2549
|
+
const ms = Date.parse(value);
|
|
2550
|
+
if (!Number.isFinite(ms)) return void 0;
|
|
2551
|
+
return new Date(ms).toISOString();
|
|
2552
|
+
}
|
|
2553
|
+
function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
|
|
2554
|
+
for (const key of nanoKeys) {
|
|
2555
|
+
const iso = parseUnixNanoToIso(span[key]);
|
|
2556
|
+
if (iso !== void 0) return iso;
|
|
2557
|
+
}
|
|
2558
|
+
for (const key of isoKeys) {
|
|
2559
|
+
const iso = parseIsoTime(span[key]);
|
|
2560
|
+
if (iso !== void 0) return iso;
|
|
2561
|
+
}
|
|
2562
|
+
return void 0;
|
|
2828
2563
|
}
|
|
2829
|
-
function
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
...sourceFile !== void 0 ? { sourceFile } : {}
|
|
2838
|
-
}
|
|
2839
|
-
]);
|
|
2564
|
+
function durationBetweenIso(startedAt, endedAt) {
|
|
2565
|
+
if (startedAt === void 0 || endedAt === void 0) return void 0;
|
|
2566
|
+
const startMs = Date.parse(startedAt);
|
|
2567
|
+
const endMs = Date.parse(endedAt);
|
|
2568
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) {
|
|
2569
|
+
return void 0;
|
|
2570
|
+
}
|
|
2571
|
+
return endMs - startMs;
|
|
2840
2572
|
}
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2573
|
+
function isSensitiveOpenInferenceAttribute(key) {
|
|
2574
|
+
return OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS.some(
|
|
2575
|
+
(sensitiveKey) => key === sensitiveKey || key.startsWith(`${sensitiveKey}.`) || key.endsWith(".message.content") || key.endsWith(".document.content")
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
function summarizeAttributeValue(value) {
|
|
2579
|
+
if (typeof value === "string") {
|
|
2580
|
+
return { type: "string", length: value.length };
|
|
2845
2581
|
}
|
|
2846
|
-
if (
|
|
2847
|
-
|
|
2848
|
-
assertInputWithinBounds(content);
|
|
2849
|
-
return { content, sourceFiles: [] };
|
|
2582
|
+
if (typeof value === "number") {
|
|
2583
|
+
return { type: "number", finite: Number.isFinite(value) };
|
|
2850
2584
|
}
|
|
2851
|
-
if (
|
|
2852
|
-
|
|
2853
|
-
assertInputWithinBounds(content, input.path);
|
|
2854
|
-
return { content, sourceFiles: [input.path] };
|
|
2585
|
+
if (typeof value === "boolean") {
|
|
2586
|
+
return { type: "boolean" };
|
|
2855
2587
|
}
|
|
2856
|
-
if (
|
|
2857
|
-
|
|
2858
|
-
const parts = await Promise.all(
|
|
2859
|
-
files.map(async (file) => (await readFile(file, "utf-8")).trimEnd())
|
|
2860
|
-
);
|
|
2861
|
-
const content = parts.filter((part) => part.trim() !== "").join("\n");
|
|
2862
|
-
assertInputWithinBounds(content, input.path);
|
|
2863
|
-
return {
|
|
2864
|
-
content,
|
|
2865
|
-
sourceFiles: files
|
|
2866
|
-
};
|
|
2588
|
+
if (Array.isArray(value)) {
|
|
2589
|
+
return { type: "array", length: value.length };
|
|
2867
2590
|
}
|
|
2868
|
-
|
|
2591
|
+
if (isRecord6(value)) {
|
|
2592
|
+
return { type: "object", keyCount: Object.keys(value).length };
|
|
2593
|
+
}
|
|
2594
|
+
if (value === null) {
|
|
2595
|
+
return { type: "null" };
|
|
2596
|
+
}
|
|
2597
|
+
return { type: typeof value };
|
|
2869
2598
|
}
|
|
2870
|
-
function
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
for (const line of content.split(/\r?\n/)) {
|
|
2881
|
-
lineNumber += 1;
|
|
2882
|
-
const trimmed = line.trim();
|
|
2883
|
-
if (trimmed === "") continue;
|
|
2884
|
-
let parsed;
|
|
2885
|
-
try {
|
|
2886
|
-
parsed = JSON.parse(trimmed);
|
|
2887
|
-
} catch {
|
|
2888
|
-
invalidJsonRows += 1;
|
|
2889
|
-
firstInvalidJsonLine ??= lineNumber;
|
|
2599
|
+
function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
|
|
2600
|
+
const out = {};
|
|
2601
|
+
const warnings = [];
|
|
2602
|
+
const unsupportedFields = [];
|
|
2603
|
+
const summarizedKeys = [];
|
|
2604
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
2605
|
+
if (isSensitiveOpenInferenceAttribute(key)) {
|
|
2606
|
+
summarizedKeys.push(key);
|
|
2607
|
+
out[`${key}.summary`] = summarizeAttributeValue(value);
|
|
2608
|
+
unsupportedFields.push(`${pathPrefix}.attributes.${key}`);
|
|
2890
2609
|
continue;
|
|
2891
2610
|
}
|
|
2892
|
-
|
|
2893
|
-
const version = parsed.schemaVersion;
|
|
2894
|
-
if (version === "0.1") {
|
|
2895
|
-
saw01 = true;
|
|
2896
|
-
validRows += 1;
|
|
2897
|
-
continue;
|
|
2898
|
-
}
|
|
2899
|
-
if (version === "0.2") {
|
|
2900
|
-
saw02 = true;
|
|
2901
|
-
validRows += 1;
|
|
2902
|
-
continue;
|
|
2903
|
-
}
|
|
2904
|
-
if (version === "1.0") {
|
|
2905
|
-
saw10 = true;
|
|
2906
|
-
validRows += 1;
|
|
2907
|
-
continue;
|
|
2908
|
-
}
|
|
2909
|
-
}
|
|
2910
|
-
unknownSchemaRows += 1;
|
|
2911
|
-
firstUnknownSchemaLine ??= lineNumber;
|
|
2611
|
+
out[key] = value;
|
|
2912
2612
|
}
|
|
2913
|
-
|
|
2914
|
-
|
|
2613
|
+
if (summarizedKeys.length > 0) {
|
|
2614
|
+
out["openinference.summarized_attributes"] = summarizedKeys;
|
|
2915
2615
|
warnings.push({
|
|
2916
|
-
code: "
|
|
2917
|
-
message:
|
|
2918
|
-
severity: "warning"
|
|
2919
|
-
...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
|
|
2616
|
+
code: "openinference_sensitive_attribute_summarized",
|
|
2617
|
+
message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
|
|
2618
|
+
severity: "warning"
|
|
2920
2619
|
});
|
|
2921
2620
|
}
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
}
|
|
2621
|
+
return { attributes: out, warnings, unsupportedFields };
|
|
2622
|
+
}
|
|
2623
|
+
function mapOpenInferenceKind(span, attributes, pathPrefix) {
|
|
2624
|
+
const warnings = [];
|
|
2625
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
2626
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG" || agentInspectKind === "OUTCOME") {
|
|
2627
|
+
return { kind: agentInspectKind, warnings };
|
|
2628
|
+
}
|
|
2629
|
+
const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
|
|
2630
|
+
const normalized = rawKind?.toUpperCase();
|
|
2631
|
+
switch (normalized) {
|
|
2632
|
+
case "LLM":
|
|
2633
|
+
return { kind: "LLM", warnings };
|
|
2634
|
+
case "TOOL":
|
|
2635
|
+
return { kind: "TOOL", warnings };
|
|
2636
|
+
case "CHAIN":
|
|
2637
|
+
return { kind: "CHAIN", warnings };
|
|
2638
|
+
case "RETRIEVER":
|
|
2639
|
+
return { kind: "RETRIEVER", warnings };
|
|
2640
|
+
case "AGENT":
|
|
2641
|
+
return { kind: "AGENT", warnings };
|
|
2642
|
+
case "EMBEDDING":
|
|
2643
|
+
warnings.push({
|
|
2644
|
+
code: "openinference_kind_semantic_loss",
|
|
2645
|
+
message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
|
|
2646
|
+
severity: "warning",
|
|
2647
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
2648
|
+
});
|
|
2649
|
+
return { kind: "LLM", warnings };
|
|
2650
|
+
case "RERANKER":
|
|
2651
|
+
warnings.push({
|
|
2652
|
+
code: "openinference_kind_semantic_loss",
|
|
2653
|
+
message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
|
|
2654
|
+
severity: "warning",
|
|
2655
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
2656
|
+
});
|
|
2657
|
+
return { kind: "RETRIEVER", warnings };
|
|
2658
|
+
case "UNKNOWN":
|
|
2659
|
+
case void 0:
|
|
2660
|
+
warnings.push({
|
|
2661
|
+
code: "openinference_kind_unknown",
|
|
2662
|
+
message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
|
|
2663
|
+
severity: "warning",
|
|
2664
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
2665
|
+
});
|
|
2666
|
+
return { kind: "LOGIC", warnings };
|
|
2667
|
+
default:
|
|
2668
|
+
warnings.push({
|
|
2669
|
+
code: "openinference_kind_unsupported",
|
|
2670
|
+
message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
|
|
2671
|
+
severity: "warning",
|
|
2672
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
2673
|
+
});
|
|
2674
|
+
return { kind: "LOGIC", warnings };
|
|
2929
2675
|
}
|
|
2930
|
-
let format = "empty";
|
|
2931
|
-
const seenFormats = [saw01, saw02, saw10].filter(Boolean).length;
|
|
2932
|
-
if (seenFormats > 1) format = "mixed";
|
|
2933
|
-
else if (saw01) format = "0.1";
|
|
2934
|
-
else if (saw02) format = "0.2";
|
|
2935
|
-
else if (saw10) format = "1.0";
|
|
2936
|
-
return { format, validRows, warnings };
|
|
2937
2676
|
}
|
|
2938
|
-
function
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2677
|
+
function mapOpenInferenceStatus(status) {
|
|
2678
|
+
if (!isRecord6(status)) return void 0;
|
|
2679
|
+
const rawCode = status.code;
|
|
2680
|
+
if (typeof rawCode !== "string") return void 0;
|
|
2681
|
+
switch (rawCode.toUpperCase()) {
|
|
2682
|
+
case "OK":
|
|
2683
|
+
return "ok";
|
|
2684
|
+
case "ERROR":
|
|
2685
|
+
return "error";
|
|
2686
|
+
case "UNSET":
|
|
2687
|
+
return "unknown";
|
|
2948
2688
|
default:
|
|
2949
|
-
return "
|
|
2689
|
+
return "unknown";
|
|
2950
2690
|
}
|
|
2951
2691
|
}
|
|
2952
|
-
function
|
|
2953
|
-
|
|
2954
|
-
|
|
2692
|
+
function readOpenInferenceTokenUsage(attributes) {
|
|
2693
|
+
const prompt = attributes["llm.token_count.prompt"];
|
|
2694
|
+
const completion = attributes["llm.token_count.completion"];
|
|
2695
|
+
const total = attributes["llm.token_count.total"];
|
|
2696
|
+
const cached = attributes["llm.token_count.prompt_details.cache_read"];
|
|
2697
|
+
const usage = {};
|
|
2698
|
+
if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
|
|
2699
|
+
usage.input = prompt;
|
|
2955
2700
|
}
|
|
2956
|
-
if (
|
|
2957
|
-
|
|
2958
|
-
if (row.format === "0.2" || row.format === "1.0") return row.event;
|
|
2959
|
-
return traceEventToPersistedInspectEvent(row.event, {
|
|
2960
|
-
eventIndex: index,
|
|
2961
|
-
sourceName: "agent-inspect-jsonl-reader"
|
|
2962
|
-
});
|
|
2963
|
-
});
|
|
2701
|
+
if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
|
|
2702
|
+
usage.output = completion;
|
|
2964
2703
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
});
|
|
2968
|
-
}
|
|
2969
|
-
function isRecord6(value) {
|
|
2970
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2971
|
-
}
|
|
2972
|
-
function isNonEmptyString3(value) {
|
|
2973
|
-
return typeof value === "string" && value.trim() !== "";
|
|
2974
|
-
}
|
|
2975
|
-
function readStringField(record, keys) {
|
|
2976
|
-
for (const key of keys) {
|
|
2977
|
-
const value = record[key];
|
|
2978
|
-
if (isNonEmptyString3(value)) return value;
|
|
2704
|
+
if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
|
|
2705
|
+
usage.total = total;
|
|
2979
2706
|
}
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
}
|
|
2986
|
-
|
|
2987
|
-
return JSON.parse(content);
|
|
2707
|
+
if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
|
|
2708
|
+
usage.cached = cached;
|
|
2709
|
+
}
|
|
2710
|
+
if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
|
|
2711
|
+
usage.total = usage.input + usage.output;
|
|
2712
|
+
}
|
|
2713
|
+
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
2988
2714
|
}
|
|
2989
|
-
function
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2715
|
+
function readOpenInferenceConfidence(attributes) {
|
|
2716
|
+
const confidence = attributes["agent_inspect.confidence"];
|
|
2717
|
+
if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
|
|
2718
|
+
return confidence;
|
|
2719
|
+
}
|
|
2720
|
+
return "correlated";
|
|
2993
2721
|
}
|
|
2994
|
-
function
|
|
2722
|
+
function mapOpenInferenceSpan(span, index, version) {
|
|
2723
|
+
const pathPrefix = `spans[${index}]`;
|
|
2995
2724
|
const warnings = [];
|
|
2996
2725
|
const unsupportedFields = [];
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
2726
|
+
const rawAttributes = readRecordField(span, "attributes") ?? {};
|
|
2727
|
+
const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
|
|
2728
|
+
warnings.push(...sanitized.warnings);
|
|
2729
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
2730
|
+
const attributes = { ...sanitized.attributes };
|
|
2731
|
+
for (const [key, value] of Object.entries(span)) {
|
|
2732
|
+
if (OPENINFERENCE_SPAN_KEYS.has(key)) continue;
|
|
2733
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
2734
|
+
if (value === null || typeof value !== "object") {
|
|
2735
|
+
attributes[`openinference.${key}`] = value;
|
|
2736
|
+
} else {
|
|
2737
|
+
attributes[`openinference.${key}.summary`] = summarizeAttributeValue(value);
|
|
3001
2738
|
warnings.push({
|
|
3002
|
-
code: "
|
|
3003
|
-
message:
|
|
3004
|
-
severity: "warning"
|
|
2739
|
+
code: "openinference_unsupported_field_summarized",
|
|
2740
|
+
message: `Unsupported OpenInference span field "${key}" was summarized.`,
|
|
2741
|
+
severity: "warning",
|
|
2742
|
+
field: `${pathPrefix}.${key}`
|
|
3005
2743
|
});
|
|
3006
2744
|
}
|
|
3007
|
-
return {
|
|
3008
|
-
spans,
|
|
3009
|
-
confidence: 0.82,
|
|
3010
|
-
description: "OpenInference span array",
|
|
3011
|
-
warnings,
|
|
3012
|
-
unsupportedFields
|
|
3013
|
-
};
|
|
3014
2745
|
}
|
|
3015
|
-
|
|
3016
|
-
const
|
|
3017
|
-
const
|
|
3018
|
-
const
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
2746
|
+
const traceId = readStringField(span, ["trace_id", "traceId"]) ?? `trace-${index}`;
|
|
2747
|
+
const spanId = readStringField(span, ["span_id", "spanId"]) ?? `span-${index}`;
|
|
2748
|
+
const parentSpanId = readStringField(span, ["parent_span_id", "parentSpanId"]);
|
|
2749
|
+
const name = readStringField(span, ["name"]) ?? spanId;
|
|
2750
|
+
const startedAt = readOpenInferenceTimestamp(
|
|
2751
|
+
span,
|
|
2752
|
+
["start_time_unix_nano", "startTimeUnixNano"],
|
|
2753
|
+
["start_time", "startTime"]
|
|
2754
|
+
);
|
|
2755
|
+
const endedAt = readOpenInferenceTimestamp(
|
|
2756
|
+
span,
|
|
2757
|
+
["end_time_unix_nano", "endTimeUnixNano"],
|
|
2758
|
+
["end_time", "endTime"]
|
|
2759
|
+
);
|
|
2760
|
+
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
2761
|
+
if (startedAt === void 0) {
|
|
2762
|
+
warnings.push({
|
|
2763
|
+
code: "openinference_missing_start_time",
|
|
2764
|
+
message: "OpenInference span is missing a valid start time; using Unix epoch.",
|
|
2765
|
+
severity: "warning",
|
|
2766
|
+
field: `${pathPrefix}.start_time_unix_nano`
|
|
2767
|
+
});
|
|
2768
|
+
unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
|
|
2769
|
+
}
|
|
2770
|
+
const { kind, warnings: kindWarnings } = mapOpenInferenceKind(
|
|
2771
|
+
span,
|
|
2772
|
+
rawAttributes,
|
|
2773
|
+
pathPrefix
|
|
2774
|
+
);
|
|
2775
|
+
warnings.push(...kindWarnings);
|
|
2776
|
+
const status = mapOpenInferenceStatus(span.status);
|
|
2777
|
+
const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
|
|
2778
|
+
const errorMessage = isRecord6(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
2779
|
+
const event = {
|
|
2780
|
+
schemaVersion: "0.2",
|
|
2781
|
+
eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
|
|
2782
|
+
runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
|
|
2783
|
+
kind,
|
|
2784
|
+
name,
|
|
2785
|
+
timestamp,
|
|
2786
|
+
confidence: readOpenInferenceConfidence(rawAttributes),
|
|
2787
|
+
source: {
|
|
2788
|
+
type: "otel",
|
|
2789
|
+
name: "openinference",
|
|
2790
|
+
...version !== void 0 ? { version } : {}
|
|
2791
|
+
},
|
|
2792
|
+
attributes,
|
|
2793
|
+
trace: {
|
|
2794
|
+
traceId,
|
|
2795
|
+
spanId,
|
|
2796
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
3043
2797
|
}
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
2798
|
+
};
|
|
2799
|
+
if (status !== void 0) {
|
|
2800
|
+
event.status = status;
|
|
2801
|
+
}
|
|
2802
|
+
if (startedAt !== void 0) {
|
|
2803
|
+
event.startedAt = startedAt;
|
|
2804
|
+
}
|
|
2805
|
+
if (endedAt !== void 0) {
|
|
2806
|
+
event.endedAt = endedAt;
|
|
2807
|
+
}
|
|
2808
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
2809
|
+
if (durationMs !== void 0) {
|
|
2810
|
+
event.durationMs = durationMs;
|
|
2811
|
+
}
|
|
2812
|
+
if (tokenUsage !== void 0) {
|
|
2813
|
+
event.tokenUsage = tokenUsage;
|
|
2814
|
+
}
|
|
2815
|
+
if (status === "error") {
|
|
2816
|
+
event.error = {
|
|
2817
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
|
|
3051
2818
|
};
|
|
3052
2819
|
}
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
2820
|
+
return {
|
|
2821
|
+
event,
|
|
2822
|
+
warnings,
|
|
2823
|
+
unsupportedFields,
|
|
2824
|
+
spanId,
|
|
2825
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
2826
|
+
};
|
|
2827
|
+
}
|
|
2828
|
+
function mapOpenInferenceEvents(document) {
|
|
2829
|
+
const mapped = document.spans.map(
|
|
2830
|
+
(span, index) => mapOpenInferenceSpan(span, index, document.version)
|
|
2831
|
+
);
|
|
2832
|
+
const spanIdToEventId = new Map(
|
|
2833
|
+
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
2834
|
+
);
|
|
2835
|
+
for (const span of mapped) {
|
|
2836
|
+
if (span.parentSpanId === void 0) continue;
|
|
2837
|
+
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
2838
|
+
}
|
|
2839
|
+
return {
|
|
2840
|
+
events: mapped.map((span) => span.event),
|
|
2841
|
+
warnings: mapped.flatMap((span) => span.warnings),
|
|
2842
|
+
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
var openInferenceJsonReader = {
|
|
2846
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
2847
|
+
name: "OpenInference JSON",
|
|
2848
|
+
async detect(input) {
|
|
2849
|
+
const resolved = await resolveInput(input);
|
|
2850
|
+
if (!resolved) return void 0;
|
|
2851
|
+
let parsed;
|
|
2852
|
+
try {
|
|
2853
|
+
parsed = parseJsonDocument(resolved.content);
|
|
2854
|
+
} catch {
|
|
2855
|
+
return void 0;
|
|
3062
2856
|
}
|
|
2857
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
2858
|
+
if (!document) return void 0;
|
|
3063
2859
|
return {
|
|
3064
|
-
|
|
3065
|
-
confidence:
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
warnings,
|
|
3069
|
-
unsupportedFields
|
|
2860
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
2861
|
+
confidence: document.confidence,
|
|
2862
|
+
readerName: "OpenInference JSON",
|
|
2863
|
+
description: document.description,
|
|
2864
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
3070
2865
|
};
|
|
3071
|
-
}
|
|
3072
|
-
|
|
2866
|
+
},
|
|
2867
|
+
async read(input) {
|
|
2868
|
+
const resolved = await resolveInput(input);
|
|
2869
|
+
if (!resolved) {
|
|
2870
|
+
throw new TraceReadError(
|
|
2871
|
+
"unsupported_format",
|
|
2872
|
+
"OpenInference JSON reader requires file, string, or buffer input."
|
|
2873
|
+
);
|
|
2874
|
+
}
|
|
2875
|
+
let parsed;
|
|
2876
|
+
try {
|
|
2877
|
+
parsed = parseJsonDocument(resolved.content);
|
|
2878
|
+
} catch {
|
|
2879
|
+
throw new TraceReadError("unsupported_format", "OpenInference JSON input is not valid JSON.", [
|
|
2880
|
+
{
|
|
2881
|
+
code: "openinference_invalid_json",
|
|
2882
|
+
message: "OpenInference JSON reader could not parse the input as JSON.",
|
|
2883
|
+
severity: "error"
|
|
2884
|
+
}
|
|
2885
|
+
]);
|
|
2886
|
+
}
|
|
2887
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
2888
|
+
if (!document || document.spans.length === 0) {
|
|
2889
|
+
throw new TraceReadError(
|
|
2890
|
+
"unsupported_format",
|
|
2891
|
+
"No valid OpenInference spans found.",
|
|
2892
|
+
attachSingleSourceFile(
|
|
2893
|
+
document?.warnings ?? [
|
|
2894
|
+
{
|
|
2895
|
+
code: "openinference_no_valid_spans",
|
|
2896
|
+
message: "OpenInference JSON input did not contain valid spans.",
|
|
2897
|
+
severity: "error"
|
|
2898
|
+
}
|
|
2899
|
+
],
|
|
2900
|
+
resolved
|
|
2901
|
+
)
|
|
2902
|
+
);
|
|
2903
|
+
}
|
|
2904
|
+
const mapped = mapOpenInferenceEvents(document);
|
|
2905
|
+
const warnings = attachSingleSourceFile(
|
|
2906
|
+
[...document.warnings, ...mapped.warnings],
|
|
2907
|
+
resolved
|
|
2908
|
+
);
|
|
2909
|
+
const unsupportedFields = [
|
|
2910
|
+
...document.unsupportedFields,
|
|
2911
|
+
...mapped.unsupportedFields
|
|
2912
|
+
].sort((a, b) => a.localeCompare(b));
|
|
3073
2913
|
return {
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
version,
|
|
2914
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
2915
|
+
events: mapped.events,
|
|
2916
|
+
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
3078
2917
|
warnings,
|
|
3079
|
-
unsupportedFields
|
|
2918
|
+
unsupportedFields,
|
|
2919
|
+
sourceFiles: resolved.sourceFiles
|
|
3080
2920
|
};
|
|
3081
2921
|
}
|
|
3082
|
-
|
|
2922
|
+
};
|
|
2923
|
+
function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
2924
|
+
if (!isRecord6(value)) {
|
|
2925
|
+
unsupportedFields.push(field);
|
|
3083
2926
|
warnings.push({
|
|
3084
|
-
code: "
|
|
3085
|
-
message: "
|
|
3086
|
-
severity: "
|
|
2927
|
+
code: "otlp_attribute_value_invalid",
|
|
2928
|
+
message: "OTLP attribute value was not an AnyValue object.",
|
|
2929
|
+
severity: "warning",
|
|
2930
|
+
field
|
|
3087
2931
|
});
|
|
3088
|
-
return
|
|
3089
|
-
spans: [],
|
|
3090
|
-
confidence: 0.7,
|
|
3091
|
-
description: "Malformed OpenInference document",
|
|
3092
|
-
version,
|
|
3093
|
-
warnings,
|
|
3094
|
-
unsupportedFields
|
|
3095
|
-
};
|
|
2932
|
+
return void 0;
|
|
3096
2933
|
}
|
|
3097
|
-
return
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
return new Date(Number(value / 1000000n)).toISOString();
|
|
2934
|
+
if (typeof value.stringValue === "string") return value.stringValue;
|
|
2935
|
+
if (typeof value.boolValue === "boolean") return value.boolValue;
|
|
2936
|
+
if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
|
|
2937
|
+
return value.intValue;
|
|
3102
2938
|
}
|
|
3103
|
-
if (typeof value === "
|
|
3104
|
-
|
|
2939
|
+
if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
|
|
2940
|
+
const n = Number(value.intValue);
|
|
2941
|
+
if (Number.isFinite(n)) return n;
|
|
3105
2942
|
}
|
|
3106
|
-
if (typeof value === "
|
|
3107
|
-
return
|
|
2943
|
+
if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
|
|
2944
|
+
return value.doubleValue;
|
|
3108
2945
|
}
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
const ms = Date.parse(value);
|
|
3114
|
-
if (!Number.isFinite(ms)) return void 0;
|
|
3115
|
-
return new Date(ms).toISOString();
|
|
3116
|
-
}
|
|
3117
|
-
function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
|
|
3118
|
-
for (const key of nanoKeys) {
|
|
3119
|
-
const iso = parseUnixNanoToIso(span[key]);
|
|
3120
|
-
if (iso !== void 0) return iso;
|
|
2946
|
+
if (isRecord6(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
|
|
2947
|
+
return value.arrayValue.values.map(
|
|
2948
|
+
(item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
|
|
2949
|
+
);
|
|
3121
2950
|
}
|
|
3122
|
-
|
|
3123
|
-
const
|
|
3124
|
-
|
|
2951
|
+
if (isRecord6(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
|
|
2952
|
+
const out = {};
|
|
2953
|
+
for (const [index, item] of value.kvlistValue.values.entries()) {
|
|
2954
|
+
if (!isRecord6(item) || typeof item.key !== "string") {
|
|
2955
|
+
unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
|
|
2956
|
+
continue;
|
|
2957
|
+
}
|
|
2958
|
+
out[item.key] = parseOtlpAnyValue(
|
|
2959
|
+
item.value,
|
|
2960
|
+
`${field}.kvlistValue.values[${index}].value`,
|
|
2961
|
+
warnings,
|
|
2962
|
+
unsupportedFields
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2965
|
+
return out;
|
|
3125
2966
|
}
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
2967
|
+
if (typeof value.bytesValue === "string") {
|
|
2968
|
+
unsupportedFields.push(field);
|
|
2969
|
+
warnings.push({
|
|
2970
|
+
code: "otlp_bytes_value_summarized",
|
|
2971
|
+
message: "OTLP bytesValue attribute was summarized instead of decoded.",
|
|
2972
|
+
severity: "warning",
|
|
2973
|
+
field
|
|
2974
|
+
});
|
|
2975
|
+
return { type: "bytes", length: value.bytesValue.length };
|
|
3134
2976
|
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
2977
|
+
unsupportedFields.push(field);
|
|
2978
|
+
warnings.push({
|
|
2979
|
+
code: "otlp_attribute_value_unsupported",
|
|
2980
|
+
message: "OTLP attribute value used an unsupported AnyValue shape.",
|
|
2981
|
+
severity: "warning",
|
|
2982
|
+
field
|
|
2983
|
+
});
|
|
2984
|
+
return void 0;
|
|
3141
2985
|
}
|
|
3142
|
-
function
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
if (
|
|
3147
|
-
return {
|
|
3148
|
-
}
|
|
3149
|
-
if (typeof value === "boolean") {
|
|
3150
|
-
return { type: "boolean" };
|
|
3151
|
-
}
|
|
3152
|
-
if (Array.isArray(value)) {
|
|
3153
|
-
return { type: "array", length: value.length };
|
|
2986
|
+
function parseOtlpAttributes(value, pathPrefix) {
|
|
2987
|
+
const attributes = {};
|
|
2988
|
+
const warnings = [];
|
|
2989
|
+
const unsupportedFields = [];
|
|
2990
|
+
if (value === void 0) {
|
|
2991
|
+
return { attributes, warnings, unsupportedFields };
|
|
3154
2992
|
}
|
|
3155
|
-
if (
|
|
3156
|
-
|
|
2993
|
+
if (!Array.isArray(value)) {
|
|
2994
|
+
unsupportedFields.push(pathPrefix);
|
|
2995
|
+
warnings.push({
|
|
2996
|
+
code: "otlp_attributes_invalid",
|
|
2997
|
+
message: "OTLP attributes field was not an array.",
|
|
2998
|
+
severity: "warning",
|
|
2999
|
+
field: pathPrefix
|
|
3000
|
+
});
|
|
3001
|
+
return { attributes, warnings, unsupportedFields };
|
|
3157
3002
|
}
|
|
3158
|
-
|
|
3159
|
-
|
|
3003
|
+
for (const [index, item] of value.entries()) {
|
|
3004
|
+
const field = `${pathPrefix}[${index}]`;
|
|
3005
|
+
if (!isRecord6(item) || typeof item.key !== "string") {
|
|
3006
|
+
unsupportedFields.push(field);
|
|
3007
|
+
warnings.push({
|
|
3008
|
+
code: "otlp_attribute_invalid",
|
|
3009
|
+
message: "Skipped OTLP attribute without a string key.",
|
|
3010
|
+
severity: "warning",
|
|
3011
|
+
field
|
|
3012
|
+
});
|
|
3013
|
+
continue;
|
|
3014
|
+
}
|
|
3015
|
+
const parsed = parseOtlpAnyValue(
|
|
3016
|
+
item.value,
|
|
3017
|
+
`${field}.value`,
|
|
3018
|
+
warnings,
|
|
3019
|
+
unsupportedFields
|
|
3020
|
+
);
|
|
3021
|
+
if (parsed !== void 0) {
|
|
3022
|
+
attributes[item.key] = parsed;
|
|
3023
|
+
}
|
|
3160
3024
|
}
|
|
3161
|
-
return {
|
|
3025
|
+
return { attributes, warnings, unsupportedFields };
|
|
3162
3026
|
}
|
|
3163
|
-
function
|
|
3164
|
-
|
|
3027
|
+
function looksLikeOtlpSpan(value) {
|
|
3028
|
+
return isRecord6(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
|
|
3029
|
+
}
|
|
3030
|
+
function extractOtlpDocument(root) {
|
|
3031
|
+
if (!isRecord6(root) || !Array.isArray(root.resourceSpans)) return void 0;
|
|
3032
|
+
const spans = [];
|
|
3165
3033
|
const warnings = [];
|
|
3166
3034
|
const unsupportedFields = [];
|
|
3167
|
-
const
|
|
3168
|
-
|
|
3169
|
-
if (
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3035
|
+
for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
|
|
3036
|
+
const resourcePath = `resourceSpans[${resourceIndex}]`;
|
|
3037
|
+
if (!isRecord6(resourceSpan)) {
|
|
3038
|
+
unsupportedFields.push(resourcePath);
|
|
3039
|
+
continue;
|
|
3040
|
+
}
|
|
3041
|
+
const resource = readRecordField(resourceSpan, "resource");
|
|
3042
|
+
const resourceParsed = parseOtlpAttributes(
|
|
3043
|
+
resource?.attributes,
|
|
3044
|
+
`${resourcePath}.resource.attributes`
|
|
3045
|
+
);
|
|
3046
|
+
warnings.push(...resourceParsed.warnings);
|
|
3047
|
+
unsupportedFields.push(...resourceParsed.unsupportedFields);
|
|
3048
|
+
if (!Array.isArray(resourceSpan.scopeSpans)) {
|
|
3049
|
+
unsupportedFields.push(`${resourcePath}.scopeSpans`);
|
|
3050
|
+
warnings.push({
|
|
3051
|
+
code: "otlp_scope_spans_missing",
|
|
3052
|
+
message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
|
|
3053
|
+
severity: "warning",
|
|
3054
|
+
field: `${resourcePath}.scopeSpans`
|
|
3055
|
+
});
|
|
3173
3056
|
continue;
|
|
3174
3057
|
}
|
|
3175
|
-
|
|
3058
|
+
for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
|
|
3059
|
+
const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
|
|
3060
|
+
if (!isRecord6(scopeSpan)) {
|
|
3061
|
+
unsupportedFields.push(scopePath);
|
|
3062
|
+
continue;
|
|
3063
|
+
}
|
|
3064
|
+
const scope = readRecordField(scopeSpan, "scope");
|
|
3065
|
+
const scopeParsed = parseOtlpAttributes(
|
|
3066
|
+
scope?.attributes,
|
|
3067
|
+
`${scopePath}.scope.attributes`
|
|
3068
|
+
);
|
|
3069
|
+
warnings.push(...scopeParsed.warnings);
|
|
3070
|
+
unsupportedFields.push(...scopeParsed.unsupportedFields);
|
|
3071
|
+
if (!Array.isArray(scopeSpan.spans)) {
|
|
3072
|
+
unsupportedFields.push(`${scopePath}.spans`);
|
|
3073
|
+
warnings.push({
|
|
3074
|
+
code: "otlp_spans_missing",
|
|
3075
|
+
message: "OTLP scopeSpans entry did not contain a spans array.",
|
|
3076
|
+
severity: "warning",
|
|
3077
|
+
field: `${scopePath}.spans`
|
|
3078
|
+
});
|
|
3079
|
+
continue;
|
|
3080
|
+
}
|
|
3081
|
+
for (const [spanIndex, span] of scopeSpan.spans.entries()) {
|
|
3082
|
+
const spanPath = `${scopePath}.spans[${spanIndex}]`;
|
|
3083
|
+
if (!looksLikeOtlpSpan(span)) {
|
|
3084
|
+
unsupportedFields.push(spanPath);
|
|
3085
|
+
warnings.push({
|
|
3086
|
+
code: "otlp_invalid_span",
|
|
3087
|
+
message: "Skipped OTLP span without required traceId, spanId, or name.",
|
|
3088
|
+
severity: "warning",
|
|
3089
|
+
field: spanPath
|
|
3090
|
+
});
|
|
3091
|
+
continue;
|
|
3092
|
+
}
|
|
3093
|
+
spans.push({
|
|
3094
|
+
span,
|
|
3095
|
+
resourceAttributes: resourceParsed.attributes,
|
|
3096
|
+
scopeAttributes: scopeParsed.attributes,
|
|
3097
|
+
scopeName: readStringField(scope ?? {}, ["name"]),
|
|
3098
|
+
scopeVersion: readStringField(scope ?? {}, ["version"]),
|
|
3099
|
+
pathPrefix: spanPath
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3176
3103
|
}
|
|
3177
|
-
if (
|
|
3178
|
-
out["openinference.summarized_attributes"] = summarizedKeys;
|
|
3104
|
+
if (spans.length === 0) {
|
|
3179
3105
|
warnings.push({
|
|
3180
|
-
code: "
|
|
3181
|
-
message: "
|
|
3182
|
-
severity: "
|
|
3106
|
+
code: "otlp_no_valid_spans",
|
|
3107
|
+
message: "OTLP JSON payload did not contain any valid spans.",
|
|
3108
|
+
severity: "error"
|
|
3183
3109
|
});
|
|
3110
|
+
return {
|
|
3111
|
+
spans,
|
|
3112
|
+
confidence: 0.7,
|
|
3113
|
+
description: "Malformed OTLP JSON trace payload",
|
|
3114
|
+
warnings,
|
|
3115
|
+
unsupportedFields
|
|
3116
|
+
};
|
|
3184
3117
|
}
|
|
3185
|
-
return {
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
}
|
|
3193
|
-
const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
|
|
3194
|
-
const normalized = rawKind?.toUpperCase();
|
|
3195
|
-
switch (normalized) {
|
|
3196
|
-
case "LLM":
|
|
3197
|
-
return { kind: "LLM", warnings };
|
|
3198
|
-
case "TOOL":
|
|
3199
|
-
return { kind: "TOOL", warnings };
|
|
3200
|
-
case "CHAIN":
|
|
3201
|
-
return { kind: "CHAIN", warnings };
|
|
3202
|
-
case "RETRIEVER":
|
|
3203
|
-
return { kind: "RETRIEVER", warnings };
|
|
3204
|
-
case "AGENT":
|
|
3205
|
-
return { kind: "AGENT", warnings };
|
|
3206
|
-
case "EMBEDDING":
|
|
3207
|
-
warnings.push({
|
|
3208
|
-
code: "openinference_kind_semantic_loss",
|
|
3209
|
-
message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
|
|
3210
|
-
severity: "warning",
|
|
3211
|
-
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
3212
|
-
});
|
|
3213
|
-
return { kind: "LLM", warnings };
|
|
3214
|
-
case "RERANKER":
|
|
3215
|
-
warnings.push({
|
|
3216
|
-
code: "openinference_kind_semantic_loss",
|
|
3217
|
-
message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
|
|
3218
|
-
severity: "warning",
|
|
3219
|
-
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
3220
|
-
});
|
|
3221
|
-
return { kind: "RETRIEVER", warnings };
|
|
3222
|
-
case "UNKNOWN":
|
|
3223
|
-
case void 0:
|
|
3224
|
-
warnings.push({
|
|
3225
|
-
code: "openinference_kind_unknown",
|
|
3226
|
-
message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
|
|
3227
|
-
severity: "warning",
|
|
3228
|
-
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
3229
|
-
});
|
|
3230
|
-
return { kind: "LOGIC", warnings };
|
|
3231
|
-
default:
|
|
3232
|
-
warnings.push({
|
|
3233
|
-
code: "openinference_kind_unsupported",
|
|
3234
|
-
message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
|
|
3235
|
-
severity: "warning",
|
|
3236
|
-
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
3237
|
-
});
|
|
3238
|
-
return { kind: "LOGIC", warnings };
|
|
3239
|
-
}
|
|
3118
|
+
return {
|
|
3119
|
+
spans,
|
|
3120
|
+
confidence: 0.93,
|
|
3121
|
+
description: "OTLP JSON trace payload",
|
|
3122
|
+
warnings,
|
|
3123
|
+
unsupportedFields
|
|
3124
|
+
};
|
|
3240
3125
|
}
|
|
3241
|
-
function
|
|
3126
|
+
function mapOtlpStatus(status) {
|
|
3242
3127
|
if (!isRecord6(status)) return void 0;
|
|
3243
3128
|
const rawCode = status.code;
|
|
3244
3129
|
if (typeof rawCode !== "string") return void 0;
|
|
3245
3130
|
switch (rawCode.toUpperCase()) {
|
|
3131
|
+
case "STATUS_CODE_OK":
|
|
3246
3132
|
case "OK":
|
|
3247
3133
|
return "ok";
|
|
3134
|
+
case "STATUS_CODE_ERROR":
|
|
3248
3135
|
case "ERROR":
|
|
3249
3136
|
return "error";
|
|
3137
|
+
case "STATUS_CODE_UNSET":
|
|
3250
3138
|
case "UNSET":
|
|
3251
3139
|
return "unknown";
|
|
3252
3140
|
default:
|
|
3253
3141
|
return "unknown";
|
|
3254
3142
|
}
|
|
3255
3143
|
}
|
|
3256
|
-
function
|
|
3257
|
-
const
|
|
3258
|
-
const
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
const usage = {};
|
|
3262
|
-
if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
|
|
3263
|
-
usage.input = prompt;
|
|
3144
|
+
function readOtlpKind(attributes, pathPrefix) {
|
|
3145
|
+
const warnings = [];
|
|
3146
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
3147
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG" || agentInspectKind === "OUTCOME") {
|
|
3148
|
+
return { kind: agentInspectKind, warnings };
|
|
3264
3149
|
}
|
|
3265
|
-
|
|
3266
|
-
|
|
3150
|
+
const operation = attributes["gen_ai.operation.name"];
|
|
3151
|
+
if (typeof operation === "string") {
|
|
3152
|
+
switch (operation) {
|
|
3153
|
+
case "generate_content":
|
|
3154
|
+
case "chat":
|
|
3155
|
+
return { kind: "LLM", warnings };
|
|
3156
|
+
case "execute_tool":
|
|
3157
|
+
return { kind: "TOOL", warnings };
|
|
3158
|
+
case "invoke_agent":
|
|
3159
|
+
return { kind: "AGENT", warnings };
|
|
3160
|
+
default:
|
|
3161
|
+
warnings.push({
|
|
3162
|
+
code: "otlp_gen_ai_operation_semantic_loss",
|
|
3163
|
+
message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
|
|
3164
|
+
severity: "warning",
|
|
3165
|
+
field: `${pathPrefix}.attributes.gen_ai.operation.name`
|
|
3166
|
+
});
|
|
3167
|
+
return { kind: "LOGIC", warnings };
|
|
3168
|
+
}
|
|
3267
3169
|
}
|
|
3268
|
-
|
|
3269
|
-
|
|
3170
|
+
warnings.push({
|
|
3171
|
+
code: "otlp_kind_unknown",
|
|
3172
|
+
message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
|
|
3173
|
+
severity: "warning",
|
|
3174
|
+
field: `${pathPrefix}.attributes`
|
|
3175
|
+
});
|
|
3176
|
+
return { kind: "LOGIC", warnings };
|
|
3177
|
+
}
|
|
3178
|
+
function readOtlpTokenUsage(attributes) {
|
|
3179
|
+
const input = attributes["gen_ai.usage.input_tokens"];
|
|
3180
|
+
const output = attributes["gen_ai.usage.output_tokens"];
|
|
3181
|
+
const usage = {};
|
|
3182
|
+
if (typeof input === "number" && Number.isFinite(input) && input >= 0) {
|
|
3183
|
+
usage.input = input;
|
|
3270
3184
|
}
|
|
3271
|
-
if (typeof
|
|
3272
|
-
usage.
|
|
3185
|
+
if (typeof output === "number" && Number.isFinite(output) && output >= 0) {
|
|
3186
|
+
usage.output = output;
|
|
3273
3187
|
}
|
|
3274
|
-
if (usage.
|
|
3188
|
+
if (usage.input !== void 0 && usage.output !== void 0) {
|
|
3275
3189
|
usage.total = usage.input + usage.output;
|
|
3276
3190
|
}
|
|
3277
3191
|
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
3278
3192
|
}
|
|
3279
|
-
function
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3193
|
+
function readOtlpConfidence(attributes) {
|
|
3194
|
+
return readOpenInferenceConfidence(attributes);
|
|
3195
|
+
}
|
|
3196
|
+
function sanitizeOtlpAttributes(attributes, pathPrefix) {
|
|
3197
|
+
const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
|
|
3198
|
+
const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
|
|
3199
|
+
return {
|
|
3200
|
+
...sanitized,
|
|
3201
|
+
warnings: sanitized.warnings.map(
|
|
3202
|
+
(warning) => warning.code === "openinference_sensitive_attribute_summarized" ? {
|
|
3203
|
+
...warning,
|
|
3204
|
+
code: "otlp_sensitive_attribute_summarized",
|
|
3205
|
+
message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
|
|
3206
|
+
} : warning
|
|
3207
|
+
)
|
|
3208
|
+
};
|
|
3209
|
+
}
|
|
3210
|
+
function mapOtlpEvents(value, pathPrefix) {
|
|
3211
|
+
const warnings = [];
|
|
3212
|
+
const unsupportedFields = [];
|
|
3213
|
+
if (value === void 0) return { warnings, unsupportedFields };
|
|
3214
|
+
if (!Array.isArray(value)) {
|
|
3215
|
+
unsupportedFields.push(pathPrefix);
|
|
3216
|
+
warnings.push({
|
|
3217
|
+
code: "otlp_events_invalid",
|
|
3218
|
+
message: "OTLP events field was not an array.",
|
|
3219
|
+
severity: "warning",
|
|
3220
|
+
field: pathPrefix
|
|
3221
|
+
});
|
|
3222
|
+
return { warnings, unsupportedFields };
|
|
3223
|
+
}
|
|
3224
|
+
const events = [];
|
|
3225
|
+
for (const [index, event] of value.entries()) {
|
|
3226
|
+
const eventPath = `${pathPrefix}[${index}]`;
|
|
3227
|
+
if (!isRecord6(event)) {
|
|
3228
|
+
unsupportedFields.push(eventPath);
|
|
3229
|
+
continue;
|
|
3230
|
+
}
|
|
3231
|
+
const parsedAttributes = parseOtlpAttributes(
|
|
3232
|
+
event.attributes,
|
|
3233
|
+
`${eventPath}.attributes`
|
|
3234
|
+
);
|
|
3235
|
+
warnings.push(...parsedAttributes.warnings);
|
|
3236
|
+
unsupportedFields.push(...parsedAttributes.unsupportedFields);
|
|
3237
|
+
const sanitized = sanitizeOtlpAttributes(
|
|
3238
|
+
parsedAttributes.attributes,
|
|
3239
|
+
`${eventPath}.attributes`
|
|
3240
|
+
);
|
|
3241
|
+
warnings.push(...sanitized.warnings);
|
|
3242
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
3243
|
+
const out = {};
|
|
3244
|
+
const name = readStringField(event, ["name"]);
|
|
3245
|
+
if (name !== void 0) {
|
|
3246
|
+
out.name = name;
|
|
3247
|
+
}
|
|
3248
|
+
const timestamp = parseUnixNanoToIso(event.timeUnixNano);
|
|
3249
|
+
if (timestamp !== void 0) {
|
|
3250
|
+
out.timestamp = timestamp;
|
|
3251
|
+
} else if (event.timeUnixNano !== void 0) {
|
|
3252
|
+
unsupportedFields.push(`${eventPath}.timeUnixNano`);
|
|
3253
|
+
warnings.push({
|
|
3254
|
+
code: "otlp_event_timestamp_invalid",
|
|
3255
|
+
message: "OTLP event timeUnixNano could not be parsed.",
|
|
3256
|
+
severity: "warning",
|
|
3257
|
+
field: `${eventPath}.timeUnixNano`
|
|
3258
|
+
});
|
|
3259
|
+
}
|
|
3260
|
+
if (Object.keys(sanitized.attributes).length > 0) {
|
|
3261
|
+
out.attributes = sanitized.attributes;
|
|
3262
|
+
}
|
|
3263
|
+
events.push(out);
|
|
3264
|
+
}
|
|
3265
|
+
return {
|
|
3266
|
+
events: events.length > 0 ? events : void 0,
|
|
3267
|
+
warnings,
|
|
3268
|
+
unsupportedFields
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
function mapOtlpSpan(context) {
|
|
3272
|
+
const { span, pathPrefix } = context;
|
|
3273
|
+
const warnings = [];
|
|
3274
|
+
const unsupportedFields = [];
|
|
3275
|
+
const parsedSpanAttributes = parseOtlpAttributes(
|
|
3276
|
+
span.attributes,
|
|
3277
|
+
`${pathPrefix}.attributes`
|
|
3278
|
+
);
|
|
3279
|
+
warnings.push(...parsedSpanAttributes.warnings);
|
|
3280
|
+
unsupportedFields.push(...parsedSpanAttributes.unsupportedFields);
|
|
3281
|
+
const sanitizedSpanAttributes = sanitizeOtlpAttributes(
|
|
3282
|
+
parsedSpanAttributes.attributes,
|
|
3283
|
+
`${pathPrefix}.attributes`
|
|
3284
|
+
);
|
|
3285
|
+
warnings.push(...sanitizedSpanAttributes.warnings);
|
|
3286
|
+
unsupportedFields.push(...sanitizedSpanAttributes.unsupportedFields);
|
|
3287
|
+
const attributes = {
|
|
3288
|
+
...sanitizedSpanAttributes.attributes
|
|
3289
|
+
};
|
|
3290
|
+
for (const [key, value] of Object.entries(context.resourceAttributes)) {
|
|
3291
|
+
attributes[`resource.${key}`] = value;
|
|
3292
|
+
}
|
|
3293
|
+
for (const [key, value] of Object.entries(context.scopeAttributes)) {
|
|
3294
|
+
attributes[`scope.${key}`] = value;
|
|
3295
|
+
}
|
|
3296
|
+
if (context.scopeName !== void 0) {
|
|
3297
|
+
attributes["scope.name"] = context.scopeName;
|
|
3298
|
+
}
|
|
3299
|
+
if (context.scopeVersion !== void 0) {
|
|
3300
|
+
attributes["scope.version"] = context.scopeVersion;
|
|
3283
3301
|
}
|
|
3284
|
-
return "correlated";
|
|
3285
|
-
}
|
|
3286
|
-
function mapOpenInferenceSpan(span, index, version) {
|
|
3287
|
-
const pathPrefix = `spans[${index}]`;
|
|
3288
|
-
const warnings = [];
|
|
3289
|
-
const unsupportedFields = [];
|
|
3290
|
-
const rawAttributes = readRecordField(span, "attributes") ?? {};
|
|
3291
|
-
const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
|
|
3292
|
-
warnings.push(...sanitized.warnings);
|
|
3293
|
-
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
3294
|
-
const attributes = { ...sanitized.attributes };
|
|
3295
3302
|
for (const [key, value] of Object.entries(span)) {
|
|
3296
|
-
if (
|
|
3303
|
+
if (OTLP_SPAN_KEYS.has(key)) continue;
|
|
3297
3304
|
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
3298
3305
|
if (value === null || typeof value !== "object") {
|
|
3299
|
-
attributes[`
|
|
3306
|
+
attributes[`otlp.${key}`] = value;
|
|
3300
3307
|
} else {
|
|
3301
|
-
attributes[`
|
|
3308
|
+
attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
|
|
3302
3309
|
warnings.push({
|
|
3303
|
-
code: "
|
|
3304
|
-
message: `Unsupported
|
|
3310
|
+
code: "otlp_unsupported_field_summarized",
|
|
3311
|
+
message: `Unsupported OTLP span field "${key}" was summarized.`,
|
|
3305
3312
|
severity: "warning",
|
|
3306
3313
|
field: `${pathPrefix}.${key}`
|
|
3307
3314
|
});
|
|
3308
3315
|
}
|
|
3309
3316
|
}
|
|
3310
|
-
const
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3317
|
+
for (const key of [
|
|
3318
|
+
"droppedAttributesCount",
|
|
3319
|
+
"droppedEventsCount",
|
|
3320
|
+
"droppedLinksCount",
|
|
3321
|
+
"links"
|
|
3322
|
+
]) {
|
|
3323
|
+
if (span[key] !== void 0) {
|
|
3324
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
3325
|
+
warnings.push({
|
|
3326
|
+
code: "otlp_span_field_not_mapped",
|
|
3327
|
+
message: `OTLP span field "${key}" is not represented in AgentInspect events.`,
|
|
3328
|
+
severity: "warning",
|
|
3329
|
+
field: `${pathPrefix}.${key}`
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
const events = mapOtlpEvents(span.events, `${pathPrefix}.events`);
|
|
3334
|
+
warnings.push(...events.warnings);
|
|
3335
|
+
unsupportedFields.push(...events.unsupportedFields);
|
|
3336
|
+
if (events.events !== void 0) {
|
|
3337
|
+
attributes["otlp.events"] = events.events;
|
|
3338
|
+
}
|
|
3339
|
+
const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
|
|
3340
|
+
const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
|
|
3341
|
+
const parentSpanId = readStringField(span, ["parentSpanId"]);
|
|
3314
3342
|
const startedAt = readOpenInferenceTimestamp(
|
|
3315
3343
|
span,
|
|
3316
|
-
["
|
|
3317
|
-
[
|
|
3318
|
-
);
|
|
3319
|
-
const endedAt = readOpenInferenceTimestamp(
|
|
3320
|
-
span,
|
|
3321
|
-
["end_time_unix_nano", "endTimeUnixNano"],
|
|
3322
|
-
["end_time", "endTime"]
|
|
3344
|
+
["startTimeUnixNano"],
|
|
3345
|
+
[]
|
|
3323
3346
|
);
|
|
3347
|
+
const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
|
|
3324
3348
|
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
3325
3349
|
if (startedAt === void 0) {
|
|
3350
|
+
unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
|
|
3326
3351
|
warnings.push({
|
|
3327
|
-
code: "
|
|
3328
|
-
message: "
|
|
3352
|
+
code: "otlp_missing_start_time",
|
|
3353
|
+
message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
|
|
3329
3354
|
severity: "warning",
|
|
3330
|
-
field: `${pathPrefix}.
|
|
3355
|
+
field: `${pathPrefix}.startTimeUnixNano`
|
|
3331
3356
|
});
|
|
3332
|
-
unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
|
|
3333
3357
|
}
|
|
3334
|
-
const { kind, warnings: kindWarnings } =
|
|
3335
|
-
|
|
3336
|
-
rawAttributes,
|
|
3358
|
+
const { kind, warnings: kindWarnings } = readOtlpKind(
|
|
3359
|
+
parsedSpanAttributes.attributes,
|
|
3337
3360
|
pathPrefix
|
|
3338
3361
|
);
|
|
3339
3362
|
warnings.push(...kindWarnings);
|
|
3340
|
-
const status =
|
|
3341
|
-
const tokenUsage =
|
|
3363
|
+
const status = mapOtlpStatus(span.status);
|
|
3364
|
+
const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
|
|
3342
3365
|
const errorMessage = isRecord6(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
3343
3366
|
const event = {
|
|
3344
3367
|
schemaVersion: "0.2",
|
|
3345
|
-
eventId: typeof
|
|
3346
|
-
runId: typeof
|
|
3368
|
+
eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
|
|
3369
|
+
runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
|
|
3347
3370
|
kind,
|
|
3348
|
-
name,
|
|
3371
|
+
name: readStringField(span, ["name"]) ?? spanId,
|
|
3349
3372
|
timestamp,
|
|
3350
|
-
confidence:
|
|
3373
|
+
confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
|
|
3351
3374
|
source: {
|
|
3352
3375
|
type: "otel",
|
|
3353
|
-
name: "
|
|
3354
|
-
...
|
|
3376
|
+
name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
|
|
3377
|
+
...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
|
|
3355
3378
|
},
|
|
3356
3379
|
attributes,
|
|
3357
3380
|
trace: {
|
|
@@ -3378,7 +3401,7 @@ function mapOpenInferenceSpan(span, index, version) {
|
|
|
3378
3401
|
}
|
|
3379
3402
|
if (status === "error") {
|
|
3380
3403
|
event.error = {
|
|
3381
|
-
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "
|
|
3404
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OTLP span error"
|
|
3382
3405
|
};
|
|
3383
3406
|
}
|
|
3384
3407
|
return {
|
|
@@ -3389,10 +3412,8 @@ function mapOpenInferenceSpan(span, index, version) {
|
|
|
3389
3412
|
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
3390
3413
|
};
|
|
3391
3414
|
}
|
|
3392
|
-
function
|
|
3393
|
-
const mapped = document.spans.map(
|
|
3394
|
-
(span, index) => mapOpenInferenceSpan(span, index, document.version)
|
|
3395
|
-
);
|
|
3415
|
+
function mapOtlpEventsToPersisted(document) {
|
|
3416
|
+
const mapped = document.spans.map((span) => mapOtlpSpan(span));
|
|
3396
3417
|
const spanIdToEventId = new Map(
|
|
3397
3418
|
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
3398
3419
|
);
|
|
@@ -3406,9 +3427,9 @@ function mapOpenInferenceEvents(document) {
|
|
|
3406
3427
|
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
3407
3428
|
};
|
|
3408
3429
|
}
|
|
3409
|
-
var
|
|
3410
|
-
format:
|
|
3411
|
-
name: "
|
|
3430
|
+
var otlpJsonReader = {
|
|
3431
|
+
format: OTLP_READER_FORMAT,
|
|
3432
|
+
name: "OTLP JSON",
|
|
3412
3433
|
async detect(input) {
|
|
3413
3434
|
const resolved = await resolveInput(input);
|
|
3414
3435
|
if (!resolved) return void 0;
|
|
@@ -3418,12 +3439,12 @@ var openInferenceJsonReader = {
|
|
|
3418
3439
|
} catch {
|
|
3419
3440
|
return void 0;
|
|
3420
3441
|
}
|
|
3421
|
-
const document =
|
|
3442
|
+
const document = extractOtlpDocument(parsed);
|
|
3422
3443
|
if (!document) return void 0;
|
|
3423
3444
|
return {
|
|
3424
|
-
format:
|
|
3445
|
+
format: OTLP_READER_FORMAT,
|
|
3425
3446
|
confidence: document.confidence,
|
|
3426
|
-
readerName: "
|
|
3447
|
+
readerName: "OTLP JSON",
|
|
3427
3448
|
description: document.description,
|
|
3428
3449
|
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
3429
3450
|
};
|
|
@@ -3433,31 +3454,31 @@ var openInferenceJsonReader = {
|
|
|
3433
3454
|
if (!resolved) {
|
|
3434
3455
|
throw new TraceReadError(
|
|
3435
3456
|
"unsupported_format",
|
|
3436
|
-
"
|
|
3457
|
+
"OTLP JSON reader requires file, string, or buffer input."
|
|
3437
3458
|
);
|
|
3438
3459
|
}
|
|
3439
3460
|
let parsed;
|
|
3440
3461
|
try {
|
|
3441
3462
|
parsed = parseJsonDocument(resolved.content);
|
|
3442
3463
|
} catch {
|
|
3443
|
-
throw new TraceReadError("unsupported_format", "
|
|
3464
|
+
throw new TraceReadError("unsupported_format", "OTLP JSON input is not valid JSON.", [
|
|
3444
3465
|
{
|
|
3445
|
-
code: "
|
|
3446
|
-
message: "
|
|
3466
|
+
code: "otlp_invalid_json",
|
|
3467
|
+
message: "OTLP JSON reader could not parse the input as JSON.",
|
|
3447
3468
|
severity: "error"
|
|
3448
3469
|
}
|
|
3449
3470
|
]);
|
|
3450
3471
|
}
|
|
3451
|
-
const document =
|
|
3472
|
+
const document = extractOtlpDocument(parsed);
|
|
3452
3473
|
if (!document || document.spans.length === 0) {
|
|
3453
3474
|
throw new TraceReadError(
|
|
3454
3475
|
"unsupported_format",
|
|
3455
|
-
"No valid
|
|
3476
|
+
"No valid OTLP spans found.",
|
|
3456
3477
|
attachSingleSourceFile(
|
|
3457
3478
|
document?.warnings ?? [
|
|
3458
3479
|
{
|
|
3459
|
-
code: "
|
|
3460
|
-
message: "
|
|
3480
|
+
code: "otlp_no_valid_spans",
|
|
3481
|
+
message: "OTLP JSON input did not contain valid spans.",
|
|
3461
3482
|
severity: "error"
|
|
3462
3483
|
}
|
|
3463
3484
|
],
|
|
@@ -3465,7 +3486,7 @@ var openInferenceJsonReader = {
|
|
|
3465
3486
|
)
|
|
3466
3487
|
);
|
|
3467
3488
|
}
|
|
3468
|
-
const mapped =
|
|
3489
|
+
const mapped = mapOtlpEventsToPersisted(document);
|
|
3469
3490
|
const warnings = attachSingleSourceFile(
|
|
3470
3491
|
[...document.warnings, ...mapped.warnings],
|
|
3471
3492
|
resolved
|
|
@@ -3475,7 +3496,7 @@ var openInferenceJsonReader = {
|
|
|
3475
3496
|
...mapped.unsupportedFields
|
|
3476
3497
|
].sort((a, b) => a.localeCompare(b));
|
|
3477
3498
|
return {
|
|
3478
|
-
format:
|
|
3499
|
+
format: OTLP_READER_FORMAT,
|
|
3479
3500
|
events: mapped.events,
|
|
3480
3501
|
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
3481
3502
|
warnings,
|
|
@@ -3484,788 +3505,770 @@ var openInferenceJsonReader = {
|
|
|
3484
3505
|
};
|
|
3485
3506
|
}
|
|
3486
3507
|
};
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
return void 0;
|
|
3497
|
-
}
|
|
3498
|
-
if (typeof value.stringValue === "string") return value.stringValue;
|
|
3499
|
-
if (typeof value.boolValue === "boolean") return value.boolValue;
|
|
3500
|
-
if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
|
|
3501
|
-
return value.intValue;
|
|
3502
|
-
}
|
|
3503
|
-
if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
|
|
3504
|
-
const n = Number(value.intValue);
|
|
3505
|
-
if (Number.isFinite(n)) return n;
|
|
3506
|
-
}
|
|
3507
|
-
if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
|
|
3508
|
-
return value.doubleValue;
|
|
3509
|
-
}
|
|
3510
|
-
if (isRecord6(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
|
|
3511
|
-
return value.arrayValue.values.map(
|
|
3512
|
-
(item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
|
|
3513
|
-
);
|
|
3514
|
-
}
|
|
3515
|
-
if (isRecord6(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
|
|
3516
|
-
const out = {};
|
|
3517
|
-
for (const [index, item] of value.kvlistValue.values.entries()) {
|
|
3518
|
-
if (!isRecord6(item) || typeof item.key !== "string") {
|
|
3519
|
-
unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
|
|
3520
|
-
continue;
|
|
3521
|
-
}
|
|
3522
|
-
out[item.key] = parseOtlpAnyValue(
|
|
3523
|
-
item.value,
|
|
3524
|
-
`${field}.kvlistValue.values[${index}].value`,
|
|
3525
|
-
warnings,
|
|
3526
|
-
unsupportedFields
|
|
3527
|
-
);
|
|
3508
|
+
var agentInspectJsonlReader = {
|
|
3509
|
+
format: "agent-inspect-jsonl",
|
|
3510
|
+
name: "AgentInspect JSONL",
|
|
3511
|
+
async detect(input) {
|
|
3512
|
+
const resolved = await resolveInput(input);
|
|
3513
|
+
if (!resolved) return void 0;
|
|
3514
|
+
const detected = detectJsonlFormat(resolved.content);
|
|
3515
|
+
if (detected.validRows === 0 || detected.format === "empty") {
|
|
3516
|
+
return void 0;
|
|
3528
3517
|
}
|
|
3529
|
-
return
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
});
|
|
3565
|
-
return { attributes, warnings, unsupportedFields };
|
|
3518
|
+
return {
|
|
3519
|
+
format: "agent-inspect-jsonl",
|
|
3520
|
+
confidence: 0.95,
|
|
3521
|
+
readerName: "AgentInspect JSONL",
|
|
3522
|
+
description: agentInspectFormatLabel(detected.format),
|
|
3523
|
+
warnings: attachSingleSourceFile(detected.warnings, resolved)
|
|
3524
|
+
};
|
|
3525
|
+
},
|
|
3526
|
+
async read(input) {
|
|
3527
|
+
const resolved = await resolveInput(input);
|
|
3528
|
+
if (!resolved) {
|
|
3529
|
+
throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
|
|
3530
|
+
}
|
|
3531
|
+
const parsed = parseTraceJsonl(resolved.content, { warnings: false });
|
|
3532
|
+
if (parsed.sourceEventCount === 0) {
|
|
3533
|
+
throw new Error("No valid AgentInspect JSONL events found.");
|
|
3534
|
+
}
|
|
3535
|
+
const events = persistedEventsForParsedTrace(parsed);
|
|
3536
|
+
return {
|
|
3537
|
+
format: agentInspectFormatLabel(parsed.format),
|
|
3538
|
+
events,
|
|
3539
|
+
runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
|
|
3540
|
+
warnings: parsed.format === "mixed" ? attachSingleSourceFile(
|
|
3541
|
+
[
|
|
3542
|
+
{
|
|
3543
|
+
code: "mixed_agent_inspect_jsonl",
|
|
3544
|
+
message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
|
|
3545
|
+
severity: "warning"
|
|
3546
|
+
}
|
|
3547
|
+
],
|
|
3548
|
+
resolved
|
|
3549
|
+
) : [],
|
|
3550
|
+
unsupportedFields: [],
|
|
3551
|
+
sourceFiles: resolved.sourceFiles
|
|
3552
|
+
};
|
|
3566
3553
|
}
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3554
|
+
};
|
|
3555
|
+
var DEFAULT_TRACE_READERS = [
|
|
3556
|
+
agentInspectJsonlReader,
|
|
3557
|
+
openInferenceJsonReader,
|
|
3558
|
+
otlpJsonReader
|
|
3559
|
+
];
|
|
3560
|
+
async function detectTraceFormat(input, options = {}) {
|
|
3561
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
3562
|
+
if (options.format !== void 0) {
|
|
3563
|
+
const reader = findReaderByFormat(options.format, readers);
|
|
3564
|
+
if (!reader) {
|
|
3565
|
+
return {
|
|
3566
|
+
status: "unsupported",
|
|
3567
|
+
candidates: [],
|
|
3568
|
+
warnings: [
|
|
3569
|
+
{
|
|
3570
|
+
code: "unsupported_format",
|
|
3571
|
+
message: `No trace reader is registered for format "${options.format}".`,
|
|
3572
|
+
severity: "error"
|
|
3573
|
+
}
|
|
3574
|
+
]
|
|
3575
|
+
};
|
|
3587
3576
|
}
|
|
3577
|
+
return {
|
|
3578
|
+
status: "detected",
|
|
3579
|
+
format: reader.format,
|
|
3580
|
+
candidates: [
|
|
3581
|
+
{
|
|
3582
|
+
format: reader.format,
|
|
3583
|
+
confidence: 1,
|
|
3584
|
+
readerName: reader.name,
|
|
3585
|
+
description: "Explicit format override"
|
|
3586
|
+
}
|
|
3587
|
+
],
|
|
3588
|
+
warnings: []
|
|
3589
|
+
};
|
|
3588
3590
|
}
|
|
3589
|
-
|
|
3590
|
-
}
|
|
3591
|
-
function looksLikeOtlpSpan(value) {
|
|
3592
|
-
return isRecord6(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
|
|
3593
|
-
}
|
|
3594
|
-
function extractOtlpDocument(root) {
|
|
3595
|
-
if (!isRecord6(root) || !Array.isArray(root.resourceSpans)) return void 0;
|
|
3596
|
-
const spans = [];
|
|
3591
|
+
const candidates = [];
|
|
3597
3592
|
const warnings = [];
|
|
3598
|
-
const
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
continue;
|
|
3604
|
-
}
|
|
3605
|
-
const resource = readRecordField(resourceSpan, "resource");
|
|
3606
|
-
const resourceParsed = parseOtlpAttributes(
|
|
3607
|
-
resource?.attributes,
|
|
3608
|
-
`${resourcePath}.resource.attributes`
|
|
3609
|
-
);
|
|
3610
|
-
warnings.push(...resourceParsed.warnings);
|
|
3611
|
-
unsupportedFields.push(...resourceParsed.unsupportedFields);
|
|
3612
|
-
if (!Array.isArray(resourceSpan.scopeSpans)) {
|
|
3613
|
-
unsupportedFields.push(`${resourcePath}.scopeSpans`);
|
|
3614
|
-
warnings.push({
|
|
3615
|
-
code: "otlp_scope_spans_missing",
|
|
3616
|
-
message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
|
|
3617
|
-
severity: "warning",
|
|
3618
|
-
field: `${resourcePath}.scopeSpans`
|
|
3619
|
-
});
|
|
3620
|
-
continue;
|
|
3621
|
-
}
|
|
3622
|
-
for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
|
|
3623
|
-
const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
|
|
3624
|
-
if (!isRecord6(scopeSpan)) {
|
|
3625
|
-
unsupportedFields.push(scopePath);
|
|
3626
|
-
continue;
|
|
3593
|
+
for (const reader of readers) {
|
|
3594
|
+
try {
|
|
3595
|
+
const candidate = await reader.detect(input);
|
|
3596
|
+
if (candidate !== void 0) {
|
|
3597
|
+
candidates.push(normalizeCandidate(reader, candidate));
|
|
3627
3598
|
}
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
`${scopePath}.scope.attributes`
|
|
3632
|
-
);
|
|
3633
|
-
warnings.push(...scopeParsed.warnings);
|
|
3634
|
-
unsupportedFields.push(...scopeParsed.unsupportedFields);
|
|
3635
|
-
if (!Array.isArray(scopeSpan.spans)) {
|
|
3636
|
-
unsupportedFields.push(`${scopePath}.spans`);
|
|
3637
|
-
warnings.push({
|
|
3638
|
-
code: "otlp_spans_missing",
|
|
3639
|
-
message: "OTLP scopeSpans entry did not contain a spans array.",
|
|
3640
|
-
severity: "warning",
|
|
3641
|
-
field: `${scopePath}.spans`
|
|
3642
|
-
});
|
|
3599
|
+
} catch (error) {
|
|
3600
|
+
if (error instanceof TraceReadError) {
|
|
3601
|
+
warnings.push(...error.warnings);
|
|
3643
3602
|
continue;
|
|
3644
3603
|
}
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
code: "otlp_invalid_span",
|
|
3651
|
-
message: "Skipped OTLP span without required traceId, spanId, or name.",
|
|
3652
|
-
severity: "warning",
|
|
3653
|
-
field: spanPath
|
|
3654
|
-
});
|
|
3655
|
-
continue;
|
|
3656
|
-
}
|
|
3657
|
-
spans.push({
|
|
3658
|
-
span,
|
|
3659
|
-
resourceAttributes: resourceParsed.attributes,
|
|
3660
|
-
scopeAttributes: scopeParsed.attributes,
|
|
3661
|
-
scopeName: readStringField(scope ?? {}, ["name"]),
|
|
3662
|
-
scopeVersion: readStringField(scope ?? {}, ["version"]),
|
|
3663
|
-
pathPrefix: spanPath
|
|
3664
|
-
});
|
|
3665
|
-
}
|
|
3604
|
+
warnings.push({
|
|
3605
|
+
code: "reader_detect_failed",
|
|
3606
|
+
message: error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed during detection.`,
|
|
3607
|
+
severity: "warning"
|
|
3608
|
+
});
|
|
3666
3609
|
}
|
|
3667
3610
|
}
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3611
|
+
const sorted = sortCandidates(
|
|
3612
|
+
candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
|
|
3613
|
+
);
|
|
3614
|
+
const candidateWarnings = collectWarnings(sorted);
|
|
3615
|
+
const lowConfidenceWarnings = candidates.length > sorted.length ? [
|
|
3616
|
+
{
|
|
3617
|
+
code: "low_confidence_candidates",
|
|
3618
|
+
message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
|
|
3619
|
+
severity: "info"
|
|
3620
|
+
}
|
|
3621
|
+
] : [];
|
|
3622
|
+
const allWarnings = dedupeWarnings([
|
|
3623
|
+
...warnings,
|
|
3624
|
+
...candidateWarnings,
|
|
3625
|
+
...lowConfidenceWarnings
|
|
3626
|
+
]);
|
|
3627
|
+
if (sorted.length === 0) {
|
|
3674
3628
|
return {
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
warnings,
|
|
3679
|
-
unsupportedFields
|
|
3629
|
+
status: "unsupported",
|
|
3630
|
+
candidates: [],
|
|
3631
|
+
warnings: allWarnings
|
|
3680
3632
|
};
|
|
3681
3633
|
}
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
case "OK":
|
|
3697
|
-
return "ok";
|
|
3698
|
-
case "STATUS_CODE_ERROR":
|
|
3699
|
-
case "ERROR":
|
|
3700
|
-
return "error";
|
|
3701
|
-
case "STATUS_CODE_UNSET":
|
|
3702
|
-
case "UNSET":
|
|
3703
|
-
return "unknown";
|
|
3704
|
-
default:
|
|
3705
|
-
return "unknown";
|
|
3706
|
-
}
|
|
3707
|
-
}
|
|
3708
|
-
function readOtlpKind(attributes, pathPrefix) {
|
|
3709
|
-
const warnings = [];
|
|
3710
|
-
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
3711
|
-
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG" || agentInspectKind === "OUTCOME") {
|
|
3712
|
-
return { kind: agentInspectKind, warnings };
|
|
3713
|
-
}
|
|
3714
|
-
const operation = attributes["gen_ai.operation.name"];
|
|
3715
|
-
if (typeof operation === "string") {
|
|
3716
|
-
switch (operation) {
|
|
3717
|
-
case "generate_content":
|
|
3718
|
-
case "chat":
|
|
3719
|
-
return { kind: "LLM", warnings };
|
|
3720
|
-
case "execute_tool":
|
|
3721
|
-
return { kind: "TOOL", warnings };
|
|
3722
|
-
case "invoke_agent":
|
|
3723
|
-
return { kind: "AGENT", warnings };
|
|
3724
|
-
default:
|
|
3725
|
-
warnings.push({
|
|
3726
|
-
code: "otlp_gen_ai_operation_semantic_loss",
|
|
3727
|
-
message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
|
|
3728
|
-
severity: "warning",
|
|
3729
|
-
field: `${pathPrefix}.attributes.gen_ai.operation.name`
|
|
3730
|
-
});
|
|
3731
|
-
return { kind: "LOGIC", warnings };
|
|
3732
|
-
}
|
|
3733
|
-
}
|
|
3734
|
-
warnings.push({
|
|
3735
|
-
code: "otlp_kind_unknown",
|
|
3736
|
-
message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
|
|
3737
|
-
severity: "warning",
|
|
3738
|
-
field: `${pathPrefix}.attributes`
|
|
3739
|
-
});
|
|
3740
|
-
return { kind: "LOGIC", warnings };
|
|
3741
|
-
}
|
|
3742
|
-
function readOtlpTokenUsage(attributes) {
|
|
3743
|
-
const input = attributes["gen_ai.usage.input_tokens"];
|
|
3744
|
-
const output = attributes["gen_ai.usage.output_tokens"];
|
|
3745
|
-
const usage = {};
|
|
3746
|
-
if (typeof input === "number" && Number.isFinite(input) && input >= 0) {
|
|
3747
|
-
usage.input = input;
|
|
3748
|
-
}
|
|
3749
|
-
if (typeof output === "number" && Number.isFinite(output) && output >= 0) {
|
|
3750
|
-
usage.output = output;
|
|
3751
|
-
}
|
|
3752
|
-
if (usage.input !== void 0 && usage.output !== void 0) {
|
|
3753
|
-
usage.total = usage.input + usage.output;
|
|
3634
|
+
const [best, second] = sorted;
|
|
3635
|
+
if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
|
|
3636
|
+
return {
|
|
3637
|
+
status: "ambiguous",
|
|
3638
|
+
candidates: sorted,
|
|
3639
|
+
warnings: [
|
|
3640
|
+
...allWarnings,
|
|
3641
|
+
{
|
|
3642
|
+
code: "ambiguous_format_candidates",
|
|
3643
|
+
message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
|
|
3644
|
+
severity: "warning"
|
|
3645
|
+
}
|
|
3646
|
+
]
|
|
3647
|
+
};
|
|
3754
3648
|
}
|
|
3755
|
-
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
3756
|
-
}
|
|
3757
|
-
function readOtlpConfidence(attributes) {
|
|
3758
|
-
return readOpenInferenceConfidence(attributes);
|
|
3759
|
-
}
|
|
3760
|
-
function sanitizeOtlpAttributes(attributes, pathPrefix) {
|
|
3761
|
-
const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
|
|
3762
|
-
const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
|
|
3763
3649
|
return {
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
code: "otlp_sensitive_attribute_summarized",
|
|
3769
|
-
message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
|
|
3770
|
-
} : warning
|
|
3771
|
-
)
|
|
3650
|
+
status: "detected",
|
|
3651
|
+
format: best.format,
|
|
3652
|
+
candidates: sorted,
|
|
3653
|
+
warnings: allWarnings
|
|
3772
3654
|
};
|
|
3773
3655
|
}
|
|
3774
|
-
function
|
|
3775
|
-
const
|
|
3776
|
-
const
|
|
3777
|
-
if (
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
severity: "warning",
|
|
3784
|
-
field: pathPrefix
|
|
3785
|
-
});
|
|
3786
|
-
return { warnings, unsupportedFields };
|
|
3656
|
+
async function readTrace(input, options = {}) {
|
|
3657
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
3658
|
+
const detection = await detectTraceFormat(input, options);
|
|
3659
|
+
if (detection.status === "unsupported" || detection.format === void 0) {
|
|
3660
|
+
throw new TraceReadError(
|
|
3661
|
+
"unsupported_format",
|
|
3662
|
+
"No trace reader could detect the input format.",
|
|
3663
|
+
detection.warnings
|
|
3664
|
+
);
|
|
3787
3665
|
}
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
continue;
|
|
3794
|
-
}
|
|
3795
|
-
const parsedAttributes = parseOtlpAttributes(
|
|
3796
|
-
event.attributes,
|
|
3797
|
-
`${eventPath}.attributes`
|
|
3666
|
+
if (detection.status === "ambiguous") {
|
|
3667
|
+
throw new TraceReadError(
|
|
3668
|
+
"ambiguous_format",
|
|
3669
|
+
"Multiple trace readers matched the input with equal confidence.",
|
|
3670
|
+
detection.warnings
|
|
3798
3671
|
);
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3672
|
+
}
|
|
3673
|
+
const reader = findReaderByFormat(detection.format, readers);
|
|
3674
|
+
if (!reader) {
|
|
3675
|
+
throw new TraceReadError(
|
|
3676
|
+
"unsupported_format",
|
|
3677
|
+
`No trace reader is registered for format "${detection.format}".`,
|
|
3678
|
+
detection.warnings
|
|
3804
3679
|
);
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
const
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
severity: "warning",
|
|
3821
|
-
field: `${eventPath}.timeUnixNano`
|
|
3822
|
-
});
|
|
3823
|
-
}
|
|
3824
|
-
if (Object.keys(sanitized.attributes).length > 0) {
|
|
3825
|
-
out.attributes = sanitized.attributes;
|
|
3680
|
+
}
|
|
3681
|
+
try {
|
|
3682
|
+
const result = await reader.read(input, { format: detection.format });
|
|
3683
|
+
return {
|
|
3684
|
+
...result,
|
|
3685
|
+
format: result.format || detection.format,
|
|
3686
|
+
warnings: [...detection.warnings, ...result.warnings]
|
|
3687
|
+
};
|
|
3688
|
+
} catch (error) {
|
|
3689
|
+
if (error instanceof TraceReadError) {
|
|
3690
|
+
throw new TraceReadError(
|
|
3691
|
+
error.code,
|
|
3692
|
+
error.message,
|
|
3693
|
+
dedupeWarnings([...detection.warnings, ...error.warnings])
|
|
3694
|
+
);
|
|
3826
3695
|
}
|
|
3827
|
-
|
|
3696
|
+
throw new TraceReadError(
|
|
3697
|
+
"reader_failed",
|
|
3698
|
+
error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
|
|
3699
|
+
detection.warnings
|
|
3700
|
+
);
|
|
3828
3701
|
}
|
|
3829
|
-
return {
|
|
3830
|
-
events: events.length > 0 ? events : void 0,
|
|
3831
|
-
warnings,
|
|
3832
|
-
unsupportedFields
|
|
3833
|
-
};
|
|
3834
3702
|
}
|
|
3835
|
-
function
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
);
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
const
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
for (const [key, value] of Object.entries(context.resourceAttributes)) {
|
|
3855
|
-
attributes[`resource.${key}`] = value;
|
|
3856
|
-
}
|
|
3857
|
-
for (const [key, value] of Object.entries(context.scopeAttributes)) {
|
|
3858
|
-
attributes[`scope.${key}`] = value;
|
|
3703
|
+
function openTrace(input, options = {}) {
|
|
3704
|
+
return readTrace(input, options);
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
// packages/core/src/diff/comparable.ts
|
|
3708
|
+
function extractOutputPreview(meta) {
|
|
3709
|
+
if (meta === void 0) return void 0;
|
|
3710
|
+
if ("outputPreview" in meta) return meta.outputPreview;
|
|
3711
|
+
if ("resultPreview" in meta) return meta.resultPreview;
|
|
3712
|
+
return void 0;
|
|
3713
|
+
}
|
|
3714
|
+
function mapStepStatus(s) {
|
|
3715
|
+
if (s === void 0) return "running";
|
|
3716
|
+
return s;
|
|
3717
|
+
}
|
|
3718
|
+
function manualTraceEventsToComparableRun(events) {
|
|
3719
|
+
const started = events.find((e) => e.event === "run_started");
|
|
3720
|
+
if (!started || started.event !== "run_started") {
|
|
3721
|
+
throw new Error("Invalid trace: missing run_started");
|
|
3859
3722
|
}
|
|
3860
|
-
|
|
3861
|
-
|
|
3723
|
+
const rs = started;
|
|
3724
|
+
const runId = rs.runId;
|
|
3725
|
+
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
3726
|
+
const lastCompleted = completedAll[completedAll.length - 1];
|
|
3727
|
+
let runStatus;
|
|
3728
|
+
if (lastCompleted === void 0) runStatus = "running";
|
|
3729
|
+
else runStatus = lastCompleted.status;
|
|
3730
|
+
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
3731
|
+
const steps = /* @__PURE__ */ new Map();
|
|
3732
|
+
let order = 0;
|
|
3733
|
+
for (const e of events) {
|
|
3734
|
+
if (e.event !== "step_started") continue;
|
|
3735
|
+
const s = e;
|
|
3736
|
+
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
3737
|
+
steps.set(s.stepId, {
|
|
3738
|
+
id: s.stepId,
|
|
3739
|
+
parentId: s.parentId,
|
|
3740
|
+
name: s.name,
|
|
3741
|
+
type: s.type,
|
|
3742
|
+
order: order++,
|
|
3743
|
+
timestamp: s.timestamp,
|
|
3744
|
+
metadata: meta
|
|
3745
|
+
});
|
|
3862
3746
|
}
|
|
3863
|
-
|
|
3864
|
-
|
|
3747
|
+
for (const e of events) {
|
|
3748
|
+
if (e.event !== "step_completed") continue;
|
|
3749
|
+
const acc = steps.get(e.stepId);
|
|
3750
|
+
if (!acc) continue;
|
|
3751
|
+
acc.status = e.status;
|
|
3752
|
+
acc.durationMs = e.durationMs;
|
|
3753
|
+
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
3754
|
+
const extra = e;
|
|
3755
|
+
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
3756
|
+
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
3757
|
+
}
|
|
3865
3758
|
}
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
if (
|
|
3870
|
-
|
|
3871
|
-
} else {
|
|
3872
|
-
attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
|
|
3873
|
-
warnings.push({
|
|
3874
|
-
code: "otlp_unsupported_field_summarized",
|
|
3875
|
-
message: `Unsupported OTLP span field "${key}" was summarized.`,
|
|
3876
|
-
severity: "warning",
|
|
3877
|
-
field: `${pathPrefix}.${key}`
|
|
3878
|
-
});
|
|
3759
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
3760
|
+
for (const acc of steps.values()) {
|
|
3761
|
+
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
3762
|
+
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
3763
|
+
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
3879
3764
|
}
|
|
3765
|
+
const outputPreview = extractOutputPreview(meta);
|
|
3766
|
+
const sc = {
|
|
3767
|
+
id: acc.id,
|
|
3768
|
+
name: acc.name,
|
|
3769
|
+
type: acc.type,
|
|
3770
|
+
status: mapStepStatus(acc.status),
|
|
3771
|
+
durationMs: acc.durationMs,
|
|
3772
|
+
error: acc.errorMsg,
|
|
3773
|
+
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
3774
|
+
outputPreview,
|
|
3775
|
+
children: []
|
|
3776
|
+
};
|
|
3777
|
+
nodes.set(acc.id, sc);
|
|
3880
3778
|
}
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
field: `${pathPrefix}.${key}`
|
|
3894
|
-
});
|
|
3779
|
+
const roots = [];
|
|
3780
|
+
const sortByOrder = (a, b) => {
|
|
3781
|
+
const oa = steps.get(a.id)?.order ?? 0;
|
|
3782
|
+
const ob = steps.get(b.id)?.order ?? 0;
|
|
3783
|
+
return oa - ob;
|
|
3784
|
+
};
|
|
3785
|
+
for (const acc of steps.values()) {
|
|
3786
|
+
const node = nodes.get(acc.id);
|
|
3787
|
+
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
3788
|
+
nodes.get(acc.parentId).children.push(node);
|
|
3789
|
+
} else {
|
|
3790
|
+
roots.push(node);
|
|
3895
3791
|
}
|
|
3896
3792
|
}
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
if (events.events !== void 0) {
|
|
3901
|
-
attributes["otlp.events"] = events.events;
|
|
3902
|
-
}
|
|
3903
|
-
const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
|
|
3904
|
-
const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
|
|
3905
|
-
const parentSpanId = readStringField(span, ["parentSpanId"]);
|
|
3906
|
-
const startedAt = readOpenInferenceTimestamp(
|
|
3907
|
-
span,
|
|
3908
|
-
["startTimeUnixNano"],
|
|
3909
|
-
[]
|
|
3910
|
-
);
|
|
3911
|
-
const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
|
|
3912
|
-
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
3913
|
-
if (startedAt === void 0) {
|
|
3914
|
-
unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
|
|
3915
|
-
warnings.push({
|
|
3916
|
-
code: "otlp_missing_start_time",
|
|
3917
|
-
message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
|
|
3918
|
-
severity: "warning",
|
|
3919
|
-
field: `${pathPrefix}.startTimeUnixNano`
|
|
3920
|
-
});
|
|
3793
|
+
roots.sort(sortByOrder);
|
|
3794
|
+
for (const n of nodes.values()) {
|
|
3795
|
+
n.children.sort(sortByOrder);
|
|
3921
3796
|
}
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
|
|
3929
|
-
const errorMessage = isRecord6(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
3930
|
-
const event = {
|
|
3931
|
-
schemaVersion: "0.2",
|
|
3932
|
-
eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
|
|
3933
|
-
runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
|
|
3934
|
-
kind,
|
|
3935
|
-
name: readStringField(span, ["name"]) ?? spanId,
|
|
3936
|
-
timestamp,
|
|
3937
|
-
confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
|
|
3938
|
-
source: {
|
|
3939
|
-
type: "otel",
|
|
3940
|
-
name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
|
|
3941
|
-
...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
|
|
3942
|
-
},
|
|
3943
|
-
attributes,
|
|
3944
|
-
trace: {
|
|
3945
|
-
traceId,
|
|
3946
|
-
spanId,
|
|
3947
|
-
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
3948
|
-
}
|
|
3797
|
+
return {
|
|
3798
|
+
runId,
|
|
3799
|
+
name: rs.name,
|
|
3800
|
+
status: runStatus,
|
|
3801
|
+
durationMs,
|
|
3802
|
+
steps: roots
|
|
3949
3803
|
};
|
|
3950
|
-
|
|
3951
|
-
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
// packages/core/src/exporters/helpers.ts
|
|
3807
|
+
var REDACT_SUBSTRINGS = [
|
|
3808
|
+
"authorization",
|
|
3809
|
+
"cookie",
|
|
3810
|
+
"token",
|
|
3811
|
+
"apikey",
|
|
3812
|
+
"password",
|
|
3813
|
+
"secret",
|
|
3814
|
+
"email"
|
|
3815
|
+
];
|
|
3816
|
+
function shouldRedactKey(key) {
|
|
3817
|
+
const k = key.toLowerCase();
|
|
3818
|
+
for (const s of REDACT_SUBSTRINGS) {
|
|
3819
|
+
if (k.includes(s)) return true;
|
|
3952
3820
|
}
|
|
3953
|
-
|
|
3954
|
-
|
|
3821
|
+
return false;
|
|
3822
|
+
}
|
|
3823
|
+
function safeString(value, maxLength) {
|
|
3824
|
+
if (value === null || value === void 0) return "";
|
|
3825
|
+
let s;
|
|
3826
|
+
if (typeof value === "string") s = value;
|
|
3827
|
+
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
3828
|
+
else s = stableJson(value, false);
|
|
3829
|
+
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
3830
|
+
return `${s.slice(0, maxLength)}\u2026`;
|
|
3955
3831
|
}
|
|
3956
|
-
|
|
3957
|
-
|
|
3832
|
+
return s;
|
|
3833
|
+
}
|
|
3834
|
+
function escapeMarkdown(value) {
|
|
3835
|
+
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
3836
|
+
}
|
|
3837
|
+
function sortKeysDeep(input) {
|
|
3838
|
+
if (input === null || typeof input !== "object") return input;
|
|
3839
|
+
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
3840
|
+
const o = input;
|
|
3841
|
+
const out = {};
|
|
3842
|
+
for (const k of Object.keys(o).sort()) {
|
|
3843
|
+
out[k] = sortKeysDeep(o[k]);
|
|
3958
3844
|
}
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3845
|
+
return out;
|
|
3846
|
+
}
|
|
3847
|
+
function stableJson(value, pretty) {
|
|
3848
|
+
const sorted = sortKeysDeep(value);
|
|
3849
|
+
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
3850
|
+
}
|
|
3851
|
+
function compactAttributes3(attrs, options) {
|
|
3852
|
+
if (attrs === void 0) return {};
|
|
3853
|
+
const maxLen = options?.maxLength ?? 500;
|
|
3854
|
+
const out = {};
|
|
3855
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
3856
|
+
if (shouldRedactKey(key)) {
|
|
3857
|
+
out[key] = "[REDACTED]";
|
|
3858
|
+
continue;
|
|
3859
|
+
}
|
|
3860
|
+
const v = attrs[key];
|
|
3861
|
+
out[key] = compactValue(v, maxLen);
|
|
3962
3862
|
}
|
|
3963
|
-
|
|
3964
|
-
|
|
3863
|
+
return out;
|
|
3864
|
+
}
|
|
3865
|
+
function compactValue(value, maxLen, redacted) {
|
|
3866
|
+
if (value === null || typeof value !== "object") {
|
|
3867
|
+
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
3965
3868
|
}
|
|
3966
|
-
if (
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3869
|
+
if (Array.isArray(value)) {
|
|
3870
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
|
|
3871
|
+
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
3872
|
+
return arr;
|
|
3970
3873
|
}
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3874
|
+
const o = value;
|
|
3875
|
+
const inner = {};
|
|
3876
|
+
for (const k of Object.keys(o)) {
|
|
3877
|
+
if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
3878
|
+
else inner[k] = compactValue(o[k], maxLen);
|
|
3879
|
+
}
|
|
3880
|
+
return inner;
|
|
3978
3881
|
}
|
|
3979
|
-
function
|
|
3980
|
-
const
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
3882
|
+
function flattenTree(tree) {
|
|
3883
|
+
const out = [];
|
|
3884
|
+
function walk(nodes) {
|
|
3885
|
+
for (const n of nodes) {
|
|
3886
|
+
out.push(n);
|
|
3887
|
+
if (n.children.length > 0) walk(n.children);
|
|
3888
|
+
}
|
|
3987
3889
|
}
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
warnings: mapped.flatMap((span) => span.warnings),
|
|
3991
|
-
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
3992
|
-
};
|
|
3890
|
+
walk(tree.children);
|
|
3891
|
+
return out;
|
|
3993
3892
|
}
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
3893
|
+
|
|
3894
|
+
// packages/core/src/diff/engine.ts
|
|
3895
|
+
var DEFAULT_THRESHOLD_MS = 0;
|
|
3896
|
+
function pathSeg(step, index) {
|
|
3897
|
+
return { index, name: step.name, stepId: step.id };
|
|
3898
|
+
}
|
|
3899
|
+
function buildPath(segments) {
|
|
3900
|
+
return { path: [...segments] };
|
|
3901
|
+
}
|
|
3902
|
+
function pairSteps(left, right) {
|
|
3903
|
+
const usedRight = /* @__PURE__ */ new Set();
|
|
3904
|
+
const pairs = [];
|
|
3905
|
+
for (let i = 0; i < left.length; i++) {
|
|
3906
|
+
const L = left[i];
|
|
3907
|
+
let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
|
|
3908
|
+
if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
|
|
3909
|
+
const cand = right[i];
|
|
3910
|
+
if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
|
|
3911
|
+
R = cand;
|
|
3912
|
+
}
|
|
4005
3913
|
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
format: OTLP_READER_FORMAT,
|
|
4010
|
-
confidence: document.confidence,
|
|
4011
|
-
readerName: "OTLP JSON",
|
|
4012
|
-
description: document.description,
|
|
4013
|
-
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
4014
|
-
};
|
|
4015
|
-
},
|
|
4016
|
-
async read(input) {
|
|
4017
|
-
const resolved = await resolveInput(input);
|
|
4018
|
-
if (!resolved) {
|
|
4019
|
-
throw new TraceReadError(
|
|
4020
|
-
"unsupported_format",
|
|
4021
|
-
"OTLP JSON reader requires file, string, or buffer input."
|
|
3914
|
+
if (R === void 0) {
|
|
3915
|
+
R = right.find(
|
|
3916
|
+
(r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
|
|
4022
3917
|
);
|
|
4023
3918
|
}
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
}
|
|
4028
|
-
|
|
4029
|
-
{
|
|
4030
|
-
code: "otlp_invalid_json",
|
|
4031
|
-
message: "OTLP JSON reader could not parse the input as JSON.",
|
|
4032
|
-
severity: "error"
|
|
4033
|
-
}
|
|
4034
|
-
]);
|
|
4035
|
-
}
|
|
4036
|
-
const document = extractOtlpDocument(parsed);
|
|
4037
|
-
if (!document || document.spans.length === 0) {
|
|
4038
|
-
throw new TraceReadError(
|
|
4039
|
-
"unsupported_format",
|
|
4040
|
-
"No valid OTLP spans found.",
|
|
4041
|
-
attachSingleSourceFile(
|
|
4042
|
-
document?.warnings ?? [
|
|
4043
|
-
{
|
|
4044
|
-
code: "otlp_no_valid_spans",
|
|
4045
|
-
message: "OTLP JSON input did not contain valid spans.",
|
|
4046
|
-
severity: "error"
|
|
4047
|
-
}
|
|
4048
|
-
],
|
|
4049
|
-
resolved
|
|
4050
|
-
)
|
|
4051
|
-
);
|
|
3919
|
+
if (R !== void 0) {
|
|
3920
|
+
usedRight.add(R.id);
|
|
3921
|
+
pairs.push([L, R]);
|
|
3922
|
+
} else {
|
|
3923
|
+
pairs.push([L, void 0]);
|
|
4052
3924
|
}
|
|
4053
|
-
const mapped = mapOtlpEventsToPersisted(document);
|
|
4054
|
-
const warnings = attachSingleSourceFile(
|
|
4055
|
-
[...document.warnings, ...mapped.warnings],
|
|
4056
|
-
resolved
|
|
4057
|
-
);
|
|
4058
|
-
const unsupportedFields = [
|
|
4059
|
-
...document.unsupportedFields,
|
|
4060
|
-
...mapped.unsupportedFields
|
|
4061
|
-
].sort((a, b) => a.localeCompare(b));
|
|
4062
|
-
return {
|
|
4063
|
-
format: OTLP_READER_FORMAT,
|
|
4064
|
-
events: mapped.events,
|
|
4065
|
-
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
4066
|
-
warnings,
|
|
4067
|
-
unsupportedFields,
|
|
4068
|
-
sourceFiles: resolved.sourceFiles
|
|
4069
|
-
};
|
|
4070
3925
|
}
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
name: "AgentInspect JSONL",
|
|
4075
|
-
async detect(input) {
|
|
4076
|
-
const resolved = await resolveInput(input);
|
|
4077
|
-
if (!resolved) return void 0;
|
|
4078
|
-
const detected = detectJsonlFormat(resolved.content);
|
|
4079
|
-
if (detected.validRows === 0 || detected.format === "empty") {
|
|
4080
|
-
return void 0;
|
|
4081
|
-
}
|
|
4082
|
-
return {
|
|
4083
|
-
format: "agent-inspect-jsonl",
|
|
4084
|
-
confidence: 0.95,
|
|
4085
|
-
readerName: "AgentInspect JSONL",
|
|
4086
|
-
description: agentInspectFormatLabel(detected.format),
|
|
4087
|
-
warnings: attachSingleSourceFile(detected.warnings, resolved)
|
|
4088
|
-
};
|
|
4089
|
-
},
|
|
4090
|
-
async read(input) {
|
|
4091
|
-
const resolved = await resolveInput(input);
|
|
4092
|
-
if (!resolved) {
|
|
4093
|
-
throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
|
|
3926
|
+
for (const R of right) {
|
|
3927
|
+
if (!usedRight.has(R.id)) {
|
|
3928
|
+
pairs.push([void 0, R]);
|
|
4094
3929
|
}
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
3930
|
+
}
|
|
3931
|
+
return pairs;
|
|
3932
|
+
}
|
|
3933
|
+
function compareLeafSteps(L, R, segments, opts, out) {
|
|
3934
|
+
const path12 = buildPath(segments);
|
|
3935
|
+
if (L.name !== R.name) {
|
|
3936
|
+
out.push({
|
|
3937
|
+
kind: "structure",
|
|
3938
|
+
severity: "warning",
|
|
3939
|
+
message: "Step name differs",
|
|
3940
|
+
path: path12,
|
|
3941
|
+
left: L.name,
|
|
3942
|
+
right: R.name
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
if ((L.type ?? "") !== (R.type ?? "")) {
|
|
3946
|
+
out.push({
|
|
3947
|
+
kind: "step-type",
|
|
3948
|
+
severity: "warning",
|
|
3949
|
+
message: "Step type differs",
|
|
3950
|
+
path: path12,
|
|
3951
|
+
left: L.type,
|
|
3952
|
+
right: R.type
|
|
3953
|
+
});
|
|
3954
|
+
}
|
|
3955
|
+
if ((L.status ?? "") !== (R.status ?? "")) {
|
|
3956
|
+
out.push({
|
|
3957
|
+
kind: "step-status",
|
|
3958
|
+
severity: "warning",
|
|
3959
|
+
message: "Step status differs",
|
|
3960
|
+
path: path12,
|
|
3961
|
+
left: L.status,
|
|
3962
|
+
right: R.status
|
|
3963
|
+
});
|
|
3964
|
+
}
|
|
3965
|
+
const le = L.error ?? "";
|
|
3966
|
+
const re = R.error ?? "";
|
|
3967
|
+
if (le !== re) {
|
|
3968
|
+
out.push({
|
|
3969
|
+
kind: "error",
|
|
3970
|
+
severity: "error",
|
|
3971
|
+
message: "Step error message differs",
|
|
3972
|
+
path: path12,
|
|
3973
|
+
left: le || void 0,
|
|
3974
|
+
right: re || void 0
|
|
3975
|
+
});
|
|
3976
|
+
}
|
|
3977
|
+
if (!opts.ignoreDuration) {
|
|
3978
|
+
const ld = L.durationMs;
|
|
3979
|
+
const rd = R.durationMs;
|
|
3980
|
+
const th = opts.durationThresholdMs;
|
|
3981
|
+
let differs = false;
|
|
3982
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
3983
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
3984
|
+
else differs = Math.abs(ld - rd) > th;
|
|
3985
|
+
if (differs) {
|
|
3986
|
+
out.push({
|
|
3987
|
+
kind: "duration",
|
|
3988
|
+
severity: "info",
|
|
3989
|
+
message: "Step duration differs",
|
|
3990
|
+
path: path12,
|
|
3991
|
+
left: ld,
|
|
3992
|
+
right: rd
|
|
3993
|
+
});
|
|
4098
3994
|
}
|
|
4099
|
-
const events = persistedEventsForParsedTrace(parsed);
|
|
4100
|
-
return {
|
|
4101
|
-
format: agentInspectFormatLabel(parsed.format),
|
|
4102
|
-
events,
|
|
4103
|
-
runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
|
|
4104
|
-
warnings: parsed.format === "mixed" ? attachSingleSourceFile(
|
|
4105
|
-
[
|
|
4106
|
-
{
|
|
4107
|
-
code: "mixed_agent_inspect_jsonl",
|
|
4108
|
-
message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
|
|
4109
|
-
severity: "warning"
|
|
4110
|
-
}
|
|
4111
|
-
],
|
|
4112
|
-
resolved
|
|
4113
|
-
) : [],
|
|
4114
|
-
unsupportedFields: [],
|
|
4115
|
-
sourceFiles: resolved.sourceFiles
|
|
4116
|
-
};
|
|
4117
3995
|
}
|
|
4118
|
-
};
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
3996
|
+
const lm = stableJson(L.metadata ?? {});
|
|
3997
|
+
const rm = stableJson(R.metadata ?? {});
|
|
3998
|
+
if (lm !== rm) {
|
|
3999
|
+
out.push({
|
|
4000
|
+
kind: "metadata",
|
|
4001
|
+
severity: "info",
|
|
4002
|
+
message: "Step metadata differs",
|
|
4003
|
+
path: path12,
|
|
4004
|
+
left: L.metadata,
|
|
4005
|
+
right: R.metadata
|
|
4006
|
+
});
|
|
4007
|
+
}
|
|
4008
|
+
const lo = stableJson(L.outputPreview ?? null);
|
|
4009
|
+
const ro = stableJson(R.outputPreview ?? null);
|
|
4010
|
+
if (lo !== ro) {
|
|
4011
|
+
out.push({
|
|
4012
|
+
kind: "output",
|
|
4013
|
+
severity: "info",
|
|
4014
|
+
message: "Output preview differs",
|
|
4015
|
+
path: path12,
|
|
4016
|
+
left: L.outputPreview,
|
|
4017
|
+
right: R.outputPreview
|
|
4018
|
+
});
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
function compareRecursive(L, R, segments, opts, out) {
|
|
4022
|
+
compareLeafSteps(L, R, segments, opts, out);
|
|
4023
|
+
const pairs = pairSteps(L.children, R.children);
|
|
4024
|
+
let ci = 0;
|
|
4025
|
+
for (const [lch, rch] of pairs) {
|
|
4026
|
+
if (lch !== void 0 && rch !== void 0) {
|
|
4027
|
+
compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
|
|
4028
|
+
} else if (lch !== void 0) {
|
|
4029
|
+
out.push({
|
|
4030
|
+
kind: "step-removed",
|
|
4031
|
+
severity: "warning",
|
|
4032
|
+
message: `Step only in left run: ${lch.name}`,
|
|
4033
|
+
path: buildPath([...segments, pathSeg(lch, ci)]),
|
|
4034
|
+
left: lch.id,
|
|
4035
|
+
right: void 0
|
|
4036
|
+
});
|
|
4037
|
+
} else if (rch !== void 0) {
|
|
4038
|
+
out.push({
|
|
4039
|
+
kind: "step-added",
|
|
4040
|
+
severity: "warning",
|
|
4041
|
+
message: `Step only in right run: ${rch.name}`,
|
|
4042
|
+
path: buildPath([...segments, pathSeg(rch, ci)]),
|
|
4043
|
+
left: void 0,
|
|
4044
|
+
right: rch.id
|
|
4045
|
+
});
|
|
4140
4046
|
}
|
|
4141
|
-
|
|
4142
|
-
status: "detected",
|
|
4143
|
-
format: reader.format,
|
|
4144
|
-
candidates: [
|
|
4145
|
-
{
|
|
4146
|
-
format: reader.format,
|
|
4147
|
-
confidence: 1,
|
|
4148
|
-
readerName: reader.name,
|
|
4149
|
-
description: "Explicit format override"
|
|
4150
|
-
}
|
|
4151
|
-
],
|
|
4152
|
-
warnings: []
|
|
4153
|
-
};
|
|
4047
|
+
ci += 1;
|
|
4154
4048
|
}
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4049
|
+
}
|
|
4050
|
+
function mergeDiffDefaults(options) {
|
|
4051
|
+
return {
|
|
4052
|
+
ignoreDuration: false,
|
|
4053
|
+
durationThresholdMs: DEFAULT_THRESHOLD_MS,
|
|
4054
|
+
focus: "all",
|
|
4055
|
+
check: "all"
|
|
4056
|
+
};
|
|
4057
|
+
}
|
|
4058
|
+
function kindMatchesFilter(kind, merged) {
|
|
4059
|
+
return true;
|
|
4060
|
+
}
|
|
4061
|
+
function diffRuns(left, right, options) {
|
|
4062
|
+
const merged = mergeDiffDefaults();
|
|
4063
|
+
const opts = {
|
|
4064
|
+
ignoreDuration: merged.ignoreDuration,
|
|
4065
|
+
durationThresholdMs: merged.durationThresholdMs
|
|
4066
|
+
};
|
|
4067
|
+
const raw = [];
|
|
4068
|
+
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
4069
|
+
raw.push({
|
|
4070
|
+
kind: "run-status",
|
|
4071
|
+
severity: "warning",
|
|
4072
|
+
message: "Run completion status differs",
|
|
4073
|
+
left: left.status,
|
|
4074
|
+
right: right.status
|
|
4075
|
+
});
|
|
4076
|
+
}
|
|
4077
|
+
{
|
|
4078
|
+
const ld = left.durationMs;
|
|
4079
|
+
const rd = right.durationMs;
|
|
4080
|
+
const th = merged.durationThresholdMs;
|
|
4081
|
+
let differs = false;
|
|
4082
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
4083
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
4084
|
+
else differs = Math.abs(ld - rd) > th;
|
|
4085
|
+
if (differs) {
|
|
4086
|
+
raw.push({
|
|
4087
|
+
kind: "duration",
|
|
4088
|
+
severity: "info",
|
|
4089
|
+
message: "Run duration differs",
|
|
4090
|
+
left: ld,
|
|
4091
|
+
right: rd
|
|
4172
4092
|
});
|
|
4173
4093
|
}
|
|
4174
4094
|
}
|
|
4175
|
-
const
|
|
4176
|
-
|
|
4177
|
-
)
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4095
|
+
const pairs = pairSteps(left.steps, right.steps);
|
|
4096
|
+
let idx = 0;
|
|
4097
|
+
for (const [ls, rs] of pairs) {
|
|
4098
|
+
if (ls !== void 0 && rs !== void 0) {
|
|
4099
|
+
compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
|
|
4100
|
+
idx += 1;
|
|
4101
|
+
} else if (ls !== void 0) {
|
|
4102
|
+
raw.push({
|
|
4103
|
+
kind: "step-removed",
|
|
4104
|
+
severity: "warning",
|
|
4105
|
+
message: `Step only in left run: ${ls.name}`,
|
|
4106
|
+
path: buildPath([pathSeg(ls, idx)]),
|
|
4107
|
+
left: ls.id,
|
|
4108
|
+
right: void 0
|
|
4109
|
+
});
|
|
4110
|
+
idx += 1;
|
|
4111
|
+
} else if (rs !== void 0) {
|
|
4112
|
+
raw.push({
|
|
4113
|
+
kind: "step-added",
|
|
4114
|
+
severity: "warning",
|
|
4115
|
+
message: `Step only in right run: ${rs.name}`,
|
|
4116
|
+
path: buildPath([pathSeg(rs, idx)]),
|
|
4117
|
+
left: void 0,
|
|
4118
|
+
right: rs.id
|
|
4119
|
+
});
|
|
4120
|
+
idx += 1;
|
|
4184
4121
|
}
|
|
4185
|
-
] : [];
|
|
4186
|
-
const allWarnings = dedupeWarnings([
|
|
4187
|
-
...warnings,
|
|
4188
|
-
...candidateWarnings,
|
|
4189
|
-
...lowConfidenceWarnings
|
|
4190
|
-
]);
|
|
4191
|
-
if (sorted.length === 0) {
|
|
4192
|
-
return {
|
|
4193
|
-
status: "unsupported",
|
|
4194
|
-
candidates: [],
|
|
4195
|
-
warnings: allWarnings
|
|
4196
|
-
};
|
|
4197
4122
|
}
|
|
4198
|
-
const
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
code: "ambiguous_format_candidates",
|
|
4207
|
-
message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
|
|
4208
|
-
severity: "warning"
|
|
4209
|
-
}
|
|
4210
|
-
]
|
|
4211
|
-
};
|
|
4123
|
+
const differences = raw.filter((d) => kindMatchesFilter(d.kind));
|
|
4124
|
+
let errors = 0;
|
|
4125
|
+
let warnings = 0;
|
|
4126
|
+
let info = 0;
|
|
4127
|
+
for (const d of differences) {
|
|
4128
|
+
if (d.severity === "error") errors += 1;
|
|
4129
|
+
else if (d.severity === "warning") warnings += 1;
|
|
4130
|
+
else info += 1;
|
|
4212
4131
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4132
|
+
const firstVisible = differences[0];
|
|
4133
|
+
const firstDivergence = firstVisible !== void 0 ? {
|
|
4134
|
+
kind: "first-divergence",
|
|
4135
|
+
severity: firstVisible.severity,
|
|
4136
|
+
message: `First divergence: ${firstVisible.message}`,
|
|
4137
|
+
path: firstVisible.path,
|
|
4138
|
+
left: firstVisible.left,
|
|
4139
|
+
right: firstVisible.right
|
|
4140
|
+
} : void 0;
|
|
4141
|
+
const summary = {
|
|
4142
|
+
leftRunId: left.runId,
|
|
4143
|
+
rightRunId: right.runId,
|
|
4144
|
+
totalDifferences: differences.length,
|
|
4145
|
+
errors,
|
|
4146
|
+
warnings,
|
|
4147
|
+
info,
|
|
4148
|
+
firstDivergence
|
|
4218
4149
|
};
|
|
4150
|
+
return { summary, differences };
|
|
4219
4151
|
}
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4152
|
+
|
|
4153
|
+
// packages/core/src/exporters/markdown-exporter.ts
|
|
4154
|
+
function renderTreeAscii(nodes, indent = "") {
|
|
4155
|
+
const lines = [];
|
|
4156
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
4157
|
+
const n = nodes[i];
|
|
4158
|
+
const last = i === nodes.length - 1;
|
|
4159
|
+
const branch = last ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
4160
|
+
const ev = n.event;
|
|
4161
|
+
const status = ev.status ?? "?";
|
|
4162
|
+
const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
|
|
4163
|
+
lines.push(`${indent}${branch}${escapeMarkdown(ev.name)} [${ev.kind}] ${status} (${dur})`);
|
|
4164
|
+
const nextIndent = indent + (last ? " " : "\u2502 ");
|
|
4165
|
+
if (n.children.length > 0) {
|
|
4166
|
+
const childStr = renderTreeAscii(n.children, nextIndent);
|
|
4167
|
+
if (childStr.length > 0) lines.push(childStr);
|
|
4168
|
+
}
|
|
4229
4169
|
}
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4170
|
+
return lines.join("\n");
|
|
4171
|
+
}
|
|
4172
|
+
function exportMarkdown(tree, options) {
|
|
4173
|
+
const warnings = [];
|
|
4174
|
+
const includeMetadata = options?.includeMetadata ?? true;
|
|
4175
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
4176
|
+
const includeErrors = options?.includeErrors ?? true;
|
|
4177
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
4178
|
+
const titleName = tree.name ?? tree.runId;
|
|
4179
|
+
const lines = [];
|
|
4180
|
+
lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
|
|
4181
|
+
lines.push("");
|
|
4182
|
+
lines.push("Generated locally by AgentInspect. Review for sensitive data before sharing.");
|
|
4183
|
+
lines.push("");
|
|
4184
|
+
if (includeMetadata) {
|
|
4185
|
+
lines.push("## Summary");
|
|
4186
|
+
lines.push("");
|
|
4187
|
+
lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
|
|
4188
|
+
if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
|
|
4189
|
+
lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
|
|
4190
|
+
lines.push(
|
|
4191
|
+
`- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
|
|
4235
4192
|
);
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
"
|
|
4241
|
-
`No trace reader is registered for format "${detection.format}".`,
|
|
4242
|
-
detection.warnings
|
|
4193
|
+
lines.push(
|
|
4194
|
+
`- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
|
|
4195
|
+
);
|
|
4196
|
+
lines.push(
|
|
4197
|
+
`- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
|
|
4243
4198
|
);
|
|
4199
|
+
lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
|
|
4200
|
+
lines.push("");
|
|
4201
|
+
lines.push("### Confidence breakdown");
|
|
4202
|
+
lines.push("");
|
|
4203
|
+
lines.push("| bucket | count |");
|
|
4204
|
+
lines.push("| --- | --- |");
|
|
4205
|
+
for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
|
|
4206
|
+
const key = k;
|
|
4207
|
+
lines.push(
|
|
4208
|
+
`| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
|
|
4209
|
+
);
|
|
4210
|
+
}
|
|
4211
|
+
lines.push("");
|
|
4212
|
+
lines.push("### Kind breakdown");
|
|
4213
|
+
lines.push("");
|
|
4214
|
+
lines.push("| kind | count |");
|
|
4215
|
+
lines.push("| --- | --- |");
|
|
4216
|
+
for (const k of Object.keys(tree.metadata.kinds).sort()) {
|
|
4217
|
+
const key = k;
|
|
4218
|
+
const c = tree.metadata.kinds[key];
|
|
4219
|
+
if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
|
|
4220
|
+
}
|
|
4221
|
+
lines.push("");
|
|
4244
4222
|
}
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4223
|
+
lines.push("## Execution tree");
|
|
4224
|
+
lines.push("");
|
|
4225
|
+
lines.push("```text");
|
|
4226
|
+
lines.push(
|
|
4227
|
+
tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
|
|
4228
|
+
);
|
|
4229
|
+
lines.push("```");
|
|
4230
|
+
lines.push("");
|
|
4231
|
+
const flat = flattenTree(tree);
|
|
4232
|
+
const errors = flat.filter((n) => n.event.status === "error");
|
|
4233
|
+
if (includeErrors && errors.length > 0) {
|
|
4234
|
+
lines.push("## Errors");
|
|
4235
|
+
lines.push("");
|
|
4236
|
+
for (const n of errors) {
|
|
4237
|
+
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
4238
|
+
n.event.attributes.error.message,
|
|
4239
|
+
maxLen
|
|
4240
|
+
) : "";
|
|
4241
|
+
lines.push(
|
|
4242
|
+
`- **${escapeMarkdown(n.event.name)}** (${escapeMarkdown(n.event.eventId)}): ${escapeMarkdown(msg || "error")}`
|
|
4258
4243
|
);
|
|
4259
4244
|
}
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4245
|
+
lines.push("");
|
|
4246
|
+
}
|
|
4247
|
+
if (includeAttributes) {
|
|
4248
|
+
lines.push("## Attributes (bounded)");
|
|
4249
|
+
lines.push("");
|
|
4250
|
+
for (const n of flat) {
|
|
4251
|
+
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
4252
|
+
const compact = compactAttributes3(n.event.attributes, {
|
|
4253
|
+
maxLength: maxLen});
|
|
4254
|
+
lines.push(`### ${escapeMarkdown(n.event.name)}`);
|
|
4255
|
+
lines.push("");
|
|
4256
|
+
lines.push("```json");
|
|
4257
|
+
lines.push(stableJson(compact, true));
|
|
4258
|
+
lines.push("```");
|
|
4259
|
+
lines.push("");
|
|
4260
|
+
}
|
|
4261
|
+
warnings.push(
|
|
4262
|
+
"Attributes may still contain sensitive data; review exports before sharing."
|
|
4264
4263
|
);
|
|
4265
4264
|
}
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4265
|
+
return {
|
|
4266
|
+
format: "markdown",
|
|
4267
|
+
content: lines.join("\n"),
|
|
4268
|
+
contentType: "text/markdown",
|
|
4269
|
+
fileExtension: ".md",
|
|
4270
|
+
warnings
|
|
4271
|
+
};
|
|
4269
4272
|
}
|
|
4270
4273
|
|
|
4271
4274
|
// packages/mcp-server/src/tools.ts
|