mirror of https://github.com/openclaw/openclaw.git
fix(status): normalize qualified context window keys openclaw#36389 thanks @haoruilee
This commit is contained in:
parent
4bc4e66057
commit
f2df5d60e6
|
|
@ -139,6 +139,9 @@ Docs: https://docs.openclaw.ai
|
|||
- Telegram/direct delivery: bridge direct delivery sends to internal `message:sent` hooks so internal hook listeners observe successful Telegram deliveries. (#40185) Thanks @vincentkoc.
|
||||
- Dependencies: refresh workspace dependencies except the pinned Carbon package, and harden ACP session-config writes against non-string SDK values so newer ACP clients fail fast instead of tripping type/runtime mismatches.
|
||||
- Telegram/polling restarts: clear bounded cleanup timeout handles after `runner.stop()` and `bot.stop()` settle so stall recovery no longer leaves stray 15-second timers behind on clean shutdown. (#43188) thanks @kyohwang.
|
||||
- Gateway/config errors: surface up to three validation issues in top-level `config.set`, `config.patch`, and `config.apply` error messages while preserving structured issue details. (#42664) Thanks @huntharo.
|
||||
- Hooks/plugin context parity followup: pass `trigger` and `channelId` through embedded `llm_input`, `agent_end`, and `llm_output` hook contexts so plugins receive the same agent metadata across hook phases. (#42362) Thanks @zhoulf1006.
|
||||
- Status/context windows: normalize provider-qualified override cache keys so `/status` resolves the active provider's configured context window even when `models.providers` keys use mixed case or surrounding whitespace. (#36389) Thanks @haoruilee.
|
||||
|
||||
## 2026.3.8
|
||||
|
||||
|
|
|
|||
|
|
@ -138,4 +138,24 @@ describe("lookupContextTokens", () => {
|
|||
});
|
||||
expect(result).toBe(1_048_576);
|
||||
});
|
||||
|
||||
it("resolveContextTokensForModel honors configured overrides when provider keys use mixed case", async () => {
|
||||
mockDiscoveryDeps(
|
||||
[{ id: "openrouter/anthropic/claude-sonnet-4-5", contextWindow: 1_048_576 }],
|
||||
{
|
||||
" OpenRouter ": {
|
||||
models: [{ id: "anthropic/claude-sonnet-4-5", contextWindow: 200_000 }],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const { resolveContextTokensForModel } = await import("./context.js");
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
|
||||
const result = resolveContextTokensForModel({
|
||||
provider: "openrouter",
|
||||
model: "anthropic/claude-sonnet-4-5",
|
||||
});
|
||||
expect(result).toBe(200_000);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,6 +107,25 @@ describe("applyConfiguredContextWindows", () => {
|
|||
expect(cache.get("openrouter/anthropic/claude-sonnet-4-5")).toBe(200_000);
|
||||
});
|
||||
|
||||
it("normalizes provider keys before writing provider-qualified override entries", () => {
|
||||
const cache = new Map<string, number>();
|
||||
cache.set("openrouter/anthropic/claude-sonnet-4-5", 1_048_576);
|
||||
applyConfiguredContextWindows({
|
||||
cache,
|
||||
modelsConfig: {
|
||||
providers: {
|
||||
" OpenRouter ": {
|
||||
models: [{ id: "anthropic/claude-sonnet-4-5", contextWindow: 200_000 }],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(cache.get("anthropic/claude-sonnet-4-5")).toBe(200_000);
|
||||
expect(cache.get("openrouter/anthropic/claude-sonnet-4-5")).toBe(200_000);
|
||||
expect(cache.has(" OpenRouter /anthropic/claude-sonnet-4-5")).toBe(false);
|
||||
});
|
||||
|
||||
it("adds config-only model context windows and ignores invalid entries", () => {
|
||||
const cache = new Map<string, number>();
|
||||
applyConfiguredContextWindows({
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import type { OpenClawConfig } from "../config/config.js";
|
|||
import { computeBackoff, type BackoffPolicy } from "../infra/backoff.js";
|
||||
import { consumeRootOptionToken, FLAG_TERMINATOR } from "../infra/cli-root-options.js";
|
||||
import { resolveOpenClawAgentDir } from "./agent-paths.js";
|
||||
import { normalizeProviderId } from "./model-selection.js";
|
||||
import { ensureOpenClawModelsJson } from "./models-config.js";
|
||||
|
||||
type ModelEntry = { id: string; contextWindow?: number };
|
||||
|
|
@ -27,6 +28,10 @@ const CONFIG_LOAD_RETRY_POLICY: BackoffPolicy = {
|
|||
jitter: 0,
|
||||
};
|
||||
|
||||
function resolveQualifiedContextWindowKey(providerId: string, modelId: string): string {
|
||||
return `${normalizeProviderId(providerId)}/${modelId}`;
|
||||
}
|
||||
|
||||
export function applyDiscoveredContextWindows(params: {
|
||||
cache: Map<string, number>;
|
||||
models: ModelEntry[];
|
||||
|
|
@ -78,7 +83,7 @@ export function applyConfiguredContextWindows(params: {
|
|||
// discovered values. This covers both bare IDs (e.g. "claude-opus-4" →
|
||||
// "anthropic/claude-opus-4") and slash-containing IDs common in OpenRouter
|
||||
// (e.g. "anthropic/claude-sonnet-4-5" → "openrouter/anthropic/claude-sonnet-4-5").
|
||||
params.cache.set(`${providerId}/${modelId}`, contextWindow);
|
||||
params.cache.set(resolveQualifiedContextWindowKey(providerId, modelId), contextWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -232,13 +237,13 @@ function resolveProviderModelRef(params: {
|
|||
}
|
||||
const providerRaw = params.provider?.trim();
|
||||
if (providerRaw) {
|
||||
return { provider: providerRaw.toLowerCase(), model: modelRaw };
|
||||
return { provider: normalizeProviderId(providerRaw), model: modelRaw };
|
||||
}
|
||||
const slash = modelRaw.indexOf("/");
|
||||
if (slash <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
const provider = modelRaw.slice(0, slash).trim().toLowerCase();
|
||||
const provider = normalizeProviderId(modelRaw.slice(0, slash));
|
||||
const model = modelRaw.slice(slash + 1).trim();
|
||||
if (!provider || !model) {
|
||||
return undefined;
|
||||
|
|
@ -282,7 +287,7 @@ export function resolveContextTokensForModel(params: {
|
|||
// When provider is known, prefer the provider-qualified key so the correct
|
||||
// entry is found even when the same bare model id is catalogued under
|
||||
// multiple providers with different context limits.
|
||||
const qualifiedKey = ref ? `${ref.provider}/${ref.model}` : undefined;
|
||||
const qualifiedKey = ref ? resolveQualifiedContextWindowKey(ref.provider, ref.model) : undefined;
|
||||
return (
|
||||
(qualifiedKey ? lookupContextTokens(qualifiedKey) : undefined) ??
|
||||
lookupContextTokens(params.model) ??
|
||||
|
|
|
|||
Loading…
Reference in New Issue