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