Skip to content

Commit 00cc569

Browse files
committed
fix: align modelLimits with reviewer feedback
1 parent 653b352 commit 00cc569

File tree

4 files changed

+51
-36
lines changed

4 files changed

+51
-36
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ DCP uses its own config file:
108108
> // to keep the model in the "smart zone" (not a hard limit)
109109
> // Accepts: number or "X%" (percentage of model's context window)
110110
> "contextLimit": 100000,
111+
> // Optional per-model overrides by exact providerID/modelID
112+
> // Accepts: number or "X%"
113+
> // Example:
114+
> // "modelLimits": {
115+
> // "openai/gpt-5": 120000,
116+
> // "anthropic/claude-3-7-sonnet": "80%"
117+
> // },
111118
> // Additional tools to protect from pruning
112119
> "protectedTools": [],
113120
> },

dcp.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
]
124124
},
125125
"modelLimits": {
126-
"description": "Model-specific context limits with optional wildcard patterns (exact match first, then most specific wildcard). Examples: \"openai/gpt-5\", \"*/zen-1\", \"ollama/*\", \"*sonnet*\"",
126+
"description": "Model-specific context limits by exact provider/model key. Examples: \"openai/gpt-5\", \"anthropic/claude-3-7-sonnet\", \"ollama/llama3.1\"",
127127
"type": "object",
128128
"additionalProperties": {
129129
"oneOf": [

lib/config.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ function getConfigKeyPaths(obj: Record<string, any>, prefix = ""): string[] {
139139
const fullKey = prefix ? `${prefix}.${key}` : key
140140
keys.push(fullKey)
141141

142-
// modelLimits is a dynamic map keyed by model ID; do not recurse into arbitrary IDs.
142+
// modelLimits is a dynamic map keyed by providerID/modelID; do not recurse into arbitrary IDs.
143143
if (fullKey === "tools.settings.modelLimits") {
144144
continue
145145
}
@@ -322,40 +322,44 @@ export function validateConfigTypes(config: Record<string, any>): ValidationErro
322322
actual: typeof tools.settings.modelLimits,
323323
})
324324
} else {
325-
for (const [modelId, limit] of Object.entries(tools.settings.modelLimits)) {
325+
for (const [providerModelKey, limit] of Object.entries(
326+
tools.settings.modelLimits,
327+
)) {
326328
const isValidNumber = typeof limit === "number"
327329
const isPercentString =
328330
typeof limit === "string" && /^\d+(?:\.\d+)?%$/.test(limit)
329331
if (!isValidNumber && !isPercentString) {
330332
errors.push({
331-
key: `tools.settings.modelLimits.${modelId}`,
333+
key: `tools.settings.modelLimits.${providerModelKey}`,
332334
expected: 'number | "${number}%"',
333335
actual: JSON.stringify(limit),
334336
})
335337
}
336338
}
337339
}
338340
}
339-
if (tools.distill?.permission !== undefined) {
340-
const validValues = ["ask", "allow", "deny"]
341-
if (!validValues.includes(tools.distill.permission)) {
341+
if (tools.distill) {
342+
if (tools.distill.permission !== undefined) {
343+
const validValues = ["ask", "allow", "deny"]
344+
if (!validValues.includes(tools.distill.permission)) {
345+
errors.push({
346+
key: "tools.distill.permission",
347+
expected: '"ask" | "allow" | "deny"',
348+
actual: JSON.stringify(tools.distill.permission),
349+
})
350+
}
351+
}
352+
if (
353+
tools.distill.showDistillation !== undefined &&
354+
typeof tools.distill.showDistillation !== "boolean"
355+
) {
342356
errors.push({
343-
key: "tools.distill.permission",
344-
expected: '"ask" | "allow" | "deny"',
345-
actual: JSON.stringify(tools.distill.permission),
357+
key: "tools.distill.showDistillation",
358+
expected: "boolean",
359+
actual: typeof tools.distill.showDistillation,
346360
})
347361
}
348362
}
349-
if (
350-
tools.distill?.showDistillation !== undefined &&
351-
typeof tools.distill.showDistillation !== "boolean"
352-
) {
353-
errors.push({
354-
key: "tools.distill.showDistillation",
355-
expected: "boolean",
356-
actual: typeof tools.distill.showDistillation,
357-
})
358-
}
359363
}
360364
if (tools.compress) {
361365
if (tools.compress.permission !== undefined) {

lib/messages/inject.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,6 @@ function parsePercentageString(value: string, total: number): number | undefined
2727
return Math.round((clampedPercent / 100) * total)
2828
}
2929

30-
export const findModelLimit = (
31-
modelId: string,
32-
modelLimits: Record<string, number | `${number}%`>,
33-
): number | `${number}%` | undefined => {
34-
return modelLimits[modelId]
35-
}
36-
3730
// XML wrappers
3831
export const wrapPrunableTools = (content: string): string => {
3932
return `<prunable-tools>
@@ -76,15 +69,18 @@ Context management was just performed. Do NOT use the ${toolName} again. A fresh
7669
const resolveContextLimit = (
7770
config: PluginConfig,
7871
state: SessionState,
79-
messages: WithParts[],
72+
providerId: string | undefined,
73+
modelId: string | undefined,
8074
): number | undefined => {
81-
const { settings } = config.tools
82-
const { modelLimits, contextLimit } = settings
75+
const modelLimits = config.tools.settings.modelLimits
76+
const contextLimit = config.tools.settings.contextLimit
8377

8478
if (modelLimits) {
85-
const userMsg = getLastUserMessage(messages)
86-
const modelId = userMsg ? (userMsg.info as UserMessage).model.modelID : undefined
87-
const limit = modelId !== undefined ? findModelLimit(modelId, modelLimits) : undefined
79+
const providerModelId =
80+
providerId !== undefined && modelId !== undefined
81+
? `${providerId}/${modelId}`
82+
: undefined
83+
const limit = providerModelId !== undefined ? modelLimits[providerModelId] : undefined
8884

8985
if (limit !== undefined) {
9086
if (typeof limit === "string" && limit.endsWith("%")) {
@@ -114,12 +110,14 @@ const shouldInjectCompressNudge = (
114110
config: PluginConfig,
115111
state: SessionState,
116112
messages: WithParts[],
113+
providerId: string | undefined,
114+
modelId: string | undefined,
117115
): boolean => {
118116
if (config.tools.compress.permission === "deny") {
119117
return false
120118
}
121119

122-
const contextLimit = resolveContextLimit(config, state, messages)
120+
const contextLimit = resolveContextLimit(config, state, providerId, modelId)
123121
if (contextLimit === undefined) {
124122
return false
125123
}
@@ -226,6 +224,13 @@ export const insertPruneToolContext = (
226224

227225
const pruneOrDistillEnabled = pruneEnabled || distillEnabled
228226
const contentParts: string[] = []
227+
const lastUserMessage = getLastUserMessage(messages)
228+
const providerId = lastUserMessage
229+
? (lastUserMessage.info as UserMessage).model.providerID
230+
: undefined
231+
const modelId = lastUserMessage
232+
? (lastUserMessage.info as UserMessage).model.modelID
233+
: undefined
229234

230235
if (state.lastToolPrune) {
231236
logger.debug("Last tool was prune - injecting cooldown message")
@@ -245,7 +250,7 @@ export const insertPruneToolContext = (
245250
contentParts.push(compressContext)
246251
}
247252

248-
if (shouldInjectCompressNudge(config, state, messages)) {
253+
if (shouldInjectCompressNudge(config, state, messages, providerId, modelId)) {
249254
logger.info("Inserting compress nudge - token usage exceeds contextLimit")
250255
contentParts.push(renderCompressNudge())
251256
} else if (
@@ -263,7 +268,6 @@ export const insertPruneToolContext = (
263268

264269
const combinedContent = contentParts.join("\n")
265270

266-
const lastUserMessage = getLastUserMessage(messages)
267271
if (!lastUserMessage) {
268272
return
269273
}

0 commit comments

Comments
 (0)