|
4 | 4 | CONTEXT_WINDOW_HARD_MIN_TOKENS, |
5 | 5 | CONTEXT_WINDOW_WARN_BELOW_TOKENS, |
6 | 6 | evaluateContextWindowGuard, |
| 7 | + formatContextWindowBlockMessage, |
| 8 | + formatContextWindowWarningMessage, |
7 | 9 | resolveContextWindowInfo, |
8 | 10 | } from "./context-window-guard.js"; |
9 | 11 |
|
@@ -222,4 +224,87 @@ describe("context-window-guard", () => { |
222 | 224 | expect(CONTEXT_WINDOW_HARD_MIN_TOKENS).toBe(16_000); |
223 | 225 | expect(CONTEXT_WINDOW_WARN_BELOW_TOKENS).toBe(32_000); |
224 | 226 | }); |
| 227 | + |
| 228 | + it("adds a local-model hint to warning messages for localhost endpoints", () => { |
| 229 | + const guard = evaluateContextWindowGuard({ |
| 230 | + info: { tokens: 24_000, source: "model" }, |
| 231 | + }); |
| 232 | + |
| 233 | + expect( |
| 234 | + formatContextWindowWarningMessage({ |
| 235 | + provider: "lmstudio", |
| 236 | + modelId: "qwen3", |
| 237 | + guard, |
| 238 | + runtimeBaseUrl: "http://127.0.0.1:1234/v1", |
| 239 | + }), |
| 240 | + ).toContain("local/self-hosted runs work best at 32000+ tokens"); |
| 241 | + }); |
| 242 | + |
| 243 | + it("does not add local-model hints for generic custom endpoints", () => { |
| 244 | + const guard = evaluateContextWindowGuard({ |
| 245 | + info: { tokens: 24_000, source: "model" }, |
| 246 | + }); |
| 247 | + |
| 248 | + expect( |
| 249 | + formatContextWindowWarningMessage({ |
| 250 | + provider: "custom", |
| 251 | + modelId: "hosted-proxy-model", |
| 252 | + guard, |
| 253 | + runtimeBaseUrl: "https://models.example.com/v1", |
| 254 | + }), |
| 255 | + ).toBe("low context window: custom/hosted-proxy-model ctx=24000 (warn<32000) source=model"); |
| 256 | + }); |
| 257 | + |
| 258 | + it("adds a local-model hint to block messages for localhost endpoints", () => { |
| 259 | + const guard = evaluateContextWindowGuard({ |
| 260 | + info: { tokens: 8_000, source: "model" }, |
| 261 | + }); |
| 262 | + |
| 263 | + expect( |
| 264 | + formatContextWindowBlockMessage({ |
| 265 | + guard, |
| 266 | + runtimeBaseUrl: "http://127.0.0.1:11434/v1", |
| 267 | + }), |
| 268 | + ).toContain("This looks like a local model endpoint."); |
| 269 | + }); |
| 270 | + |
| 271 | + it("points config-backed block remediation at agents.defaults.contextTokens", () => { |
| 272 | + const guard = evaluateContextWindowGuard({ |
| 273 | + info: { tokens: 8_000, source: "agentContextTokens" }, |
| 274 | + }); |
| 275 | + |
| 276 | + const message = formatContextWindowBlockMessage({ |
| 277 | + guard, |
| 278 | + runtimeBaseUrl: "http://127.0.0.1:11434/v1", |
| 279 | + }); |
| 280 | + |
| 281 | + expect(message).toContain("OpenClaw is capped by agents.defaults.contextTokens."); |
| 282 | + expect(message).not.toContain("choose a larger model"); |
| 283 | + }); |
| 284 | + |
| 285 | + it("points model config block remediation at contextWindow/contextTokens", () => { |
| 286 | + const guard = evaluateContextWindowGuard({ |
| 287 | + info: { tokens: 8_000, source: "modelsConfig" }, |
| 288 | + }); |
| 289 | + |
| 290 | + expect( |
| 291 | + formatContextWindowBlockMessage({ |
| 292 | + guard, |
| 293 | + runtimeBaseUrl: "http://127.0.0.1:11434/v1", |
| 294 | + }), |
| 295 | + ).toContain("Raise contextWindow/contextTokens or choose a larger model."); |
| 296 | + }); |
| 297 | + |
| 298 | + it("keeps block messages concise for public providers", () => { |
| 299 | + const guard = evaluateContextWindowGuard({ |
| 300 | + info: { tokens: 8_000, source: "model" }, |
| 301 | + }); |
| 302 | + |
| 303 | + expect( |
| 304 | + formatContextWindowBlockMessage({ |
| 305 | + guard, |
| 306 | + runtimeBaseUrl: "https://api.openai.com/v1", |
| 307 | + }), |
| 308 | + ).toBe(`Model context window too small (8000 tokens; source=model). Minimum is 16000.`); |
| 309 | + }); |
225 | 310 | }); |
0 commit comments