mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:00:42 +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");
|
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", () => {
|
it("shows 1M context window when anthropic context1m is enabled", () => {
|
||||||
const text = buildStatusMessage({
|
const text = buildStatusMessage({
|
||||||
config: {
|
config: {
|
||||||
|
|||||||
@@ -489,6 +489,57 @@ const formatVoiceModeLine = (
|
|||||||
return parts.join(" · ");
|
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 {
|
export function buildStatusMessage(args: StatusArgs): string {
|
||||||
const now = args.now ?? Date.now();
|
const now = args.now ?? Date.now();
|
||||||
const entry = args.sessionEntry;
|
const entry = args.sessionEntry;
|
||||||
@@ -650,10 +701,21 @@ export function buildStatusMessage(args: StatusArgs): string {
|
|||||||
model: contextLookupModel,
|
model: contextLookupModel,
|
||||||
allowAsyncLoad: false,
|
allowAsyncLoad: false,
|
||||||
});
|
});
|
||||||
|
const channelModelNote = resolveChannelModelNote({
|
||||||
|
config: args.config,
|
||||||
|
entry,
|
||||||
|
selectedProvider,
|
||||||
|
selectedModel,
|
||||||
|
parentSessionKey: args.parentSessionKey,
|
||||||
|
});
|
||||||
const persistedContextTokens =
|
const persistedContextTokens =
|
||||||
typeof entry?.contextTokens === "number" && entry.contextTokens > 0
|
typeof entry?.contextTokens === "number" && entry.contextTokens > 0
|
||||||
? entry.contextTokens
|
? entry.contextTokens
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const agentContextTokens =
|
||||||
|
typeof args.agent?.contextTokens === "number" && args.agent.contextTokens > 0
|
||||||
|
? args.agent.contextTokens
|
||||||
|
: undefined;
|
||||||
const explicitRuntimeContextTokens =
|
const explicitRuntimeContextTokens =
|
||||||
typeof args.runtimeContextTokens === "number" && args.runtimeContextTokens > 0
|
typeof args.runtimeContextTokens === "number" && args.runtimeContextTokens > 0
|
||||||
? args.runtimeContextTokens
|
? args.runtimeContextTokens
|
||||||
@@ -669,6 +731,15 @@ export function buildStatusMessage(args: StatusArgs): string {
|
|||||||
? Math.min(explicitConfiguredContextTokens, activeContextTokens)
|
? Math.min(explicitConfiguredContextTokens, activeContextTokens)
|
||||||
: explicitConfiguredContextTokens
|
: explicitConfiguredContextTokens
|
||||||
: undefined;
|
: 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
|
// 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
|
// callers keep on the agent config is often stale. Prefer an explicit runtime
|
||||||
// snapshot when available. Separately, callers can pass an explicit configured
|
// snapshot when available. Separately, callers can pass an explicit configured
|
||||||
@@ -715,7 +786,8 @@ export function buildStatusMessage(args: StatusArgs): string {
|
|||||||
cfg: contextConfig,
|
cfg: contextConfig,
|
||||||
...(contextLookupProvider ? { provider: contextLookupProvider } : {}),
|
...(contextLookupProvider ? { provider: contextLookupProvider } : {}),
|
||||||
model: contextLookupModel,
|
model: contextLookupModel,
|
||||||
contextTokensOverride: persistedContextTokens ?? args.agent?.contextTokens,
|
contextTokensOverride:
|
||||||
|
channelOverrideContextTokens ?? persistedContextTokens ?? agentContextTokens,
|
||||||
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS,
|
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS,
|
||||||
allowAsyncLoad: false,
|
allowAsyncLoad: false,
|
||||||
}) ?? DEFAULT_CONTEXT_TOKENS);
|
}) ?? DEFAULT_CONTEXT_TOKENS);
|
||||||
@@ -854,50 +926,6 @@ export function buildStatusMessage(args: StatusArgs): string {
|
|||||||
const costLabel = showCost && hasUsage ? formatUsd(cost) : undefined;
|
const costLabel = showCost && hasUsage ? formatUsd(cost) : undefined;
|
||||||
|
|
||||||
const selectedAuthLabel = selectedAuthLabelValue ? ` · 🔑 ${selectedAuthLabelValue}` : "";
|
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 modelNote = channelModelNote ? ` · ${channelModelNote}` : "";
|
||||||
const modelLine = `🧠 Model: ${selectedModelLabel}${selectedAuthLabel}${modelNote}`;
|
const modelLine = `🧠 Model: ${selectedModelLabel}${selectedAuthLabel}${modelNote}`;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user