@theoremjs/react 0.3.4-next.618.g0612421 → 0.3.4-next.622.g75b9875

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theoremjs/react",
3
- "version": "0.3.4-next.618.g0612421",
3
+ "version": "0.3.4-next.622.g75b9875",
4
4
  "description": "React bindings for Theorem: headless hooks + transport, an Astryx chat UI, and a Web-standard server handler.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -60,8 +60,8 @@
60
60
  "@stylexjs/stylex": "0.19.0",
61
61
  "react": "^19.0.0",
62
62
  "react-dom": "^19.0.0",
63
- "@theoremjs/agents": "^0.3.4-next.618.g0612421",
64
- "@theoremjs/web": "^0.3.4-next.618.g0612421"
63
+ "@theoremjs/agents": "^0.3.4-next.622.g75b9875",
64
+ "@theoremjs/web": "^0.3.4-next.622.g75b9875"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@astryxdesign/core": {
package/ui/SidePanel.d.ts CHANGED
@@ -22,12 +22,13 @@ export type SidePanelLabels = {
22
22
  resize: string;
23
23
  };
24
24
  /** Shows or hides one side panel (`panelId` from useSidePanel). */
25
- export declare function SidePanelToggle({ labels, icon, panelId, open, onToggle, }: {
25
+ export declare function SidePanelToggle({ labels, icon, panelId, open, onToggle, size, }: {
26
26
  labels: SidePanelLabels;
27
27
  icon: ReactNode;
28
28
  panelId: string;
29
29
  open: boolean;
30
30
  onToggle: () => void;
31
+ size?: 'sm';
31
32
  }): import("react").JSX.Element;
32
33
  export type SidePanelProps = {
33
34
  id?: string;
package/ui/SidePanel.js CHANGED
@@ -45,9 +45,9 @@ export function SidePanelHeader({ children, floating = false, }) {
45
45
  return (_jsx(LayoutHeader, { hasDivider: false, children: _jsx(HStack, { hAlign: "end", gap: 1, children: children }) }));
46
46
  }
47
47
  /** Shows or hides one side panel (`panelId` from useSidePanel). */
48
- export function SidePanelToggle({ labels, icon, panelId, open, onToggle, }) {
48
+ export function SidePanelToggle({ labels, icon, panelId, open, onToggle, size, }) {
49
49
  const action = open ? labels.hide : labels.show;
50
- return (_jsx(IconButton, { label: action, tooltip: action, variant: open ? 'secondary' : 'ghost', "aria-expanded": open, "aria-controls": panelId, icon: icon, onClick: onToggle }));
50
+ return (_jsx(IconButton, { label: action, size: size, tooltip: action, variant: open ? 'secondary' : 'ghost', "aria-expanded": open, "aria-controls": panelId, icon: icon, onClick: onToggle }));
51
51
  }
52
52
  /** Page corners like the host's raised panels, no border or shadow; the card fill, so surface-filled cards read on it. */
53
53
  export const RAISED = {
@@ -2,9 +2,11 @@ import type { TraceRecord } from '@theoremjs/agents';
2
2
  import type { TraceFeed } from '@theoremjs/web/client';
3
3
  import { type ReactNode } from 'react';
4
4
  /** The inspector over the records a feed has delivered; the panel loads this module when it first opens. */
5
- export declare function TraceFeedBody({ traces, isWide, }: {
5
+ export declare function TraceFeedBody({ traces, isWide, end, }: {
6
6
  traces: TraceFeed | undefined;
7
7
  isWide: boolean;
8
+ /** Drawn after the turn arrows: the panel's toggle. */
9
+ end?: ReactNode;
8
10
  }): import("react").JSX.Element;
9
11
  /**
10
12
  * Only what the records say of guardrails: how long the checks took, how many
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Badge } from '@astryxdesign/core/Badge';
3
3
  import { EmptyState } from '@astryxdesign/core/EmptyState';
4
4
  import { HStack } from '@astryxdesign/core/HStack';
@@ -143,9 +143,9 @@ function TurnOverview({ turns, index, onPick, onBack, selectedId, onSelect, end,
143
143
  const tools = traceSpans([root]).filter((node) => node.meta.type === 'tool').length;
144
144
  return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(TurnPicker, { turns: turns, index: index, onChange: onPick, onBack: onBack, end: end }), _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] }));
145
145
  }
146
- function EmptyTrace() {
146
+ function EmptyTrace({ end }) {
147
147
  const { t } = useTraceFormat();
148
- 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 }) }));
148
+ return (_jsxs(VStack, { height: "100%", vAlign: "center", padding: 4, children: [end ? _jsx(HStack, { justify: "end", children: end }) : null, _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 })] }));
149
149
  }
150
150
  /** The open span of the open turn; opening another turn closes it. */
151
151
  function useOpenSpan(turns) {
@@ -207,19 +207,19 @@ function TraceDownload({ records }) {
207
207
  * The panel's content. Narrow, the turn fills it and a span opens in its
208
208
  * place; wide, an open span sits beside the turn.
209
209
  */
210
- function TraceInspectorBody({ records, isWide, }) {
210
+ function TraceInspectorBody({ records, isWide, end, }) {
211
211
  const tree = useMemo(() => traceTree(records), [records]);
212
212
  const turns = useMemo(() => traceTurns(tree), [tree]);
213
213
  const { t } = useTraceFormat();
214
214
  const { index, root, open, select, close, pick } = useOpenSpan(turns);
215
215
  if (turns.length === 0)
216
- return _jsx(EmptyTrace, {});
217
- const overview = (_jsx(Overview, { turns: turns, index: index, pick: pick, selectedId: open?.id, select: select, end: _jsx(TraceDownload, { records: records }) }));
216
+ return _jsx(EmptyTrace, { end: end });
217
+ const overview = (_jsx(Overview, { turns: turns, index: index, pick: pick, selectedId: open?.id, select: select, end: _jsxs(_Fragment, { children: [_jsx(TraceDownload, { records: records }), end] }) }));
218
218
  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 })) }));
219
219
  }
220
220
  /** The inspector over the records a feed has delivered; the panel loads this module when it first opens. */
221
- export function TraceFeedBody({ traces, isWide, }) {
222
- return _jsx(TraceInspectorBody, { records: useTraceRecords(traces), isWide: isWide });
221
+ export function TraceFeedBody({ traces, isWide, end, }) {
222
+ return _jsx(TraceInspectorBody, { records: useTraceRecords(traces), isWide: isWide, end: end });
223
223
  }
224
224
  /**
225
225
  * Only what the records say of guardrails: how long the checks took, how many
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Icon } from '@astryxdesign/core/Icon';
3
3
  import { IconActivity } from '@tabler/icons-react';
4
4
  import { cloneElement, createContext, isValidElement, lazy, Suspense, use, useCallback, useId, useRef, useState, } from 'react';
@@ -13,8 +13,8 @@ const TracePlacementContext = createContext('replace');
13
13
  export function TracePlacement({ value, children, }) {
14
14
  return _jsx(TracePlacementContext, { value: value, children: children });
15
15
  }
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 }) })) }));
16
+ function TraceView({ id, toggle, traces, }) {
17
+ return (_jsx(RaisedPane, { id: id, children: (isWide) => (_jsx(Suspense, { fallback: _jsx(SidePanelHeader, { floating: true, children: toggle }), children: _jsx(TraceFeedBody, { traces: traces, isWide: isWide, end: toggle }) })) }));
18
18
  }
19
19
  /**
20
20
  * The trace inspector, offered when the profile records traces. By default the
@@ -39,21 +39,21 @@ export function useTraceInspector(iface, traces, open) {
39
39
  if (iface.observability?.record !== true)
40
40
  return { toggle: null, view: null, labels, offered: false };
41
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;
42
+ const toggleOf = (size) => open === undefined ? (_jsx(SidePanelToggle, { labels: labels, icon: _jsx(Icon, { icon: IconActivity }), panelId: id, open: isOpen, size: size, onToggle: () => setOwn((value) => !value) })) : null;
43
+ const toggle = toggleOf();
44
+ const view = isOpen ? openTrace(placement === 'panel', id, traces, toggleOf('sm')) : null;
44
45
  return { toggle, view, labels, offered: true };
45
46
  }
46
- /** The open trace: filling the host's docked panel, or as its own panel; the toggle on top. */
47
+ /** The open trace: filling the host's docked panel, or as its own panel; the toggle sits in its top row. */
47
48
  function openTrace(docked, id, traces, toggle) {
48
- const header = toggle ? _jsx(SidePanelHeader, { floating: true, children: toggle }) : null;
49
49
  if (docked)
50
- return _jsx(TraceFill, { id: id, header: header, traces: traces });
51
- return _jsx(TraceView, { id: id, header: header, traces: traces });
50
+ return _jsx(TraceFill, { id: id, toggle: toggle, traces: traces });
51
+ return _jsx(TraceView, { id: id, toggle: toggle, traces: traces });
52
52
  }
53
53
  /** The trace body, filling the docked panel. The panel supplies the raised card. */
54
- function TraceFill({ id, header, traces, }) {
54
+ function TraceFill({ id, toggle, traces, }) {
55
55
  const { ref, width } = useWidth();
56
- return (_jsxs("div", { id: id, ref: ref, style: { height: '100%', position: 'relative' }, children: [header, _jsx(Suspense, { fallback: null, children: _jsx(TraceFeedBody, { traces: traces, isWide: isSidePanelWide(width) }) })] }));
56
+ return (_jsx("div", { id: id, ref: ref, style: { height: '100%', position: 'relative' }, children: _jsx(Suspense, { fallback: _jsx(SidePanelHeader, { floating: true, children: toggle }), children: _jsx(TraceFeedBody, { traces: traces, isWide: isSidePanelWide(width), end: toggle }) }) }));
57
57
  }
58
58
  /** The surface, with the trace docked at its inline end. A host-driven trace stays in place. */
59
59
  function TraceDock({ inspector, children }) {
@@ -137,13 +137,49 @@ function ProsePanel({ attribute, text }) {
137
137
  return null;
138
138
  return (_jsx(PanePanel, { ...label(attribute), children: _jsx(Prose, { text: text }) }));
139
139
  }
140
+ /** Content folded under a title, with its count when it is a list. */
141
+ function Folded({ title, count, children, }) {
142
+ return (_jsx(Collapsible, { defaultIsOpen: false, trigger: _jsx(Text, { weight: "medium", children: count === undefined ? title : `${title} · ${String(count)}` }), children: _jsx(Card, { padding: 3, variant: "muted", style: { background: 'var(--color-background-surface)' }, children: children }) }));
143
+ }
144
+ /** The system prompt a model call was sent, part by part. */
145
+ function SystemInstructions({ node }) {
146
+ const parts = inlineContent(node.record, node.span.attributes['gen_ai.system_instructions']);
147
+ const texts = (Array.isArray(parts) ? parts.filter(isObject) : []).flatMap((part) => typeof part.content === 'string' && part.content ? [part.content] : []);
148
+ if (texts.length === 0)
149
+ return null;
150
+ return (_jsx(Folded, { title: label('gen_ai.system_instructions').title, children: _jsx(VStack, { gap: 3, children: keyedByContent(texts, (text) => text).map(({ item: text, key }) => (_jsx(Prose, { text: text }, key))) }) }));
151
+ }
152
+ /** The tools a model call was offered: each one's name, what it does, and what it takes. */
153
+ function ToolDefinitions({ node }) {
154
+ const value = storedValue(node, 'gen_ai.tool.definitions');
155
+ const tools = Array.isArray(value) ? value.filter(isObject) : [];
156
+ if (tools.length === 0)
157
+ return null;
158
+ return (_jsx(Folded, { title: label('gen_ai.tool.definitions').title, count: tools.length, children: _jsx(VStack, { gap: 3, children: keyedByContent(tools, (tool) => String(tool.name)).map(({ item: tool, key }) => (_jsxs(VStack, { gap: 1, children: [_jsx(Text, { weight: "medium", children: String(tool.name ?? '') }), typeof tool.description === 'string' ? (_jsx(Text, { color: "secondary", children: tool.description })) : null, tool.parameters === undefined ? null : _jsx(ShapedData, { value: tool.parameters })] }, key))) }) }));
159
+ }
160
+ /** What a model call was given besides its messages: the system prompt and the tools. */
161
+ function CallContext({ node }) {
162
+ return (_jsxs(_Fragment, { children: [_jsx(SystemInstructions, { node: node }), _jsx(ToolDefinitions, { node: node })] }));
163
+ }
164
+ /** A turn's first model call: the one whose system prompt and tools the turn opened with. */
165
+ function firstCall(node) {
166
+ for (const child of node.children) {
167
+ if ('gen_ai.system_instructions' in child.span.attributes)
168
+ return child;
169
+ const found = firstCall(child);
170
+ if (found)
171
+ return found;
172
+ }
173
+ return undefined;
174
+ }
140
175
  function TurnIO({ node }) {
141
- return (_jsxs(_Fragment, { children: [_jsx(ProsePanel, { attribute: "gen_ai.input.messages", text: messageText(node, 'gen_ai.input.messages', 'user') }), _jsx(ProsePanel, { attribute: "gen_ai.output.messages", text: messageText(node, 'gen_ai.output.messages', 'assistant') })] }));
176
+ const call = firstCall(node);
177
+ return (_jsxs(_Fragment, { children: [call ? _jsx(CallContext, { node: call }) : null, _jsx(ProsePanel, { attribute: "gen_ai.input.messages", text: messageText(node, 'gen_ai.input.messages', 'user') }), _jsx(ProsePanel, { attribute: "gen_ai.output.messages", text: messageText(node, 'gen_ai.output.messages', 'assistant') })] }));
142
178
  }
143
179
  function CallIO({ node }) {
144
180
  const input = messagesOf(node, 'gen_ai.input.messages');
145
181
  const output = messagesOf(node, 'gen_ai.output.messages');
146
- return (_jsxs(_Fragment, { children: [output.length > 0 ? (_jsx(PanePanel, { ...label('gen_ai.output.messages'), children: _jsx(MessageList, { messages: output }) })) : null, input.length > 0 ? (_jsx(Collapsible, { defaultIsOpen: false, trigger: _jsx(Text, { weight: "medium", children: `${label('gen_ai.input.messages').title} · ${input.length}` }), children: _jsx(Card, { padding: 3, variant: "muted", style: { background: 'var(--color-background-surface)' }, children: _jsx(MessageList, { messages: input }) }) })) : null] }));
182
+ return (_jsxs(_Fragment, { children: [output.length > 0 ? (_jsx(PanePanel, { ...label('gen_ai.output.messages'), children: _jsx(MessageList, { messages: output }) })) : null, _jsx(CallContext, { node: node }), input.length > 0 ? (_jsx(Folded, { title: label('gen_ai.input.messages').title, count: input.length, children: _jsx(MessageList, { messages: input }) })) : null] }));
147
183
  }
148
184
  function ToolIO({ node }) {
149
185
  const { attributes } = node.span;