mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:20:43 +00:00
fix(status): honor channel model context windows
This commit is contained in:
@@ -601,6 +601,51 @@ describe("buildStatusMessage", () => {
|
||||
expect(normalized).toContain("channel override");
|
||||
});
|
||||
|
||||
it("uses the channel override model context window instead of stale persisted context", () => {
|
||||
const text = buildStatusMessage({
|
||||
config: {
|
||||
channels: {
|
||||
modelByChannel: {
|
||||
discord: {
|
||||
"123": "minimax-portal/MiniMax-M2.7",
|
||||
},
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
"minimax-portal": {
|
||||
models: [{ id: "MiniMax-M2.7", contextWindow: 200_000 }],
|
||||
},
|
||||
anthropic: {
|
||||
models: [{ id: "claude-opus-4-6", contextWindow: 1_048_576 }],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig,
|
||||
agent: {
|
||||
model: "minimax-portal/MiniMax-M2.7",
|
||||
contextTokens: 1_048_576,
|
||||
},
|
||||
sessionEntry: {
|
||||
sessionId: "channel-context-window",
|
||||
updatedAt: 0,
|
||||
channel: "discord",
|
||||
groupId: "123",
|
||||
totalTokens: 49_000,
|
||||
contextTokens: 1_048_576,
|
||||
},
|
||||
sessionKey: "agent:main:main",
|
||||
sessionScope: "per-sender",
|
||||
queue: { mode: "collect", depth: 0 },
|
||||
});
|
||||
const normalized = normalizeTestText(text);
|
||||
|
||||
expect(normalized).toContain("Model: minimax-portal/MiniMax-M2.7");
|
||||
expect(normalized).toContain("channel override");
|
||||
expect(normalized).toContain("Context: 49k/200k");
|
||||
expect(normalized).not.toContain("Context: 49k/1.0m");
|
||||
});
|
||||
|
||||
it("shows 1M context window when anthropic context1m is enabled", () => {
|
||||
const text = buildStatusMessage({
|
||||
config: {
|
||||
|
||||
@@ -489,6 +489,57 @@ const formatVoiceModeLine = (
|
||||
return parts.join(" · ");
|
||||
};
|
||||
|
||||
function resolveChannelModelNote(params: {
|
||||
config?: OpenClawConfig;
|
||||
entry?: SessionEntry;
|
||||
selectedProvider: string;
|
||||
selectedModel: string;
|
||||
parentSessionKey?: string;
|
||||
}): string | undefined {
|
||||
if (!params.config || !params.entry) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
normalizeOptionalString(params.entry.modelOverride) ||
|
||||
normalizeOptionalString(params.entry.providerOverride)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
const channelOverride = resolveChannelModelOverride({
|
||||
cfg: params.config,
|
||||
channel: params.entry.channel ?? params.entry.origin?.provider,
|
||||
groupId: params.entry.groupId,
|
||||
groupChatType: params.entry.chatType ?? params.entry.origin?.chatType,
|
||||
groupChannel: params.entry.groupChannel,
|
||||
groupSubject: params.entry.subject,
|
||||
parentSessionKey: params.parentSessionKey,
|
||||
});
|
||||
if (!channelOverride) {
|
||||
return undefined;
|
||||
}
|
||||
const aliasIndex = buildModelAliasIndex({
|
||||
cfg: params.config,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
allowPluginNormalization: false,
|
||||
});
|
||||
const resolvedOverride = resolveModelRefFromString({
|
||||
raw: channelOverride.model,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
aliasIndex,
|
||||
allowPluginNormalization: false,
|
||||
});
|
||||
if (!resolvedOverride) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
resolvedOverride.ref.provider !== params.selectedProvider ||
|
||||
resolvedOverride.ref.model !== params.selectedModel
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return "channel override";
|
||||
}
|
||||
|
||||
export function buildStatusMessage(args: StatusArgs): string {
|
||||
const now = args.now ?? Date.now();
|
||||
const entry = args.sessionEntry;
|
||||
@@ -650,10 +701,21 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
model: contextLookupModel,
|
||||
allowAsyncLoad: false,
|
||||
});
|
||||
const channelModelNote = resolveChannelModelNote({
|
||||
config: args.config,
|
||||
entry,
|
||||
selectedProvider,
|
||||
selectedModel,
|
||||
parentSessionKey: args.parentSessionKey,
|
||||
});
|
||||
const persistedContextTokens =
|
||||
typeof entry?.contextTokens === "number" && entry.contextTokens > 0
|
||||
? entry.contextTokens
|
||||
: undefined;
|
||||
const agentContextTokens =
|
||||
typeof args.agent?.contextTokens === "number" && args.agent.contextTokens > 0
|
||||
? args.agent.contextTokens
|
||||
: undefined;
|
||||
const explicitRuntimeContextTokens =
|
||||
typeof args.runtimeContextTokens === "number" && args.runtimeContextTokens > 0
|
||||
? args.runtimeContextTokens
|
||||
@@ -669,6 +731,15 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
? Math.min(explicitConfiguredContextTokens, activeContextTokens)
|
||||
: explicitConfiguredContextTokens
|
||||
: undefined;
|
||||
const channelOverrideContextTokens = channelModelNote
|
||||
? (explicitRuntimeContextTokens ??
|
||||
cappedConfiguredContextTokens ??
|
||||
(typeof activeContextTokens === "number"
|
||||
? typeof agentContextTokens === "number"
|
||||
? Math.min(agentContextTokens, activeContextTokens)
|
||||
: activeContextTokens
|
||||
: agentContextTokens))
|
||||
: undefined;
|
||||
// When a fallback model is active, the selected-model context limit that
|
||||
// callers keep on the agent config is often stale. Prefer an explicit runtime
|
||||
// snapshot when available. Separately, callers can pass an explicit configured
|
||||
@@ -715,7 +786,8 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
cfg: contextConfig,
|
||||
...(contextLookupProvider ? { provider: contextLookupProvider } : {}),
|
||||
model: contextLookupModel,
|
||||
contextTokensOverride: persistedContextTokens ?? args.agent?.contextTokens,
|
||||
contextTokensOverride:
|
||||
channelOverrideContextTokens ?? persistedContextTokens ?? agentContextTokens,
|
||||
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS,
|
||||
allowAsyncLoad: false,
|
||||
}) ?? DEFAULT_CONTEXT_TOKENS);
|
||||
@@ -854,50 +926,6 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
const costLabel = showCost && hasUsage ? formatUsd(cost) : undefined;
|
||||
|
||||
const selectedAuthLabel = selectedAuthLabelValue ? ` · 🔑 ${selectedAuthLabelValue}` : "";
|
||||
const channelModelNote = (() => {
|
||||
if (!args.config || !entry) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
normalizeOptionalString(entry.modelOverride) ||
|
||||
normalizeOptionalString(entry.providerOverride)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
const channelOverride = resolveChannelModelOverride({
|
||||
cfg: args.config,
|
||||
channel: entry.channel ?? entry.origin?.provider,
|
||||
groupId: entry.groupId,
|
||||
groupChatType: entry.chatType ?? entry.origin?.chatType,
|
||||
groupChannel: entry.groupChannel,
|
||||
groupSubject: entry.subject,
|
||||
parentSessionKey: args.parentSessionKey,
|
||||
});
|
||||
if (!channelOverride) {
|
||||
return undefined;
|
||||
}
|
||||
const aliasIndex = buildModelAliasIndex({
|
||||
cfg: args.config,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
allowPluginNormalization: false,
|
||||
});
|
||||
const resolvedOverride = resolveModelRefFromString({
|
||||
raw: channelOverride.model,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
aliasIndex,
|
||||
allowPluginNormalization: false,
|
||||
});
|
||||
if (!resolvedOverride) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
resolvedOverride.ref.provider !== selectedProvider ||
|
||||
resolvedOverride.ref.model !== selectedModel
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return "channel override";
|
||||
})();
|
||||
const modelNote = channelModelNote ? ` · ${channelModelNote}` : "";
|
||||
const modelLine = `🧠 Model: ${selectedModelLabel}${selectedAuthLabel}${modelNote}`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user