fix(sessions): clear stale contextTokens on model switch (#38044)

Merged via squash.

Prepared head SHA: bac2df4b7f
Co-authored-by: yuweuii <82372187+yuweuii@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
yuweuii
2026-03-09 01:59:16 +08:00
committed by GitHub
parent caf1b84822
commit 6c9b49a10b
8 changed files with 99 additions and 11 deletions

View File

@@ -30,6 +30,7 @@ describe("applyModelOverrideToSessionEntry", () => {
model: "claude-sonnet-4-6",
providerOverride: "anthropic",
modelOverride: "claude-sonnet-4-6",
contextTokens: 160_000,
fallbackNoticeSelectedModel: "anthropic/claude-sonnet-4-6",
fallbackNoticeActiveModel: "anthropic/claude-sonnet-4-6",
fallbackNoticeReason: "provider temporary failure",
@@ -39,6 +40,7 @@ describe("applyModelOverrideToSessionEntry", () => {
expect(result.updated).toBe(true);
expectRuntimeModelFieldsCleared(entry, before);
expect(entry.contextTokens).toBeUndefined();
expect(entry.fallbackNoticeSelectedModel).toBeUndefined();
expect(entry.fallbackNoticeActiveModel).toBeUndefined();
expect(entry.fallbackNoticeReason).toBeUndefined();
@@ -53,12 +55,14 @@ describe("applyModelOverrideToSessionEntry", () => {
model: "claude-sonnet-4-6",
providerOverride: "openai",
modelOverride: "gpt-5.2",
contextTokens: 160_000,
};
const result = applyOpenAiSelection(entry);
expect(result.updated).toBe(true);
expectRuntimeModelFieldsCleared(entry, before);
expect(entry.contextTokens).toBeUndefined();
});
it("retains aligned runtime model fields when selection and runtime already match", () => {
@@ -70,6 +74,7 @@ describe("applyModelOverrideToSessionEntry", () => {
model: "gpt-5.2",
providerOverride: "openai",
modelOverride: "gpt-5.2",
contextTokens: 200_000,
};
const result = applyModelOverrideToSessionEntry({
@@ -83,6 +88,33 @@ describe("applyModelOverrideToSessionEntry", () => {
expect(result.updated).toBe(false);
expect(entry.modelProvider).toBe("openai");
expect(entry.model).toBe("gpt-5.2");
expect(entry.contextTokens).toBe(200_000);
expect(entry.updatedAt).toBe(before);
});
it("clears stale contextTokens when switching back to the default model", () => {
const before = Date.now() - 5_000;
const entry: SessionEntry = {
sessionId: "sess-4",
updatedAt: before,
providerOverride: "local",
modelOverride: "sunapi386/llama-3-lexi-uncensored:8b",
contextTokens: 4_096,
};
const result = applyModelOverrideToSessionEntry({
entry,
selection: {
provider: "local",
model: "llama3.1:8b",
isDefault: true,
},
});
expect(result.updated).toBe(true);
expect(entry.providerOverride).toBeUndefined();
expect(entry.modelOverride).toBeUndefined();
expect(entry.contextTokens).toBeUndefined();
expect((entry.updatedAt ?? 0) > before).toBe(true);
});
});

View File

@@ -61,6 +61,17 @@ export function applyModelOverrideToSessionEntry(params: {
}
}
// contextTokens are derived from the active session model. When the selected
// model changes (or runtime model is already stale), the cached window can
// pin the session to an older/smaller limit until another run refreshes it.
if (
entry.contextTokens !== undefined &&
(selectionUpdated || (runtimePresent && !runtimeAligned))
) {
delete entry.contextTokens;
updated = true;
}
if (profileOverride) {
if (entry.authProfileOverride !== profileOverride) {
entry.authProfileOverride = profileOverride;