feat: validate min tokens for compaction
Nicolas Leão committed
Mar 26, 2026 at 16:20 UTC
140869eb9ac0226ce15401cb4e621045a8054638
6 files changed
+43
-110
plugins/_chat_compaction/api/compact_chat.py
+9
-1
@@ -2,6 +2,7 @@
2
from helpers.api import ApiHandler, Input, Output, Request, Response
3
from agent import AgentContext
4
from plugins._chat_compaction.helpers.compactor import (
5
+ MIN_COMPACTION_TOKENS,
6
get_compaction_stats,
7
run_compaction,
8
)
@@ -28,8 +29,15 @@ class CompactChat(ApiHandler):
29
if visible_count <= 1:
30
return Response("Not enough messages to compact", 400)
31
32
+ # Gate both stats and compact — no point opening the modal for tiny chats
33
+ stats = await get_compaction_stats(context)
34
+ if stats["token_count"] < MIN_COMPACTION_TOKENS:
35
+ return {
36
+ "ok": False,
37
+ "message": f"Not enough content to compact (minimum {MIN_COMPACTION_TOKENS:,} tokens)",
38
+ }
39
+
40
if action == "stats":
32
- stats = await get_compaction_stats(context)
41
return {"ok": True, "stats": stats}
42
43
elif action == "compact":
plugins/_chat_compaction/helpers/compactor.py
+2
@@ -13,6 +13,8 @@ from helpers.persist_chat import (
13
remove_msg_files,
14
)
15
from helpers.state_monitor_integration import mark_dirty_all
16
+
17
+MIN_COMPACTION_TOKENS = 1000
18
from plugins._model_config.helpers.model_config import (
19
get_chat_model_config,
20
get_utility_model_config,
plugins/_chat_compaction/plugin.yaml
-4
@@ -2,7 +2,3 @@ name: _chat_compaction
2
title: Chat Compaction
3
description: Compact entire chat history into a single optimized summary message.
4
version: 1.0.0
5
-settings_sections:
6
- - agent
7
-per_project_config: false
8
-per_agent_config: false
plugins/_chat_compaction/webui/compact-modal.html
+26
-1
@@ -61,6 +61,13 @@
61
<div class="model-name" x-text="$store.compactStore.selectedModelDisplay"></div>
62
</div>
63
64
+ <template x-if="!$store.compactStore?.canCompact">
65
+ <div class="stats-info">
66
+ <span class="material-symbols-outlined">info</span>
67
+ <span>Conversation is too short to compact (minimum 1,000 tokens).</span>
68
+ </div>
69
+ </template>
70
+
71
<div class="stats-warning">
72
<span class="material-symbols-outlined">warning</span>
73
<span>This action cannot be undone. The original conversation will be replaced with a summary.</span>
@@ -83,7 +90,7 @@
90
<button
91
class="button primary danger"
92
@click="$store.compactStore?.compact()"
86
- :disabled="$store.compactStore?.compacting || !$store.compactStore?.stats"
93
+ :disabled="$store.compactStore?.compacting || !$store.compactStore?.canCompact"
94
>
95
<template x-if="$store.compactStore?.compacting">
96
<span class="loading-spinner"></span>
@@ -265,6 +272,24 @@
272
text-overflow: ellipsis;
273
}
274
275
+ .stats-info {
276
+ display: flex;
277
+ align-items: flex-start;
278
+ gap: var(--spacing-sm);
279
+ padding: var(--spacing-md);
280
+ margin-bottom: var(--spacing-md);
281
+ background: var(--color-info-bg, rgba(59, 130, 246, 0.1));
282
+ border: 1px solid var(--color-info-border, rgba(59, 130, 246, 0.3));
283
+ border-radius: var(--border-radius-md);
284
+ color: var(--color-info-text, #1e40af);
285
+ font-size: 0.85rem;
286
+ }
287
+
288
+ .stats-info .material-symbols-outlined {
289
+ font-size: 1.2rem;
290
+ flex-shrink: 0;
291
+ }
292
+
293
.stats-warning {
294
display: flex;
295
align-items: flex-start;
plugins/_chat_compaction/webui/compact-store.js
+6
@@ -13,6 +13,12 @@ export const store = createStore("compactStore", {
13
selectedPresetName: "",
14
useChatModel: true,
15
16
+ minTokens: 1000,
17
+
18
+ get canCompact() {
19
+ return this.stats && this.stats.token_count >= this.minTokens;
20
+ },
21
+
22
get selectedModelDisplay() {
23
if (this.selectedPresetName) {
24
const preset = this.presets.find(
plugins/_chat_compaction/webui/config.html
deleted
-104
@@ -1,104 +0,0 @@
1
-<html>
2
-<head>
3
- <title>Compaction Plugin Settings</title>
4
-</head>
5
-<body>
6
- <div x-data>
7
- <template x-if="config">
8
- <div>
9
- <div class="section-title">Compaction Configuration</div>
10
- <div class="section-description">
11
- Configure how the chat compaction feature works.
12
- </div>
13
-
14
- <div class="field">
15
- <div class="field-label">
16
- <div class="field-title">Use chat model</div>
17
- <div class="field-description">
18
- When enabled, uses the currently selected chat model for compaction.
19
- When disabled, uses the utility model (usually faster and cheaper).
20
- </div>
21
- </div>
22
- <div class="field-control">
23
- <label class="toggle">
24
- <input type="checkbox" x-model="config.use_chat_model" />
25
- <span class="toggler"></span>
26
- </label>
27
- </div>
28
- </div>
29
- </div>
30
- </template>
31
- </div>
32
-
33
- <style>
34
- .section-title {
35
- font-size: 1rem;
36
- font-weight: 600;
37
- margin-bottom: 8px;
38
- color: var(--color-text, #e5e5e5);
39
- }
40
- .section-description {
41
- font-size: 0.85rem;
42
- color: var(--color-text-secondary, #999);
43
- margin-bottom: 20px;
44
- line-height: 1.5;
45
- }
46
- .field {
47
- margin-bottom: 20px;
48
- }
49
- .field-label {
50
- margin-bottom: 8px;
51
- }
52
- .field-title {
53
- font-weight: 500;
54
- color: var(--color-text, #e5e5e5);
55
- margin-bottom: 4px;
56
- }
57
- .field-description {
58
- font-size: 0.8rem;
59
- color: var(--color-text-secondary, #999);
60
- line-height: 1.4;
61
- }
62
- .field-control {
63
- margin-top: 8px;
64
- }
65
- .toggle {
66
- display: inline-flex;
67
- align-items: center;
68
- cursor: pointer;
69
- }
70
- .toggle input {
71
- display: none;
72
- }
73
- .toggler {
74
- width: 44px;
75
- height: 24px;
76
- background: var(--color-border, #444);
77
- border-radius: 12px;
78
- position: relative;
79
- transition: background 0.2s;
80
- }
81
- .toggler::after {
82
- content: '';
83
- position: absolute;
84
- width: 20px;
85
- height: 20px;
86
- background: white;
87
- border-radius: 50%;
88
- top: 2px;
89
- left: 2px;
90
- transition: transform 0.2s;
91
- }
92
- .toggle input:checked + .toggler {
93
- background: var(--color-primary, #3b82f6);
94
- }
95
- .toggle input:checked + .toggler::after {
96
- transform: translateX(20px);
97
- }
98
- </style>
99
-
100
- <script type="module">
101
- import { store } from "/components/plugins/plugin-settings-store.js";
102
- </script>
103
-</body>
104
-</html>