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