Skip to content

Commit 80b8160

Browse files
committed
fix: resolve provider-qualified context window via direct config scan
applyConfiguredContextWindows now writes only bare model-id keys to MODEL_CACHE, reserving the provider-qualified key space exclusively for raw discovery entries (e.g. OpenRouter slash-paths). Call sites that need per-provider disambiguation (get-reply-directives, memory-flush, agent-runner-memory) now call resolveContextTokensForModel with the full cfg so they scan models.providers directly, rather than relying on a qualified cache key that was never reliably populated. resolveContextTokens in model-selection.ts reverted to remove the now- unused provider param; resolveMemoryFlushContextWindowTokens accepts cfg and delegates to resolveContextTokensForModel.
1 parent 9950b35 commit 80b8160

File tree

5 files changed

+30
-38
lines changed

5 files changed

+30
-38
lines changed

src/agents/context.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function applyConfiguredContextWindows(params: {
6262
if (!providers || typeof providers !== "object") {
6363
return;
6464
}
65-
for (const [providerId, provider] of Object.entries(providers)) {
65+
for (const provider of Object.values(providers)) {
6666
if (!Array.isArray(provider?.models)) {
6767
continue;
6868
}
@@ -73,18 +73,13 @@ export function applyConfiguredContextWindows(params: {
7373
if (!modelId || !contextWindow || contextWindow <= 0) {
7474
continue;
7575
}
76-
// Store with provider-qualified key (e.g. "rdsec/claude-4.6-sonnet") so
77-
// models that share the same id across providers but differ in context
78-
// window are resolved correctly via lookupContextTokens.
79-
// Use normalizeProviderId so the key format matches resolveContextTokensForModel
80-
// which also normalizes before constructing the qualified lookup key.
81-
if (providerId) {
82-
params.cache.set(`${normalizeProviderId(providerId)}/${modelId}`, contextWindow);
83-
}
84-
// Also store by bare model id as a fallback for callers that only have
85-
// the model name. When multiple providers define the same id the last
86-
// one wins, but a provider-qualified lookup above will always take
87-
// precedence when provider info is available.
76+
// Only write the bare model id. The provider-qualified key space is
77+
// reserved for raw discovery entries (e.g. "google-gemini-cli/gemini-3.1-pro-preview")
78+
// and must not be polluted by synthetic config writes that could corrupt
79+
// slash-containing model IDs (e.g. OpenRouter's "anthropic/claude-opus-4-6").
80+
// Callers with provider info use resolveContextTokensForModel which scans
81+
// cfg.models.providers directly and never relies on this cache for the
82+
// qualified lookup.
8883
params.cache.set(modelId, contextWindow);
8984
}
9085
}

src/auto-reply/reply/agent-runner-memory.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ export async function runMemoryFlushIfNeeded(params: {
289289
params.sessionEntry ??
290290
(params.sessionKey ? params.sessionStore?.[params.sessionKey] : undefined);
291291
const contextWindowTokens = resolveMemoryFlushContextWindowTokens({
292+
cfg: params.cfg,
292293
modelId: params.followupRun.run.model ?? params.defaultModel,
293294
providerId: params.followupRun.run.provider,
294295
agentCfgContextTokens: params.agentCfgContextTokens,

src/auto-reply/reply/get-reply-directives.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { ExecToolDefaults } from "../../agents/bash-tools.js";
2+
import { resolveContextTokensForModel } from "../../agents/context.js";
3+
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
24
import { resolveFastModeState } from "../../agents/fast-mode.js";
35
import type { ModelAliasIndex } from "../../agents/model-selection.js";
46
import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
@@ -17,7 +19,7 @@ import { applyInlineDirectiveOverrides } from "./get-reply-directives-apply.js";
1719
import { clearExecInlineDirectives, clearInlineDirectives } from "./get-reply-directives-utils.js";
1820
import { defaultGroupActivation, resolveGroupRequireMention } from "./groups.js";
1921
import { CURRENT_MESSAGE_MARKER, stripMentions, stripStructuralPrefixes } from "./mentions.js";
20-
import { createModelSelectionState, resolveContextTokens } from "./model-selection.js";
22+
import { createModelSelectionState } from "./model-selection.js";
2123
import { formatElevatedUnavailableMessage, resolveElevatedPermissions } from "./reply-elevated.js";
2224
import { stripInlineStatus } from "./reply-inline.js";
2325
import type { TypingController } from "./typing.js";
@@ -418,11 +420,14 @@ export async function resolveReplyDirectives(params: {
418420
resolvedReasoningLevel = await modelState.resolveDefaultReasoningLevel();
419421
}
420422

421-
let contextTokens = resolveContextTokens({
422-
agentCfg,
423-
model,
424-
provider,
425-
});
423+
let contextTokens =
424+
resolveContextTokensForModel({
425+
cfg,
426+
provider,
427+
model,
428+
contextTokensOverride: agentCfg?.contextTokens,
429+
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS,
430+
}) ?? DEFAULT_CONTEXT_TOKENS;
426431

427432
const initialModelLabel = `${provider}/${model}`;
428433
const formatModelSwitchEvent = (label: string, alias?: string) =>

src/auto-reply/reply/memory-flush.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { lookupContextTokens } from "../../agents/context.js";
1+
import { resolveContextTokensForModel } from "../../agents/context.js";
22
import { resolveCronStyleNow } from "../../agents/current-time.js";
33
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
4-
import { normalizeProviderId } from "../../agents/model-selection.js";
54
import { DEFAULT_PI_COMPACTION_RESERVE_TOKENS_FLOOR } from "../../agents/pi-settings.js";
65
import { parseNonNegativeByteSize } from "../../config/byte-size.js";
76
import type { OpenClawConfig } from "../../config/config.js";
@@ -160,19 +159,19 @@ function ensureMemoryFlushSafetyHints(text: string): string {
160159
}
161160

162161
export function resolveMemoryFlushContextWindowTokens(params: {
162+
cfg?: OpenClawConfig;
163163
modelId?: string;
164164
providerId?: string;
165165
agentCfgContextTokens?: number;
166166
}): number {
167-
const qualified =
168-
params.providerId && params.modelId
169-
? lookupContextTokens(`${normalizeProviderId(params.providerId)}/${params.modelId}`)
170-
: undefined;
171167
return (
172-
qualified ??
173-
lookupContextTokens(params.modelId) ??
174-
params.agentCfgContextTokens ??
175-
DEFAULT_CONTEXT_TOKENS
168+
resolveContextTokensForModel({
169+
cfg: params.cfg,
170+
provider: params.providerId,
171+
model: params.modelId,
172+
contextTokensOverride: params.agentCfgContextTokens,
173+
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS,
174+
}) ?? DEFAULT_CONTEXT_TOKENS
176175
);
177176
}
178177

src/auto-reply/reply/model-selection.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -603,16 +603,8 @@ export function resolveModelDirectiveSelection(params: {
603603
export function resolveContextTokens(params: {
604604
agentCfg: NonNullable<NonNullable<OpenClawConfig["agents"]>["defaults"]> | undefined;
605605
model: string;
606-
provider?: string;
607606
}): number {
608-
const qualified =
609-
params.provider
610-
? lookupContextTokens(`${normalizeProviderId(params.provider)}/${params.model}`)
611-
: undefined;
612607
return (
613-
params.agentCfg?.contextTokens ??
614-
qualified ??
615-
lookupContextTokens(params.model) ??
616-
DEFAULT_CONTEXT_TOKENS
608+
params.agentCfg?.contextTokens ?? lookupContextTokens(params.model) ?? DEFAULT_CONTEXT_TOKENS
617609
);
618610
}

0 commit comments

Comments
 (0)