Skip to content

Commit 7b3ace7

Browse files
authored
Merge pull request #158 from luojiyin1987/feat/auto-compact-threshold
feat: add /compact-threshold command for configurable auto-compact threshold
2 parents b1ddd2f + 8e989ff commit 7b3ace7

File tree

7 files changed

+134
-8
lines changed

7 files changed

+134
-8
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ As long as you have an openai-like endpoint, it should work.
231231
- `/config` - Open configuration panel
232232
- `/cost` - Show token usage and costs
233233
- `/clear` - Clear conversation history
234+
- `/compact-threshold` - View or set auto-compact threshold ratio (e.g. `/compact-threshold 0.85` or `/compact-threshold 85%`)
235+
236+
| Value | Effect | Use Case |
237+
|-------|--------|----------|
238+
| 0.80 | Compress earlier, more conservative | Small context models (e.g. deepseek 131k) |
239+
| 0.85 | Balanced | Medium context models |
240+
| 0.90 | Default | Large context models (e.g. Claude 200k) |
241+
234242
- `/init` - Initialize project context
235243

236244
## Multi-Model Intelligent Collaboration

README.zh-CN.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ Kode 同时使用 `~/.kode` 目录(存放额外数据,如内存文件)和
141141
- `/config` - 打开配置面板
142142
- `/cost` - 显示 token 使用量和成本
143143
- `/clear` - 清除对话历史
144+
- `/compact-threshold` - 查看或设置自动压缩阈值比例(如 `/compact-threshold 0.85``/compact-threshold 85%`
145+
146+
|| 效果 | 适用场景 |
147+
|----|------|----------|
148+
| 0.80 | 更早压缩,更保守 | 小上下文模型(如 deepseek 131k) |
149+
| 0.85 | 平衡 | 中等上下文模型 |
150+
| 0.90 | 默认 | 大上下文模型(如 Claude 200k) |
151+
144152
- `/init` - 初始化项目上下文
145153

146154
## 多模型智能协同

src/commands/compact-threshold.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import chalk from 'chalk'
2+
import type { Command } from '@commands'
3+
import { getGlobalConfig, saveGlobalConfig } from '@utils/config'
4+
import {
5+
AUTO_COMPACT_THRESHOLD_RATIO,
6+
getAutoCompactThresholdRatio,
7+
isValidAutoCompactThresholdRatio,
8+
} from '@utils/session/autoCompactThreshold'
9+
10+
const HELP_ARGS = new Set(['help', '-h', '--help', '?'])
11+
const RESET_ARGS = new Set(['reset', 'default'])
12+
13+
function parseThresholdInput(raw: string): number | null {
14+
const trimmed = raw.trim()
15+
if (!trimmed) return null
16+
17+
let valueText = trimmed
18+
let isPercent = false
19+
20+
if (valueText.endsWith('%')) {
21+
isPercent = true
22+
valueText = valueText.slice(0, -1).trim()
23+
}
24+
25+
if (!valueText) return null
26+
27+
const value = Number(valueText)
28+
if (!Number.isFinite(value)) return null
29+
30+
let ratio = value
31+
// Treat bare values >1 as percentages (85 => 0.85) while still allowing ratios like 0.85.
32+
if (isPercent || (value > 1 && value <= 100)) {
33+
ratio = value / 100
34+
}
35+
36+
return isValidAutoCompactThresholdRatio(ratio) ? ratio : null
37+
}
38+
39+
function formatRatio(ratio: number): string {
40+
const percent = Math.round(ratio * 100)
41+
return `${ratio} (${percent}%)`
42+
}
43+
44+
const compactThreshold = {
45+
type: 'local',
46+
name: 'compact-threshold',
47+
description: 'View or set the auto-compact threshold ratio',
48+
isEnabled: true,
49+
isHidden: false,
50+
argumentHint: '[ratio]',
51+
userFacingName() {
52+
return 'compact-threshold'
53+
},
54+
async call(args) {
55+
const raw = args.trim()
56+
57+
if (!raw || HELP_ARGS.has(raw)) {
58+
const configured = getGlobalConfig().autoCompactThreshold
59+
const isCustom = isValidAutoCompactThresholdRatio(configured)
60+
const ratio = getAutoCompactThresholdRatio()
61+
const defaultNote = isCustom ? '' : ' (default)'
62+
63+
return [
64+
`Auto-compact threshold: ${formatRatio(ratio)}${defaultNote}`,
65+
'Usage: /compact-threshold 0.85',
66+
'Tip: You can also use percentages, e.g. /compact-threshold 85%',
67+
].join('\n')
68+
}
69+
70+
if (RESET_ARGS.has(raw)) {
71+
const nextConfig = { ...getGlobalConfig() }
72+
delete nextConfig.autoCompactThreshold
73+
saveGlobalConfig(nextConfig)
74+
return `Auto-compact threshold reset to default (${AUTO_COMPACT_THRESHOLD_RATIO}).`
75+
}
76+
77+
const parsed = parseThresholdInput(raw)
78+
if (!parsed) {
79+
return [
80+
`Invalid threshold: ${chalk.bold(raw)}`,
81+
'Provide a ratio greater than 0 and less than 1 (e.g. 0.85 or 85%).',
82+
].join('\n')
83+
}
84+
85+
const config = getGlobalConfig()
86+
saveGlobalConfig({ ...config, autoCompactThreshold: parsed })
87+
return `Auto-compact threshold set to ${formatRatio(parsed)}.`
88+
},
89+
} satisfies Command
90+
91+
export default compactThreshold

src/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react'
22
import bug from './bug'
33
import clear from './clear'
44
import compact from './compact'
5+
import compactThreshold from './compact-threshold'
56
import config from './config'
67
import cost from './cost'
78
import ctxViz from './ctx-viz'
@@ -92,6 +93,7 @@ const COMMANDS = memoize((): Command[] => [
9293
agents,
9394
clear,
9495
compact,
96+
compactThreshold,
9597
config,
9698
cost,
9799
doctor,

src/core/config/schema.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export type GlobalConfig = {
165165
}
166166
primaryProvider?: ProviderType
167167
maxTokens?: number
168+
autoCompactThreshold?: number
168169
hasAcknowledgedCostThreshold?: boolean
169170
oauthAccount?: AccountInfo
170171
proxy?: string
@@ -187,6 +188,7 @@ export const GLOBAL_CONFIG_KEYS = [
187188
'primaryProvider',
188189
'preferredNotifChannel',
189190
'maxTokens',
191+
'autoCompactThreshold',
190192
] as const
191193

192194
export type GlobalConfigKey = (typeof GLOBAL_CONFIG_KEYS)[number]
@@ -214,4 +216,3 @@ export type ProjectMcpServerDefinitions = {
214216
mcpJsonPath: string
215217
mcprcPath: string
216218
}
217-

src/utils/session/autoCompactCore.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { getModelManager } from '@utils/model'
1313
import { debug as debugLogger } from '@utils/log/debugLogger'
1414
import { logError } from '@utils/log'
1515
import {
16-
AUTO_COMPACT_THRESHOLD_RATIO,
1716
calculateAutoCompactThresholds,
1817
} from './autoCompactThreshold'
1918

@@ -63,11 +62,7 @@ Focus on information essential for continuing the conversation effectively, incl
6362

6463
async function calculateThresholds(tokenCount: number) {
6564
const contextLimit = await getMainConversationContextLimit()
66-
return calculateAutoCompactThresholds(
67-
tokenCount,
68-
contextLimit,
69-
AUTO_COMPACT_THRESHOLD_RATIO,
70-
)
65+
return calculateAutoCompactThresholds(tokenCount, contextLimit)
7166
}
7267

7368
async function shouldAutoCompact(messages: Message[]): Promise<boolean> {

src/utils/session/autoCompactThreshold.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
1+
import { getGlobalConfig } from '@utils/config'
2+
13
export const AUTO_COMPACT_THRESHOLD_RATIO = 0.9
24

5+
export function isValidAutoCompactThresholdRatio(
6+
value: unknown,
7+
): value is number {
8+
return (
9+
typeof value === 'number' &&
10+
Number.isFinite(value) &&
11+
value > 0 &&
12+
value < 1
13+
)
14+
}
15+
16+
export function getAutoCompactThresholdRatio(): number {
17+
const config = getGlobalConfig()
18+
if (isValidAutoCompactThresholdRatio(config.autoCompactThreshold)) {
19+
return config.autoCompactThreshold
20+
}
21+
return AUTO_COMPACT_THRESHOLD_RATIO
22+
}
23+
324
export function calculateAutoCompactThresholds(
425
tokenCount: number,
526
contextLimit: number,
6-
ratio: number = AUTO_COMPACT_THRESHOLD_RATIO,
27+
ratio: number = getAutoCompactThresholdRatio(),
728
): {
829
isAboveAutoCompactThreshold: boolean
930
percentUsed: number

0 commit comments

Comments
 (0)