

























@@ -6,6 +6,7 @@ import {
66evaluateContextWindowGuard,
77formatContextWindowBlockMessage,
88formatContextWindowWarningMessage,
9+resolveContextWindowGuardThresholds,
910resolveContextWindowInfo,
1011} from "./context-window-guard.js";
1112@@ -35,41 +36,43 @@ describe("context-window-guard", () => {
3536} satisfies OpenClawConfig;
3637}
373838-it("blocks below 16k (model metadata)", () => {
39+it("blocks below the hard-min floor (model metadata)", () => {
3940const info = resolveContextWindowInfo({
4041cfg: undefined,
4142provider: "openrouter",
4243modelId: "tiny",
43-modelContextWindow: 8000,
44+modelContextWindow: 3999,
4445defaultTokens: 200_000,
4546});
4647const guard = evaluateContextWindowGuard({ info });
4748expect(guard.source).toBe("model");
48-expect(guard.tokens).toBe(8000);
49+expect(guard.tokens).toBe(3999);
50+expect(guard.hardMinTokens).toBe(4000);
51+expect(guard.warnBelowTokens).toBe(8000);
4952expect(guard.shouldWarn).toBe(true);
5053expect(guard.shouldBlock).toBe(true);
5154});
525553-it("warns below 32k but does not block at 16k+", () => {
56+it("warns below the warning floor but does not block at hard-min+", () => {
5457const info = resolveContextWindowInfo({
5558cfg: undefined,
5659provider: "openai",
5760modelId: "small",
58-modelContextWindow: 24_000,
61+modelContextWindow: 6_000,
5962defaultTokens: 200_000,
6063});
6164const guard = evaluateContextWindowGuard({ info });
62-expect(guard.tokens).toBe(24_000);
65+expect(guard.tokens).toBe(6_000);
6366expect(guard.shouldWarn).toBe(true);
6467expect(guard.shouldBlock).toBe(false);
6568});
666967-it("does not warn at 32k+ (model metadata)", () => {
70+it("does not warn at the warning floor (model metadata)", () => {
6871const info = resolveContextWindowInfo({
6972cfg: undefined,
7073provider: "openai",
7174modelId: "ok",
72-modelContextWindow: 64_000,
75+modelContextWindow: 8_000,
7376defaultTokens: 200_000,
7477});
7578const guard = evaluateContextWindowGuard({ info });
@@ -78,7 +81,7 @@ describe("context-window-guard", () => {
7881});
79828083it("uses models.providers.*.models[].contextWindow when present", () => {
81-const cfg = openRouterModelConfig({ contextWindow: 12_000 });
84+const cfg = openRouterModelConfig({ contextWindow: 3_000 });
82858386const info = resolveContextWindowInfo({
8487 cfg,
@@ -160,6 +163,10 @@ describe("context-window-guard", () => {
160163});
161164const guard = evaluateContextWindowGuard({ info });
162165expect(info.source).toBe("agentContextTokens");
166+expect(info.tokens).toBe(20_000);
167+expect(info.referenceTokens).toBe(200_000);
168+expect(guard.hardMinTokens).toBe(20_000);
169+expect(guard.warnBelowTokens).toBe(40_000);
163170expect(guard.shouldWarn).toBe(true);
164171expect(guard.shouldBlock).toBe(false);
165172});
@@ -193,25 +200,86 @@ describe("context-window-guard", () => {
193200expect(guard.shouldBlock).toBe(false);
194201});
195202203+it("normalizes invalid default context tokens to the warning floor", () => {
204+const info = resolveContextWindowInfo({
205+cfg: undefined,
206+provider: "anthropic",
207+modelId: "unknown",
208+defaultTokens: Number.NaN,
209+});
210+const guard = evaluateContextWindowGuard({ info });
211+expect(info).toEqual({ source: "default", tokens: 8_000 });
212+expect(guard.shouldWarn).toBe(false);
213+expect(guard.shouldBlock).toBe(false);
214+});
215+216+it("blocks invalid guard token counts instead of silently passing", () => {
217+const guard = evaluateContextWindowGuard({
218+info: { tokens: Number.NaN, source: "model" },
219+});
220+expect(guard.tokens).toBe(0);
221+expect(guard.hardMinTokens).toBe(4_000);
222+expect(guard.warnBelowTokens).toBe(8_000);
223+expect(guard.shouldWarn).toBe(true);
224+expect(guard.shouldBlock).toBe(true);
225+});
226+196227it("allows overriding thresholds", () => {
197228const info = { tokens: 10_000, source: "model" as const };
198229const guard = evaluateContextWindowGuard({
199230 info,
200231warnBelowTokens: 12_000,
201232hardMinTokens: 9_000,
202233});
234+expect(guard.hardMinTokens).toBe(9_000);
235+expect(guard.warnBelowTokens).toBe(12_000);
203236expect(guard.shouldWarn).toBe(true);
204237expect(guard.shouldBlock).toBe(false);
205238});
206239207-it("exports thresholds as expected", () => {
208-expect(CONTEXT_WINDOW_HARD_MIN_TOKENS).toBe(16_000);
209-expect(CONTEXT_WINDOW_WARN_BELOW_TOKENS).toBe(32_000);
240+it("exports threshold floors as expected", () => {
241+expect(CONTEXT_WINDOW_HARD_MIN_TOKENS).toBe(4_000);
242+expect(CONTEXT_WINDOW_WARN_BELOW_TOKENS).toBe(8_000);
243+});
244+245+it("derives percentage-based thresholds above the safe floors", () => {
246+expect(resolveContextWindowGuardThresholds(1_000_000)).toEqual({
247+hardMinTokens: 100_000,
248+warnBelowTokens: 200_000,
249+});
250+expect(resolveContextWindowGuardThresholds(64_000)).toEqual({
251+hardMinTokens: 6_400,
252+warnBelowTokens: 12_800,
253+});
254+expect(resolveContextWindowGuardThresholds(Number.NaN)).toEqual({
255+hardMinTokens: 4_000,
256+warnBelowTokens: 8_000,
257+});
258+});
259+260+it("derives guard thresholds from the reference window when capped", () => {
261+const guard = evaluateContextWindowGuard({
262+info: { tokens: 150_000, referenceTokens: 1_000_000, source: "agentContextTokens" },
263+});
264+expect(guard.hardMinTokens).toBe(100_000);
265+expect(guard.warnBelowTokens).toBe(200_000);
266+expect(guard.shouldWarn).toBe(true);
267+expect(guard.shouldBlock).toBe(false);
268+});
269+270+it("does not let inflated reference metadata hard-block a valid effective cap", () => {
271+const guard = evaluateContextWindowGuard({
272+info: { tokens: 20_000, referenceTokens: 1_000_000_000, source: "agentContextTokens" },
273+});
274+expect(guard.hardMinTokens).toBe(20_000);
275+expect(guard.warnBelowTokens).toBe(200_000_000);
276+expect(guard.shouldWarn).toBe(true);
277+expect(guard.shouldBlock).toBe(false);
210278});
211279212280it("adds a local-model hint to warning messages for localhost endpoints", () => {
213281const guard = evaluateContextWindowGuard({
214-info: { tokens: 24_000, source: "model" },
282+info: { tokens: 6_000, source: "model" },
215283});
216284217285expect(
@@ -221,12 +289,12 @@ describe("context-window-guard", () => {
221289 guard,
222290runtimeBaseUrl: "http://127.0.0.1:1234/v1",
223291}),
224-).toContain("local/self-hosted runs work best at 32000+ tokens");
292+).toContain("local/self-hosted runs work best at 8000+ tokens");
225293});
226294227295it("does not add local-model hints for generic custom endpoints", () => {
228296const guard = evaluateContextWindowGuard({
229-info: { tokens: 24_000, source: "model" },
297+info: { tokens: 6_000, source: "model" },
230298});
231299232300expect(
@@ -236,7 +304,7 @@ describe("context-window-guard", () => {
236304 guard,
237305runtimeBaseUrl: "https://models.example.com/v1",
238306}),
239-).toBe("low context window: custom/hosted-proxy-model ctx=24000 (warn<32000) source=model");
307+).toBe("low context window: custom/hosted-proxy-model ctx=6000 (warn<8000) source=model");
240308});
241309242310it("adds a local-model hint to block messages for localhost endpoints", () => {
@@ -281,14 +349,14 @@ describe("context-window-guard", () => {
281349282350it("keeps block messages concise for public providers", () => {
283351const guard = evaluateContextWindowGuard({
284-info: { tokens: 8_000, source: "model" },
352+info: { tokens: 3_000, source: "model" },
285353});
286354287355expect(
288356formatContextWindowBlockMessage({
289357 guard,
290358runtimeBaseUrl: "https://api.openai.com/v1",
291359}),
292-).toBe(`Model context window too small (8000 tokens; source=model). Minimum is 16000.`);
360+).toBe(`Model context window too small (3000 tokens; source=model). Minimum is 4000.`);
293361});
294362});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。