Skip to content

Commit 64599e7

Browse files
ericdalloeca-agent
andcommitted
Fix potential infinite auto-compact loop on context overflow
Add auto-compacted? one-shot guard to prevent Compact → Resume → Overflow → Compact loops when context overflow persists after compaction. Mirrors the existing auto-continued? pattern. #391 🤖 Generated with [eca](https://eca.dev) Co-Authored-By: eca <git@eca.dev>
1 parent c0401a1 commit 64599e7

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Fix exceptions on openai responses models when creating tasks.
6+
- Fix potential infinite auto-compact loop when context overflow persists after compaction. #391
67

78
## 0.123.2
89

src/eca/features/chat.clj

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@
391391
:text "Continue with the task. The previous user request was:"}]}]
392392
user-messages)
393393
:auto-compact
394-
chat-ctx))))
394+
(assoc chat-ctx :auto-compacted? true)))))
395395
nil))
396396

397397
(defn ^:private assert-compatible-apis-between-models!
@@ -527,7 +527,8 @@
527527
(when (= :idle (get-in @db* [:chats chat-id :status]))
528528
(db/update-workspaces-cache! @db* metrics)))))))
529529
(lifecycle/send-content! chat-ctx :system {:type :progress :state :running :text "Waiting model"})
530-
(if (lifecycle/auto-compact? chat-id agent full-model config @db*)
530+
(if (and (lifecycle/auto-compact? chat-id agent full-model config @db*)
531+
(not (:auto-compacted? chat-ctx)))
531532
(trigger-auto-compact! chat-ctx all-tools user-messages)
532533
(future* config
533534
(try
@@ -636,7 +637,8 @@
636637
(do (swap! db* update-in [:chats chat-id] dissoc :compact-done?)
637638
(lifecycle/finish-chat-prompt! :idle chat-ctx)
638639
nil)
639-
(if (lifecycle/auto-compact? chat-id agent full-model config @db*)
640+
(if (and (lifecycle/auto-compact? chat-id agent full-model config @db*)
641+
(not (:auto-compacted? chat-ctx)))
640642
(trigger-auto-compact! chat-ctx tc-all-tools tc-user-messages)
641643
(do
642644
(consume-steer-message! chat-id db* chat-ctx add-to-history!)
@@ -742,7 +744,8 @@
742744
compacting? (or (get-in db [:chats chat-id :compacting?])
743745
(get-in db [:chats chat-id :auto-compacting?]))]
744746
(if (and (= :context-overflow error-type)
745-
(not compacting?))
747+
(not compacting?)
748+
(not (:auto-compacted? chat-ctx)))
746749
(do
747750
(logger/warn logger-tag "Context overflow detected, pruning tool results and auto-compacting"
748751
{:chat-id chat-id})

test/eca/features/chat_test.clj

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,44 @@
197197
:role :system}]}
198198
(h/messages)))))))
199199

200+
(deftest context-overflow-auto-compact-guard-test
201+
(testing "context overflow after auto-compact reports error instead of looping"
202+
(h/reset-components!)
203+
(let [api-call-count* (atom 0)
204+
auto-compact-count* (atom 0)]
205+
(with-redefs-fn
206+
{#'f.chat/trigger-auto-compact!
207+
(fn [chat-ctx _all-tools user-messages]
208+
(swap! auto-compact-count* inc)
209+
(#'f.chat/prompt-messages!
210+
user-messages
211+
:auto-compact
212+
(assoc chat-ctx :auto-compacted? true)))}
213+
(fn []
214+
(let [{:keys [chat-id]}
215+
(prompt!
216+
{:message "Do something"}
217+
{:all-tools-mock (constantly [])
218+
:api-mock
219+
(fn [{:keys [on-error]}]
220+
(swap! api-call-count* inc)
221+
(on-error {:error/type :context-overflow
222+
:message "token limit exceeded"}))})]
223+
(is (= 1 @auto-compact-count*)
224+
"auto-compact should trigger exactly once, not loop")
225+
(is (= 2 @api-call-count*)
226+
"LLM should be called exactly twice: initial prompt and resume after compact")
227+
(is (match?
228+
{:chat-content-received
229+
(m/embeds [{:role :system
230+
:content {:type :text
231+
:text "Context window exceeded. Auto-compacting conversation..."}}
232+
{:role :system
233+
:content {:type :text
234+
:text "\n\ntoken limit exceeded"}}])}
235+
(h/messages))
236+
"Should show auto-compact attempt and then the final error")))))))
237+
200238
(defn ^:private make-tool-output-msg [id text]
201239
{:role "tool_call_output"
202240
:content {:id id

0 commit comments

Comments
 (0)