feat(coding-agent): show cache hit rate in footer
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added the latest prompt cache hit rate to the interactive footer.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed built-in tool expand hints to style closing parentheses consistently ([#5359](https://github.com/earendil-works/pi/issues/5359)).
|
- Fixed built-in tool expand hints to style closing parentheses consistently ([#5359](https://github.com/earendil-works/pi/issues/5359)).
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ The interface from top to bottom:
|
|||||||
- **Startup header** - Shows shortcuts (`/hotkeys` for all), loaded AGENTS.md files, prompt templates, skills, and extensions
|
- **Startup header** - Shows shortcuts (`/hotkeys` for all), loaded AGENTS.md files, prompt templates, skills, and extensions
|
||||||
- **Messages** - Your messages, assistant responses, tool calls and results, notifications, errors, and extension UI
|
- **Messages** - Your messages, assistant responses, tool calls and results, notifications, errors, and extension UI
|
||||||
- **Editor** - Where you type; border color indicates thinking level
|
- **Editor** - Where you type; border color indicates thinking level
|
||||||
- **Footer** - Working directory, session name, total token/cache usage, cost, context usage, current model
|
- **Footer** - Working directory, session name, total token/cache usage (`↑` input, `↓` output, `R` cache read, `W` cache write, `CH` latest cache hit rate), cost, context usage, current model
|
||||||
|
|
||||||
The editor can be temporarily replaced by other UI, like built-in `/settings` or custom UI from extensions (e.g., a Q&A tool that lets the user answer model questions in a structured format). [Extensions](#extensions) can also replace the editor, add widgets above/below it, a status line, custom footer, or overlays.
|
The editor can be temporarily replaced by other UI, like built-in `/settings` or custom UI from extensions (e.g., a Q&A tool that lets the user answer model questions in a structured format). [Extensions](#extensions) can also replace the editor, add widgets above/below it, a status line, custom footer, or overlays.
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export class FooterComponent implements Component {
|
|||||||
let totalCacheRead = 0;
|
let totalCacheRead = 0;
|
||||||
let totalCacheWrite = 0;
|
let totalCacheWrite = 0;
|
||||||
let totalCost = 0;
|
let totalCost = 0;
|
||||||
|
let latestCacheHitRate: number | undefined;
|
||||||
|
|
||||||
for (const entry of this.session.sessionManager.getEntries()) {
|
for (const entry of this.session.sessionManager.getEntries()) {
|
||||||
if (entry.type === "message" && entry.message.role === "assistant") {
|
if (entry.type === "message" && entry.message.role === "assistant") {
|
||||||
@@ -96,6 +97,11 @@ export class FooterComponent implements Component {
|
|||||||
totalCacheRead += entry.message.usage.cacheRead;
|
totalCacheRead += entry.message.usage.cacheRead;
|
||||||
totalCacheWrite += entry.message.usage.cacheWrite;
|
totalCacheWrite += entry.message.usage.cacheWrite;
|
||||||
totalCost += entry.message.usage.cost.total;
|
totalCost += entry.message.usage.cost.total;
|
||||||
|
|
||||||
|
const latestPromptTokens =
|
||||||
|
entry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;
|
||||||
|
latestCacheHitRate =
|
||||||
|
latestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +133,9 @@ export class FooterComponent implements Component {
|
|||||||
if (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);
|
if (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);
|
||||||
if (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);
|
if (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);
|
||||||
if (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);
|
if (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);
|
||||||
|
if ((totalCacheRead > 0 || totalCacheWrite > 0) && latestCacheHitRate !== undefined) {
|
||||||
|
statsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);
|
||||||
|
}
|
||||||
|
|
||||||
// Show cost with "(sub)" indicator if using OAuth subscription
|
// Show cost with "(sub)" indicator if using OAuth subscription
|
||||||
const usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;
|
const usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { AgentSession } from "../src/core/agent-session.ts";
|
|||||||
import type { ReadonlyFooterDataProvider } from "../src/core/footer-data-provider.ts";
|
import type { ReadonlyFooterDataProvider } from "../src/core/footer-data-provider.ts";
|
||||||
import { FooterComponent, formatCwdForFooter } from "../src/modes/interactive/components/footer.ts";
|
import { FooterComponent, formatCwdForFooter } from "../src/modes/interactive/components/footer.ts";
|
||||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||||
|
import { stripAnsi } from "../src/utils/ansi.ts";
|
||||||
|
|
||||||
type AssistantUsage = {
|
type AssistantUsage = {
|
||||||
input: number;
|
input: number;
|
||||||
@@ -123,4 +124,21 @@ describe("FooterComponent width handling", () => {
|
|||||||
expect(visibleWidth(line)).toBeLessThanOrEqual(width);
|
expect(visibleWidth(line)).toBeLessThanOrEqual(width);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows the latest cache hit rate when cache usage is present", () => {
|
||||||
|
const session = createSession({
|
||||||
|
sessionName: "",
|
||||||
|
usage: {
|
||||||
|
input: 100,
|
||||||
|
output: 10,
|
||||||
|
cacheRead: 50,
|
||||||
|
cacheWrite: 50,
|
||||||
|
cost: { total: 0.001 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const footer = new FooterComponent(session, createFooterData(1));
|
||||||
|
|
||||||
|
const statsLine = stripAnsi(footer.render(120)[1]);
|
||||||
|
expect(statsLine).toContain("CH25.0%");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user