Skip to content
Source
Sign inSign up

contextWindow.test.ts

209 lines7.0 KB

1import { describe, expect, it } from "vite-plus/test";
2import { OrchestrationV2ProviderTurnTokenUsage } from "@t3tools/contracts";
3import * as Schema from "effect/Schema";
4import { formatContextWindowCompactionMessage } from "../components/chat/compaction.logic";
5import * as DateTime from "effect/DateTime";
6import {
7 ContextTransferId,
8 ProviderDriverKind,
9 ProviderInstanceId,
10 RunId,
11 ThreadId,
12} from "@t3tools/contracts";
13import { makeThreadProjectionFixture } from "../test-fixtures";
14import {
15 deriveLatestContextWindowSnapshot,
16 latestProviderTurnTokenUsage,
17 shouldOfferResumeCompaction,
18} from "@t3tools/client-runtime/state/context-window";
19​
20const decodeTokenUsage = Schema.decodeSync(OrchestrationV2ProviderTurnTokenUsage);
21​
22describe("V2 context window presentation", () => {
23 it("uses retained compaction token data when available", () => {
24 const snapshot = deriveLatestContextWindowSnapshot([
25 {
26 item: {
27 id: "compaction-1" as never,
28 threadId: "thread-1" as never,
29 runId: null,
30 nodeId: null,
31 providerThreadId: null,
32 providerTurnId: null,
33 nativeItemRef: null,
34 parentItemId: null,
35 ordinal: 1,
36 status: "completed",
37 title: null,
38 startedAt: null,
39 completedAt: null,
40 updatedAt: DateTime.makeUnsafe("2026-06-20T00:00:00.000Z"),
41 type: "compaction",
42 driver: null,
43 beforeTokenCount: 10_000,
44 afterTokenCount: 2_000,
45 },
46 },
47 ]);
48 expect(snapshot?.usedTokens).toBe(2_000);
49 expect(snapshot?.totalProcessedTokens).toBe(10_000);
50 });
51});
52​
53describe("live provider-turn usage (#8144)", () => {
54 it("prefers the provider's live report over compaction items", () => {
55 const usage = decodeTokenUsage({
56 usedTokens: 42_000,
57 maxTokens: 200_000,
58 inputTokens: 40_000,
59 outputTokens: 2_000,
60 autoCompactThreshold: 150_000,
61 updatedAt: "2026-08-27T00:00:00.000Z",
62 });
63 const snapshot = deriveLatestContextWindowSnapshot([], usage);
64 expect(snapshot).not.toBeNull();
65 expect(snapshot?.usedTokens).toBe(42_000);
66 expect(snapshot?.maxTokens).toBe(200_000);
67 expect(snapshot?.remainingTokens).toBe(158_000);
68 expect(snapshot?.usedPercentage).toBe(21);
69 expect(snapshot?.autoCompactThreshold).toBe(150_000);
70 expect(formatContextWindowCompactionMessage(snapshot?.autoCompactThreshold)).toBe(
71 "Compacts automatically at about 150,000 tokens.",
72 );
73 });
74});
75​
76describe("latestProviderTurnTokenUsage", () => {
77 it("picks the newest report, not the last turn in snapshot order", () => {
78 const usage = (usedTokens: number, updatedAt: string) => ({
79 tokenUsage: { usedTokens, updatedAt },
80 });
81 const latest = latestProviderTurnTokenUsage([
82 usage(300_000, "2026-06-20T12:00:00.000Z"),
83 {},
84 usage(90_000, "2026-06-20T09:00:00.000Z"),
85 ]);
86 expect(latest?.usedTokens).toBe(300_000);
87 });
88​
89 const sourceUsage = {
90 usedTokens: 513_000,
91 maxTokens: 1_000_000,
92 promptCacheTtlMs: 5 * 60_000,
93 updatedAt: "2026-10-19T12:00:00.000Z",
94 };
95 function forkProjection() {
96 const projection = makeThreadProjectionFixture();
97 const sourceThreadId = ThreadId.make("parent");
98 const runId = RunId.make("fork-point");
99 return {
100 thread: {
101 ...projection.thread,
102 forkedFrom: { type: "run" as const, threadId: sourceThreadId, runId },
103 },
104 contextTransfers: [
105 {
106 id: ContextTransferId.make("fork-transfer"),
107 type: "fork" as const,
108 sourceThreadId,
109 targetThreadId: projection.thread.id,
110 sourcePoint: { threadId: sourceThreadId, runId },
111 basePoint: null,
112 sourceProviderInstanceId: projection.thread.modelSelection.instanceId,
113 sourceTokenUsage: sourceUsage,
114 targetProviderInstanceId: null,
115 targetRunId: null,
116 status: "pending" as const,
117 resolution: null,
118 createdBy: "user" as const,
119 error: null,
120 createdAt: projection.updatedAt,
121 updatedAt: projection.updatedAt,
122 consumedAt: null,
123 },
124 ],
125 };
126 }
127​
128 it("keeps fork-point context and its cache expiry before the fork reports usage", () => {
129 const usage = latestProviderTurnTokenUsage([], forkProjection());
130 expect(usage).toEqual(sourceUsage);
131 const snapshot = deriveLatestContextWindowSnapshot([], usage);
132 expect(snapshot?.usedTokens).toBe(513_000);
133 expect(
134 shouldOfferResumeCompaction({
135 provider: "claudeAgent",
136 usedTokens: snapshot?.usedTokens,
137 updatedAt: snapshot?.updatedAt,
138 promptCacheTtlMs: usage?.promptCacheTtlMs,
139 now: "2026-10-19T12:06:00.000Z",
140 }),
141 ).toBe(true);
142 });
143​
144 it("replaces inherited usage with the fork's own report", () => {
145 const tokenUsage = { usedTokens: 42_000, updatedAt: "2026-10-19T12:10:00.000Z" };
146 expect(latestProviderTurnTokenUsage([{ tokenUsage }], forkProjection())).toEqual(tokenUsage);
147 });
148​
149 it.each([0, 42_000])(
150 "prefers target provider-thread usage of %d tokens over inherited usage",
151 (usedTokens) => {
152 const providerThread = {
153 contextUsage: {
154 usedTokens,
155 maxTokens: 200_000,
156 cost: { amount: 0.42, currency: "USD" },
157 },
158 updatedAt: DateTime.makeUnsafe("2026-10-19T12:10:00.000Z"),
159 };
160 const usage = latestProviderTurnTokenUsage([], forkProjection(), providerThread);
161 const snapshot = deriveLatestContextWindowSnapshot([], usage, providerThread);
162​
163 expect(snapshot).toMatchObject({
164 usedTokens,
165 maxTokens: 200_000,
166 cost: { amount: 0.42, currency: "USD" },
167 updatedAt: "2026-10-19T12:10:00.000Z",
168 });
169 },
170 );
171​
172 it("keeps the fallback after a native fork is consumed", () => {
173 const projection = forkProjection();
174 const transfer = projection.contextTransfers[0]!;
175 expect(
176 latestProviderTurnTokenUsage([], {
177 ...projection,
178 contextTransfers: [
179 {
180 ...transfer,
181 status: "consumed",
182 resolution: {
183 strategy: "native_fork",
184 providerThreadRef: {
185 driver: ProviderDriverKind.make("claudeAgent"),
186 nativeId: "fork",
187 strength: "strong",
188 },
189 },
190 },
191 ],
192 }),
193 ).toEqual(sourceUsage);
194 });
195​
196 it("does not reuse usage for portable handoffs, different providers, or missing history", () => {
197 const projection = forkProjection();
198 const transfer = projection.contextTransfers[0]!;
199 for (const contextTransfers of [
200 [],
201 [{ ...transfer, sourceTokenUsage: undefined }],
202 [{ ...transfer, status: "resolved_portable" as const }],
203 [{ ...transfer, sourceProviderInstanceId: ProviderInstanceId.make("other") }],
204 [{ ...transfer, sourcePoint: { ...transfer.sourcePoint, runId: RunId.make("later") } }],
205 ]) {
206 expect(latestProviderTurnTokenUsage([], { ...projection, contextTransfers })).toBeNull();
207 }
208 });
209});