@tangle-network/charts 0.18.1 → 0.19.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/README.md
CHANGED
|
@@ -335,7 +335,7 @@ const trace = otlpFlowTrace(spans, { price: catalogPrice(await (await fetch(cata
|
|
|
335
335
|
<FlowWaterfall trace={trace} live={running} defaultView="agents" renderIcon={brandMark} />;
|
|
336
336
|
```
|
|
337
337
|
|
|
338
|
-
`defaultView="agents"` opens the agent timeline: one row per agent with its model, thinking and tool marks, hatched waiting while it delegates, elbow connectors to each agent it spawned and back, retry and failure glyphs, and a headline with the measured speedup over sequential (agent busy time over wall time). Expanding an agent opens the span view with that agent unfolded. The span view virtualizes rows (5,000 spans scroll at 58+ fps in headless Chromium), pins ancestors while their children scroll, and labels each bar with its duration and recorded cost. Hover shows a span card; click pins it and zooms the axis to a span too narrow to read. `onSelect(span)` hears each opened row and pinned bar, so a host can open its own drill-down; `markers={[{ at, label, tone }]}` draws labelled instants, such as releases, across the rows. Give an agent span an `outcome` (`{ tone, label }`) and its lane takes the outcome color and the header a legend. Ctrl/Cmd+wheel or `+`/`-`/`0` zoom, Shift+wheel pans, the scrub track under the controls shows the whole trace, and arrow keys walk the tree.
|
|
338
|
+
`defaultView="agents"` opens the agent timeline: one row per agent with its model, thinking and tool marks, hatched waiting while it delegates, elbow connectors to each agent it spawned and back, retry and failure glyphs, and a headline with the measured speedup over sequential (agent busy time over wall time). Expanding an agent opens the span view with that agent unfolded. The span view virtualizes rows (5,000 spans scroll at 58+ fps in headless Chromium), pins ancestors while their children scroll, and labels each bar with its duration and recorded cost. Hover shows a span card; click pins it and zooms the axis to a span too narrow to read. `onSelect(span)` hears each opened row and pinned bar, so a host can open its own drill-down; `markers={[{ at, label, tone }]}` draws labelled instants, such as releases, across the rows. Give an agent span an `outcome` (`{ tone, label }`) and its lane takes the outcome color and the header a legend. `detail` becomes the agent's subtitle, and `facts` (`[{ label, value }]`) adds cells to its card. Ctrl/Cmd+wheel or `+`/`-`/`0` zoom, Shift+wheel pans, the scrub track under the controls shows the whole trace, and arrow keys walk the tree.
|
|
339
339
|
|
|
340
340
|
Live views append spans as they arrive: `traces watch <id|run-dir> --format jsonl --spans` emits a `traces.stream.span` event per new or changed span, and `otlpFlowTrace` keeps the last span per `span_id`. Running agent spans grow to the clock between deltas without React renders (CSS variables carry the clock). Search supports Cmd/Ctrl+F and keeps ancestor context. Missing cost and authoritative LLM counts stay Unknown; a cost priced from a catalog is labelled list price. Invalid intervals retain a per-row `—` timing mark. The static phase waterfall remains a separate chart for disjoint phases.
|
|
341
341
|
|
|
@@ -334,6 +334,11 @@ interface FlowSpan {
|
|
|
334
334
|
handoffTo?: string;
|
|
335
335
|
/** The host's verdict on an agent span, such as shipped or crashed: colors its lane and joins the legend. */
|
|
336
336
|
outcome?: FlowOutcome;
|
|
337
|
+
/** Up to three host facts shown as extra key/value cells in the span card, such as releases or commits. */
|
|
338
|
+
facts?: readonly {
|
|
339
|
+
label: string;
|
|
340
|
+
value: string;
|
|
341
|
+
}[];
|
|
337
342
|
meta?: Record<string, unknown>;
|
|
338
343
|
}
|
|
339
344
|
/** A host verdict: `label` is the legend text, `tone` the status color. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as BreakdownInput, B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, H as Tier, I as Interval, L as IntervalMethod, M as CostBasis, N as Estimate, O as YTarget, P as Exclusion, R as MatrixCell, S as ChartMark, T as MarkerShape, U as TierCounts, V as Setup, W as isRefusal, _ as checkerColor, a as TimelineLane, b as BandKind, c as TimelineTick, d as ENDING_COLORS, f as ENDING_ORDER, g as TickOutcome, h as RunEnding, i as FlowTrace, j as ComparisonRow, k as YTick, l as timeline, m as OUTCOME_ORDER, n as FlowOutcome, o as TimelineOptions, p as OUTCOME_COLORS, r as FlowSpan, s as TimelineSpan, t as FlowMarker, u as CHECKER_HUES, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph, z as RateRow } from "./flow-trace-
|
|
1
|
+
import { A as BreakdownInput, B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, H as Tier, I as Interval, L as IntervalMethod, M as CostBasis, N as Estimate, O as YTarget, P as Exclusion, R as MatrixCell, S as ChartMark, T as MarkerShape, U as TierCounts, V as Setup, W as isRefusal, _ as checkerColor, a as TimelineLane, b as BandKind, c as TimelineTick, d as ENDING_COLORS, f as ENDING_ORDER, g as TickOutcome, h as RunEnding, i as FlowTrace, j as ComparisonRow, k as YTick, l as timeline, m as OUTCOME_ORDER, n as FlowOutcome, o as TimelineOptions, p as OUTCOME_COLORS, r as FlowSpan, s as TimelineSpan, t as FlowMarker, u as CHECKER_HUES, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph, z as RateRow } from "./flow-trace-D6RDkVrJ.js";
|
|
2
2
|
//#region src/block.d.ts
|
|
3
3
|
interface RenderFigureOptions {
|
|
4
4
|
/** Chart uses a closed data disclosure; plot keeps only the SVG and accessible data. */
|
|
@@ -160,6 +160,7 @@
|
|
|
160
160
|
.fw-root .fw-view-agents .fw-label { padding-left: 16px; gap: 12px; border-right: 0; }
|
|
161
161
|
.fw-root .fw-view-agents .fw-names { flex-direction: column; align-items: flex-start; gap: 0; }
|
|
162
162
|
.fw-root .fw-view-agents .fw-span-name { font-weight: 600; line-height: 18px; }
|
|
163
|
+
.fw-root .fw-view-agents .fw-subtitle { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fw-muted); font-size: 12px; line-height: 16px; }
|
|
163
164
|
.fw-root .fw-view-agents .fw-detail { font-size: 12px; line-height: 16px; }
|
|
164
165
|
.fw-root .fw-agent-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--line, var(--fw-muted)); align-self: flex-start; margin-top: calc((var(--fw-row-h) - 34px) / 2 + 8px); }
|
|
165
166
|
|
|
@@ -223,6 +224,9 @@
|
|
|
223
224
|
opacity: clamp(0, (var(--fw-t) - var(--s)) * 1000, 1);
|
|
224
225
|
}
|
|
225
226
|
.fw-root .fw-view-spans .fw-link { opacity: calc(clamp(0, (var(--fw-t) - var(--s)) * 1000, 1) * .7); }
|
|
227
|
+
/* Agent-view connectors stay quiet at 30% until their agent is hovered, pinned or focused. */
|
|
228
|
+
.fw-root .fw-view-agents .fw-link:not(.fw-link-hot) { opacity: calc(clamp(0, (var(--fw-t) - var(--s)) * 1000, 1) * .3); }
|
|
229
|
+
.fw-root .fw-link { transition: opacity 160ms var(--fw-ease); }
|
|
226
230
|
.fw-root .fw-link-down { left: calc(var(--x) - 8px); border-left-width: 1.5px; border-bottom-left-radius: 6px; }
|
|
227
231
|
.fw-root .fw-link-up { left: var(--x); border-right-width: 1.5px; border-bottom-right-radius: 6px; }
|
|
228
232
|
.fw-root .fw-link::before { content: ""; position: absolute; top: -3.5px; width: 5px; height: 5px; border-radius: 50%; background: var(--line); box-shadow: 0 0 0 1.5px var(--fw-surface); }
|
|
@@ -233,6 +237,8 @@
|
|
|
233
237
|
.fw-root .fw-card-anchor { display: contents; }
|
|
234
238
|
.fw-root .fw-card { position: absolute; z-index: 20; width: 320px; padding: 12px; border-radius: 12px; background: var(--fw-raised); border: 1px solid var(--fw-border);
|
|
235
239
|
box-shadow: 0 12px 32px -8px rgb(0 0 0 / .28), 0 2px 6px rgb(0 0 0 / .08); animation: fw-arrive 140ms var(--fw-ease); }
|
|
240
|
+
/* A hover card never covers the next mark: only a pinned card (a dialog) takes the pointer. */
|
|
241
|
+
.fw-root .fw-card[role="tooltip"] { pointer-events: none; }
|
|
236
242
|
.fw-root .fw-card-above .fw-card { transform: translateY(-100%); animation-name: fw-arrive-above; }
|
|
237
243
|
.fw-root .fw-card-agent { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 12px; font-weight: 600; }
|
|
238
244
|
.fw-root .fw-card-agent .fw-agent-dot { margin-top: 0; align-self: center; }
|
|
@@ -257,7 +263,9 @@
|
|
|
257
263
|
.fw-root .fw-card-compact { width: 280px; padding: 12px; }
|
|
258
264
|
.fw-root .fw-card-compact .fw-card-agent { margin-bottom: 8px; font-size: 14px; line-height: 20px; }
|
|
259
265
|
.fw-root .fw-card-who { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
260
|
-
.fw-root .fw-card-
|
|
266
|
+
.fw-root .fw-card-detail { margin: -4px 0 8px 16px; color: var(--fw-muted); font-size: 12px; line-height: 16px; overflow-wrap: anywhere; }
|
|
267
|
+
.fw-root .fw-pill-outcome { color: var(--line); background: color-mix(in oklab, var(--tone) 16%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--tone) 35%, transparent); }
|
|
268
|
+
.fw-root .fw-card-cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); gap: 8px; padding: 8px 12px; border-radius: 8px; background: var(--fw-well); }
|
|
261
269
|
.fw-root .fw-card-cells dt { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
|
|
262
270
|
.fw-root .fw-card-cells dd { font-size: 14px; line-height: 20px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
263
271
|
.fw-root .fw-card-result { margin-top: 8px; font-size: 14px; line-height: 20px; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, O as YTarget, S as ChartMark, T as MarkerShape, a as TimelineLane, b as BandKind, c as TimelineTick, g as TickOutcome, i as FlowTrace, k as YTick, o as TimelineOptions, r as FlowSpan, s as TimelineSpan, t as FlowMarker, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph } from "../flow-trace-
|
|
1
|
+
import { B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, O as YTarget, S as ChartMark, T as MarkerShape, a as TimelineLane, b as BandKind, c as TimelineTick, g as TickOutcome, i as FlowTrace, k as YTick, o as TimelineOptions, r as FlowSpan, s as TimelineSpan, t as FlowMarker, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph } from "../flow-trace-D6RDkVrJ.js";
|
|
2
2
|
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
3
3
|
//#region src/react/sparkline.d.ts
|
|
4
4
|
type SparklineDirection = "rising" | "falling" | "flat";
|
package/dist/react/index.js
CHANGED
|
@@ -2663,16 +2663,22 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2663
2663
|
const duration = Number.isFinite(end - span.startMs) && end >= span.startMs ? formatSpanDuration(end - span.startMs) : "—";
|
|
2664
2664
|
if (compact) {
|
|
2665
2665
|
const who = owner ?? row;
|
|
2666
|
+
const outcome = who.span.outcome;
|
|
2666
2667
|
const tokenCount = span.promptTokens !== void 0 || span.completionTokens !== void 0 ? (span.promptTokens ?? 0) + (span.completionTokens ?? 0) : void 0;
|
|
2667
2668
|
const cells = [[span.kind === "model" ? "Thinking" : KIND_LABEL[span.kind], duration]];
|
|
2669
|
+
for (const fact of (span.facts ?? who.span.facts ?? []).slice(0, 3)) cells.push([fact.label, fact.value]);
|
|
2670
|
+
cells.push(["Started", `+${formatSpanDuration(Math.max(0, span.startMs))}`]);
|
|
2668
2671
|
if (tokenCount !== void 0) cells.push(["Tokens", count(tokenCount)]);
|
|
2669
2672
|
if (span.costUsd !== void 0) cells.push(["Cost", formatCost(span.costUsd)]);
|
|
2673
|
+
cells.splice(6);
|
|
2674
|
+
const sentence = [span.output, span.id === who.id ? void 0 : span.name].find((text) => text && text !== who.name);
|
|
2670
2675
|
return /* @__PURE__ */ jsxs("div", {
|
|
2671
2676
|
id,
|
|
2672
2677
|
role: pinned ? "dialog" : "tooltip",
|
|
2673
2678
|
"aria-label": pinned ? `${span.name} details` : void 0,
|
|
2674
2679
|
className: "fw-card fw-card-compact",
|
|
2675
2680
|
"data-tone": toneKey(who.tone),
|
|
2681
|
+
"data-outcome": outcome?.tone,
|
|
2676
2682
|
style: {
|
|
2677
2683
|
left,
|
|
2678
2684
|
top
|
|
@@ -2695,7 +2701,12 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2695
2701
|
className: "fw-card-who",
|
|
2696
2702
|
children: who.name
|
|
2697
2703
|
}),
|
|
2698
|
-
/* @__PURE__ */ jsx("span", {
|
|
2704
|
+
outcome ? /* @__PURE__ */ jsx("span", {
|
|
2705
|
+
className: "fw-pill fw-pill-outcome",
|
|
2706
|
+
"data-tone": "0",
|
|
2707
|
+
"data-outcome": outcome.tone,
|
|
2708
|
+
children: outcome.label
|
|
2709
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
2699
2710
|
className: `fw-pill fw-pill-${status.toLowerCase()}`,
|
|
2700
2711
|
children: status
|
|
2701
2712
|
}),
|
|
@@ -2713,13 +2724,17 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2713
2724
|
})
|
|
2714
2725
|
]
|
|
2715
2726
|
}),
|
|
2727
|
+
who.span.detail && /* @__PURE__ */ jsx("p", {
|
|
2728
|
+
className: "fw-card-detail",
|
|
2729
|
+
children: who.span.detail
|
|
2730
|
+
}),
|
|
2716
2731
|
/* @__PURE__ */ jsx("dl", {
|
|
2717
2732
|
className: "fw-card-cells",
|
|
2718
2733
|
children: cells.map(([name, value]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", { children: value })] }, name))
|
|
2719
2734
|
}),
|
|
2720
|
-
/* @__PURE__ */ jsx("p", {
|
|
2735
|
+
sentence && /* @__PURE__ */ jsx("p", {
|
|
2721
2736
|
className: "fw-card-result",
|
|
2722
|
-
children:
|
|
2737
|
+
children: sentence
|
|
2723
2738
|
})
|
|
2724
2739
|
]
|
|
2725
2740
|
});
|
|
@@ -2918,21 +2933,28 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
2918
2933
|
/* @__PURE__ */ jsxs("span", {
|
|
2919
2934
|
className: "fw-names",
|
|
2920
2935
|
title: `${row.name}${span.detail ? ` · ${span.detail}` : ""} · ${KIND_LABEL[span.kind]}${running ? " · running" : ""}${row.ok ? "" : " · failed"}`,
|
|
2921
|
-
children: [
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
"
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
+
children: [
|
|
2937
|
+
/* @__PURE__ */ jsxs("span", {
|
|
2938
|
+
className: `fw-span-name${isAgent || row.hasChildren ? " fw-strong" : ""}`,
|
|
2939
|
+
children: [running && /* @__PURE__ */ jsx("span", {
|
|
2940
|
+
className: "fw-spinner",
|
|
2941
|
+
role: "status",
|
|
2942
|
+
"aria-label": "running"
|
|
2943
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2944
|
+
className: "fw-name-text",
|
|
2945
|
+
children: row.name
|
|
2946
|
+
})]
|
|
2947
|
+
}),
|
|
2948
|
+
agentView && span.detail && /* @__PURE__ */ jsx("span", {
|
|
2949
|
+
className: "fw-subtitle",
|
|
2950
|
+
children: span.detail
|
|
2951
|
+
}),
|
|
2952
|
+
agentView && renderIcon && /* @__PURE__ */ jsx("span", {
|
|
2953
|
+
className: "fw-detail-icon",
|
|
2954
|
+
"aria-hidden": true,
|
|
2955
|
+
children: renderIcon(span)
|
|
2956
|
+
})
|
|
2957
|
+
]
|
|
2936
2958
|
}),
|
|
2937
2959
|
folded && /* @__PURE__ */ jsxs("span", {
|
|
2938
2960
|
className: "fw-count",
|
|
@@ -2992,7 +3014,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
2992
3014
|
});
|
|
2993
3015
|
}, (a, b) => a.row === b.row && a.view === b.view && a.top === b.top && a.collapsed === b.collapsed && a.searching === b.searching && a.slowest === b.slowest && a.active === b.active && a.focusable === b.focusable && a.selected === b.selected && a.sticky === b.sticky && a.renderIcon === b.renderIcon && (a.clockMs === b.clockMs || !open(a.row, a.clockMs) && !open(b.row, b.clockMs)));
|
|
2994
3016
|
/** An elbow connector between two rows at one moment, drawn in the row's tone; appears when the clock reaches it. */
|
|
2995
|
-
function LinkMark({ link, rowHeight }) {
|
|
3017
|
+
function LinkMark({ link, rowHeight, hot }) {
|
|
2996
3018
|
const upper = Math.min(link.from, link.to), lower = Math.max(link.from, link.to);
|
|
2997
3019
|
const style = {
|
|
2998
3020
|
...at(link.atMs, link.atMs),
|
|
@@ -3000,7 +3022,7 @@ function LinkMark({ link, rowHeight }) {
|
|
|
3000
3022
|
height: (lower - upper) * rowHeight - 5
|
|
3001
3023
|
};
|
|
3002
3024
|
return /* @__PURE__ */ jsx("span", {
|
|
3003
|
-
className: `fw-link fw-link-${link.dir}`,
|
|
3025
|
+
className: `fw-link fw-link-${link.dir}${hot ? " fw-link-hot" : ""}`,
|
|
3004
3026
|
"data-tone": toneKey(link.tone),
|
|
3005
3027
|
style,
|
|
3006
3028
|
"aria-hidden": true
|
|
@@ -3397,7 +3419,8 @@ function FlowBody(props) {
|
|
|
3397
3419
|
"aria-hidden": true,
|
|
3398
3420
|
children: links.filter((link) => Math.max(link.from, link.to) >= first && Math.min(link.from, link.to) <= last).map((link) => /* @__PURE__ */ jsx(LinkMark, {
|
|
3399
3421
|
link,
|
|
3400
|
-
rowHeight
|
|
3422
|
+
rowHeight,
|
|
3423
|
+
hot: props.focusId !== void 0 && (rows[link.from]?.id === props.focusId || rows[link.to]?.id === props.focusId)
|
|
3401
3424
|
}, link.id))
|
|
3402
3425
|
}), rows.slice(first, last).map((item, offset) => row(item, first + offset))]
|
|
3403
3426
|
}), rows.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
@@ -4169,6 +4192,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4169
4192
|
slowestId: slowest?.id,
|
|
4170
4193
|
activeId,
|
|
4171
4194
|
selectedId: card?.pinned ? card.target : void 0,
|
|
4195
|
+
focusId: cardAgent?.id ?? (view === "agents" ? activeId : void 0),
|
|
4172
4196
|
clockMs,
|
|
4173
4197
|
window: win,
|
|
4174
4198
|
trackPx,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Static SVG and HTML figures for benchmarks and operational timing. Draws from producer data and refuses what it cannot draw honestly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|