@agent-inspect/mcp-server 4.4.0 → 5.1.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 +2252 -2249
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2252 -2249
- 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,1723 +1644,1745 @@ 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
|
|
2130
|
+
return out;
|
|
2323
2131
|
}
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
const
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2132
|
+
|
|
2133
|
+
// packages/core/src/persisted/tree-bridge.ts
|
|
2134
|
+
function persistedInspectEventsToRunTrees(events, options) {
|
|
2135
|
+
const inspectEvents = persistedInspectEventsToInspectEvents(events, {
|
|
2136
|
+
skipInvalid: options?.skipInvalid
|
|
2137
|
+
});
|
|
2138
|
+
return new TreeBuilder().build(inspectEvents);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
// packages/core/src/readers/index.ts
|
|
2142
|
+
var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
|
|
2143
|
+
var MIN_DETECTION_CONFIDENCE = 0.5;
|
|
2144
|
+
var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
|
|
2145
|
+
var resolvedInputCache = /* @__PURE__ */ new WeakMap();
|
|
2146
|
+
var OPENINFERENCE_READER_FORMAT = "openinference-json";
|
|
2147
|
+
var OTLP_READER_FORMAT = "otlp-json";
|
|
2148
|
+
var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
2149
|
+
"trace_id",
|
|
2150
|
+
"traceId",
|
|
2151
|
+
"span_id",
|
|
2152
|
+
"spanId",
|
|
2153
|
+
"parent_span_id",
|
|
2154
|
+
"parentSpanId",
|
|
2155
|
+
"name",
|
|
2156
|
+
"start_time_unix_nano",
|
|
2157
|
+
"startTimeUnixNano",
|
|
2158
|
+
"end_time_unix_nano",
|
|
2159
|
+
"endTimeUnixNano",
|
|
2160
|
+
"start_time",
|
|
2161
|
+
"startTime",
|
|
2162
|
+
"end_time",
|
|
2163
|
+
"endTime",
|
|
2164
|
+
"attributes",
|
|
2165
|
+
"status",
|
|
2166
|
+
"kind",
|
|
2167
|
+
"span_kind",
|
|
2168
|
+
"spanKind"
|
|
2169
|
+
]);
|
|
2170
|
+
var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
|
|
2171
|
+
"input.value",
|
|
2172
|
+
"output.value",
|
|
2173
|
+
"input.mime_type",
|
|
2174
|
+
"output.mime_type",
|
|
2175
|
+
"llm.input_messages",
|
|
2176
|
+
"llm.output_messages",
|
|
2177
|
+
"llm.prompts",
|
|
2178
|
+
"llm.completions",
|
|
2179
|
+
"retrieval.documents",
|
|
2180
|
+
"reranker.input_documents",
|
|
2181
|
+
"reranker.output_documents",
|
|
2182
|
+
"document.content",
|
|
2183
|
+
"gen_ai.prompt",
|
|
2184
|
+
"gen_ai.completion",
|
|
2185
|
+
"gen_ai.input.messages",
|
|
2186
|
+
"gen_ai.output.messages"
|
|
2187
|
+
];
|
|
2188
|
+
var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
2189
|
+
"traceId",
|
|
2190
|
+
"spanId",
|
|
2191
|
+
"parentSpanId",
|
|
2192
|
+
"name",
|
|
2193
|
+
"kind",
|
|
2194
|
+
"startTimeUnixNano",
|
|
2195
|
+
"endTimeUnixNano",
|
|
2196
|
+
"attributes",
|
|
2197
|
+
"events",
|
|
2198
|
+
"status",
|
|
2199
|
+
"droppedAttributesCount",
|
|
2200
|
+
"droppedEventsCount",
|
|
2201
|
+
"droppedLinksCount",
|
|
2202
|
+
"links",
|
|
2203
|
+
"flags"
|
|
2204
|
+
]);
|
|
2205
|
+
var TraceReadError = class extends Error {
|
|
2206
|
+
code;
|
|
2207
|
+
warnings;
|
|
2208
|
+
constructor(code, message, warnings = []) {
|
|
2209
|
+
super(message);
|
|
2210
|
+
this.name = "TraceReadError";
|
|
2211
|
+
this.code = code;
|
|
2212
|
+
this.warnings = warnings;
|
|
2213
|
+
}
|
|
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 } : {}
|
|
2355
2282
|
}
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
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;
|
|
2379
2335
|
}
|
|
2380
|
-
|
|
2381
|
-
const
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
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;
|
|
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;
|
|
2408
2342
|
}
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
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;
|
|
2343
|
+
if (version === "0.2") {
|
|
2344
|
+
saw02 = true;
|
|
2345
|
+
validRows += 1;
|
|
2346
|
+
continue;
|
|
2463
2347
|
}
|
|
2464
|
-
if (
|
|
2465
|
-
|
|
2348
|
+
if (version === "1.0") {
|
|
2349
|
+
saw10 = true;
|
|
2350
|
+
validRows += 1;
|
|
2351
|
+
continue;
|
|
2466
2352
|
}
|
|
2467
|
-
return out;
|
|
2468
|
-
}
|
|
2469
|
-
default: {
|
|
2470
|
-
const _exhaustive = event;
|
|
2471
|
-
throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
|
|
2472
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
|
+
});
|
|
2473
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 };
|
|
2474
2381
|
}
|
|
2475
|
-
function
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
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
|
+
}
|
|
2479
2395
|
}
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
const out = {};
|
|
2484
|
-
for (const [key, value] of Object.entries(entries)) {
|
|
2485
|
-
if (value !== void 0) {
|
|
2486
|
-
out[key] = value;
|
|
2487
|
-
}
|
|
2396
|
+
function persistedEventsForParsedTrace(parsed) {
|
|
2397
|
+
if ((parsed.format === "0.2" || parsed.format === "1.0") && parsed.persisted.length > 0) {
|
|
2398
|
+
return [...parsed.persisted];
|
|
2488
2399
|
}
|
|
2489
|
-
|
|
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"
|
|
2411
|
+
});
|
|
2490
2412
|
}
|
|
2491
|
-
function
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2413
|
+
function isRecord6(value) {
|
|
2414
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2415
|
+
}
|
|
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;
|
|
2495
2423
|
}
|
|
2496
|
-
return
|
|
2424
|
+
return void 0;
|
|
2497
2425
|
}
|
|
2498
|
-
function
|
|
2499
|
-
const
|
|
2500
|
-
|
|
2501
|
-
|
|
2426
|
+
function readRecordField(record, key) {
|
|
2427
|
+
const value = record[key];
|
|
2428
|
+
return isRecord6(value) ? value : void 0;
|
|
2429
|
+
}
|
|
2430
|
+
function parseJsonDocument(content) {
|
|
2431
|
+
return JSON.parse(content);
|
|
2432
|
+
}
|
|
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);
|
|
2437
|
+
}
|
|
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
|
+
}
|
|
2502
2451
|
return {
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2452
|
+
spans,
|
|
2453
|
+
confidence: 0.82,
|
|
2454
|
+
description: "OpenInference span array",
|
|
2455
|
+
warnings,
|
|
2456
|
+
unsupportedFields
|
|
2506
2457
|
};
|
|
2507
2458
|
}
|
|
2508
|
-
if (
|
|
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
|
+
}
|
|
2509
2488
|
return {
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
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
|
|
2513
2495
|
};
|
|
2514
2496
|
}
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
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;
|
|
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
|
+
});
|
|
2552
2506
|
}
|
|
2507
|
+
return {
|
|
2508
|
+
spans,
|
|
2509
|
+
confidence: 0.8,
|
|
2510
|
+
description: "OpenInference data document",
|
|
2511
|
+
version,
|
|
2512
|
+
warnings,
|
|
2513
|
+
unsupportedFields
|
|
2514
|
+
};
|
|
2553
2515
|
}
|
|
2554
|
-
if (
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
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";
|
|
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
|
+
};
|
|
2582
2525
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
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
|
+
};
|
|
2595
2540
|
}
|
|
2596
|
-
|
|
2597
|
-
|
|
2541
|
+
return void 0;
|
|
2542
|
+
}
|
|
2543
|
+
function parseUnixNanoToIso(value) {
|
|
2544
|
+
if (typeof value === "bigint" && value >= 0n) {
|
|
2545
|
+
return new Date(Number(value / 1000000n)).toISOString();
|
|
2598
2546
|
}
|
|
2599
|
-
if (
|
|
2600
|
-
|
|
2547
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
2548
|
+
return new Date(Math.floor(value / 1e6)).toISOString();
|
|
2601
2549
|
}
|
|
2602
|
-
|
|
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));
|
|
2550
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
2551
|
+
return new Date(Number(BigInt(value) / 1000000n)).toISOString();
|
|
2615
2552
|
}
|
|
2616
|
-
return
|
|
2617
|
-
}
|
|
2618
|
-
|
|
2619
|
-
// packages/core/src/logs/tree-builder.ts
|
|
2620
|
-
function inc(map, key) {
|
|
2621
|
-
map[key] = (map[key] ?? 0) + 1;
|
|
2553
|
+
return void 0;
|
|
2622
2554
|
}
|
|
2623
|
-
function
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
}
|
|
2629
|
-
if (hasRunning) return "running";
|
|
2630
|
-
return "ok";
|
|
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();
|
|
2631
2560
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
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;
|
|
2635
2565
|
}
|
|
2636
|
-
|
|
2637
|
-
const
|
|
2638
|
-
|
|
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;
|
|
2566
|
+
for (const key of isoKeys) {
|
|
2567
|
+
const iso = parseIsoTime(span[key]);
|
|
2568
|
+
if (iso !== void 0) return iso;
|
|
2696
2569
|
}
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
// packages/core/src/persisted/tree-bridge.ts
|
|
2700
|
-
function persistedInspectEventsToRunTrees(events, options) {
|
|
2701
|
-
const inspectEvents = persistedInspectEventsToInspectEvents(events, {
|
|
2702
|
-
skipInvalid: options?.skipInvalid
|
|
2703
|
-
});
|
|
2704
|
-
return new TreeBuilder().build(inspectEvents);
|
|
2570
|
+
return void 0;
|
|
2705
2571
|
}
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
2713
|
-
"trace_id",
|
|
2714
|
-
"traceId",
|
|
2715
|
-
"span_id",
|
|
2716
|
-
"spanId",
|
|
2717
|
-
"parent_span_id",
|
|
2718
|
-
"parentSpanId",
|
|
2719
|
-
"name",
|
|
2720
|
-
"start_time_unix_nano",
|
|
2721
|
-
"startTimeUnixNano",
|
|
2722
|
-
"end_time_unix_nano",
|
|
2723
|
-
"endTimeUnixNano",
|
|
2724
|
-
"start_time",
|
|
2725
|
-
"startTime",
|
|
2726
|
-
"end_time",
|
|
2727
|
-
"endTime",
|
|
2728
|
-
"attributes",
|
|
2729
|
-
"status",
|
|
2730
|
-
"kind",
|
|
2731
|
-
"span_kind",
|
|
2732
|
-
"spanKind"
|
|
2733
|
-
]);
|
|
2734
|
-
var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
|
|
2735
|
-
"input.value",
|
|
2736
|
-
"output.value",
|
|
2737
|
-
"input.mime_type",
|
|
2738
|
-
"output.mime_type",
|
|
2739
|
-
"llm.input_messages",
|
|
2740
|
-
"llm.output_messages",
|
|
2741
|
-
"llm.prompts",
|
|
2742
|
-
"llm.completions",
|
|
2743
|
-
"retrieval.documents",
|
|
2744
|
-
"reranker.input_documents",
|
|
2745
|
-
"reranker.output_documents",
|
|
2746
|
-
"document.content",
|
|
2747
|
-
"gen_ai.prompt",
|
|
2748
|
-
"gen_ai.completion",
|
|
2749
|
-
"gen_ai.input.messages",
|
|
2750
|
-
"gen_ai.output.messages"
|
|
2751
|
-
];
|
|
2752
|
-
var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
2753
|
-
"traceId",
|
|
2754
|
-
"spanId",
|
|
2755
|
-
"parentSpanId",
|
|
2756
|
-
"name",
|
|
2757
|
-
"kind",
|
|
2758
|
-
"startTimeUnixNano",
|
|
2759
|
-
"endTimeUnixNano",
|
|
2760
|
-
"attributes",
|
|
2761
|
-
"events",
|
|
2762
|
-
"status",
|
|
2763
|
-
"droppedAttributesCount",
|
|
2764
|
-
"droppedEventsCount",
|
|
2765
|
-
"droppedLinksCount",
|
|
2766
|
-
"links",
|
|
2767
|
-
"flags"
|
|
2768
|
-
]);
|
|
2769
|
-
var TraceReadError = class extends Error {
|
|
2770
|
-
code;
|
|
2771
|
-
warnings;
|
|
2772
|
-
constructor(code, message, warnings = []) {
|
|
2773
|
-
super(message);
|
|
2774
|
-
this.name = "TraceReadError";
|
|
2775
|
-
this.code = code;
|
|
2776
|
-
this.warnings = warnings;
|
|
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;
|
|
2777
2578
|
}
|
|
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
|
-
};
|
|
2787
|
-
}
|
|
2788
|
-
function sortCandidates(candidates) {
|
|
2789
|
-
return [...candidates].sort((a, b) => {
|
|
2790
|
-
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
2791
|
-
return a.format.localeCompare(b.format);
|
|
2792
|
-
});
|
|
2579
|
+
return endMs - startMs;
|
|
2793
2580
|
}
|
|
2794
|
-
function
|
|
2795
|
-
return
|
|
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
|
+
);
|
|
2796
2585
|
}
|
|
2797
|
-
function
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2586
|
+
function summarizeAttributeValue(value) {
|
|
2587
|
+
if (typeof value === "string") {
|
|
2588
|
+
return { type: "string", length: value.length };
|
|
2589
|
+
}
|
|
2590
|
+
if (typeof value === "number") {
|
|
2591
|
+
return { type: "number", finite: Number.isFinite(value) };
|
|
2592
|
+
}
|
|
2593
|
+
if (typeof value === "boolean") {
|
|
2594
|
+
return { type: "boolean" };
|
|
2595
|
+
}
|
|
2596
|
+
if (Array.isArray(value)) {
|
|
2597
|
+
return { type: "array", length: value.length };
|
|
2598
|
+
}
|
|
2599
|
+
if (isRecord6(value)) {
|
|
2600
|
+
return { type: "object", keyCount: Object.keys(value).length };
|
|
2812
2601
|
}
|
|
2813
|
-
|
|
2814
|
-
}
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
const [sourceFile] = resolved.sourceFiles;
|
|
2818
|
-
return warnings.map((warning) => ({
|
|
2819
|
-
...warning,
|
|
2820
|
-
sourceFile: warning.sourceFile ?? sourceFile
|
|
2821
|
-
}));
|
|
2822
|
-
}
|
|
2823
|
-
function findReaderByFormat(format, readers) {
|
|
2824
|
-
return readers.find((reader) => reader.format === format);
|
|
2602
|
+
if (value === null) {
|
|
2603
|
+
return { type: "null" };
|
|
2604
|
+
}
|
|
2605
|
+
return { type: typeof value };
|
|
2825
2606
|
}
|
|
2826
|
-
|
|
2827
|
-
const
|
|
2828
|
-
|
|
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}`);
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
out[key] = value;
|
|
2620
|
+
}
|
|
2621
|
+
if (summarizedKeys.length > 0) {
|
|
2622
|
+
out["openinference.summarized_attributes"] = summarizedKeys;
|
|
2623
|
+
warnings.push({
|
|
2624
|
+
code: "openinference_sensitive_attribute_summarized",
|
|
2625
|
+
message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
|
|
2626
|
+
severity: "warning"
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
return { attributes: out, warnings, unsupportedFields };
|
|
2829
2630
|
}
|
|
2830
|
-
|
|
2831
|
-
const
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
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 };
|
|
2683
|
+
}
|
|
2836
2684
|
}
|
|
2837
|
-
function
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
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";
|
|
2696
|
+
default:
|
|
2697
|
+
return "unknown";
|
|
2698
|
+
}
|
|
2848
2699
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
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;
|
|
2853
2708
|
}
|
|
2854
|
-
if (
|
|
2855
|
-
|
|
2856
|
-
assertInputWithinBounds(content);
|
|
2857
|
-
return { content, sourceFiles: [] };
|
|
2709
|
+
if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
|
|
2710
|
+
usage.output = completion;
|
|
2858
2711
|
}
|
|
2859
|
-
if (
|
|
2860
|
-
|
|
2861
|
-
assertInputWithinBounds(content, input.path);
|
|
2862
|
-
return { content, sourceFiles: [input.path] };
|
|
2712
|
+
if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
|
|
2713
|
+
usage.total = total;
|
|
2863
2714
|
}
|
|
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
|
-
};
|
|
2715
|
+
if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
|
|
2716
|
+
usage.cached = cached;
|
|
2875
2717
|
}
|
|
2876
|
-
|
|
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;
|
|
2877
2722
|
}
|
|
2878
|
-
function
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
let validRows = 0;
|
|
2883
|
-
let invalidJsonRows = 0;
|
|
2884
|
-
let unknownSchemaRows = 0;
|
|
2885
|
-
let firstInvalidJsonLine;
|
|
2886
|
-
let firstUnknownSchemaLine;
|
|
2887
|
-
let lineNumber = 0;
|
|
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;
|
|
2898
|
-
continue;
|
|
2899
|
-
}
|
|
2900
|
-
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
|
|
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;
|
|
2723
|
+
function readOpenInferenceConfidence(attributes) {
|
|
2724
|
+
const confidence = attributes["agent_inspect.confidence"];
|
|
2725
|
+
if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
|
|
2726
|
+
return confidence;
|
|
2920
2727
|
}
|
|
2728
|
+
return "correlated";
|
|
2729
|
+
}
|
|
2730
|
+
function mapOpenInferenceSpan(span, index, version) {
|
|
2731
|
+
const pathPrefix = `spans[${index}]`;
|
|
2921
2732
|
const warnings = [];
|
|
2922
|
-
|
|
2733
|
+
const unsupportedFields = [];
|
|
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);
|
|
2746
|
+
warnings.push({
|
|
2747
|
+
code: "openinference_unsupported_field_summarized",
|
|
2748
|
+
message: `Unsupported OpenInference span field "${key}" was summarized.`,
|
|
2749
|
+
severity: "warning",
|
|
2750
|
+
field: `${pathPrefix}.${key}`
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
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) {
|
|
2923
2770
|
warnings.push({
|
|
2924
|
-
code: "
|
|
2925
|
-
message:
|
|
2771
|
+
code: "openinference_missing_start_time",
|
|
2772
|
+
message: "OpenInference span is missing a valid start time; using Unix epoch.",
|
|
2926
2773
|
severity: "warning",
|
|
2927
|
-
|
|
2774
|
+
field: `${pathPrefix}.start_time_unix_nano`
|
|
2928
2775
|
});
|
|
2776
|
+
unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
|
|
2929
2777
|
}
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
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 } : {}
|
|
2805
|
+
}
|
|
2806
|
+
};
|
|
2807
|
+
if (status !== void 0) {
|
|
2808
|
+
event.status = status;
|
|
2937
2809
|
}
|
|
2938
|
-
|
|
2939
|
-
|
|
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
|
-
}
|
|
2946
|
-
function agentInspectFormatLabel(format) {
|
|
2947
|
-
switch (format) {
|
|
2948
|
-
case "0.1":
|
|
2949
|
-
return "agent-inspect-v0.1-jsonl";
|
|
2950
|
-
case "0.2":
|
|
2951
|
-
return "agent-inspect-v0.2-jsonl";
|
|
2952
|
-
case "1.0":
|
|
2953
|
-
return "agent-inspect-v1.0-jsonl";
|
|
2954
|
-
case "mixed":
|
|
2955
|
-
return "agent-inspect-mixed-jsonl";
|
|
2956
|
-
default:
|
|
2957
|
-
return "agent-inspect-jsonl";
|
|
2810
|
+
if (startedAt !== void 0) {
|
|
2811
|
+
event.startedAt = startedAt;
|
|
2958
2812
|
}
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
if ((parsed.format === "0.2" || parsed.format === "1.0") && parsed.persisted.length > 0) {
|
|
2962
|
-
return [...parsed.persisted];
|
|
2813
|
+
if (endedAt !== void 0) {
|
|
2814
|
+
event.endedAt = endedAt;
|
|
2963
2815
|
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
return traceEventToPersistedInspectEvent(row.event, {
|
|
2968
|
-
eventIndex: index,
|
|
2969
|
-
sourceName: "agent-inspect-jsonl-reader"
|
|
2970
|
-
});
|
|
2971
|
-
});
|
|
2816
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
2817
|
+
if (durationMs !== void 0) {
|
|
2818
|
+
event.durationMs = durationMs;
|
|
2972
2819
|
}
|
|
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;
|
|
2820
|
+
if (tokenUsage !== void 0) {
|
|
2821
|
+
event.tokenUsage = tokenUsage;
|
|
2987
2822
|
}
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2823
|
+
if (status === "error") {
|
|
2824
|
+
event.error = {
|
|
2825
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
|
|
2826
|
+
};
|
|
2827
|
+
}
|
|
2828
|
+
return {
|
|
2829
|
+
event,
|
|
2830
|
+
warnings,
|
|
2831
|
+
unsupportedFields,
|
|
2832
|
+
spanId,
|
|
2833
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
2834
|
+
};
|
|
2996
2835
|
}
|
|
2997
|
-
function
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
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
|
+
};
|
|
3001
2852
|
}
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
const
|
|
3007
|
-
if (
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
});
|
|
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;
|
|
3014
2864
|
}
|
|
2865
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
2866
|
+
if (!document) return void 0;
|
|
3015
2867
|
return {
|
|
3016
|
-
|
|
3017
|
-
confidence:
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
2868
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
2869
|
+
confidence: document.confidence,
|
|
2870
|
+
readerName: "OpenInference JSON",
|
|
2871
|
+
description: document.description,
|
|
2872
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
3021
2873
|
};
|
|
3022
|
-
}
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
warnings.push({
|
|
3031
|
-
code: "openinference_no_valid_spans",
|
|
3032
|
-
message: "OpenInference document did not contain any valid spans.",
|
|
3033
|
-
severity: "error"
|
|
3034
|
-
});
|
|
3035
|
-
return {
|
|
3036
|
-
spans,
|
|
3037
|
-
confidence: 0.7,
|
|
3038
|
-
description: "Malformed OpenInference document",
|
|
3039
|
-
version,
|
|
3040
|
-
warnings,
|
|
3041
|
-
unsupportedFields
|
|
3042
|
-
};
|
|
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
|
+
);
|
|
3043
2882
|
}
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
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
|
+
]);
|
|
3051
2894
|
}
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
severity: "warning"
|
|
3069
|
-
});
|
|
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
|
+
);
|
|
3070
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));
|
|
3071
2921
|
return {
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
version,
|
|
3076
|
-
warnings,
|
|
3077
|
-
unsupportedFields
|
|
3078
|
-
};
|
|
3079
|
-
}
|
|
3080
|
-
if (looksLikeOpenInferenceSpan(root)) {
|
|
3081
|
-
return {
|
|
3082
|
-
spans: [root],
|
|
3083
|
-
confidence: 0.76,
|
|
3084
|
-
description: "OpenInference single span",
|
|
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/exporters/helpers.ts
|
|
3716
|
+
var REDACT_SUBSTRINGS = [
|
|
3717
|
+
"authorization",
|
|
3718
|
+
"cookie",
|
|
3719
|
+
"token",
|
|
3720
|
+
"apikey",
|
|
3721
|
+
"password",
|
|
3722
|
+
"secret",
|
|
3723
|
+
"email"
|
|
3724
|
+
];
|
|
3725
|
+
function shouldRedactKey(key) {
|
|
3726
|
+
const k = key.toLowerCase();
|
|
3727
|
+
for (const s of REDACT_SUBSTRINGS) {
|
|
3728
|
+
if (k.includes(s)) return true;
|
|
3867
3729
|
}
|
|
3868
|
-
|
|
3869
|
-
|
|
3730
|
+
return false;
|
|
3731
|
+
}
|
|
3732
|
+
function safeString(value, maxLength) {
|
|
3733
|
+
if (value === null || value === void 0) return "";
|
|
3734
|
+
let s;
|
|
3735
|
+
if (typeof value === "string") s = value;
|
|
3736
|
+
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
3737
|
+
else s = stableJson(value, false);
|
|
3738
|
+
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
3739
|
+
return `${s.slice(0, maxLength)}\u2026`;
|
|
3870
3740
|
}
|
|
3871
|
-
|
|
3872
|
-
|
|
3741
|
+
return s;
|
|
3742
|
+
}
|
|
3743
|
+
function escapeMarkdown(value) {
|
|
3744
|
+
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
3745
|
+
}
|
|
3746
|
+
function sortKeysDeep(input) {
|
|
3747
|
+
if (input === null || typeof input !== "object") return input;
|
|
3748
|
+
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
3749
|
+
const o = input;
|
|
3750
|
+
const out = {};
|
|
3751
|
+
for (const k of Object.keys(o).sort()) {
|
|
3752
|
+
out[k] = sortKeysDeep(o[k]);
|
|
3873
3753
|
}
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3754
|
+
return out;
|
|
3755
|
+
}
|
|
3756
|
+
function stableJson(value, pretty) {
|
|
3757
|
+
const sorted = sortKeysDeep(value);
|
|
3758
|
+
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
3759
|
+
}
|
|
3760
|
+
function compactAttributes3(attrs, options) {
|
|
3761
|
+
if (attrs === void 0) return {};
|
|
3762
|
+
const maxLen = options?.maxLength ?? 500;
|
|
3763
|
+
const out = {};
|
|
3764
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
3765
|
+
if (shouldRedactKey(key)) {
|
|
3766
|
+
out[key] = "[REDACTED]";
|
|
3767
|
+
continue;
|
|
3887
3768
|
}
|
|
3769
|
+
const v = attrs[key];
|
|
3770
|
+
out[key] = compactValue(v, maxLen);
|
|
3888
3771
|
}
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
"
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3772
|
+
return out;
|
|
3773
|
+
}
|
|
3774
|
+
function compactValue(value, maxLen, redacted) {
|
|
3775
|
+
if (value === null || typeof value !== "object") {
|
|
3776
|
+
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
3777
|
+
}
|
|
3778
|
+
if (Array.isArray(value)) {
|
|
3779
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
|
|
3780
|
+
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
3781
|
+
return arr;
|
|
3782
|
+
}
|
|
3783
|
+
const o = value;
|
|
3784
|
+
const inner = {};
|
|
3785
|
+
for (const k of Object.keys(o)) {
|
|
3786
|
+
if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
3787
|
+
else inner[k] = compactValue(o[k], maxLen);
|
|
3788
|
+
}
|
|
3789
|
+
return inner;
|
|
3790
|
+
}
|
|
3791
|
+
function flattenTree(tree) {
|
|
3792
|
+
const out = [];
|
|
3793
|
+
function walk(nodes) {
|
|
3794
|
+
for (const n of nodes) {
|
|
3795
|
+
out.push(n);
|
|
3796
|
+
if (n.children.length > 0) walk(n.children);
|
|
3903
3797
|
}
|
|
3904
3798
|
}
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3799
|
+
walk(tree.children);
|
|
3800
|
+
return out;
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
// packages/core/src/diff/comparable.ts
|
|
3804
|
+
function extractOutputPreview(meta) {
|
|
3805
|
+
if (meta === void 0) return void 0;
|
|
3806
|
+
if ("outputPreview" in meta) return meta.outputPreview;
|
|
3807
|
+
if ("resultPreview" in meta) return meta.resultPreview;
|
|
3808
|
+
return void 0;
|
|
3809
|
+
}
|
|
3810
|
+
function mapStepStatus(s) {
|
|
3811
|
+
if (s === void 0) return "running";
|
|
3812
|
+
return s;
|
|
3813
|
+
}
|
|
3814
|
+
function manualTraceEventsToComparableRun(events) {
|
|
3815
|
+
const started = events.find((e) => e.event === "run_started");
|
|
3816
|
+
if (!started || started.event !== "run_started") {
|
|
3817
|
+
throw new Error("Invalid trace: missing run_started");
|
|
3910
3818
|
}
|
|
3911
|
-
const
|
|
3912
|
-
const
|
|
3913
|
-
const
|
|
3914
|
-
const
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
);
|
|
3919
|
-
const
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3819
|
+
const rs = started;
|
|
3820
|
+
const runId = rs.runId;
|
|
3821
|
+
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
3822
|
+
const lastCompleted = completedAll[completedAll.length - 1];
|
|
3823
|
+
let runStatus;
|
|
3824
|
+
if (lastCompleted === void 0) runStatus = "running";
|
|
3825
|
+
else runStatus = lastCompleted.status;
|
|
3826
|
+
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
3827
|
+
const steps = /* @__PURE__ */ new Map();
|
|
3828
|
+
let order = 0;
|
|
3829
|
+
for (const e of events) {
|
|
3830
|
+
if (e.event !== "step_started") continue;
|
|
3831
|
+
const s = e;
|
|
3832
|
+
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
3833
|
+
steps.set(s.stepId, {
|
|
3834
|
+
id: s.stepId,
|
|
3835
|
+
parentId: s.parentId,
|
|
3836
|
+
name: s.name,
|
|
3837
|
+
type: s.type,
|
|
3838
|
+
order: order++,
|
|
3839
|
+
timestamp: s.timestamp,
|
|
3840
|
+
metadata: meta
|
|
3928
3841
|
});
|
|
3929
3842
|
}
|
|
3930
|
-
const
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
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 } : {}
|
|
3843
|
+
for (const e of events) {
|
|
3844
|
+
if (e.event !== "step_completed") continue;
|
|
3845
|
+
const acc = steps.get(e.stepId);
|
|
3846
|
+
if (!acc) continue;
|
|
3847
|
+
acc.status = e.status;
|
|
3848
|
+
acc.durationMs = e.durationMs;
|
|
3849
|
+
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
3850
|
+
const extra = e;
|
|
3851
|
+
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
3852
|
+
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
3956
3853
|
}
|
|
3957
|
-
};
|
|
3958
|
-
if (status !== void 0) {
|
|
3959
|
-
event.status = status;
|
|
3960
|
-
}
|
|
3961
|
-
if (startedAt !== void 0) {
|
|
3962
|
-
event.startedAt = startedAt;
|
|
3963
|
-
}
|
|
3964
|
-
if (endedAt !== void 0) {
|
|
3965
|
-
event.endedAt = endedAt;
|
|
3966
|
-
}
|
|
3967
|
-
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
3968
|
-
if (durationMs !== void 0) {
|
|
3969
|
-
event.durationMs = durationMs;
|
|
3970
3854
|
}
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3855
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
3856
|
+
for (const acc of steps.values()) {
|
|
3857
|
+
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
3858
|
+
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
3859
|
+
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
3860
|
+
}
|
|
3861
|
+
const outputPreview = extractOutputPreview(meta);
|
|
3862
|
+
const sc = {
|
|
3863
|
+
id: acc.id,
|
|
3864
|
+
name: acc.name,
|
|
3865
|
+
type: acc.type,
|
|
3866
|
+
status: mapStepStatus(acc.status),
|
|
3867
|
+
durationMs: acc.durationMs,
|
|
3868
|
+
error: acc.errorMsg,
|
|
3869
|
+
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
3870
|
+
outputPreview,
|
|
3871
|
+
children: []
|
|
3977
3872
|
};
|
|
3873
|
+
nodes.set(acc.id, sc);
|
|
3978
3874
|
}
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
3875
|
+
const roots = [];
|
|
3876
|
+
const sortByOrder = (a, b) => {
|
|
3877
|
+
const oa = steps.get(a.id)?.order ?? 0;
|
|
3878
|
+
const ob = steps.get(b.id)?.order ?? 0;
|
|
3879
|
+
return oa - ob;
|
|
3985
3880
|
};
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3881
|
+
for (const acc of steps.values()) {
|
|
3882
|
+
const node = nodes.get(acc.id);
|
|
3883
|
+
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
3884
|
+
nodes.get(acc.parentId).children.push(node);
|
|
3885
|
+
} else {
|
|
3886
|
+
roots.push(node);
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
roots.sort(sortByOrder);
|
|
3890
|
+
for (const n of nodes.values()) {
|
|
3891
|
+
n.children.sort(sortByOrder);
|
|
3995
3892
|
}
|
|
3996
3893
|
return {
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
3894
|
+
runId,
|
|
3895
|
+
name: rs.name,
|
|
3896
|
+
status: runStatus,
|
|
3897
|
+
durationMs,
|
|
3898
|
+
steps: roots
|
|
4000
3899
|
};
|
|
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
|