Skip to content

Commit 275a36c

Browse files
committed
fix: skip min-threshold checks when modelContextLimit is unknown
opencode runs the chat.message transform before the system.prompt hook, so on the first message after a restart state.modelContextLimit is not yet cached. resolveContextTokenLimit then fails for percentage limits and overMinLimit fell back to unconditional true, injecting compression nudges on every turn for a normal ~300K context on 1M models. Fix: overMinLimit now returns false when the limit cannot be resolved (skip nudge instead of unconditionally triggering). The model limit is a session constant: from the second turn onward the system.prompt hook has cached it and threshold checks work normally. The real model limit is still enforced by the API layer. Tests: 108/108 pass (added min-threshold skip and 300K no-false-alarm regressions).
1 parent 70203ed commit 275a36c

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

lib/messages/inject/utils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,12 @@ export function isContextOverLimits(
154154
const currentTokens = getCurrentTokenUsage(state, messages)
155155

156156
const overMaxLimit = maxContextLimit === undefined ? false : currentTokens > maxContextLimit
157-
const overMinLimit = minContextLimit === undefined ? true : currentTokens >= minContextLimit
157+
// minContextLimit 无法解析时(如重启后第一轮,modelContextLimit 尚未被
158+
// system.prompt hook 缓存)不能无条件触发:在 1M 模型上会把 300K 的正常
159+
// 上下文误判为超限,每轮注入压缩提醒。此时跳过 nudge——模型 limit 是
160+
// 会话常量,第二轮起 system.prompt 已缓存,判定即恢复正常。
161+
const overMinLimit =
162+
minContextLimit === undefined ? false : currentTokens >= minContextLimit
158163

159164
return {
160165
overMaxLimit,

tests/token-usage.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import { createSessionState, type WithParts } from "../lib/state"
77
import type { CompressionBlock } from "../lib/state"
88
import { getCurrentTokenUsage } from "../lib/token-utils"
99

10-
function buildConfig(maxContextLimit: number, minContextLimit = 1): PluginConfig {
10+
function buildConfig(
11+
maxContextLimit: number | `${number}%`,
12+
minContextLimit: number | `${number}%` = 1,
13+
): PluginConfig {
1114
return {
1215
enabled: true,
1316
debug: false,
@@ -298,3 +301,38 @@ test("isContextOverLimits does not extend the max threshold when summaryBuffer i
298301

299302
assert.equal(overLimit.overMaxLimit, true)
300303
})
304+
305+
test("isContextOverLimits skips min threshold when modelContextLimit is unknown", () => {
306+
// 回归:modelContextLimit 未缓存(如重启后第一轮)时,
307+
// 修复前 overMinLimit 无条件 true(每轮注入压缩提醒);
308+
// 修复后应跳过判定,避免 1M 模型上 300K 正常上下文被误判。
309+
const messages = buildCompactedMessages()
310+
messages.push(buildPostCompactionAssistantMessage())
311+
const state = createSessionState() // modelContextLimit = undefined
312+
313+
const pctConfig = buildConfig("85%", "60%")
314+
const result = isContextOverLimits(pctConfig, state, undefined, undefined, messages)
315+
assert.equal(result.overMinLimit, false)
316+
assert.equal(result.overMaxLimit, false)
317+
})
318+
319+
test("isContextOverLimits does not force compression for large-but-normal context when limit is unknown", () => {
320+
// 关键回归:1M 模型上 300K 上下文(30%)在 modelContextLimit 未知时
321+
// 绝不能触发强制压缩警告(修复前 min 侧 fallback 误判导致误压缩)。
322+
const messages = buildCompactedMessages()
323+
messages.push(buildPostCompactionAssistantMessage())
324+
const state = createSessionState()
325+
326+
const lastMsg = messages[messages.length - 1]
327+
;(lastMsg.info as any).tokens = {
328+
input: 300000,
329+
output: 500,
330+
reasoning: 0,
331+
cache: { read: 100, write: 0 },
332+
}
333+
334+
const pctConfig = buildConfig("85%", "60%")
335+
const result = isContextOverLimits(pctConfig, state, undefined, undefined, messages)
336+
assert.equal(result.overMaxLimit, false)
337+
assert.equal(result.overMinLimit, false)
338+
})

0 commit comments

Comments
 (0)