refactor(ui): revamp exit stats display (#2771)
This commit is contained in:
parent
3587054d32
commit
f91927569c
|
@ -33,7 +33,7 @@ const renderWithMockedStats = (metrics: SessionMetrics) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('<SessionSummaryDisplay />', () => {
|
describe('<SessionSummaryDisplay />', () => {
|
||||||
it('correctly sums and displays stats from multiple models', () => {
|
it('renders the summary display with a title', () => {
|
||||||
const metrics: SessionMetrics = {
|
const metrics: SessionMetrics = {
|
||||||
models: {
|
models: {
|
||||||
'gemini-2.5-pro': {
|
'gemini-2.5-pro': {
|
||||||
|
@ -47,17 +47,6 @@ describe('<SessionSummaryDisplay />', () => {
|
||||||
tool: 200,
|
tool: 200,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'gemini-2.5-flash': {
|
|
||||||
api: { totalRequests: 5, totalErrors: 0, totalLatencyMs: 12345 },
|
|
||||||
tokens: {
|
|
||||||
prompt: 500,
|
|
||||||
candidates: 1000,
|
|
||||||
total: 1500,
|
|
||||||
cached: 100,
|
|
||||||
thoughts: 50,
|
|
||||||
tool: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
tools: {
|
tools: {
|
||||||
totalCalls: 0,
|
totalCalls: 0,
|
||||||
|
@ -72,25 +61,7 @@ describe('<SessionSummaryDisplay />', () => {
|
||||||
const { lastFrame } = renderWithMockedStats(metrics);
|
const { lastFrame } = renderWithMockedStats(metrics);
|
||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
|
|
||||||
// Verify totals are summed correctly
|
expect(output).toContain('Agent powering down. Goodbye!');
|
||||||
expect(output).toContain('Cumulative Stats (15 API calls)');
|
|
||||||
expect(output).toMatchSnapshot();
|
expect(output).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders zero state correctly', () => {
|
|
||||||
const zeroMetrics: SessionMetrics = {
|
|
||||||
models: {},
|
|
||||||
tools: {
|
|
||||||
totalCalls: 0,
|
|
||||||
totalSuccess: 0,
|
|
||||||
totalFail: 0,
|
|
||||||
totalDurationMs: 0,
|
|
||||||
totalDecisions: { accept: 0, reject: 0, modify: 0 },
|
|
||||||
byName: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { lastFrame } = renderWithMockedStats(zeroMetrics);
|
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,101 +5,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { StatsDisplay } from './StatsDisplay.js';
|
||||||
import Gradient from 'ink-gradient';
|
|
||||||
import { Colors } from '../colors.js';
|
|
||||||
import { formatDuration } from '../utils/formatters.js';
|
|
||||||
import { useSessionStats } from '../contexts/SessionContext.js';
|
|
||||||
import { computeSessionStats } from '../utils/computeStats.js';
|
|
||||||
import { FormattedStats, StatRow, StatsColumn } from './Stats.js';
|
|
||||||
|
|
||||||
// --- Prop and Data Structures ---
|
|
||||||
|
|
||||||
interface SessionSummaryDisplayProps {
|
interface SessionSummaryDisplayProps {
|
||||||
duration: string;
|
duration: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Main Component ---
|
|
||||||
|
|
||||||
export const SessionSummaryDisplay: React.FC<SessionSummaryDisplayProps> = ({
|
export const SessionSummaryDisplay: React.FC<SessionSummaryDisplayProps> = ({
|
||||||
duration,
|
duration,
|
||||||
}) => {
|
}) => (
|
||||||
const { stats } = useSessionStats();
|
<StatsDisplay title="Agent powering down. Goodbye!" duration={duration} />
|
||||||
const { metrics } = stats;
|
);
|
||||||
const computed = computeSessionStats(metrics);
|
|
||||||
|
|
||||||
const cumulativeFormatted: FormattedStats = {
|
|
||||||
inputTokens: Object.values(metrics.models).reduce(
|
|
||||||
(acc, model) => acc + model.tokens.prompt,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
outputTokens: Object.values(metrics.models).reduce(
|
|
||||||
(acc, model) => acc + model.tokens.candidates,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
toolUseTokens: Object.values(metrics.models).reduce(
|
|
||||||
(acc, model) => acc + model.tokens.tool,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
thoughtsTokens: Object.values(metrics.models).reduce(
|
|
||||||
(acc, model) => acc + model.tokens.thoughts,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
cachedTokens: Object.values(metrics.models).reduce(
|
|
||||||
(acc, model) => acc + model.tokens.cached,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
totalTokens: Object.values(metrics.models).reduce(
|
|
||||||
(acc, model) => acc + model.tokens.total,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
const totalRequests = Object.values(metrics.models).reduce(
|
|
||||||
(acc, model) => acc + model.api.totalRequests,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
const title = 'Agent powering down. Goodbye!';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
borderStyle="round"
|
|
||||||
borderColor="gray"
|
|
||||||
flexDirection="column"
|
|
||||||
paddingY={1}
|
|
||||||
paddingX={2}
|
|
||||||
alignSelf="flex-start"
|
|
||||||
>
|
|
||||||
<Box marginBottom={1} flexDirection="column">
|
|
||||||
{Colors.GradientColors ? (
|
|
||||||
<Gradient colors={Colors.GradientColors}>
|
|
||||||
<Text bold>{title}</Text>
|
|
||||||
</Gradient>
|
|
||||||
) : (
|
|
||||||
<Text bold>{title}</Text>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box marginTop={1}>
|
|
||||||
<StatsColumn
|
|
||||||
title={`Cumulative Stats (${totalRequests} API calls)`}
|
|
||||||
stats={cumulativeFormatted}
|
|
||||||
isCumulative={true}
|
|
||||||
>
|
|
||||||
<Box marginTop={1} flexDirection="column">
|
|
||||||
<StatRow
|
|
||||||
label="Total duration (API)"
|
|
||||||
value={formatDuration(computed.totalApiTime)}
|
|
||||||
/>
|
|
||||||
<StatRow
|
|
||||||
label="Total duration (Tools)"
|
|
||||||
value={formatDuration(computed.totalToolTime)}
|
|
||||||
/>
|
|
||||||
<StatRow label="Total duration (wall)" value={duration} />
|
|
||||||
</Box>
|
|
||||||
</StatsColumn>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2025 Google LLC
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { render } from 'ink-testing-library';
|
|
||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import {
|
|
||||||
StatRow,
|
|
||||||
StatsColumn,
|
|
||||||
DurationColumn,
|
|
||||||
FormattedStats,
|
|
||||||
} from './Stats.js';
|
|
||||||
import { Colors } from '../colors.js';
|
|
||||||
|
|
||||||
describe('<StatRow />', () => {
|
|
||||||
it('renders a label and value', () => {
|
|
||||||
const { lastFrame } = render(
|
|
||||||
<StatRow label="Test Label" value="Test Value" />,
|
|
||||||
);
|
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders with a specific value color', () => {
|
|
||||||
const { lastFrame } = render(
|
|
||||||
<StatRow
|
|
||||||
label="Test Label"
|
|
||||||
value="Test Value"
|
|
||||||
valueColor={Colors.AccentGreen}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('<StatsColumn />', () => {
|
|
||||||
const mockStats: FormattedStats = {
|
|
||||||
inputTokens: 100,
|
|
||||||
outputTokens: 200,
|
|
||||||
toolUseTokens: 50,
|
|
||||||
thoughtsTokens: 25,
|
|
||||||
cachedTokens: 10,
|
|
||||||
totalTokens: 385,
|
|
||||||
};
|
|
||||||
|
|
||||||
it('renders a stats column with children', () => {
|
|
||||||
const { lastFrame } = render(
|
|
||||||
<StatsColumn title="Test Stats" stats={mockStats}>
|
|
||||||
<StatRow label="Child Prop" value="Child Value" />
|
|
||||||
</StatsColumn>,
|
|
||||||
);
|
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders a stats column with a specific width', () => {
|
|
||||||
const { lastFrame } = render(
|
|
||||||
<StatsColumn title="Test Stats" stats={mockStats} width="50%" />,
|
|
||||||
);
|
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders a cumulative stats column with percentages', () => {
|
|
||||||
const { lastFrame } = render(
|
|
||||||
<StatsColumn title="Cumulative Stats" stats={mockStats} isCumulative />,
|
|
||||||
);
|
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('hides the tool use row when there are no tool use tokens', () => {
|
|
||||||
const statsWithNoToolUse: FormattedStats = {
|
|
||||||
...mockStats,
|
|
||||||
toolUseTokens: 0,
|
|
||||||
};
|
|
||||||
const { lastFrame } = render(
|
|
||||||
<StatsColumn title="Test Stats" stats={statsWithNoToolUse} />,
|
|
||||||
);
|
|
||||||
expect(lastFrame()).not.toContain('Tool Use Tokens');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('<DurationColumn />', () => {
|
|
||||||
it('renders a duration column', () => {
|
|
||||||
const { lastFrame } = render(
|
|
||||||
<DurationColumn apiTime="5s" wallTime="10s" />,
|
|
||||||
);
|
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,118 +0,0 @@
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2025 Google LLC
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Box, Text } from 'ink';
|
|
||||||
import { Colors } from '../colors.js';
|
|
||||||
|
|
||||||
// --- Prop and Data Structures ---
|
|
||||||
|
|
||||||
export interface FormattedStats {
|
|
||||||
inputTokens: number;
|
|
||||||
outputTokens: number;
|
|
||||||
toolUseTokens: number;
|
|
||||||
thoughtsTokens: number;
|
|
||||||
cachedTokens: number;
|
|
||||||
totalTokens: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Helper Components ---
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a single row with a colored label on the left and a value on the right.
|
|
||||||
*/
|
|
||||||
export const StatRow: React.FC<{
|
|
||||||
label: string;
|
|
||||||
value: string | number;
|
|
||||||
valueColor?: string;
|
|
||||||
}> = ({ label, value, valueColor }) => (
|
|
||||||
<Box justifyContent="space-between" gap={2}>
|
|
||||||
<Text color={Colors.LightBlue}>{label}</Text>
|
|
||||||
<Text color={valueColor}>{value}</Text>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a full column for either "Last Turn" or "Cumulative" stats.
|
|
||||||
*/
|
|
||||||
export const StatsColumn: React.FC<{
|
|
||||||
title: string;
|
|
||||||
stats: FormattedStats;
|
|
||||||
isCumulative?: boolean;
|
|
||||||
width?: string | number;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}> = ({ title, stats, isCumulative = false, width, children }) => {
|
|
||||||
const cachedDisplay =
|
|
||||||
isCumulative && stats.totalTokens > 0
|
|
||||||
? `${stats.cachedTokens.toLocaleString()} (${((stats.cachedTokens / stats.totalTokens) * 100).toFixed(1)}%)`
|
|
||||||
: stats.cachedTokens.toLocaleString();
|
|
||||||
|
|
||||||
const cachedColor =
|
|
||||||
isCumulative && stats.cachedTokens > 0 ? Colors.AccentGreen : undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box flexDirection="column" width={width}>
|
|
||||||
<Text bold>{title}</Text>
|
|
||||||
<Box marginTop={1} flexDirection="column">
|
|
||||||
{/* All StatRows below will now inherit the gap */}
|
|
||||||
<StatRow
|
|
||||||
label="Input Tokens"
|
|
||||||
value={stats.inputTokens.toLocaleString()}
|
|
||||||
/>
|
|
||||||
<StatRow
|
|
||||||
label="Output Tokens"
|
|
||||||
value={stats.outputTokens.toLocaleString()}
|
|
||||||
/>
|
|
||||||
{stats.toolUseTokens > 0 && (
|
|
||||||
<StatRow
|
|
||||||
label="Tool Use Tokens"
|
|
||||||
value={stats.toolUseTokens.toLocaleString()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<StatRow
|
|
||||||
label="Thoughts Tokens"
|
|
||||||
value={stats.thoughtsTokens.toLocaleString()}
|
|
||||||
/>
|
|
||||||
{stats.cachedTokens > 0 && (
|
|
||||||
<StatRow
|
|
||||||
label="Cached Tokens"
|
|
||||||
value={cachedDisplay}
|
|
||||||
valueColor={cachedColor}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{/* Divider Line */}
|
|
||||||
<Box
|
|
||||||
borderTop={true}
|
|
||||||
borderLeft={false}
|
|
||||||
borderRight={false}
|
|
||||||
borderBottom={false}
|
|
||||||
borderStyle="single"
|
|
||||||
/>
|
|
||||||
<StatRow
|
|
||||||
label="Total Tokens"
|
|
||||||
value={stats.totalTokens.toLocaleString()}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a column for displaying duration information.
|
|
||||||
*/
|
|
||||||
export const DurationColumn: React.FC<{
|
|
||||||
apiTime: string;
|
|
||||||
wallTime: string;
|
|
||||||
}> = ({ apiTime, wallTime }) => (
|
|
||||||
<Box flexDirection="column" width={'48%'}>
|
|
||||||
<Text bold>Duration</Text>
|
|
||||||
<Box marginTop={1} flexDirection="column">
|
|
||||||
<StatRow label="API Time" value={apiTime} />
|
|
||||||
<StatRow label="Wall Time" value={wallTime} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
|
@ -260,4 +260,44 @@ describe('<StatsDisplay />', () => {
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Title Rendering', () => {
|
||||||
|
const zeroMetrics: SessionMetrics = {
|
||||||
|
models: {},
|
||||||
|
tools: {
|
||||||
|
totalCalls: 0,
|
||||||
|
totalSuccess: 0,
|
||||||
|
totalFail: 0,
|
||||||
|
totalDurationMs: 0,
|
||||||
|
totalDecisions: { accept: 0, reject: 0, modify: 0 },
|
||||||
|
byName: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('renders the default title when no title prop is provided', () => {
|
||||||
|
const { lastFrame } = renderWithMockedStats(zeroMetrics);
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain('Session Stats');
|
||||||
|
expect(output).not.toContain('Agent powering down');
|
||||||
|
expect(output).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the custom title when a title prop is provided', () => {
|
||||||
|
useSessionStatsMock.mockReturnValue({
|
||||||
|
stats: {
|
||||||
|
sessionStartTime: new Date(),
|
||||||
|
metrics: zeroMetrics,
|
||||||
|
lastPromptTokenCount: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { lastFrame } = render(
|
||||||
|
<StatsDisplay duration="1s" title="Agent powering down. Goodbye!" />,
|
||||||
|
);
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain('Agent powering down. Goodbye!');
|
||||||
|
expect(output).not.toContain('Session Stats');
|
||||||
|
expect(output).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
|
import Gradient from 'ink-gradient';
|
||||||
import { Colors } from '../colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { formatDuration } from '../utils/formatters.js';
|
import { formatDuration } from '../utils/formatters.js';
|
||||||
import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js';
|
import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js';
|
||||||
|
@ -140,9 +141,13 @@ const ModelUsageTable: React.FC<{
|
||||||
|
|
||||||
interface StatsDisplayProps {
|
interface StatsDisplayProps {
|
||||||
duration: string;
|
duration: string;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StatsDisplay: React.FC<StatsDisplayProps> = ({ duration }) => {
|
export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||||
|
duration,
|
||||||
|
title,
|
||||||
|
}) => {
|
||||||
const { stats } = useSessionStats();
|
const { stats } = useSessionStats();
|
||||||
const { metrics } = stats;
|
const { metrics } = stats;
|
||||||
const { models, tools } = metrics;
|
const { models, tools } = metrics;
|
||||||
|
@ -162,6 +167,25 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({ duration }) => {
|
||||||
agreementThresholds,
|
agreementThresholds,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const renderTitle = () => {
|
||||||
|
if (title) {
|
||||||
|
return Colors.GradientColors && Colors.GradientColors.length > 0 ? (
|
||||||
|
<Gradient colors={Colors.GradientColors}>
|
||||||
|
<Text bold>{title}</Text>
|
||||||
|
</Gradient>
|
||||||
|
) : (
|
||||||
|
<Text bold color={Colors.AccentPurple}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Text bold color={Colors.AccentPurple}>
|
||||||
|
Session Stats
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
|
@ -170,9 +194,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({ duration }) => {
|
||||||
paddingY={1}
|
paddingY={1}
|
||||||
paddingX={2}
|
paddingX={2}
|
||||||
>
|
>
|
||||||
<Text bold color={Colors.AccentPurple}>
|
{renderTitle()}
|
||||||
Session Stats
|
|
||||||
</Text>
|
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
|
|
||||||
{tools.totalCalls > 0 && (
|
{tools.totalCalls > 0 && (
|
||||||
|
|
|
@ -1,45 +1,24 @@
|
||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`<SessionSummaryDisplay /> > correctly sums and displays stats from multiple models 1`] = `
|
exports[`<SessionSummaryDisplay /> > renders the summary display with a title 1`] = `
|
||||||
"╭─────────────────────────────────────╮
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ │
|
│ │
|
||||||
│ Agent powering down. Goodbye! │
|
│ Agent powering down. Goodbye! │
|
||||||
│ │
|
│ │
|
||||||
│ │
|
│ Performance │
|
||||||
│ Cumulative Stats (15 API calls) │
|
│ Wall Time: 1h 23m 45s │
|
||||||
│ │
|
│ Agent Active: 50.2s │
|
||||||
│ Input Tokens 1,500 │
|
│ » API Time: 50.2s (100.0%) │
|
||||||
│ Output Tokens 3,000 │
|
│ » Tool Time: 0s (0.0%) │
|
||||||
│ Tool Use Tokens 220 │
|
│ │
|
||||||
│ Thoughts Tokens 350 │
|
│ │
|
||||||
│ Cached Tokens 600 (12.0%) │
|
│ Model Usage Reqs Input Tokens Output Tokens │
|
||||||
│ ───────────────────────────────── │
|
│ ─────────────────────────────────────────────────────────────── │
|
||||||
│ Total Tokens 5,000 │
|
│ gemini-2.5-pro 10 1,000 2,000 │
|
||||||
│ │
|
│ │
|
||||||
│ Total duration (API) 1m 2s │
|
│ Savings Highlight: 500 (50.0%) of input tokens were served from the cache, reducing costs. │
|
||||||
│ Total duration (Tools) 0s │
|
│ │
|
||||||
│ Total duration (wall) 1h 23m 45s │
|
│ » Tip: For a full token breakdown, run \`/stats model\`. │
|
||||||
│ │
|
│ │
|
||||||
╰─────────────────────────────────────╯"
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<SessionSummaryDisplay /> > renders zero state correctly 1`] = `
|
|
||||||
"╭─────────────────────────────────────╮
|
|
||||||
│ │
|
|
||||||
│ Agent powering down. Goodbye! │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ Cumulative Stats (0 API calls) │
|
|
||||||
│ │
|
|
||||||
│ Input Tokens 0 │
|
|
||||||
│ Output Tokens 0 │
|
|
||||||
│ Thoughts Tokens 0 │
|
|
||||||
│ ───────────────────────────────── │
|
|
||||||
│ Total Tokens 0 │
|
|
||||||
│ │
|
|
||||||
│ Total duration (API) 0s │
|
|
||||||
│ Total duration (Tools) 0s │
|
|
||||||
│ Total duration (wall) 1h 23m 45s │
|
|
||||||
│ │
|
|
||||||
╰─────────────────────────────────────╯"
|
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
||||||
|
|
||||||
exports[`<DurationColumn /> > renders a duration column 1`] = `
|
|
||||||
"Duration
|
|
||||||
|
|
||||||
API Time 5s
|
|
||||||
Wall Time 10s"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<StatRow /> > renders a label and value 1`] = `"Test Label Test Value"`;
|
|
||||||
|
|
||||||
exports[`<StatRow /> > renders with a specific value color 1`] = `"Test Label Test Value"`;
|
|
||||||
|
|
||||||
exports[`<StatsColumn /> > renders a cumulative stats column with percentages 1`] = `
|
|
||||||
"Cumulative Stats
|
|
||||||
|
|
||||||
Input Tokens 100
|
|
||||||
Output Tokens 200
|
|
||||||
Tool Use Tokens 50
|
|
||||||
Thoughts Tokens 25
|
|
||||||
Cached Tokens 10 (2.6%)
|
|
||||||
────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
||||||
Total Tokens 385"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<StatsColumn /> > renders a stats column with a specific width 1`] = `
|
|
||||||
"Test Stats
|
|
||||||
|
|
||||||
Input Tokens 100
|
|
||||||
Output Tokens 200
|
|
||||||
Tool Use Tokens 50
|
|
||||||
Thoughts Tokens 25
|
|
||||||
Cached Tokens 10
|
|
||||||
──────────────────────────────────────────────────
|
|
||||||
Total Tokens 385"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<StatsColumn /> > renders a stats column with children 1`] = `
|
|
||||||
"Test Stats
|
|
||||||
|
|
||||||
Input Tokens 100
|
|
||||||
Output Tokens 200
|
|
||||||
Tool Use Tokens 50
|
|
||||||
Thoughts Tokens 25
|
|
||||||
Cached Tokens 10
|
|
||||||
────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
||||||
Total Tokens 385
|
|
||||||
Child Prop Child Value"
|
|
||||||
`;
|
|
|
@ -95,6 +95,36 @@ exports[`<StatsDisplay /> > Conditional Rendering Tests > hides User Agreement w
|
||||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`<StatsDisplay /> > Title Rendering > renders the custom title when a title prop is provided 1`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Agent powering down. Goodbye! │
|
||||||
|
│ │
|
||||||
|
│ Performance │
|
||||||
|
│ Wall Time: 1s │
|
||||||
|
│ Agent Active: 0s │
|
||||||
|
│ » API Time: 0s (0.0%) │
|
||||||
|
│ » Tool Time: 0s (0.0%) │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<StatsDisplay /> > Title Rendering > renders the default title when no title prop is provided 1`] = `
|
||||||
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Session Stats │
|
||||||
|
│ │
|
||||||
|
│ Performance │
|
||||||
|
│ Wall Time: 1s │
|
||||||
|
│ Agent Active: 0s │
|
||||||
|
│ » API Time: 0s (0.0%) │
|
||||||
|
│ » Tool Time: 0s (0.0%) │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`<StatsDisplay /> > renders a table with two models correctly 1`] = `
|
exports[`<StatsDisplay /> > renders a table with two models correctly 1`] = `
|
||||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ │
|
│ │
|
||||||
|
|
Loading…
Reference in New Issue