Skip to content

Commit 72875c0

Browse files
luoxuanzaoQoder-AI
andcommitted
fix: keep the chosen context-window tier in the usage meter
The post-response context-usage refresh fell back to the model catalog default window whenever the CLI omitted maxTokens, so a tier chosen in the per-model editor (such as 400K) reverted to the default (200K) after the first message. Route the effective per-model context window into the turn tracker and prefer it over the catalog fallback when the CLI reports no absolute window. Co-authored-by: QoderAI (Qwen 3.8 Max) <qoder_ai@qoder.com>
1 parent 80380ce commit 72875c0

5 files changed

Lines changed: 64 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ version with its date and start a fresh empty `[Unreleased]` above it.
6262
1.1.21 changed its context-usage report to a percentage-based shape
6363
without absolute token counts, which the meter could not read, so it
6464
stayed stuck at its "appears after the first response" placeholder.
65+
- The context usage meter keeps the context-window tier chosen in the
66+
per-model editor after a response; previously the post-response
67+
refresh silently fell back to the model catalog default (such as
68+
200K), so a 400K selection reverted to 200K once a message was sent.
6569

6670
## [1.0.4] - 2026-08-12
6771

‎src/qoder/runtime/qoder-chat-runtime.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import { getActiveQoderCliEdition, getQoderCliBinaryBaseName } from '../config/c
5959
import { loadSubagentFinalResult, loadSubagentToolCalls } from '../history/qoder-history-store';
6060
import type { McpServerManager } from '../mcp/mcp-server-manager';
6161
import { toQoderRuntimeModelId } from '../models/model-selection';
62+
import { qoderModelConfig } from '../models/qoder-model-config';
6263
import { stripCurrentNoteContext } from '../prompt/context/prompt-context';
6364
import { encodeQoderTurn } from '../prompt/qoder-turn-encoder';
6465
import type { QoderHostContext } from '../qoder-host-context';
@@ -183,6 +184,10 @@ export class QoderChatRuntime implements ChatRuntime {
183184
getCurrentQuery: () => this.persistentQuery,
184185
getMessageChannel: () => this.messageChannel,
185186
getConfiguredModel: () => this.getScopedSettings().model,
187+
getConfiguredContextWindow: () => {
188+
const settings = this.getScopedSettings();
189+
return qoderModelConfig.getEffectiveContextWindowSize(settings.model, settings);
190+
},
186191
getSessionId: () => this.sessionManager.getSessionId(),
187192
onSessionInit: event => {
188193
const wasFork = this.pendingForkSession;

‎src/qoder/runtime/qoder-response-router.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ interface QoderResponseRouterDeps {
1616
getCurrentQuery: () => Query | null;
1717
getMessageChannel: () => QoderMessageChannel | null;
1818
getConfiguredModel: () => string;
19+
/** Effective context window from the per-model override, if any. */
20+
getConfiguredContextWindow: () => number | undefined;
1921
getSessionId: () => string | null;
2022
onSessionInit: (event: SessionInitEvent) => void;
2123
onPlanModeEntered: () => void;
@@ -137,6 +139,7 @@ export class QoderResponseRouter {
137139
query: this.deps.getCurrentQuery(),
138140
isCurrentQuery: query => this.deps.getCurrentQuery() === query,
139141
configuredModel: this.deps.getConfiguredModel(),
142+
configuredContextWindow: this.deps.getConfiguredContextWindow(),
140143
sessionId: this.deps.getSessionId(),
141144
});
142145
if (contextUsageChunk) {

‎src/qoder/runtime/qoder-turn-tracker.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ interface ContextUsageRequest {
1616
query: Query | null;
1717
isCurrentQuery: (query: Query) => boolean;
1818
configuredModel: string;
19+
/** Effective context window from the per-model editor override, if any. */
20+
configuredContextWindow?: number;
1921
sessionId: string | null;
2022
}
2123

@@ -128,7 +130,14 @@ export class QoderTurnTracker {
128130
const previousContextWindow = previousUsage?.model === model && previousUsage.contextWindow > 0
129131
? previousUsage.contextWindow
130132
: undefined;
133+
// Without a CLI-reported window the configured tier is the source of
134+
// truth; buffered chunks only carry catalog fallbacks.
135+
const configuredContextWindow = Number.isFinite(request.configuredContextWindow)
136+
&& (request.configuredContextWindow as number) > 0
137+
? request.configuredContextWindow
138+
: undefined;
131139
const contextWindow = reportedMaxTokens
140+
?? configuredContextWindow
132141
?? previousContextWindow
133142
?? getContextWindowSize(model);
134143

‎tests/unit/qoder/runtime/qoder-chat-runtime.test.ts‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,49 @@ describe('QoderChatRuntime', () => {
13381338
expect(onDone).toHaveBeenCalled();
13391339
});
13401340

1341+
it('should honor the configured context-window tier when the CLI omits the window', async () => {
1342+
(mockPlugin as any).settings.model = 'performance';
1343+
(mockPlugin as any).settings.qoder = {
1344+
discoveredModels: [{
1345+
value: 'performance',
1346+
contextTiers: [
1347+
{ label: '200K', tokenCount: 200_000, isDefault: true },
1348+
{ label: '400K', tokenCount: 400_000, isDefault: false },
1349+
],
1350+
}],
1351+
modelOverrides: { performance: { contextWindow: 400_000 } },
1352+
};
1353+
(service as any).persistentQuery = {
1354+
getContextUsage: jest.fn().mockResolvedValue({
1355+
model: 'performance',
1356+
tokenCountsAvailable: false,
1357+
contextWindow: { usedPercentage: 10 },
1358+
categories: [],
1359+
}),
1360+
};
1361+
1362+
await (service as any).responseRouter.route({
1363+
type: 'result',
1364+
subtype: 'success',
1365+
result: 'completed',
1366+
});
1367+
1368+
expect(onChunk).toHaveBeenCalledWith({
1369+
type: 'usage',
1370+
usage: {
1371+
model: 'performance',
1372+
inputTokens: 0,
1373+
cacheCreationInputTokens: 0,
1374+
cacheReadInputTokens: 0,
1375+
contextWindow: 400_000,
1376+
contextWindowIsAuthoritative: false,
1377+
contextTokens: 40_000,
1378+
percentage: 10,
1379+
},
1380+
sessionId: null,
1381+
});
1382+
});
1383+
13411384
it('should prefer public context token counts when Qoder CLI returns them', async () => {
13421385
(service as any).persistentQuery = {
13431386
getContextUsage: jest.fn().mockResolvedValue({

0 commit comments

Comments
 (0)