agentscope-ai / agentscope-ai/agentscope-java
[Bug]: Lightweight compaction results are discarded when summarization is skipped
- Langage dominant
- Java
- Étoiles
- 5.6k
- Forks
- 1.3k
- Merge moyen
- 4 j 12 h
- PR mergées (30 j)
- 77
Description
## Describe the bug
`ConversationCompactor.compactIfNeeded()` performs argument truncation and tool-result pruning before deciding whether to summarize. Both lightweight passes return a replacement list when they change messages.
However, the method returns `Optional.empty()` in either of these cases:
1. The reduced conversation is below the summarization trigger.
2. The trigger fires, but the safe cutoff is zero because the conversation fits within the preserved tail.
`CompactionMiddleware` interprets the empty result as a no-op and forwards the original `ReasoningInput`. The reduced messages therefore reach neither the immediate model request nor the working `AgentState`. This can leave oversized tool content in subsequent requests even though the lightweight pass successfully reduced it.
## Reproduction
Reproduced on upstream `main` at `ea511ec2f68ca929a6bbc4e92cd0c0952a38c592` (`2.0.3-SNAPSHOT`), using Java 17 and a mocked model; no provider calls are required.
- Build a conversation containing a user message, an assistant tool call, and its matching tool result.
- Put `"x".repeat(10_000)` in the tool result.
- Configure `triggerTokens(1_000)`, `keepTokens(0)`, `keepMessages(20)` and pruning with `protectTokens(0)`, `minimumTokens(1)`, `maxOutputChars(100)`.
- Invoke `CompactionMiddleware.onReasoning()` and capture the input received by `next`.
- The tool result is pruned internally, but `next` receives the original input and state is unchanged.
Setting `triggerMessages(3)` reproduces the second early-return path (zero cutoff). Enabling argument truncation instead of result pruning reproduces both paths with a large `write_file` content argument.
A regression test exercises all four combinations at the real middleware boundary: all four fail on the baseline, while two unchanged-conversation controls pass.
## Expected behavior
Return the reduced conversation whenever a lightweight pass changed it, even when summarization is skipped. Reserve `Optional.empty()` for a genuine no-op. This lets existing callers update both the request and working state without adding an LLM call or changing the public method signature.
## Related work and scope
This is distinct from #2267 / #2319, which fixed `ToolResultEvictionMiddleware` updating state without updating the copied reasoning input. The issue here is the two early returns in `ConversationCompactor`.
This report does not propose changing pruning thresholds, protected windows, default argument truncation settings, or summary-error handling. I am preparing a small fix with regression tests.
Guide de contribution
Ouvrir le guide de contribution
Évaluation
Cette issue n'a pas encore été évaluée.