mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
fix(agents): skip model normalization in context warmup
This commit is contained in:
@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- TUI/chat: skip full provider model normalization during context-window warmup while preserving provider-owned context metadata, avoiding cold-start stalls with large model registries. Thanks @547895019.
|
||||
- Memory Wiki: accept relative Markdown links that include the `.md` suffix during broken-wikilink validation, avoiding false positives for native render-mode links. Thanks @Kenneth8128.
|
||||
- Plugins/CLI: cache plugin CLI registration entries per command program so completion state generation does not repeat the full plugin sweep in one invocation. Thanks @ScientificProgrammer.
|
||||
- Plugins: reuse gateway-bindable plugin loader cache entries for later default-mode loads without serving default-built registries to gateway-bound requests, reducing repeated plugin registration during dispatch. Refs #61756. Thanks @DmitryPogodaev.
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
type DiscoveredModel = { id: string; contextWindow?: number; contextTokens?: number };
|
||||
type DiscoveredModel = {
|
||||
id: string;
|
||||
provider?: string;
|
||||
contextWindow?: number;
|
||||
contextTokens?: number;
|
||||
};
|
||||
type ContextModule = typeof import("./context.js");
|
||||
|
||||
const contextTestState = vi.hoisted(() => {
|
||||
@@ -280,6 +285,27 @@ describe("lookupContextTokens", () => {
|
||||
expect(lookupContextTokens("gemini-3.1-pro-preview")).toBe(128_000);
|
||||
});
|
||||
|
||||
it("skips model normalization during warmup but preserves provider-owned context metadata", async () => {
|
||||
mockDiscoveryDeps([
|
||||
{
|
||||
id: "anthropic/claude-opus-4.7-20260219",
|
||||
provider: "anthropic",
|
||||
contextWindow: 200_000,
|
||||
},
|
||||
]);
|
||||
|
||||
const { lookupContextTokens } = await importContextModule();
|
||||
lookupContextTokens("anthropic/claude-opus-4.7-20260219");
|
||||
await flushAsyncWarmup();
|
||||
|
||||
expect(contextTestState.discoverModels).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
"/tmp/openclaw-agent",
|
||||
{ normalizeModels: false },
|
||||
);
|
||||
expect(lookupContextTokens("anthropic/claude-opus-4.7-20260219")).toBe(1_048_576);
|
||||
});
|
||||
|
||||
it("resolveContextTokensForModel returns discovery value when provider-qualified entry exists in cache", async () => {
|
||||
// Registry returns provider-qualified entries (real-world scenario from #35976).
|
||||
// When no explicit config override exists, the bare cache lookup hits the
|
||||
|
||||
@@ -93,6 +93,22 @@ describe("applyDiscoveredContextWindows", () => {
|
||||
expect(cache.get("anthropic/claude-opus-4.7-20260219")).toBe(200_000);
|
||||
});
|
||||
|
||||
it("upgrades provider-owned anthropic opus 4.7 discovery ids", () => {
|
||||
const cache = new Map<string, number>();
|
||||
applyDiscoveredContextWindows({
|
||||
cache,
|
||||
models: [
|
||||
{
|
||||
id: "anthropic/claude-opus-4.7-20260219",
|
||||
provider: "anthropic",
|
||||
contextWindow: 200_000,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(cache.get("anthropic/claude-opus-4.7-20260219")).toBe(ANTHROPIC_CONTEXT_1M_TOKENS);
|
||||
});
|
||||
|
||||
it("does not upgrade bare opus 4.7 discovery ids without verified ownership", () => {
|
||||
const cache = new Map<string, number>();
|
||||
applyDiscoveredContextWindows({
|
||||
|
||||
@@ -15,7 +15,12 @@ import { normalizeProviderId } from "./model-selection.js";
|
||||
|
||||
export { resetContextWindowCacheForTest } from "./context-runtime-state.js";
|
||||
|
||||
type ModelEntry = { id: string; contextWindow?: number; contextTokens?: number };
|
||||
type ModelEntry = {
|
||||
id: string;
|
||||
provider?: string;
|
||||
contextWindow?: number;
|
||||
contextTokens?: number;
|
||||
};
|
||||
type ModelRegistryLike = {
|
||||
getAvailable?: () => ModelEntry[];
|
||||
getAll: () => ModelEntry[];
|
||||
@@ -53,7 +58,7 @@ export function applyDiscoveredContextWindows(params: {
|
||||
: typeof model.contextWindow === "number"
|
||||
? Math.trunc(model.contextWindow)
|
||||
: undefined;
|
||||
const contextTokens = shouldUseDiscoveredAnthropicOpus47ContextWindow(model.id)
|
||||
const contextTokens = shouldUseDiscoveredAnthropicOpus47ContextWindow(model)
|
||||
? ANTHROPIC_CONTEXT_1M_TOKENS
|
||||
: discoveredContextTokens;
|
||||
if (!contextTokens || contextTokens <= 0) {
|
||||
@@ -236,7 +241,9 @@ function ensureContextWindowCacheLoaded(): Promise<void> {
|
||||
await import("./pi-model-discovery-runtime.js");
|
||||
const agentDir = resolveOpenClawAgentDir();
|
||||
const authStorage = discoverAuthStorage(agentDir);
|
||||
const modelRegistry = discoverModels(authStorage, agentDir) as unknown as ModelRegistryLike;
|
||||
const modelRegistry = discoverModels(authStorage, agentDir, {
|
||||
normalizeModels: false,
|
||||
}) as unknown as ModelRegistryLike;
|
||||
const models =
|
||||
typeof modelRegistry.getAvailable === "function"
|
||||
? modelRegistry.getAvailable()
|
||||
@@ -418,17 +425,23 @@ function shouldUseAnthropicOpus47ContextWindow(params: {
|
||||
);
|
||||
}
|
||||
|
||||
function shouldUseDiscoveredAnthropicOpus47ContextWindow(modelId: string): boolean {
|
||||
function shouldUseDiscoveredAnthropicOpus47ContextWindow(model: ModelEntry): boolean {
|
||||
const provider =
|
||||
typeof model.provider === "string" ? normalizeProviderId(model.provider) : undefined;
|
||||
const modelId = model.id;
|
||||
if (!isClaudeOpus47Model(modelId)) {
|
||||
return false;
|
||||
}
|
||||
if (provider) {
|
||||
return provider === "anthropic" || provider === "claude-cli";
|
||||
}
|
||||
const normalized = normalizeLowercaseStringOrEmpty(modelId);
|
||||
const slash = normalized.indexOf("/");
|
||||
if (slash < 0) {
|
||||
return false;
|
||||
}
|
||||
const provider = normalizeProviderId(normalized.slice(0, slash));
|
||||
return provider === "claude-cli";
|
||||
const inferredProvider = normalizeProviderId(normalized.slice(0, slash));
|
||||
return inferredProvider === "claude-cli";
|
||||
}
|
||||
|
||||
function resolveModelFamilyId(modelId: string): string {
|
||||
|
||||
Reference in New Issue
Block a user