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 />', () => {
|
||||
it('correctly sums and displays stats from multiple models', () => {
|
||||
it('renders the summary display with a title', () => {
|
||||
const metrics: SessionMetrics = {
|
||||
models: {
|
||||
'gemini-2.5-pro': {
|
||||
|
@ -47,17 +47,6 @@ describe('<SessionSummaryDisplay />', () => {
|
|||
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: {
|
||||
totalCalls: 0,
|
||||
|
@ -72,25 +61,7 @@ describe('<SessionSummaryDisplay />', () => {
|
|||
const { lastFrame } = renderWithMockedStats(metrics);
|
||||
const output = lastFrame();
|
||||
|
||||
// Verify totals are summed correctly
|
||||
expect(output).toContain('Cumulative Stats (15 API calls)');
|
||||
expect(output).toContain('Agent powering down. Goodbye!');
|
||||
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 { Box, Text } from 'ink';
|
||||
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 ---
|
||||
import { StatsDisplay } from './StatsDisplay.js';
|
||||
|
||||
interface SessionSummaryDisplayProps {
|
||||
duration: string;
|
||||
}
|
||||
|
||||
// --- Main Component ---
|
||||
|
||||
export const SessionSummaryDisplay: React.FC<SessionSummaryDisplayProps> = ({
|
||||
duration,
|
||||
}) => {
|
||||
const { stats } = useSessionStats();
|
||||
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>
|
||||
);
|
||||
};
|
||||
}) => (
|
||||
<StatsDisplay title="Agent powering down. Goodbye!" duration={duration} />
|
||||
);
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
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 { Box, Text } from 'ink';
|
||||
import Gradient from 'ink-gradient';
|
||||
import { Colors } from '../colors.js';
|
||||
import { formatDuration } from '../utils/formatters.js';
|
||||
import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js';
|
||||
|
@ -140,9 +141,13 @@ const ModelUsageTable: React.FC<{
|
|||
|
||||
interface StatsDisplayProps {
|
||||
duration: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const StatsDisplay: React.FC<StatsDisplayProps> = ({ duration }) => {
|
||||
export const StatsDisplay: React.FC<StatsDisplayProps> = ({
|
||||
duration,
|
||||
title,
|
||||
}) => {
|
||||
const { stats } = useSessionStats();
|
||||
const { metrics } = stats;
|
||||
const { models, tools } = metrics;
|
||||
|
@ -162,6 +167,25 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({ duration }) => {
|
|||
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 (
|
||||
<Box
|
||||
borderStyle="round"
|
||||
|
@ -170,9 +194,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({ duration }) => {
|
|||
paddingY={1}
|
||||
paddingX={2}
|
||||
>
|
||||
<Text bold color={Colors.AccentPurple}>
|
||||
Session Stats
|
||||
</Text>
|
||||
{renderTitle()}
|
||||
<Box height={1} />
|
||||
|
||||
{tools.totalCalls > 0 && (
|
||||
|
|
|
@ -1,45 +1,24 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<SessionSummaryDisplay /> > correctly sums and displays stats from multiple models 1`] = `
|
||||
"╭─────────────────────────────────────╮
|
||||
│ │
|
||||
│ Agent powering down. Goodbye! │
|
||||
│ │
|
||||
│ │
|
||||
│ Cumulative Stats (15 API calls) │
|
||||
│ │
|
||||
│ Input Tokens 1,500 │
|
||||
│ Output Tokens 3,000 │
|
||||
│ Tool Use Tokens 220 │
|
||||
│ Thoughts Tokens 350 │
|
||||
│ Cached Tokens 600 (12.0%) │
|
||||
│ ───────────────────────────────── │
|
||||
│ Total Tokens 5,000 │
|
||||
│ │
|
||||
│ Total duration (API) 1m 2s │
|
||||
│ Total duration (Tools) 0s │
|
||||
│ Total duration (wall) 1h 23m 45s │
|
||||
│ │
|
||||
╰─────────────────────────────────────╯"
|
||||
`;
|
||||
|
||||
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 │
|
||||
│ │
|
||||
╰─────────────────────────────────────╯"
|
||||
exports[`<SessionSummaryDisplay /> > renders the summary display with a title 1`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ Agent powering down. Goodbye! │
|
||||
│ │
|
||||
│ Performance │
|
||||
│ Wall Time: 1h 23m 45s │
|
||||
│ Agent Active: 50.2s │
|
||||
│ » API Time: 50.2s (100.0%) │
|
||||
│ » Tool Time: 0s (0.0%) │
|
||||
│ │
|
||||
│ │
|
||||
│ Model Usage Reqs Input Tokens Output Tokens │
|
||||
│ ─────────────────────────────────────────────────────────────── │
|
||||
│ gemini-2.5-pro 10 1,000 2,000 │
|
||||
│ │
|
||||
│ Savings Highlight: 500 (50.0%) of input tokens were served from the cache, reducing costs. │
|
||||
│ │
|
||||
│ » Tip: For a full token breakdown, run \`/stats model\`. │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
`;
|
||||
|
|
|
@ -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`] = `
|
||||
"╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
|
|
Loading…
Reference in New Issue