compressed-context 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -51
- package/package.json +64 -64
- package/src/compression/active-summary-cache.ts +50 -50
- package/src/compression/adopt.ts +57 -57
- package/src/compression/auto.ts +386 -372
- package/src/compression/babel.ts +85 -85
- package/src/compression/block.ts +25 -25
- package/src/compression/cache-history.ts +75 -75
- package/src/compression/compaction.ts +189 -189
- package/src/compression/decompress-tool.ts +74 -74
- package/src/compression/decompress.ts +292 -292
- package/src/compression/deleted-sessions.ts +24 -24
- package/src/compression/distill.ts +220 -220
- package/src/compression/events.ts +275 -275
- package/src/compression/markdown.ts +53 -53
- package/src/compression/normalize.ts +114 -114
- package/src/compression/render.ts +70 -70
- package/src/compression/schema.ts +131 -84
- package/src/compression/select.ts +123 -123
- package/src/compression/session-message.ts +112 -112
- package/src/compression/settings-rpc.ts +123 -111
- package/src/compression/settings.ts +175 -175
- package/src/compression/shape.ts +75 -0
- package/src/compression/state.ts +248 -248
- package/src/compression/summarize.ts +534 -595
- package/src/compression/summary.ts +73 -73
- package/src/compression/tool.ts +184 -142
- package/src/compression/transcript-store.ts +258 -258
- package/src/compression/transcript.ts +63 -63
- package/src/compression/validate.ts +81 -81
- package/src/i18n/en.ts +93 -93
- package/src/i18n/index.ts +28 -28
- package/src/i18n/types.ts +89 -89
- package/src/i18n/zh.ts +93 -93
- package/src/index.ts +116 -108
- package/src/shared/blocks.ts +17 -17
- package/src/shared/cache.ts +199 -199
- package/src/shared/format.ts +23 -23
- package/src/shared/media.ts +184 -184
- package/src/shared/parse.ts +25 -25
- package/src/shared/parts.ts +72 -72
- package/src/shared/rpc.ts +145 -145
- package/src/shared/scan.ts +32 -32
- package/src/shared/settings.ts +466 -471
- package/src/shared/stats.ts +142 -142
- package/src/shared/tokens.ts +259 -259
- package/src/tui/chart/cache-chart.ts +237 -237
- package/src/tui/chart/color.ts +58 -58
- package/src/tui/index.tsx +109 -111
- package/src/tui/panel/cache.tsx +203 -203
- package/src/tui/panel/data.ts +105 -105
- package/src/tui/panel/index.tsx +176 -176
- package/src/tui/panel/summaries.tsx +203 -192
- package/src/tui/settings/dialogs.tsx +215 -215
- package/src/tui/settings/options.ts +238 -238
- package/src/tui/settings.tsx +418 -418
- package/src/tui/types.ts +4 -4
package/src/shared/settings.ts
CHANGED
|
@@ -1,471 +1,466 @@
|
|
|
1
|
-
import { readInt } from "./parse.ts"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* TUI 面板设置。
|
|
5
|
-
*
|
|
6
|
-
* 存在 TUI 自己的 storage 里(`context.storage.store`),用户不用改 config.json。
|
|
7
|
-
* 优先级:UI 改过的值 > 插件 options 给的初始值 > 内置默认。
|
|
8
|
-
*
|
|
9
|
-
* 摘要上限按「窗口百分比」存储:换模型、换窗口后自动缩放,不会因为旧的绝对值放不下。
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export const LOCALE_SETTINGS = ["auto", "zh", "en"] as const
|
|
13
|
-
|
|
14
|
-
export type LocaleSetting = (typeof LOCALE_SETTINGS)[number]
|
|
15
|
-
|
|
16
|
-
/** 摘要语言;auto 跟随源对话。 */
|
|
17
|
-
export const SUMMARY_LANGUAGES = ["auto", "zh", "en"] as const
|
|
18
|
-
|
|
19
|
-
export type SummaryLanguage = (typeof SUMMARY_LANGUAGES)[number]
|
|
20
|
-
|
|
21
|
-
/** 摘要格式:JSON 结构化、自由 Markdown,或模型间高密度 Babel。 */
|
|
22
|
-
export const SUMMARY_FORMATS = ["json", "markdown", "babel"] as const
|
|
23
|
-
|
|
24
|
-
export type SummaryFormat = (typeof SUMMARY_FORMATS)[number]
|
|
25
|
-
|
|
26
|
-
export const DEFAULT_SUMMARY_FORMAT: SummaryFormat = "markdown"
|
|
27
|
-
|
|
28
|
-
/** 输入:任意值;输出:是否是合法摘要格式。 */
|
|
29
|
-
export function isSummaryFormat(value: unknown): value is SummaryFormat {
|
|
30
|
-
return typeof value === "string" && (SUMMARY_FORMATS as readonly string[]).includes(value)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** 单条摘要的最小字符数;只用于把绝对值输入换算成百分比时的下界。 */
|
|
34
|
-
export const MIN_SUMMARY_BLOCK_CHARS = 500
|
|
35
|
-
|
|
36
|
-
/** 窗口未知时(还没拿到模型信息)退回的绝对值上限。 */
|
|
37
|
-
export const DEFAULT_SUMMARY_BLOCK_CHARS = 20_000
|
|
38
|
-
export const DEFAULT_SUMMARY_TOTAL_CHARS = 100_000
|
|
39
|
-
|
|
40
|
-
/** 摘要上限允许的窗口百分比范围。 */
|
|
41
|
-
export const MIN_SUMMARY_PERCENT = 1
|
|
42
|
-
export const MAX_SUMMARY_PERCENT = 90
|
|
43
|
-
export const DEFAULT_SUMMARY_BLOCK_PERCENT = 10
|
|
44
|
-
export const DEFAULT_SUMMARY_TOTAL_PERCENT = 50
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 上下文窗口里预留给 system/tools/recent 的 token 数。
|
|
48
|
-
* 摘要上限按 1 字符 ≈ 1 token 的最坏情况折算,保证 CJK 摘要也不会撑爆窗口。
|
|
49
|
-
*/
|
|
50
|
-
export const SUMMARY_WINDOW_RESERVE = 10_000
|
|
51
|
-
|
|
52
|
-
/** 上下文窗口上限:auto 复用模型原生值,数字表示自定义 token 数。 */
|
|
53
|
-
export type ContextLimit = "auto" | number
|
|
54
|
-
|
|
55
|
-
export const MIN_TRIGGER_PERCENT = 60
|
|
56
|
-
export const MAX_TRIGGER_PERCENT = 95
|
|
57
|
-
export const DEFAULT_TRIGGER_PERCENT = 80
|
|
58
|
-
|
|
59
|
-
/** 主动压缩提醒的默认增长量;0 表示关闭。 */
|
|
60
|
-
export const DEFAULT_NUDGE_GROWTH_TOKENS = 50_000
|
|
61
|
-
export const MIN_NUDGE_GROWTH_TOKENS = 0
|
|
62
|
-
export const MAX_NUDGE_GROWTH_TOKENS = 200_000
|
|
63
|
-
|
|
64
|
-
/** 可用窗口下限:低于它连最小摘要都放不下。 */
|
|
65
|
-
export const MIN_CONTEXT_LIMIT = 50_000
|
|
66
|
-
export const MAX_CONTEXT_LIMIT = 10_000_000
|
|
67
|
-
|
|
68
|
-
/** 自动压缩总开关:关掉后只保留手动 /compact 和 compress 工具。 */
|
|
69
|
-
export const DEFAULT_AUTO_COMPRESS = true
|
|
70
|
-
|
|
71
|
-
/** 工具开关:关掉后工具仍注册,但调用会返回明确错误。 */
|
|
72
|
-
export const DEFAULT_ENABLE_COMPRESS_TOOL = true
|
|
73
|
-
export const DEFAULT_ENABLE_DECOMPRESS_TOOL = true
|
|
74
|
-
|
|
75
|
-
/** 块头是否显示时间线 / 估算 token;默认关闭,避免多占 token。 */
|
|
76
|
-
export const DEFAULT_SHOW_TIMELINE = false
|
|
77
|
-
export const DEFAULT_SHOW_BLOCK_TOKENS = false
|
|
78
|
-
|
|
79
|
-
/** 面板显示项;都是 TUI 本地状态,不发给服务端。 */
|
|
80
|
-
export const DEFAULT_CACHE_EXPANDED = true
|
|
81
|
-
export const DEFAULT_SHOW_CACHE_CHART = true
|
|
82
|
-
export const DEFAULT_CACHE_CHART_EXPANDED = true
|
|
83
|
-
export const DEFAULT_SHOW_CACHE_LOG = true
|
|
84
|
-
export const DEFAULT_CACHE_LOG_EXPANDED = false
|
|
85
|
-
export const DEFAULT_SHOW_SUMMARIES = true
|
|
86
|
-
export const DEFAULT_SUMMARIES_EXPANDED = true
|
|
87
|
-
|
|
88
|
-
/** decompress 返回分级与临时文件上限。 */
|
|
89
|
-
export const DEFAULT_DECOMPRESS_INLINE_MAX = 10_000
|
|
90
|
-
export const DEFAULT_DECOMPRESS_TEMP_CAP = 50
|
|
91
|
-
export const DEFAULT_DECOMPRESS_SHOW_TOKENS = true
|
|
92
|
-
export const MIN_DECOMPRESS_INLINE_MAX = 1_000
|
|
93
|
-
export const MAX_DECOMPRESS_INLINE_MAX = 1_000_000
|
|
94
|
-
export const MIN_DECOMPRESS_TEMP_CAP = 1
|
|
95
|
-
export const MAX_DECOMPRESS_TEMP_CAP = 1_000
|
|
96
|
-
|
|
97
|
-
/** 服务端关心的压缩设置;TUI 的 Settings 在此基础上加显示项。 */
|
|
98
|
-
export interface CompressionSettings {
|
|
99
|
-
/** 上下文窗口上限;数字是上限,实际取「设置值」和「模型原生窗口」中较小的那个 */
|
|
100
|
-
contextLimit: ContextLimit
|
|
101
|
-
/** 强制压缩阈值(占用百分比) */
|
|
102
|
-
triggerPercent: number
|
|
103
|
-
/** 每增长多少 token 提醒模型可以主动压缩;0 关闭 */
|
|
104
|
-
nudgeGrowthTokens: number
|
|
105
|
-
/** 单条摘要最多占窗口的百分比 */
|
|
106
|
-
summaryBlockPercent: number
|
|
107
|
-
/** 全部摘要加起来最多占窗口的百分比 */
|
|
108
|
-
summaryTotalPercent: number
|
|
109
|
-
/** 摘要语言;只影响提示词 */
|
|
110
|
-
summaryLanguage: SummaryLanguage
|
|
111
|
-
/** 摘要格式:JSON 结构化 / Markdown 自然语言 / Babel 模型间高密度 */
|
|
112
|
-
summaryFormat: SummaryFormat
|
|
113
|
-
/** 自动压缩总开关 */
|
|
114
|
-
autoCompress: boolean
|
|
115
|
-
/** compress 工具开关 */
|
|
116
|
-
enableCompressTool: boolean
|
|
117
|
-
/** decompress 工具开关 */
|
|
118
|
-
enableDecompressTool: boolean
|
|
119
|
-
/** 块头显示时间线 */
|
|
120
|
-
showTimeline: boolean
|
|
121
|
-
/** 块头显示估算 token */
|
|
122
|
-
showBlockTokens: boolean
|
|
123
|
-
/** decompress 内联上限(字符) */
|
|
124
|
-
decompressInlineMax: number
|
|
125
|
-
/** decompress 临时文件上限 */
|
|
126
|
-
decompressTempCap: number
|
|
127
|
-
/** decompress 返回是否带估算 token */
|
|
128
|
-
decompressShowTokens: boolean
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export interface Settings extends CompressionSettings {
|
|
132
|
-
/** 是否显示整块上下文面板 */
|
|
133
|
-
enabled: boolean
|
|
134
|
-
/** 面板是否展开(折叠后只剩标题行) */
|
|
135
|
-
expanded: boolean
|
|
136
|
-
/** 界面语言;auto 跟随系统 */
|
|
137
|
-
locale: LocaleSetting
|
|
138
|
-
/** 缓存记录区是否展开 */
|
|
139
|
-
cacheExpanded: boolean
|
|
140
|
-
/** 显示缓存折线图 */
|
|
141
|
-
showCacheChart: boolean
|
|
142
|
-
/** 折线图是否展开 */
|
|
143
|
-
cacheChartExpanded: boolean
|
|
144
|
-
/** 显示缓存日志 */
|
|
145
|
-
showCacheLog: boolean
|
|
146
|
-
/** 日志是否展开 */
|
|
147
|
-
cacheLogExpanded: boolean
|
|
148
|
-
/** 显示摘要块 */
|
|
149
|
-
showSummaries: boolean
|
|
150
|
-
/** 摘要区是否展开 */
|
|
151
|
-
summariesExpanded: boolean
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
*
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (
|
|
226
|
-
const
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
*
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
if (
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
*
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
typeof value["
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
*/
|
|
364
|
-
export function
|
|
365
|
-
return
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
export function
|
|
370
|
-
return readSummaryPercent(settings.
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
export function
|
|
375
|
-
return
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
export function
|
|
380
|
-
return
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (isContextLimit(value) && value !== "auto") {
|
|
468
|
-
return native > 0 ? Math.min(value, native) : value
|
|
469
|
-
}
|
|
470
|
-
return native > 0 ? native : undefined
|
|
471
|
-
}
|
|
1
|
+
import { readInt } from "./parse.ts"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TUI 面板设置。
|
|
5
|
+
*
|
|
6
|
+
* 存在 TUI 自己的 storage 里(`context.storage.store`),用户不用改 config.json。
|
|
7
|
+
* 优先级:UI 改过的值 > 插件 options 给的初始值 > 内置默认。
|
|
8
|
+
*
|
|
9
|
+
* 摘要上限按「窗口百分比」存储:换模型、换窗口后自动缩放,不会因为旧的绝对值放不下。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const LOCALE_SETTINGS = ["auto", "zh", "en"] as const
|
|
13
|
+
|
|
14
|
+
export type LocaleSetting = (typeof LOCALE_SETTINGS)[number]
|
|
15
|
+
|
|
16
|
+
/** 摘要语言;auto 跟随源对话。 */
|
|
17
|
+
export const SUMMARY_LANGUAGES = ["auto", "zh", "en"] as const
|
|
18
|
+
|
|
19
|
+
export type SummaryLanguage = (typeof SUMMARY_LANGUAGES)[number]
|
|
20
|
+
|
|
21
|
+
/** 摘要格式:JSON 结构化、自由 Markdown,或模型间高密度 Babel。 */
|
|
22
|
+
export const SUMMARY_FORMATS = ["json", "markdown", "babel"] as const
|
|
23
|
+
|
|
24
|
+
export type SummaryFormat = (typeof SUMMARY_FORMATS)[number]
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_SUMMARY_FORMAT: SummaryFormat = "markdown"
|
|
27
|
+
|
|
28
|
+
/** 输入:任意值;输出:是否是合法摘要格式。 */
|
|
29
|
+
export function isSummaryFormat(value: unknown): value is SummaryFormat {
|
|
30
|
+
return typeof value === "string" && (SUMMARY_FORMATS as readonly string[]).includes(value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 单条摘要的最小字符数;只用于把绝对值输入换算成百分比时的下界。 */
|
|
34
|
+
export const MIN_SUMMARY_BLOCK_CHARS = 500
|
|
35
|
+
|
|
36
|
+
/** 窗口未知时(还没拿到模型信息)退回的绝对值上限。 */
|
|
37
|
+
export const DEFAULT_SUMMARY_BLOCK_CHARS = 20_000
|
|
38
|
+
export const DEFAULT_SUMMARY_TOTAL_CHARS = 100_000
|
|
39
|
+
|
|
40
|
+
/** 摘要上限允许的窗口百分比范围。 */
|
|
41
|
+
export const MIN_SUMMARY_PERCENT = 1
|
|
42
|
+
export const MAX_SUMMARY_PERCENT = 90
|
|
43
|
+
export const DEFAULT_SUMMARY_BLOCK_PERCENT = 10
|
|
44
|
+
export const DEFAULT_SUMMARY_TOTAL_PERCENT = 50
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 上下文窗口里预留给 system/tools/recent 的 token 数。
|
|
48
|
+
* 摘要上限按 1 字符 ≈ 1 token 的最坏情况折算,保证 CJK 摘要也不会撑爆窗口。
|
|
49
|
+
*/
|
|
50
|
+
export const SUMMARY_WINDOW_RESERVE = 10_000
|
|
51
|
+
|
|
52
|
+
/** 上下文窗口上限:auto 复用模型原生值,数字表示自定义 token 数。 */
|
|
53
|
+
export type ContextLimit = "auto" | number
|
|
54
|
+
|
|
55
|
+
export const MIN_TRIGGER_PERCENT = 60
|
|
56
|
+
export const MAX_TRIGGER_PERCENT = 95
|
|
57
|
+
export const DEFAULT_TRIGGER_PERCENT = 80
|
|
58
|
+
|
|
59
|
+
/** 主动压缩提醒的默认增长量;0 表示关闭。 */
|
|
60
|
+
export const DEFAULT_NUDGE_GROWTH_TOKENS = 50_000
|
|
61
|
+
export const MIN_NUDGE_GROWTH_TOKENS = 0
|
|
62
|
+
export const MAX_NUDGE_GROWTH_TOKENS = 200_000
|
|
63
|
+
|
|
64
|
+
/** 可用窗口下限:低于它连最小摘要都放不下。 */
|
|
65
|
+
export const MIN_CONTEXT_LIMIT = 50_000
|
|
66
|
+
export const MAX_CONTEXT_LIMIT = 10_000_000
|
|
67
|
+
|
|
68
|
+
/** 自动压缩总开关:关掉后只保留手动 /compact 和 compress 工具。 */
|
|
69
|
+
export const DEFAULT_AUTO_COMPRESS = true
|
|
70
|
+
|
|
71
|
+
/** 工具开关:关掉后工具仍注册,但调用会返回明确错误。 */
|
|
72
|
+
export const DEFAULT_ENABLE_COMPRESS_TOOL = true
|
|
73
|
+
export const DEFAULT_ENABLE_DECOMPRESS_TOOL = true
|
|
74
|
+
|
|
75
|
+
/** 块头是否显示时间线 / 估算 token;默认关闭,避免多占 token。 */
|
|
76
|
+
export const DEFAULT_SHOW_TIMELINE = false
|
|
77
|
+
export const DEFAULT_SHOW_BLOCK_TOKENS = false
|
|
78
|
+
|
|
79
|
+
/** 面板显示项;都是 TUI 本地状态,不发给服务端。 */
|
|
80
|
+
export const DEFAULT_CACHE_EXPANDED = true
|
|
81
|
+
export const DEFAULT_SHOW_CACHE_CHART = true
|
|
82
|
+
export const DEFAULT_CACHE_CHART_EXPANDED = true
|
|
83
|
+
export const DEFAULT_SHOW_CACHE_LOG = true
|
|
84
|
+
export const DEFAULT_CACHE_LOG_EXPANDED = false
|
|
85
|
+
export const DEFAULT_SHOW_SUMMARIES = true
|
|
86
|
+
export const DEFAULT_SUMMARIES_EXPANDED = true
|
|
87
|
+
|
|
88
|
+
/** decompress 返回分级与临时文件上限。 */
|
|
89
|
+
export const DEFAULT_DECOMPRESS_INLINE_MAX = 10_000
|
|
90
|
+
export const DEFAULT_DECOMPRESS_TEMP_CAP = 50
|
|
91
|
+
export const DEFAULT_DECOMPRESS_SHOW_TOKENS = true
|
|
92
|
+
export const MIN_DECOMPRESS_INLINE_MAX = 1_000
|
|
93
|
+
export const MAX_DECOMPRESS_INLINE_MAX = 1_000_000
|
|
94
|
+
export const MIN_DECOMPRESS_TEMP_CAP = 1
|
|
95
|
+
export const MAX_DECOMPRESS_TEMP_CAP = 1_000
|
|
96
|
+
|
|
97
|
+
/** 服务端关心的压缩设置;TUI 的 Settings 在此基础上加显示项。 */
|
|
98
|
+
export interface CompressionSettings {
|
|
99
|
+
/** 上下文窗口上限;数字是上限,实际取「设置值」和「模型原生窗口」中较小的那个 */
|
|
100
|
+
contextLimit: ContextLimit
|
|
101
|
+
/** 强制压缩阈值(占用百分比) */
|
|
102
|
+
triggerPercent: number
|
|
103
|
+
/** 每增长多少 token 提醒模型可以主动压缩;0 关闭 */
|
|
104
|
+
nudgeGrowthTokens: number
|
|
105
|
+
/** 单条摘要最多占窗口的百分比 */
|
|
106
|
+
summaryBlockPercent: number
|
|
107
|
+
/** 全部摘要加起来最多占窗口的百分比 */
|
|
108
|
+
summaryTotalPercent: number
|
|
109
|
+
/** 摘要语言;只影响提示词 */
|
|
110
|
+
summaryLanguage: SummaryLanguage
|
|
111
|
+
/** 摘要格式:JSON 结构化 / Markdown 自然语言 / Babel 模型间高密度 */
|
|
112
|
+
summaryFormat: SummaryFormat
|
|
113
|
+
/** 自动压缩总开关 */
|
|
114
|
+
autoCompress: boolean
|
|
115
|
+
/** compress 工具开关 */
|
|
116
|
+
enableCompressTool: boolean
|
|
117
|
+
/** decompress 工具开关 */
|
|
118
|
+
enableDecompressTool: boolean
|
|
119
|
+
/** 块头显示时间线 */
|
|
120
|
+
showTimeline: boolean
|
|
121
|
+
/** 块头显示估算 token */
|
|
122
|
+
showBlockTokens: boolean
|
|
123
|
+
/** decompress 内联上限(字符) */
|
|
124
|
+
decompressInlineMax: number
|
|
125
|
+
/** decompress 临时文件上限 */
|
|
126
|
+
decompressTempCap: number
|
|
127
|
+
/** decompress 返回是否带估算 token */
|
|
128
|
+
decompressShowTokens: boolean
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface Settings extends CompressionSettings {
|
|
132
|
+
/** 是否显示整块上下文面板 */
|
|
133
|
+
enabled: boolean
|
|
134
|
+
/** 面板是否展开(折叠后只剩标题行) */
|
|
135
|
+
expanded: boolean
|
|
136
|
+
/** 界面语言;auto 跟随系统 */
|
|
137
|
+
locale: LocaleSetting
|
|
138
|
+
/** 缓存记录区是否展开 */
|
|
139
|
+
cacheExpanded: boolean
|
|
140
|
+
/** 显示缓存折线图 */
|
|
141
|
+
showCacheChart: boolean
|
|
142
|
+
/** 折线图是否展开 */
|
|
143
|
+
cacheChartExpanded: boolean
|
|
144
|
+
/** 显示缓存日志 */
|
|
145
|
+
showCacheLog: boolean
|
|
146
|
+
/** 日志是否展开 */
|
|
147
|
+
cacheLogExpanded: boolean
|
|
148
|
+
/** 显示摘要块 */
|
|
149
|
+
showSummaries: boolean
|
|
150
|
+
/** 摘要区是否展开 */
|
|
151
|
+
summariesExpanded: boolean
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const DEFAULT_SETTINGS: Settings = {
|
|
155
|
+
enabled: true,
|
|
156
|
+
expanded: true,
|
|
157
|
+
locale: "auto",
|
|
158
|
+
contextLimit: "auto",
|
|
159
|
+
triggerPercent: DEFAULT_TRIGGER_PERCENT,
|
|
160
|
+
nudgeGrowthTokens: DEFAULT_NUDGE_GROWTH_TOKENS,
|
|
161
|
+
summaryBlockPercent: DEFAULT_SUMMARY_BLOCK_PERCENT,
|
|
162
|
+
summaryTotalPercent: DEFAULT_SUMMARY_TOTAL_PERCENT,
|
|
163
|
+
summaryLanguage: "auto",
|
|
164
|
+
summaryFormat: DEFAULT_SUMMARY_FORMAT,
|
|
165
|
+
autoCompress: DEFAULT_AUTO_COMPRESS,
|
|
166
|
+
enableCompressTool: DEFAULT_ENABLE_COMPRESS_TOOL,
|
|
167
|
+
enableDecompressTool: DEFAULT_ENABLE_DECOMPRESS_TOOL,
|
|
168
|
+
showTimeline: DEFAULT_SHOW_TIMELINE,
|
|
169
|
+
showBlockTokens: DEFAULT_SHOW_BLOCK_TOKENS,
|
|
170
|
+
cacheExpanded: DEFAULT_CACHE_EXPANDED,
|
|
171
|
+
showCacheChart: DEFAULT_SHOW_CACHE_CHART,
|
|
172
|
+
cacheChartExpanded: DEFAULT_CACHE_CHART_EXPANDED,
|
|
173
|
+
showCacheLog: DEFAULT_SHOW_CACHE_LOG,
|
|
174
|
+
cacheLogExpanded: DEFAULT_CACHE_LOG_EXPANDED,
|
|
175
|
+
showSummaries: DEFAULT_SHOW_SUMMARIES,
|
|
176
|
+
summariesExpanded: DEFAULT_SUMMARIES_EXPANDED,
|
|
177
|
+
decompressInlineMax: DEFAULT_DECOMPRESS_INLINE_MAX,
|
|
178
|
+
decompressTempCap: DEFAULT_DECOMPRESS_TEMP_CAP,
|
|
179
|
+
decompressShowTokens: DEFAULT_DECOMPRESS_SHOW_TOKENS,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** 输入:任意值;输出:是否是合法语言设置。 */
|
|
183
|
+
export function isLocaleSetting(value: unknown): value is LocaleSetting {
|
|
184
|
+
return typeof value === "string" && (LOCALE_SETTINGS as readonly string[]).includes(value)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** 输入:任意值;输出:是否是合法摘要语言。 */
|
|
188
|
+
export function isSummaryLanguage(value: unknown): value is SummaryLanguage {
|
|
189
|
+
return typeof value === "string" && (SUMMARY_LANGUAGES as readonly string[]).includes(value)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** 输入:任意值;输出:合法的主动压缩提醒增长量;0 表示关闭。 */
|
|
193
|
+
export function readNudgeGrowthTokens(value: unknown, fallback: number): number {
|
|
194
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback
|
|
195
|
+
const rounded = Math.round(value)
|
|
196
|
+
if (rounded === 0) return 0
|
|
197
|
+
if (rounded < 1_000 || rounded > MAX_NUDGE_GROWTH_TOKENS) return fallback
|
|
198
|
+
return rounded
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** 输入:任意值;输出:合法的窗口百分比(保留一位小数)。 */
|
|
202
|
+
export function readSummaryPercent(value: unknown, fallback: number): number {
|
|
203
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback
|
|
204
|
+
const rounded = Math.round(value * 10) / 10
|
|
205
|
+
if (rounded < MIN_SUMMARY_PERCENT || rounded > MAX_SUMMARY_PERCENT) return fallback
|
|
206
|
+
return rounded
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 输入:用户输入的摘要上限文本(`10%` / `20k` / `20000`)+ 当前窗口。
|
|
211
|
+
* 输出:窗口百分比;非法或无法换算返回 undefined。
|
|
212
|
+
* 预期:绝对值按窗口换算;窗口未知时只接受百分比写法。
|
|
213
|
+
*/
|
|
214
|
+
export function parseSummaryPercent(text: string, windowTokens: number | undefined): number | undefined {
|
|
215
|
+
const trimmed = text.trim()
|
|
216
|
+
if (trimmed.endsWith("%")) {
|
|
217
|
+
const value = Number(trimmed.slice(0, -1))
|
|
218
|
+
if (!Number.isFinite(value)) return undefined
|
|
219
|
+
const percent = Math.round(value * 10) / 10
|
|
220
|
+
if (percent < MIN_SUMMARY_PERCENT || percent > MAX_SUMMARY_PERCENT) return undefined
|
|
221
|
+
return percent
|
|
222
|
+
}
|
|
223
|
+
if (windowTokens === undefined || !Number.isFinite(windowTokens) || windowTokens <= 0) return undefined
|
|
224
|
+
const tokens = parseTokenCount(trimmed, MIN_SUMMARY_BLOCK_CHARS, Math.floor(windowTokens))
|
|
225
|
+
if (tokens === undefined) return undefined
|
|
226
|
+
const percent = Math.round((tokens / windowTokens) * 1000) / 10
|
|
227
|
+
if (percent < MIN_SUMMARY_PERCENT || percent > MAX_SUMMARY_PERCENT) return undefined
|
|
228
|
+
return percent
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** 输入:百分比 + 窗口;输出:折算后的字符上限;窗口未知返回 undefined。 */
|
|
232
|
+
export function percentToChars(percent: number, windowTokens: number | undefined): number | undefined {
|
|
233
|
+
if (windowTokens === undefined || !Number.isFinite(windowTokens) || windowTokens <= 0) return undefined
|
|
234
|
+
return Math.round((windowTokens * percent) / 100)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 输入:窗口 token 数。
|
|
239
|
+
* 输出:摘要百分比允许的最大值(扣掉预留)。
|
|
240
|
+
* 预期:窗口未知时返回全局上限;窗口太小放不下最小摘要时返回 MIN。
|
|
241
|
+
*/
|
|
242
|
+
export function maxSummaryPercent(windowTokens: number | undefined): number {
|
|
243
|
+
if (windowTokens === undefined || !Number.isFinite(windowTokens) || windowTokens <= 0) {
|
|
244
|
+
return MAX_SUMMARY_PERCENT
|
|
245
|
+
}
|
|
246
|
+
const physical = ((windowTokens - SUMMARY_WINDOW_RESERVE) / windowTokens) * 100
|
|
247
|
+
return Math.max(MIN_SUMMARY_PERCENT, Math.min(MAX_SUMMARY_PERCENT, Math.floor(physical)))
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 输入:用户输入的 token 文本(支持 `200k` / `1m` / `200000`)+ 范围。
|
|
252
|
+
* 输出:token 数;非法或越界返回 undefined。
|
|
253
|
+
* 预期:大小写不敏感,允许小数(如 `1.5m`)。
|
|
254
|
+
*/
|
|
255
|
+
export function parseTokenCount(text: string, min: number, max: number): number | undefined {
|
|
256
|
+
const match = /^(\d+(?:\.\d+)?)\s*([km])?$/i.exec(text.trim())
|
|
257
|
+
if (match === null) return undefined
|
|
258
|
+
const value = Number(match[1])
|
|
259
|
+
if (!Number.isFinite(value) || value <= 0) return undefined
|
|
260
|
+
const unit = match[2]?.toLowerCase()
|
|
261
|
+
const multiplier = unit === "k" ? 1_000 : unit === "m" ? 1_000_000 : 1
|
|
262
|
+
const tokens = Math.round(value * multiplier)
|
|
263
|
+
if (tokens < min || tokens > max) return undefined
|
|
264
|
+
return tokens
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** 输入:用户输入的上下文上限文本;输出:token 数;非法或越界返回 undefined。 */
|
|
268
|
+
export function parseTokenLimit(text: string): number | undefined {
|
|
269
|
+
return parseTokenCount(text, MIN_CONTEXT_LIMIT, MAX_CONTEXT_LIMIT)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** 输入:任意输入文本;输出:只保留数字 + 结尾单个 k/m 的合法形态。 */
|
|
273
|
+
export function sanitizeLimitInput(value: string): string {
|
|
274
|
+
const cleaned = value.toLowerCase().replace(/[^0-9km]/g, "")
|
|
275
|
+
const match = /^(\d*)([km]?)/.exec(cleaned)
|
|
276
|
+
return `${match?.[1] ?? ""}${match?.[2] ?? ""}`
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** 输入:任意值;输出:是否是合法上下文上限。 */
|
|
280
|
+
export function isContextLimit(value: unknown): value is ContextLimit {
|
|
281
|
+
if (value === "auto") return true
|
|
282
|
+
return (
|
|
283
|
+
typeof value === "number" &&
|
|
284
|
+
Number.isFinite(value) &&
|
|
285
|
+
value >= MIN_CONTEXT_LIMIT &&
|
|
286
|
+
value <= MAX_CONTEXT_LIMIT
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** 输入:任意值;输出:合法的上下文上限设置(支持 `200k` 这类字符串)。 */
|
|
291
|
+
function readContextLimit(value: unknown): ContextLimit {
|
|
292
|
+
if (isContextLimit(value)) return value
|
|
293
|
+
if (typeof value === "string") return parseTokenLimit(value) ?? DEFAULT_SETTINGS.contextLimit
|
|
294
|
+
return DEFAULT_SETTINGS.contextLimit
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 输入:任意设置对象(可能来自旧版本、缺字段或类型不对)。
|
|
299
|
+
* 输出:补齐后的合法设置。
|
|
300
|
+
* 预期:用于启动时迁移 storage 里的旧数据,绝不抛错;旧的绝对值字段不再读取,统一回到百分比默认。
|
|
301
|
+
*/
|
|
302
|
+
export function normalizeSettings(value: Readonly<Record<string, unknown>>): Settings {
|
|
303
|
+
const percent = value["triggerPercent"]
|
|
304
|
+
const blockPercent = readSummaryPercent(value["summaryBlockPercent"], DEFAULT_SUMMARY_BLOCK_PERCENT)
|
|
305
|
+
const totalPercent = readSummaryPercent(value["summaryTotalPercent"], DEFAULT_SUMMARY_TOTAL_PERCENT)
|
|
306
|
+
return {
|
|
307
|
+
enabled: typeof value["enabled"] === "boolean" ? value["enabled"] : DEFAULT_SETTINGS.enabled,
|
|
308
|
+
expanded: typeof value["expanded"] === "boolean" ? value["expanded"] : DEFAULT_SETTINGS.expanded,
|
|
309
|
+
locale: isLocaleSetting(value["locale"]) ? value["locale"] : DEFAULT_SETTINGS.locale,
|
|
310
|
+
contextLimit: readContextLimit(value["contextLimit"]),
|
|
311
|
+
triggerPercent:
|
|
312
|
+
typeof percent === "number" && Number.isFinite(percent) && percent >= MIN_TRIGGER_PERCENT && percent <= MAX_TRIGGER_PERCENT
|
|
313
|
+
? Math.round(percent)
|
|
314
|
+
: DEFAULT_SETTINGS.triggerPercent,
|
|
315
|
+
nudgeGrowthTokens: readNudgeGrowthTokens(value["nudgeGrowthTokens"], DEFAULT_NUDGE_GROWTH_TOKENS),
|
|
316
|
+
summaryBlockPercent: blockPercent,
|
|
317
|
+
summaryTotalPercent: totalPercent,
|
|
318
|
+
summaryLanguage: isSummaryLanguage(value["summaryLanguage"]) ? value["summaryLanguage"] : "auto",
|
|
319
|
+
summaryFormat: isSummaryFormat(value["summaryFormat"]) ? value["summaryFormat"] : DEFAULT_SUMMARY_FORMAT,
|
|
320
|
+
autoCompress: typeof value["autoCompress"] === "boolean" ? value["autoCompress"] : DEFAULT_AUTO_COMPRESS,
|
|
321
|
+
enableCompressTool:
|
|
322
|
+
typeof value["enableCompressTool"] === "boolean" ? value["enableCompressTool"] : DEFAULT_ENABLE_COMPRESS_TOOL,
|
|
323
|
+
enableDecompressTool:
|
|
324
|
+
typeof value["enableDecompressTool"] === "boolean" ? value["enableDecompressTool"] : DEFAULT_ENABLE_DECOMPRESS_TOOL,
|
|
325
|
+
showTimeline: typeof value["showTimeline"] === "boolean" ? value["showTimeline"] : DEFAULT_SHOW_TIMELINE,
|
|
326
|
+
showBlockTokens: typeof value["showBlockTokens"] === "boolean" ? value["showBlockTokens"] : DEFAULT_SHOW_BLOCK_TOKENS,
|
|
327
|
+
cacheExpanded: typeof value["cacheExpanded"] === "boolean" ? value["cacheExpanded"] : DEFAULT_CACHE_EXPANDED,
|
|
328
|
+
showCacheChart: typeof value["showCacheChart"] === "boolean" ? value["showCacheChart"] : DEFAULT_SHOW_CACHE_CHART,
|
|
329
|
+
cacheChartExpanded:
|
|
330
|
+
typeof value["cacheChartExpanded"] === "boolean" ? value["cacheChartExpanded"] : DEFAULT_CACHE_CHART_EXPANDED,
|
|
331
|
+
showCacheLog: typeof value["showCacheLog"] === "boolean" ? value["showCacheLog"] : DEFAULT_SHOW_CACHE_LOG,
|
|
332
|
+
cacheLogExpanded:
|
|
333
|
+
typeof value["cacheLogExpanded"] === "boolean" ? value["cacheLogExpanded"] : DEFAULT_CACHE_LOG_EXPANDED,
|
|
334
|
+
showSummaries: typeof value["showSummaries"] === "boolean" ? value["showSummaries"] : DEFAULT_SHOW_SUMMARIES,
|
|
335
|
+
summariesExpanded:
|
|
336
|
+
typeof value["summariesExpanded"] === "boolean" ? value["summariesExpanded"] : DEFAULT_SUMMARIES_EXPANDED,
|
|
337
|
+
decompressInlineMax: readInt(
|
|
338
|
+
value["decompressInlineMax"],
|
|
339
|
+
MIN_DECOMPRESS_INLINE_MAX,
|
|
340
|
+
MAX_DECOMPRESS_INLINE_MAX,
|
|
341
|
+
DEFAULT_DECOMPRESS_INLINE_MAX,
|
|
342
|
+
),
|
|
343
|
+
decompressTempCap: readInt(
|
|
344
|
+
value["decompressTempCap"],
|
|
345
|
+
MIN_DECOMPRESS_TEMP_CAP,
|
|
346
|
+
MAX_DECOMPRESS_TEMP_CAP,
|
|
347
|
+
DEFAULT_DECOMPRESS_TEMP_CAP,
|
|
348
|
+
),
|
|
349
|
+
decompressShowTokens:
|
|
350
|
+
typeof value["decompressShowTokens"] === "boolean" ? value["decompressShowTokens"] : DEFAULT_DECOMPRESS_SHOW_TOKENS,
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* 输入:插件 options 的原始值(可能缺失或类型不对)。
|
|
356
|
+
* 输出:合法的初始设置。
|
|
357
|
+
* 预期:非法值回退默认值,绝不抛错。
|
|
358
|
+
*/
|
|
359
|
+
export function initialSettings(options: Readonly<Record<string, unknown>>): Settings {
|
|
360
|
+
return normalizeSettings(options)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** 输入:设置对象(可能是热重载前的旧对象,字段缺失)。输出:合法的单条摘要百分比。 */
|
|
364
|
+
export function effectiveSummaryBlockPercent(settings: Settings): number {
|
|
365
|
+
return readSummaryPercent(settings.summaryBlockPercent, DEFAULT_SUMMARY_BLOCK_PERCENT)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** 输入:设置对象。输出:合法的全部摘要百分比。 */
|
|
369
|
+
export function effectiveSummaryTotalPercent(settings: Settings): number {
|
|
370
|
+
return readSummaryPercent(settings.summaryTotalPercent, DEFAULT_SUMMARY_TOTAL_PERCENT)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** 输入:设置;输出:合法的摘要语言。 */
|
|
374
|
+
export function effectiveSummaryLanguage(settings: Settings): SummaryLanguage {
|
|
375
|
+
return isSummaryLanguage(settings.summaryLanguage) ? settings.summaryLanguage : "auto"
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** 输入:设置;输出:合法的 decompress 内联上限。 */
|
|
379
|
+
export function effectiveDecompressInlineMax(settings: { readonly decompressInlineMax?: unknown }): number {
|
|
380
|
+
return readInt(
|
|
381
|
+
settings.decompressInlineMax,
|
|
382
|
+
MIN_DECOMPRESS_INLINE_MAX,
|
|
383
|
+
MAX_DECOMPRESS_INLINE_MAX,
|
|
384
|
+
DEFAULT_DECOMPRESS_INLINE_MAX,
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** 输入:设置;输出:合法的 decompress 临时文件上限。 */
|
|
389
|
+
export function effectiveDecompressTempCap(settings: { readonly decompressTempCap?: unknown }): number {
|
|
390
|
+
return readInt(
|
|
391
|
+
settings.decompressTempCap,
|
|
392
|
+
MIN_DECOMPRESS_TEMP_CAP,
|
|
393
|
+
MAX_DECOMPRESS_TEMP_CAP,
|
|
394
|
+
DEFAULT_DECOMPRESS_TEMP_CAP,
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** 输入:TUI 设置;输出:发给服务端的压缩设置子集;预期:字段列表只在这里维护。 */
|
|
399
|
+
export function serverSettingsPayload(settings: Settings): CompressionSettings {
|
|
400
|
+
return {
|
|
401
|
+
contextLimit: settings.contextLimit,
|
|
402
|
+
triggerPercent: settings.triggerPercent,
|
|
403
|
+
nudgeGrowthTokens: settings.nudgeGrowthTokens,
|
|
404
|
+
summaryBlockPercent: effectiveSummaryBlockPercent(settings),
|
|
405
|
+
summaryTotalPercent: effectiveSummaryTotalPercent(settings),
|
|
406
|
+
summaryLanguage: effectiveSummaryLanguage(settings),
|
|
407
|
+
summaryFormat: settings.summaryFormat,
|
|
408
|
+
autoCompress: settings.autoCompress,
|
|
409
|
+
enableCompressTool: settings.enableCompressTool,
|
|
410
|
+
enableDecompressTool: settings.enableDecompressTool,
|
|
411
|
+
showTimeline: settings.showTimeline,
|
|
412
|
+
showBlockTokens: settings.showBlockTokens,
|
|
413
|
+
decompressInlineMax: effectiveDecompressInlineMax(settings),
|
|
414
|
+
decompressTempCap: effectiveDecompressTempCap(settings),
|
|
415
|
+
decompressShowTokens: settings.decompressShowTokens,
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* 输入:设置(结构同 Settings,允许缺字段)+ 已知窗口 token 数。
|
|
421
|
+
* 输出:折算后的字符上限与合并预算。
|
|
422
|
+
* 预期:窗口未知时退回默认绝对值;窗口已知时按百分比折算,并扣除 system/tools/recent 预留。
|
|
423
|
+
*/
|
|
424
|
+
export function resolveSummaryLimits(
|
|
425
|
+
settings: {
|
|
426
|
+
readonly summaryBlockPercent?: unknown
|
|
427
|
+
readonly summaryTotalPercent?: unknown
|
|
428
|
+
},
|
|
429
|
+
windowTokens: number | undefined,
|
|
430
|
+
): { blockChars: number; totalChars: number; budgetTokens: number } {
|
|
431
|
+
const blockPercent = effectiveSummaryBlockPercent(settings as Settings)
|
|
432
|
+
const totalPercent = effectiveSummaryTotalPercent(settings as Settings)
|
|
433
|
+
if (windowTokens === undefined || !Number.isFinite(windowTokens) || windowTokens <= 0) {
|
|
434
|
+
return {
|
|
435
|
+
blockChars: DEFAULT_SUMMARY_BLOCK_CHARS,
|
|
436
|
+
totalChars: DEFAULT_SUMMARY_TOTAL_CHARS,
|
|
437
|
+
budgetTokens: DEFAULT_SUMMARY_TOTAL_CHARS,
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const physical = Math.max(0, Math.floor(windowTokens) - SUMMARY_WINDOW_RESERVE)
|
|
441
|
+
const blockChars = Math.min(physical, Math.max(1, Math.round((windowTokens * blockPercent) / 100)))
|
|
442
|
+
const totalChars = Math.min(physical, Math.max(1, Math.round((windowTokens * totalPercent) / 100)))
|
|
443
|
+
return { blockChars, totalChars, budgetTokens: totalChars }
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* 输入:语言设置。
|
|
448
|
+
* 输出:传给 resolveMessages 的 locale。
|
|
449
|
+
* 预期:auto 返回 undefined(交给系统检测),其余原样返回。
|
|
450
|
+
*/
|
|
451
|
+
export function localeArgument(locale: LocaleSetting): string | undefined {
|
|
452
|
+
return locale === "auto" ? undefined : locale
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* 输入:设置 + 模型原生上下文长度。
|
|
457
|
+
* 输出:实际使用的上下文上限;未知时返回 undefined。
|
|
458
|
+
* 预期:数字设置是上限,不能超过模型原生窗口;auto 直接用原生值。
|
|
459
|
+
*/
|
|
460
|
+
export function effectiveLimit(settings: Settings, native: number): number | undefined {
|
|
461
|
+
const value: unknown = settings.contextLimit
|
|
462
|
+
if (isContextLimit(value) && value !== "auto") {
|
|
463
|
+
return native > 0 ? Math.min(value, native) : value
|
|
464
|
+
}
|
|
465
|
+
return native > 0 ? native : undefined
|
|
466
|
+
}
|