@theoremjs/react 0.3.0 → 0.3.1
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 +728 -202
- package/client/composer-hints.js +1 -1
- package/client/decision-images.d.ts +76 -0
- package/client/decision-images.js +139 -0
- package/client/decision-transport.d.ts +26 -4
- package/client/decision-transport.js +79 -6
- package/client/embedding-input.d.ts +84 -0
- package/client/embedding-input.js +194 -0
- package/client/embedding-space.d.ts +82 -0
- package/client/embedding-space.js +389 -0
- package/client/embedding-transport.d.ts +40 -0
- package/client/embedding-transport.js +71 -0
- package/client/failure.d.ts +7 -10
- package/client/failure.js +9 -9
- package/client/host-call.d.ts +1 -1
- package/client/host-call.js +7 -2
- package/client/host-transport.d.ts +2 -0
- package/client/host-transport.js +3 -2
- package/client/index.d.ts +19 -11
- package/client/index.js +14 -10
- package/client/ink-waveform.d.ts +1 -1
- package/client/ink-waveform.js +1 -2
- package/client/live/apply-live-turn-tool-event.js +1 -1
- package/client/live/live-captions.d.ts +30 -0
- package/client/live/live-captions.js +105 -1
- package/client/live/live-mic-forward.d.ts +1 -0
- package/client/live/live-mic-forward.js +11 -2
- package/client/live/live-page-tool.d.ts +34 -0
- package/client/live/live-page-tool.js +11 -0
- package/client/live/live-state.d.ts +4 -2
- package/client/live/live-state.js +2 -0
- package/client/live/live-video.js +1 -1
- package/client/live/run-live-tool-call.d.ts +7 -2
- package/client/live/run-live-tool-call.js +48 -10
- package/client/live/use-live-caption-log.d.ts +17 -0
- package/client/live/use-live-caption-log.js +51 -0
- package/client/live-client.d.ts +52 -4
- package/client/live-client.js +250 -59
- package/client/live-messages.d.ts +51 -13
- package/client/live-messages.js +42 -9
- package/client/page-dispatch.d.ts +15 -0
- package/client/page-dispatch.js +63 -0
- package/client/run-commit.d.ts +16 -43
- package/client/run-commit.js +26 -101
- package/client/run-session.d.ts +40 -41
- package/client/run-session.js +104 -112
- package/client/schema-fields.js +27 -6
- package/client/shaped-data.js +37 -8
- package/client/source-citations.js +1 -1
- package/client/space-geometry.d.ts +39 -0
- package/client/space-geometry.js +183 -0
- package/client/state-fields.js +1 -1
- package/client/tool-result.js +30 -12
- package/client/tool-resume.d.ts +18 -1
- package/client/tool-resume.js +4 -1
- package/client/trace-feed.d.ts +1 -1
- package/client/trace-story.d.ts +2 -2
- package/client/trace-story.js +43 -19
- package/client/trace-view.d.ts +2 -2
- package/client/trace-view.js +56 -16
- package/client/transcript-groups.d.ts +28 -2
- package/client/transcript-groups.js +63 -13
- package/client/transcript-schema.d.ts +10 -0
- package/client/transcript-schema.js +120 -0
- package/client/transport.d.ts +114 -26
- package/client/transport.js +106 -32
- package/client/turn-client.d.ts +17 -8
- package/client/turn-client.js +26 -31
- package/client/voice-recorder.d.ts +1 -1
- package/client/voice-recorder.js +2 -5
- package/client/wire-line.js +5 -5
- package/client/worklets/mic-capture.js +9 -9
- package/components/InkWaveform.d.ts +12 -1
- package/components/InkWaveform.js +65 -42
- package/components/live/use-live-runner-controls.d.ts +3 -4
- package/components/live/use-live-runner-controls.js +6 -10
- package/components/live/use-live-runner-model.d.ts +10 -6
- package/components/live/use-live-runner-model.js +29 -14
- package/components/live/use-live-runner-ui.d.ts +10 -8
- package/components/live/use-live-runner-ui.js +19 -11
- package/components/live/use-live-session-client.d.ts +4 -4
- package/components/live/use-live-session-client.js +11 -8
- package/components/use-composer-voice.d.ts +4 -4
- package/components/use-composer-voice.js +8 -8
- package/components/use-editor-selection.js +4 -1
- package/components/use-voice-playback.d.ts +2 -1
- package/components/use-voice-playback.js +4 -4
- package/hooks/use-described.d.ts +5 -1
- package/hooks/use-described.js +11 -9
- package/hooks/use-embedding-index.d.ts +27 -0
- package/hooks/use-embedding-index.js +43 -0
- package/hooks/use-one-call.d.ts +31 -0
- package/hooks/use-one-call.js +76 -0
- package/hooks/use-page-tool-warnings.d.ts +6 -0
- package/hooks/use-page-tool-warnings.js +25 -0
- package/hooks/use-second-ticker.d.ts +2 -0
- package/hooks/use-second-ticker.js +13 -0
- package/hooks/use-theorem-chat-actions.d.ts +25 -21
- package/hooks/use-theorem-chat-actions.js +67 -45
- package/hooks/use-theorem-chat-state.d.ts +6 -6
- package/hooks/use-theorem-chat-state.js +13 -9
- package/hooks/use-theorem-chat.d.ts +48 -14
- package/hooks/use-theorem-chat.js +329 -141
- package/hooks/use-theorem-decision.d.ts +5 -10
- package/hooks/use-theorem-decision.js +4 -67
- package/hooks/use-theorem-embedding.d.ts +18 -0
- package/hooks/use-theorem-embedding.js +12 -0
- package/hooks/use-theorem-host.d.ts +5 -4
- package/hooks/use-theorem-host.js +26 -8
- package/hooks/use-theorem-interface.d.ts +6 -3
- package/hooks/use-theorem-interface.js +12 -22
- package/index.d.ts +6 -3
- package/index.js +5 -2
- package/live.d.ts +1 -0
- package/live.js +1 -1
- package/package.json +6 -5
- package/server/checkpoint-ownership.d.ts +6 -0
- package/server/checkpoint-ownership.js +42 -0
- package/server/conversation-store.d.ts +92 -0
- package/server/conversation-store.js +131 -0
- package/server/conversation.d.ts +193 -0
- package/server/conversation.js +552 -0
- package/server/decision-handler.d.ts +12 -4
- package/server/decision-handler.js +29 -38
- package/server/embedding-handler.d.ts +33 -0
- package/server/embedding-handler.js +45 -0
- package/server/handler.d.ts +58 -20
- package/server/handler.js +441 -238
- package/server/mod.d.ts +15 -6
- package/server/mod.js +13 -5
- package/server/page-dispatch.d.ts +22 -0
- package/server/page-dispatch.js +127 -0
- package/server/request-body.d.ts +2 -0
- package/server/request-body.js +42 -0
- package/server/request-check.d.ts +6 -1
- package/server/request-check.js +27 -12
- package/server/session-authority.d.ts +8 -0
- package/server/session-authority.js +78 -0
- package/server/session-boundary.d.ts +26 -0
- package/server/session-boundary.js +64 -0
- package/server/session-identity.d.ts +16 -0
- package/server/session-identity.js +41 -0
- package/server/session-limits.d.ts +12 -0
- package/server/session-limits.js +59 -0
- package/server/session-store.d.ts +69 -20
- package/server/session-store.js +220 -5
- package/server/single-action.d.ts +21 -0
- package/server/single-action.js +30 -0
- package/server/steer-inbox.js +1 -1
- package/server/tool-operations.d.ts +6 -0
- package/server/tool-operations.js +132 -0
- package/server/turn-input.d.ts +10 -0
- package/server/turn-input.js +29 -0
- package/styles/ink-controls.css +78 -78
- package/ui/CardWall.d.ts +10 -0
- package/ui/CardWall.js +61 -0
- package/ui/ChatComposerBar.d.ts +28 -5
- package/ui/ChatComposerBar.js +85 -30
- package/ui/ChatTranscript.d.ts +14 -1
- package/ui/ChatTranscript.js +110 -57
- package/ui/ConsoleFrame.d.ts +61 -4
- package/ui/ConsoleFrame.js +109 -9
- package/ui/DecisionAnswers.d.ts +40 -2
- package/ui/DecisionAnswers.js +166 -35
- package/ui/DecisionImages.d.ts +16 -0
- package/ui/DecisionImages.js +46 -0
- package/ui/EmbeddingDocuments.d.ts +78 -0
- package/ui/EmbeddingDocuments.js +278 -0
- package/ui/EmbeddingQuery.d.ts +27 -0
- package/ui/EmbeddingQuery.js +95 -0
- package/ui/EmbeddingResults.d.ts +41 -0
- package/ui/EmbeddingResults.js +165 -0
- package/ui/EmbeddingSpace.d.ts +26 -0
- package/ui/EmbeddingSpace.js +106 -0
- package/ui/LiveCaptionsPanel.d.ts +22 -0
- package/ui/LiveCaptionsPanel.js +27 -0
- package/ui/LiveRunner.d.ts +6 -4
- package/ui/LiveRunner.js +42 -55
- package/ui/PaneState.d.ts +33 -0
- package/ui/PaneState.js +50 -0
- package/ui/SchemaFields.d.ts +1 -1
- package/ui/SchemaFields.js +54 -26
- package/ui/ShapedData.js +73 -28
- package/ui/SidePanel.d.ts +74 -5
- package/ui/SidePanel.js +82 -9
- package/ui/StateFields.d.ts +10 -2
- package/ui/StateFields.js +56 -13
- package/ui/TheoremChat.d.ts +65 -14
- package/ui/TheoremChat.js +66 -78
- package/ui/TheoremDecision.d.ts +12 -20
- package/ui/TheoremDecision.js +78 -35
- package/ui/TheoremEmbedding.d.ts +27 -0
- package/ui/TheoremEmbedding.js +122 -0
- package/ui/TheoremHost.d.ts +22 -5
- package/ui/TheoremHost.js +127 -37
- package/ui/ToolGateCard.d.ts +5 -1
- package/ui/ToolGateCard.js +30 -23
- package/ui/ToolResult.js +12 -10
- package/ui/ToolResultCharts.js +29 -12
- package/ui/TraceCharts.d.ts +2 -2
- package/ui/TraceCharts.js +48 -17
- package/ui/TraceGuardrails.d.ts +3 -2
- package/ui/TraceGuardrails.js +24 -6
- package/ui/TraceInspector.d.ts +11 -1
- package/ui/TraceInspector.js +45 -28
- package/ui/TraceInspectorPanel.d.ts +31 -9
- package/ui/TraceInspectorPanel.js +77 -39
- package/ui/TraceOverview.d.ts +7 -3
- package/ui/TraceOverview.js +44 -14
- package/ui/TraceSpanDetail.js +24 -22
- package/ui/TraceStory.js +9 -8
- package/ui/TraceValues.js +19 -9
- package/ui/TraceWaterfall.js +12 -9
- package/ui/VoiceNote.js +4 -4
- package/ui/approval-heading.d.ts +4 -0
- package/ui/approval-heading.js +9 -0
- package/ui/arrive.d.ts +18 -0
- package/ui/arrive.js +46 -0
- package/ui/built/theme.css +86 -68
- package/ui/built/theme.css.d.ts +9 -0
- package/ui/built/theorem.d.ts +3 -3
- package/ui/built/theorem.js +34 -5
- package/ui/built/theorem.variants.d.ts +8 -2
- package/ui/code-language.d.ts +12 -0
- package/ui/code-language.js +50 -0
- package/ui/disclosure-motion.js +53 -9
- package/ui/embedding-space-gl.d.ts +37 -0
- package/ui/embedding-space-gl.js +663 -0
- package/ui/embedding-space-view.d.ts +8 -0
- package/ui/embedding-space-view.js +8 -0
- package/ui/glide.d.ts +7 -0
- package/ui/glide.js +44 -0
- package/ui/icons.js +2 -1
- package/ui/index.d.ts +20 -9
- package/ui/index.js +20 -10
- package/ui/labels-provider.js +5 -2
- package/ui/labels.d.ts +374 -85
- package/ui/labels.js +1028 -188
- package/ui/markdown-plugins.d.ts +9 -0
- package/ui/markdown-plugins.js +13 -0
- package/ui/mcp-icon.d.ts +6 -0
- package/ui/mcp-icon.js +8 -0
- package/ui/row-keys.d.ts +19 -0
- package/ui/row-keys.js +33 -0
- package/ui/theme.d.ts +1 -1
- package/ui/theme.js +2 -2
- package/ui/theorem-theme.js +32 -10
- package/ui/trace-palette.d.ts +2 -16
- package/ui/trace-palette.js +2 -16
- package/ui/trace-patterns.d.ts +11 -3
- package/ui/trace-patterns.js +16 -3
- package/ui/transcript-copy-text.js +1 -0
- package/ui/wall-columns.d.ts +8 -0
- package/ui/wall-columns.js +19 -0
- package/client/walk-away.d.ts +0 -34
- package/client/walk-away.js +0 -48
- package/server/walk-away.d.ts +0 -22
- package/server/walk-away.js +0 -41
package/ui/TraceInspector.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Badge } from '@astryxdesign/core/Badge';
|
|
3
3
|
import { EmptyState } from '@astryxdesign/core/EmptyState';
|
|
4
|
+
import { HStack } from '@astryxdesign/core/HStack';
|
|
4
5
|
import { Icon } from '@astryxdesign/core/Icon';
|
|
5
6
|
import { Item } from '@astryxdesign/core/Item';
|
|
6
7
|
import { PowerSearch, } from '@astryxdesign/core/PowerSearch';
|
|
7
|
-
import { HStack } from '@astryxdesign/core/HStack';
|
|
8
|
-
import { ScrollableArea } from '@astryxdesign/core/ScrollableArea';
|
|
9
|
-
import { StackItem } from '@astryxdesign/core/Stack';
|
|
10
8
|
import { Text } from '@astryxdesign/core/Text';
|
|
11
9
|
import { VStack } from '@astryxdesign/core/VStack';
|
|
12
|
-
import {
|
|
10
|
+
import { IconActivity } from '@tabler/icons-react';
|
|
13
11
|
import { useMemo, useState } from 'react';
|
|
14
12
|
import { isCallTrace, traceGuardrails, traceLatency, traceStory, traceTurnRow, traceTurns, } from "../client/trace-story.js";
|
|
15
13
|
import { TRACE_SEARCH_OPERATORS, TRACE_TEXT_FIELD, traceMatchIds, traceSearchFields, traceSpans, traceTotals, traceTree, } from "../client/trace-view.js";
|
|
16
14
|
import { useTraceRecords } from "../hooks/use-trace-records.js";
|
|
15
|
+
import { Stream, useArrive } from "./arrive.js";
|
|
16
|
+
import { PaneLayout } from "./SidePanel.js";
|
|
17
17
|
import { ConversationCharts, TraceCharts } from "./TraceCharts.js";
|
|
18
18
|
import { TraceGuardrails } from "./TraceGuardrails.js";
|
|
19
|
-
import { TurnPicker, TurnStats } from "./TraceOverview.js";
|
|
19
|
+
import { GuardrailStats, TurnPicker, TurnStats } from "./TraceOverview.js";
|
|
20
20
|
import { TraceSpanDetail } from "./TraceSpanDetail.js";
|
|
21
21
|
import { ActorMark, TraceStory } from "./TraceStory.js";
|
|
22
22
|
import { useTraceFormat } from "./TraceValues.js";
|
|
@@ -37,7 +37,10 @@ function unitOf(t, field) {
|
|
|
37
37
|
function operatorValue(t, field) {
|
|
38
38
|
switch (field.kind) {
|
|
39
39
|
case 'options':
|
|
40
|
-
return {
|
|
40
|
+
return {
|
|
41
|
+
type: 'enum_list',
|
|
42
|
+
values: (field.options ?? []).map(({ value, label }) => ({ value, label })),
|
|
43
|
+
};
|
|
41
44
|
case 'number': {
|
|
42
45
|
const units = unitOf(t, field);
|
|
43
46
|
return units ? { type: 'float', units } : { type: 'float' };
|
|
@@ -55,7 +58,11 @@ function searchField(t, field) {
|
|
|
55
58
|
label: field.label,
|
|
56
59
|
description: field.doc,
|
|
57
60
|
...(field.group && { group: field.group }),
|
|
58
|
-
operators: TRACE_SEARCH_OPERATORS[field.kind].map((key) => ({
|
|
61
|
+
operators: TRACE_SEARCH_OPERATORS[field.kind].map((key) => ({
|
|
62
|
+
key,
|
|
63
|
+
value,
|
|
64
|
+
i18nKey: `@astryx.powersearch.operator.${key}`,
|
|
65
|
+
})),
|
|
59
66
|
};
|
|
60
67
|
}
|
|
61
68
|
/** The search over the spans shown: any text first, then the fields the spans recorded. */
|
|
@@ -97,20 +104,27 @@ function TurnRowTotals({ turn }) {
|
|
|
97
104
|
return (_jsxs(VStack, { gap: 0, hAlign: "end", children: [_jsx(Text, { type: "supporting", hasTabularNumbers: true, children: format.duration(turn.totals.durationMs) }), turn.totals.cost ? (_jsx(Text, { type: "supporting", color: "secondary", hasTabularNumbers: true, children: format.sum(turn.totals.cost, format.usd) })) : null] }));
|
|
98
105
|
}
|
|
99
106
|
/** A row of the whole-trace list: a turn by what the person asked, a host's call by its tool. */
|
|
100
|
-
function TurnRow({ turn, index, isCall, onOpen }) {
|
|
107
|
+
function TurnRow({ turn, index, isCall, onOpen, }) {
|
|
101
108
|
const { t } = useTraceFormat();
|
|
109
|
+
const arrive = useArrive();
|
|
102
110
|
const row = useMemo(() => traceTurnRow(turn, isCall), [turn, isCall]);
|
|
103
|
-
return (_jsx(Item, { startContent: _jsx(ActorMark, { actor: row.actor }), label: _jsx(Text, { weight: "medium", maxLines: 1, children: row.title ??
|
|
111
|
+
return (_jsx(Item, { className: arrive?.className, style: arrive?.style, startContent: _jsx(ActorMark, { actor: row.actor }), label: _jsx(Text, { weight: "medium", maxLines: 1, children: row.title ??
|
|
112
|
+
t(isCall ? '@theorem.panel.trace.charts.hostCall' : '@theorem.panel.trace.charts.turn', { index: index + 1 }) }), description: turnRowNote(row), endContent: _jsx(TurnRowTotals, { turn: turn }), align: "start", density: "compact", onClick: onOpen }));
|
|
104
113
|
}
|
|
105
114
|
/** Every turn at once: the conversation's totals, each turn's time and tokens, and the turns themselves. */
|
|
106
|
-
function ConversationOverview({ turns, onPick }) {
|
|
115
|
+
function ConversationOverview({ turns, onPick, }) {
|
|
107
116
|
const { t } = useTraceFormat();
|
|
108
117
|
const nodes = useMemo(() => turns.map((turn) => turn.node), [turns]);
|
|
109
|
-
// Time between turns is the person's, not the agent's: the duration sums the turns.
|
|
110
|
-
const totals = useMemo(() => ({
|
|
118
|
+
// why: Time between turns is the person's, not the agent's: the duration sums the turns.
|
|
119
|
+
const totals = useMemo(() => ({
|
|
120
|
+
...traceTotals(nodes),
|
|
121
|
+
durationMs: turns.reduce((total, turn) => total + turn.totals.durationMs, 0),
|
|
122
|
+
}), [nodes, turns]);
|
|
111
123
|
const tools = useMemo(() => traceSpans(nodes).filter((node) => node.meta.type === 'tool').length, [nodes]);
|
|
112
124
|
const isCalls = isCallTrace(turns);
|
|
113
|
-
return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(Text, { weight: "semibold", children: t(isCalls ? '@theorem.panel.trace.calls.count' : '@theorem.panel.trace.conversation', {
|
|
125
|
+
return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(Text, { weight: "semibold", children: t(isCalls ? '@theorem.panel.trace.calls.count' : '@theorem.panel.trace.conversation', {
|
|
126
|
+
count: turns.length,
|
|
127
|
+
}) }), _jsxs(VStack, { gap: 3, children: [_jsx(TurnStats, { totals: totals, tools: tools }), _jsx(ConversationCharts, { turns: turns, onPick: onPick })] }), _jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t(isCalls ? '@theorem.panel.trace.calls' : '@theorem.panel.trace.turns') }), _jsx(Stream, { children: _jsx(VStack, { gap: 0, children: turns.map((turn, index) => (_jsx(TurnRow, { turn: turn, index: index, isCall: isCalls, onOpen: () => onPick(index) }, turn.node.id))) }) })] })] }));
|
|
114
128
|
}
|
|
115
129
|
/** One turn at a glance, then its story, then its timeline under the search. */
|
|
116
130
|
function TurnOverview({ turns, index, onPick, onBack, selectedId, onSelect, }) {
|
|
@@ -126,19 +140,11 @@ function TurnOverview({ turns, index, onPick, onBack, selectedId, onSelect, }) {
|
|
|
126
140
|
if (!turn || !root)
|
|
127
141
|
return null;
|
|
128
142
|
const tools = traceSpans([root]).filter((node) => node.meta.type === 'tool').length;
|
|
129
|
-
return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(TurnPicker, { turns: turns, index: index, onChange: onPick, onBack: onBack }), _jsxs(VStack, { gap: 3, children: [_jsx(TurnStats, { totals: turn.totals, tools: tools, latency: latency }), _jsx(TraceCharts, { root: root, split: turn.split, onSelect: onSelect })] }), _jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.timeline') }), _jsx(PowerSearch, { config: config, filters: filters, onChange: setFilters, label: t('@theorem.panel.trace.search'), isLabelHidden: true, placeholder: t('@theorem.panel.trace.search.placeholder'), resultCount: matches?.size, size: "sm" }), _jsx(TraceWaterfall, { root: root, selectedId: selectedId, matches: matches, onSelect: onSelect })] }), story.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.story') }), _jsx(TraceStory, { steps: story, selectedId: selectedId, matches: matches, onSelect: onSelect })] })) : null, guardrails.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.guardrails') }), _jsx(TraceGuardrails, { checks: guardrails, onSelect: onSelect })] })) : null] }));
|
|
143
|
+
return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(TurnPicker, { turns: turns, index: index, onChange: onPick, onBack: onBack }), _jsxs(VStack, { gap: 3, children: [_jsx(TurnStats, { totals: turn.totals, tools: tools, latency: latency }), _jsx(TraceCharts, { root: root, split: turn.split, onSelect: onSelect })] }), _jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.timeline') }), _jsx(PowerSearch, { config: config, filters: filters, onChange: setFilters, label: t('@theorem.panel.trace.search'), isLabelHidden: true, placeholder: t('@theorem.panel.trace.search.placeholder'), resultCount: matches?.size, size: "sm" }), _jsx(TraceWaterfall, { root: root, selectedId: selectedId, matches: matches, onSelect: onSelect }, root.id)] }), story.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.story') }), _jsx(TraceStory, { steps: story, selectedId: selectedId, matches: matches, onSelect: onSelect })] })) : null, guardrails.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.guardrails') }), _jsx(TraceGuardrails, { checks: guardrails, onSelect: onSelect })] })) : null] }));
|
|
130
144
|
}
|
|
131
145
|
function EmptyTrace() {
|
|
132
146
|
const { t } = useTraceFormat();
|
|
133
|
-
return (_jsx(VStack, { height: "100%", vAlign: "center", padding: 4, children: _jsx(EmptyState, { icon: _jsx(Icon, { icon:
|
|
134
|
-
}
|
|
135
|
-
/** Narrow, the open span takes the overview's place; wide, it sits beside it. */
|
|
136
|
-
function TraceLayout({ isWide, overview, detail, detailLabel }) {
|
|
137
|
-
const { t } = useTraceFormat();
|
|
138
|
-
if (!isWide) {
|
|
139
|
-
return (_jsxs(ScrollableArea, { label: t('@theorem.panel.trace.name'), height: "100%", children: [_jsx(TracePatterns, {}), detail ?? overview] }));
|
|
140
|
-
}
|
|
141
|
-
return (_jsxs(HStack, { height: "100%", gap: 0, children: [_jsx(StackItem, { size: "fill", children: _jsxs(ScrollableArea, { label: t('@theorem.panel.trace.name'), height: "100%", children: [_jsx(TracePatterns, {}), overview] }) }), detail ? (_jsx("div", { style: { width: '48%', flexShrink: 0, height: '100%', borderInlineStart: '1px solid var(--color-border)' }, children: _jsx(ScrollableArea, { label: detailLabel, height: "100%", children: detail }) })) : null] }));
|
|
147
|
+
return (_jsx(VStack, { height: "100%", vAlign: "center", padding: 4, children: _jsx(EmptyState, { icon: _jsx(Icon, { icon: IconActivity, size: "lg", color: "secondary" }), title: t('@theorem.panel.trace.empty.title'), description: t('@theorem.panel.trace.empty.description'), isCompact: true }) }));
|
|
142
148
|
}
|
|
143
149
|
/** The open span of the open turn; opening another turn closes it. */
|
|
144
150
|
function useOpenSpan(turns) {
|
|
@@ -159,7 +165,7 @@ function useOpenSpan(turns) {
|
|
|
159
165
|
};
|
|
160
166
|
}
|
|
161
167
|
/** The open span beside or in place of the overview; nothing while none is open. */
|
|
162
|
-
function SpanPane({ open, turnStartMs, isWide, onBack, onOpen }) {
|
|
168
|
+
function SpanPane({ open, turnStartMs, isWide, onBack, onOpen, }) {
|
|
163
169
|
if (!open)
|
|
164
170
|
return null;
|
|
165
171
|
return (_jsx(VStack, { padding: 4, children: _jsx(TraceSpanDetail, { node: open, turnStartMs: turnStartMs, isClose: isWide, onBack: onBack, onOpen: onOpen }) }));
|
|
@@ -174,16 +180,27 @@ function Overview({ turns, index, pick, selectedId, select, }) {
|
|
|
174
180
|
* The panel's content. Narrow, the turn fills it and a span opens in its
|
|
175
181
|
* place; wide, an open span sits beside the turn.
|
|
176
182
|
*/
|
|
177
|
-
function TraceInspectorBody({ records, isWide }) {
|
|
183
|
+
function TraceInspectorBody({ records, isWide, }) {
|
|
178
184
|
const tree = useMemo(() => traceTree(records), [records]);
|
|
179
185
|
const turns = useMemo(() => traceTurns(tree), [tree]);
|
|
186
|
+
const { t } = useTraceFormat();
|
|
180
187
|
const { index, root, open, select, close, pick } = useOpenSpan(turns);
|
|
181
188
|
if (turns.length === 0)
|
|
182
189
|
return _jsx(EmptyTrace, {});
|
|
183
|
-
const overview = _jsx(Overview, { turns: turns, index: index, pick: pick, selectedId: open?.id, select: select });
|
|
184
|
-
return (_jsx(
|
|
190
|
+
const overview = (_jsx(Overview, { turns: turns, index: index, pick: pick, selectedId: open?.id, select: select }));
|
|
191
|
+
return (_jsx(PaneLayout, { isWide: isWide, label: t('@theorem.panel.trace.name'), detailLabel: open?.meta.label ?? '', lead: _jsx(TracePatterns, {}), overview: overview, detail: open && (_jsx(SpanPane, { open: open, turnStartMs: root?.startMs ?? 0, isWide: isWide, onBack: close, onOpen: select })) }));
|
|
185
192
|
}
|
|
186
193
|
/** The inspector over the records a feed has delivered; the panel loads this module when it first opens. */
|
|
187
|
-
export function TraceFeedBody({ traces, isWide }) {
|
|
194
|
+
export function TraceFeedBody({ traces, isWide, }) {
|
|
188
195
|
return _jsx(TraceInspectorBody, { records: useTraceRecords(traces), isWide: isWide });
|
|
189
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Only what the records say of guardrails: how long the checks took, how many
|
|
199
|
+
* ran and acted, then each check. `head` leads it.
|
|
200
|
+
*/
|
|
201
|
+
export function TraceGuardrailsBody({ records, head, }) {
|
|
202
|
+
const { t } = useTraceFormat();
|
|
203
|
+
const roots = useMemo(() => traceTurns(traceTree(records)).map((turn) => turn.node), [records]);
|
|
204
|
+
const checks = useMemo(() => roots.flatMap(traceGuardrails), [roots]);
|
|
205
|
+
return (_jsxs(VStack, { gap: 5, padding: 4, children: [head, roots.map((root) => (_jsx(GuardrailStats, { latency: traceLatency(root) }, root.id))), checks.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.guardrails') }), _jsx(TraceGuardrails, { checks: checks })] })) : null] }));
|
|
206
|
+
}
|
|
@@ -1,24 +1,46 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { TraceRecord } from '@theoremjs/agents';
|
|
2
2
|
import type { ProfileObservabilityView } from '@theoremjs/agents/interface';
|
|
3
|
+
import { type ReactNode } from 'react';
|
|
3
4
|
import type { TraceFeed } from '../client/trace-feed.ts';
|
|
5
|
+
import { type SidePanelLabels } from './SidePanel.tsx';
|
|
4
6
|
export type TraceInspector = {
|
|
5
7
|
/** The header toggle; null when the host drives the trace or the profile records none. */
|
|
6
8
|
toggle: ReactNode;
|
|
7
|
-
/** The trace,
|
|
9
|
+
/** The trace, while it's open; null otherwise. */
|
|
8
10
|
view: ReactNode;
|
|
11
|
+
/** Words for the docked panel. */
|
|
12
|
+
labels: SidePanelLabels;
|
|
13
|
+
/** The profile records traces, so a panel page can dock them even when the host drives the toggle. */
|
|
14
|
+
offered: boolean;
|
|
9
15
|
};
|
|
16
|
+
/** Where an open trace sits on this page. */
|
|
17
|
+
export declare function TracePlacement({ value, children, }: {
|
|
18
|
+
value: 'replace' | 'panel';
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
10
21
|
/**
|
|
11
|
-
* The trace inspector, offered when the profile records traces.
|
|
12
|
-
* trace takes the surface's place
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
22
|
+
* The trace inspector, offered when the profile records traces. By default the
|
|
23
|
+
* open trace takes the surface's place. Inside `TracePlacement value="panel"`
|
|
24
|
+
* it docks at the inline end instead, and the theme eases that panel's width.
|
|
25
|
+
* It reads the records the host delivers on `traces`; a host that delivers none
|
|
26
|
+
* shows the empty state. Pass `open` to drive it from the host's own control,
|
|
27
|
+
* which hides the built-in toggle. The open trace takes the surface's place,
|
|
28
|
+
* unless this page is inside `TracePlacement value="panel"`, where it docks.
|
|
16
29
|
*/
|
|
17
30
|
export declare function useTraceInspector(iface: {
|
|
18
31
|
observability?: Pick<ProfileObservabilityView, 'record'>;
|
|
19
32
|
}, traces?: TraceFeed, open?: boolean): TraceInspector;
|
|
20
|
-
/** The surface, or the trace in its place
|
|
21
|
-
export declare function WithTrace({ inspector, children }: {
|
|
33
|
+
/** The surface, or the trace in its place — or, on a docked page, beside it. */
|
|
34
|
+
export declare function WithTrace({ inspector, children, }: {
|
|
22
35
|
inspector: TraceInspector;
|
|
23
36
|
children: ReactNode;
|
|
24
37
|
}): import("react").JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* What `records` say of guardrails, for a host that holds a trace of its own
|
|
40
|
+
* (the studio's guardrail test): the checks' time and count, then each
|
|
41
|
+
* check, laid out as the trace lays them out. `head` leads it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function TraceGuardrailsView({ records, head, }: {
|
|
44
|
+
records: readonly TraceRecord[];
|
|
45
|
+
head?: ReactNode;
|
|
46
|
+
}): import("react").JSX.Element;
|
|
@@ -1,57 +1,95 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import { Card } from '@astryxdesign/core/Card';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
2
|
import { Icon } from '@astryxdesign/core/Icon';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// The inspector and its charts are fetched when the trace first opens, not with the chat.
|
|
3
|
+
import { IconActivity } from '@tabler/icons-react';
|
|
4
|
+
import { cloneElement, createContext, isValidElement, lazy, Suspense, use, useCallback, useId, useRef, useState, } from 'react';
|
|
5
|
+
import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
|
|
6
|
+
import { InPlace, isSidePanelWide, RaisedPane, SidePanel, SidePanelHeader, SidePanelToggle, useSidePanel, useWidth, } from "./SidePanel.js";
|
|
7
|
+
// why: The inspector and its charts are fetched when the trace first opens, not with the chat.
|
|
8
|
+
const TraceGuardrailsBody = lazy(() => import("./TraceInspector.js").then((module) => ({ default: module.TraceGuardrailsBody })));
|
|
11
9
|
const TraceFeedBody = lazy(() => import("./TraceInspector.js").then((module) => ({ default: module.TraceFeedBody })));
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return;
|
|
18
|
-
const observer = new ResizeObserver(([entry]) => setWidth(entry?.contentRect.width ?? 0));
|
|
19
|
-
observer.observe(node);
|
|
20
|
-
return () => observer.disconnect();
|
|
21
|
-
}, []);
|
|
22
|
-
return { ref, width };
|
|
10
|
+
/** `replace` takes the surface's place. `panel` docks at the inline end; its width eases with the theme. */
|
|
11
|
+
const TracePlacementContext = createContext('replace');
|
|
12
|
+
/** Where an open trace sits on this page. */
|
|
13
|
+
export function TracePlacement({ value, children, }) {
|
|
14
|
+
return _jsx(TracePlacementContext, { value: value, children: children });
|
|
23
15
|
}
|
|
24
|
-
function TraceView({ id, header, traces }) {
|
|
25
|
-
|
|
26
|
-
return (_jsx(Layout, { height: "fill", header: header, content: _jsx(LayoutContent, { padding: 0, isScrollable: false, children: _jsx(VStack, { height: "100%", paddingInline: 3, children: _jsx(Card, { variant: "transparent", height: "100%", padding: 0, style: RAISED, children: _jsx("div", { id: id, ref: ref, style: { height: '100%' }, children: _jsx(Suspense, { fallback: null, children: _jsx(TraceFeedBody, { traces: traces, isWide: isSidePanelWide(width) }) }) }) }) }) }) }));
|
|
16
|
+
function TraceView({ id, header, traces, }) {
|
|
17
|
+
return (_jsx(RaisedPane, { id: id, header: header, children: (isWide) => (_jsx(Suspense, { fallback: null, children: _jsx(TraceFeedBody, { traces: traces, isWide: isWide }) })) }));
|
|
27
18
|
}
|
|
28
19
|
/**
|
|
29
|
-
* The trace inspector, offered when the profile records traces.
|
|
30
|
-
* trace takes the surface's place
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
20
|
+
* The trace inspector, offered when the profile records traces. By default the
|
|
21
|
+
* open trace takes the surface's place. Inside `TracePlacement value="panel"`
|
|
22
|
+
* it docks at the inline end instead, and the theme eases that panel's width.
|
|
23
|
+
* It reads the records the host delivers on `traces`; a host that delivers none
|
|
24
|
+
* shows the empty state. Pass `open` to drive it from the host's own control,
|
|
25
|
+
* which hides the built-in toggle. The open trace takes the surface's place,
|
|
26
|
+
* unless this page is inside `TracePlacement value="panel"`, where it docks.
|
|
34
27
|
*/
|
|
35
28
|
export function useTraceInspector(iface, traces, open) {
|
|
36
29
|
const t = useLabels();
|
|
37
30
|
const id = useId();
|
|
38
31
|
const [own, setOwn] = useState(false);
|
|
39
|
-
|
|
40
|
-
return { toggle: null, view: null };
|
|
41
|
-
const isOpen = open ?? own;
|
|
32
|
+
const placement = use(TracePlacementContext);
|
|
42
33
|
const labels = {
|
|
43
34
|
name: t('@theorem.panel.trace.name'),
|
|
44
35
|
show: t('@theorem.panel.trace.show'),
|
|
45
36
|
hide: t('@theorem.panel.trace.hide'),
|
|
46
37
|
resize: t('@theorem.panel.trace.resize'),
|
|
47
38
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
if (iface.observability?.record !== true)
|
|
40
|
+
return { toggle: null, view: null, labels, offered: false };
|
|
41
|
+
const isOpen = open ?? own;
|
|
42
|
+
const toggle = open === undefined ? (_jsx(SidePanelToggle, { labels: labels, icon: _jsx(Icon, { icon: IconActivity }), panelId: id, open: isOpen, onToggle: () => setOwn((value) => !value) })) : null;
|
|
43
|
+
const view = isOpen ? openTrace(placement === 'panel', id, traces, toggle) : null;
|
|
44
|
+
return { toggle, view, labels, offered: true };
|
|
45
|
+
}
|
|
46
|
+
/** The open trace: filling the host's docked panel, or as its own panel with the toggle on top. */
|
|
47
|
+
function openTrace(docked, id, traces, toggle) {
|
|
48
|
+
if (docked)
|
|
49
|
+
return _jsx(TraceFill, { id: id, traces: traces });
|
|
50
|
+
return (_jsx(TraceView, { id: id, header: toggle ? _jsx(SidePanelHeader, { floating: true, children: toggle }) : undefined, traces: traces }));
|
|
53
51
|
}
|
|
54
|
-
/** The
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
/** The trace body, filling the docked panel. The panel supplies the raised card. */
|
|
53
|
+
function TraceFill({ id, traces }) {
|
|
54
|
+
const { ref, width } = useWidth();
|
|
55
|
+
return (_jsx("div", { id: id, ref: ref, style: { height: '100%' }, children: _jsx(Suspense, { fallback: null, children: _jsx(TraceFeedBody, { traces: traces, isWide: isSidePanelWide(width) }) }) }));
|
|
56
|
+
}
|
|
57
|
+
/** The surface, with the trace docked at its inline end. A host-driven trace stays in place. */
|
|
58
|
+
function TraceDock({ inspector, children }) {
|
|
59
|
+
const layoutRef = useRef(null);
|
|
60
|
+
const side = useSidePanel(layoutRef, false);
|
|
61
|
+
const childRef = useRef(undefined);
|
|
62
|
+
const setLayout = useCallback((node) => {
|
|
63
|
+
layoutRef.current = node;
|
|
64
|
+
const ref = childRef.current;
|
|
65
|
+
if (typeof ref === 'function')
|
|
66
|
+
ref(node);
|
|
67
|
+
else if (ref != null)
|
|
68
|
+
ref.current = node;
|
|
69
|
+
}, []);
|
|
70
|
+
if (!isValidElement(children)) {
|
|
71
|
+
return _jsx(InPlace, { view: inspector.view, children: children });
|
|
72
|
+
}
|
|
73
|
+
childRef.current = children.props.ref;
|
|
74
|
+
const open = inspector.view != null;
|
|
75
|
+
return cloneElement(children, {
|
|
76
|
+
ref: setLayout,
|
|
77
|
+
end: (_jsx(SidePanel, { labels: inspector.labels, resizable: side.resizable, open: open, children: inspector.view })),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** The surface, or the trace in its place — or, on a docked page, beside it. */
|
|
81
|
+
export function WithTrace({ inspector, children, }) {
|
|
82
|
+
const placement = use(TracePlacementContext);
|
|
83
|
+
if (placement === 'panel' && inspector.offered) {
|
|
84
|
+
return _jsx(TraceDock, { inspector: inspector, children: children });
|
|
85
|
+
}
|
|
86
|
+
return _jsx(InPlace, { view: inspector.view, children: children });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* What `records` say of guardrails, for a host that holds a trace of its own
|
|
90
|
+
* (the studio's guardrail test): the checks' time and count, then each
|
|
91
|
+
* check, laid out as the trace lays them out. `head` leads it.
|
|
92
|
+
*/
|
|
93
|
+
export function TraceGuardrailsView({ records, head, }) {
|
|
94
|
+
return (_jsx(TheoremLabelsProvider, { children: _jsx(Suspense, { fallback: null, children: _jsx(TraceGuardrailsBody, { records: records, head: head }) }) }));
|
|
57
95
|
}
|
package/ui/TraceOverview.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
2
|
import { type TraceLatency, type TraceTurn } from '../client/trace-story.ts';
|
|
3
3
|
import type { TraceTotals } from '../client/trace-view.ts';
|
|
4
4
|
/** Width a chart draws at before it has measured its column: the panel's narrowest. */
|
|
@@ -23,11 +23,15 @@ export declare function TurnPicker({ turns, index, onChange, onBack, }: {
|
|
|
23
23
|
/** Leaves the turn for the whole conversation; absent when there is only the one turn. */
|
|
24
24
|
onBack?: (() => void) | undefined;
|
|
25
25
|
}): import("react").JSX.Element | null;
|
|
26
|
+
/** A turn's guardrail figures alone, as {@link TurnStats} lays them out. */
|
|
27
|
+
export declare function GuardrailStats({ latency }: {
|
|
28
|
+
latency: TraceLatency;
|
|
29
|
+
}): import("react").JSX.Element | null;
|
|
26
30
|
/**
|
|
27
|
-
* Duration, first text, cost, tokens, steps and guardrails of the open turn;
|
|
31
|
+
* Duration, first text, cost, tokens, steps, and guardrails of the open turn;
|
|
28
32
|
* a total the trace did not record is left out.
|
|
29
33
|
*/
|
|
30
|
-
export declare function TurnStats({ totals, tools, latency }: {
|
|
34
|
+
export declare function TurnStats({ totals, tools, latency, }: {
|
|
31
35
|
totals: TraceTotals;
|
|
32
36
|
tools: number;
|
|
33
37
|
latency?: TraceLatency;
|
package/ui/TraceOverview.js
CHANGED
|
@@ -9,9 +9,10 @@ import { Text } from '@astryxdesign/core/Text';
|
|
|
9
9
|
import { VStack } from '@astryxdesign/core/VStack';
|
|
10
10
|
import { IconArrowLeft, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
|
|
11
11
|
import { TRACE_FIELDS, traceAttributeMeta } from '@theoremjs/agents';
|
|
12
|
+
import { useEffect, useRef } from 'react';
|
|
12
13
|
import { isCallTrace } from "../client/trace-story.js";
|
|
13
|
-
import { TRACE_HUE } from "./trace-palette.js";
|
|
14
14
|
import { useTraceFormat } from "./TraceValues.js";
|
|
15
|
+
import { TRACE_HUE } from "./trace-palette.js";
|
|
15
16
|
/** Width a chart draws at before it has measured its column: the panel's narrowest. */
|
|
16
17
|
export const FIRST_WIDTH_PX = 280;
|
|
17
18
|
/**
|
|
@@ -20,7 +21,15 @@ export const FIRST_WIDTH_PX = 280;
|
|
|
20
21
|
* where it was. Clicks and pointer drags still reach the chart.
|
|
21
22
|
*/
|
|
22
23
|
export function ChartFrame({ children }) {
|
|
23
|
-
|
|
24
|
+
const frame = useRef(null);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const node = frame.current;
|
|
27
|
+
// why: A native listener keeps the frame a plain element; it is not a control, so it takes no role.
|
|
28
|
+
const keepFocus = (event) => event.preventDefault();
|
|
29
|
+
node?.addEventListener('mousedown', keepFocus);
|
|
30
|
+
return () => node?.removeEventListener('mousedown', keepFocus);
|
|
31
|
+
}, []);
|
|
32
|
+
return _jsx("div", { ref: frame, children: children });
|
|
24
33
|
}
|
|
25
34
|
/** A chart's hover card, drawn like Astryx's own tooltip. */
|
|
26
35
|
export function ChartTip({ children }) {
|
|
@@ -40,9 +49,13 @@ export function TurnPicker({ turns, index, onChange, onBack, }) {
|
|
|
40
49
|
if (!turn)
|
|
41
50
|
return null;
|
|
42
51
|
const isCalls = isCallTrace(turns);
|
|
43
|
-
return (_jsxs(VStack, { gap: 1, hAlign: "start", children: [onBack ? (_jsx(Button, { label: t(isCalls ? '@theorem.panel.trace.calls.back' : '@theorem.panel.trace.conversation.back'), variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconArrowLeft }), onClick: onBack })) : null, _jsxs(HStack, { gap: 1, align: "center", justify: "between", width: "100%", children: [_jsxs(HStack, { gap: 2, align: "center", children: [_jsx(Text, { weight: "semibold", children: t('@theorem.panel.trace.turn', {
|
|
52
|
+
return (_jsxs(VStack, { gap: 1, hAlign: "start", children: [onBack ? (_jsx(Button, { label: t(isCalls ? '@theorem.panel.trace.calls.back' : '@theorem.panel.trace.conversation.back'), variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconArrowLeft }), onClick: onBack })) : null, _jsxs(HStack, { gap: 1, align: "center", justify: "between", width: "100%", children: [_jsxs(HStack, { gap: 2, align: "center", children: [_jsx(Text, { weight: "semibold", children: t('@theorem.panel.trace.turn', {
|
|
53
|
+
label: turn.node.meta.label,
|
|
54
|
+
index: index + 1,
|
|
55
|
+
count: turns.length,
|
|
56
|
+
}) }), turn.node.meta.subject ? (_jsx(Text, { color: "secondary", maxLines: 1, children: turn.node.meta.subject })) : null] }), _jsxs(HStack, { gap: 0, children: [_jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.previous' : '@theorem.panel.trace.previous'), icon: _jsx(Icon, { icon: IconChevronLeft }), variant: "ghost", size: "sm", isDisabled: index === 0, onClick: () => onChange(index - 1) }), _jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.next' : '@theorem.panel.trace.next'), icon: _jsx(Icon, { icon: IconChevronRight }), variant: "ghost", size: "sm", isDisabled: index === turns.length - 1, onClick: () => onChange(index + 1) })] })] })] }));
|
|
44
57
|
}
|
|
45
|
-
function Stat({ label, value, detail }) {
|
|
58
|
+
function Stat({ label, value, detail, }) {
|
|
46
59
|
return (_jsx(Card, { padding: 3, variant: "muted", style: { background: 'var(--color-background-surface)' }, children: _jsxs(VStack, { gap: 1, children: [_jsx(Text, { type: "supporting", color: "secondary", children: label }), _jsx(Text, { type: "large", weight: "semibold", hasTabularNumbers: true, children: value }), _jsx(VStack, { gap: 0, children: [detail]
|
|
47
60
|
.flat()
|
|
48
61
|
.filter(Boolean)
|
|
@@ -53,14 +66,18 @@ function FirstTextStat({ latency, format }) {
|
|
|
53
66
|
return null;
|
|
54
67
|
const { t } = format;
|
|
55
68
|
return (_jsx(Stat, { label: t('@theorem.panel.trace.firstText'), value: format.duration(latency.firstTextMs), detail: [
|
|
56
|
-
latency.tokensPerSecond !== undefined &&
|
|
57
|
-
|
|
69
|
+
latency.tokensPerSecond !== undefined &&
|
|
70
|
+
t('@theorem.panel.trace.rate', {
|
|
71
|
+
rate: format.number(Math.round(latency.tokensPerSecond)),
|
|
72
|
+
}),
|
|
73
|
+
latency.heldMs &&
|
|
74
|
+
t('@theorem.panel.trace.held', { duration: format.duration(latency.heldMs) }),
|
|
58
75
|
] }));
|
|
59
76
|
}
|
|
60
77
|
function CostStat({ totals, format }) {
|
|
61
78
|
if (!totals.cost)
|
|
62
79
|
return null;
|
|
63
|
-
return _jsx(Stat, { label: traceAttributeMeta('theorem.usage.cost_usd')?.label ?? '', value: format.sum(totals.cost, format.usd) });
|
|
80
|
+
return (_jsx(Stat, { label: traceAttributeMeta('theorem.usage.cost_usd')?.label ?? '', value: format.sum(totals.cost, format.usd) }));
|
|
64
81
|
}
|
|
65
82
|
/** A token total as its card reads it: "–" when no call reported it. */
|
|
66
83
|
function tokenSide(total, format) {
|
|
@@ -71,26 +88,39 @@ function TokensStat({ totals, format }) {
|
|
|
71
88
|
if (!input && !output)
|
|
72
89
|
return null;
|
|
73
90
|
const all = [input, output].reduce((sum, total) => sum + (total?.value ?? 0), 0);
|
|
74
|
-
return (_jsx(Stat, { label: format.t('@theorem.panel.trace.tokens'), value: format.number(all), detail: format.t('@theorem.panel.trace.tokens.detail', {
|
|
91
|
+
return (_jsx(Stat, { label: format.t('@theorem.panel.trace.tokens'), value: format.number(all), detail: format.t('@theorem.panel.trace.tokens.detail', {
|
|
92
|
+
input: tokenSide(input, format),
|
|
93
|
+
output: tokenSide(output, format),
|
|
94
|
+
}) }));
|
|
75
95
|
}
|
|
76
|
-
function StepsStat({ totals, tools, format }) {
|
|
96
|
+
function StepsStat({ totals, tools, format, }) {
|
|
77
97
|
const { t } = format;
|
|
78
|
-
// With no model, every step is a tool call the heading already counts.
|
|
98
|
+
// why: With no model, every step is a tool call the heading already counts.
|
|
79
99
|
if (totals.calls === 0 && totals.errors === 0)
|
|
80
100
|
return null;
|
|
81
101
|
return (_jsx(Stat, { label: t('@theorem.panel.trace.steps'), value: format.number(totals.calls + tools), detail: totals.errors > 0 ? (_jsx("span", { style: { color: TRACE_HUE.oxblood }, children: t('@theorem.panel.trace.failed', { count: totals.errors }) })) : (t('@theorem.panel.trace.steps.detail', { calls: totals.calls, tools })) }));
|
|
82
102
|
}
|
|
83
|
-
function GuardrailsStat({ latency, format }) {
|
|
103
|
+
function GuardrailsStat({ latency, format, }) {
|
|
84
104
|
const guardrails = latency?.guardrails;
|
|
85
105
|
if (!guardrails)
|
|
86
106
|
return null;
|
|
87
|
-
return (_jsx(Stat, { label: format.t('@theorem.panel.trace.guardrails'), value: format.duration(guardrails.ms), detail: format.t('@theorem.panel.trace.guardrails.detail', {
|
|
107
|
+
return (_jsx(Stat, { label: format.t('@theorem.panel.trace.guardrails'), value: format.duration(guardrails.ms), detail: format.t('@theorem.panel.trace.guardrails.detail', {
|
|
108
|
+
checks: guardrails.checks,
|
|
109
|
+
flagged: guardrails.flagged,
|
|
110
|
+
}) }));
|
|
111
|
+
}
|
|
112
|
+
/** A turn's guardrail figures alone, as {@link TurnStats} lays them out. */
|
|
113
|
+
export function GuardrailStats({ latency }) {
|
|
114
|
+
const format = useTraceFormat();
|
|
115
|
+
if (!latency.guardrails)
|
|
116
|
+
return null;
|
|
117
|
+
return (_jsx(Grid, { columns: { minWidth: 120, repeat: 'fit' }, gap: 2, children: _jsx(GuardrailsStat, { latency: latency, format: format }) }));
|
|
88
118
|
}
|
|
89
119
|
/**
|
|
90
|
-
* Duration, first text, cost, tokens, steps and guardrails of the open turn;
|
|
120
|
+
* Duration, first text, cost, tokens, steps, and guardrails of the open turn;
|
|
91
121
|
* a total the trace did not record is left out.
|
|
92
122
|
*/
|
|
93
|
-
export function TurnStats({ totals, tools, latency }) {
|
|
123
|
+
export function TurnStats({ totals, tools, latency, }) {
|
|
94
124
|
const format = useTraceFormat();
|
|
95
125
|
return (_jsxs(Grid, { columns: { minWidth: 120, repeat: 'fit' }, gap: 2, children: [_jsx(Stat, { label: TRACE_FIELDS.duration.label, value: format.duration(totals.durationMs) }), _jsx(FirstTextStat, { latency: latency, format: format }), _jsx(CostStat, { totals: totals, format: format }), _jsx(TokensStat, { totals: totals, format: format }), _jsx(StepsStat, { totals: totals, tools: tools, format: format }), _jsx(GuardrailsStat, { latency: latency, format: format })] }));
|
|
96
126
|
}
|