summaryrefslogtreecommitdiffhomepage
path: root/packages/frontend/tests
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-30 18:53:42 +0900
committerAdam Malczewski <[email protected]>2026-05-30 18:53:42 +0900
commit2228691e14be2368394e38e600bfa2ce227487b1 (patch)
treed6b3cfffd11dc9f7eec8c88f5fc97a7ec81e61a0 /packages/frontend/tests
parent497b397e873f96d6fde3d8a44b3318e1ee1cbef4 (diff)
downloaddispatch-2228691e14be2368394e38e600bfa2ce227487b1.tar.gz
dispatch-2228691e14be2368394e38e600bfa2ce227487b1.zip
feat(cache): Anthropic prompt caching, usage telemetry, and Cache Rate view
- send prompt-caching + oauth anthropic-beta headers on the Claude OAuth provider - restructure the OAuth request body (billing header, identity split, relocate third-party system prompt to the first user message) to match Claude Code - apply rolling cache_control breakpoints and group a turn's tool results into a single role:tool message for correct breakpoint placement - emit per-step usage events (cache read/write split) and add the Cache Rate sidebar panel - dedup byte-identical tool calls within a single batch
Diffstat (limited to 'packages/frontend/tests')
-rw-r--r--packages/frontend/tests/chat-store.test.ts47
1 files changed, 47 insertions, 0 deletions
diff --git a/packages/frontend/tests/chat-store.test.ts b/packages/frontend/tests/chat-store.test.ts
index bafd79e..c485fc7 100644
--- a/packages/frontend/tests/chat-store.test.ts
+++ b/packages/frontend/tests/chat-store.test.ts
@@ -1050,3 +1050,50 @@ describe("handleEvent statuses with TabStatusSnapshot", () => {
expect(msgA?.isStreaming).toBe(false);
});
});
+
+describe("tabStore — cache rate (usage events)", () => {
+ it("accumulates usage events into per-tab cacheStats and tracks the last request", async () => {
+ const { store, tabId } = await setupStoreWithTab();
+
+ // No usage yet.
+ expect(store.tabs.find((t) => t.id === tabId)?.cacheStats).toBeUndefined();
+
+ // First request: mostly a cache write (cold prefix).
+ store.handleEvent({
+ type: "usage",
+ tabId,
+ usage: { inputTokens: 1000, outputTokens: 40, cacheReadTokens: 0, cacheWriteTokens: 900 },
+ });
+ // Second request: mostly a cache hit.
+ store.handleEvent({
+ type: "usage",
+ tabId,
+ usage: { inputTokens: 1200, outputTokens: 60, cacheReadTokens: 1000, cacheWriteTokens: 100 },
+ });
+
+ const stats = store.tabs.find((t) => t.id === tabId)?.cacheStats;
+ expect(stats).toBeDefined();
+ expect(stats?.requests).toBe(2);
+ // Cumulative totals.
+ expect(stats?.inputTokens).toBe(2200);
+ expect(stats?.outputTokens).toBe(100);
+ expect(stats?.cacheReadTokens).toBe(1000);
+ expect(stats?.cacheWriteTokens).toBe(1000);
+ // `last` reflects only the most recent request.
+ expect(stats?.last).toEqual({
+ inputTokens: 1200,
+ outputTokens: 60,
+ cacheReadTokens: 1000,
+ cacheWriteTokens: 100,
+ });
+ });
+
+ it("ignores usage events with no tabId", async () => {
+ const { store } = await setupStoreWithTab();
+ store.handleEvent({
+ type: "usage",
+ usage: { inputTokens: 10, outputTokens: 1, cacheReadTokens: 5, cacheWriteTokens: 0 },
+ });
+ expect(store.tabs[0]?.cacheStats).toBeUndefined();
+ });
+});