fix: compaction fallback to lowest model context limit#2379
fix: compaction fallback to lowest model context limit#2379shaj13 wants to merge 1 commit intodocker:mainfrom
Conversation
aheritier
left a comment
There was a problem hiding this comment.
Thanks for identifying this bug — compaction silently failing for custom providers is a real problem worth fixing.
The approach (sentinel error + fallback to lowest context limit across providers) is sound, but unfortunately main has diverged significantly since this PR was opened. The session_compaction.go file was refactored to extract a compactionContextLimit() method, add hook support, and delegate heavy lifting to a new compactor package. This PR has merge conflicts and cannot be merged as-is.
What needs to happen:
-
Rebase onto
main— thestore.gochange should apply cleanly; thesession_compaction.gochanges need to be rewritten against the current code. -
Target
compactionContextLimit()— this is the new single place where model context limits are resolved. The fallback logic belongs there:
func (r *LocalRuntime) compactionContextLimit(ctx context.Context, a *agent.Agent) int64 {
...
m, err := r.modelsStore.GetModel(ctx, summaryModel.ID())
if err != nil && errors.Is(err, modelsdev.ErrProviderNotFound) {
// fallback: find lowest context limit across all providers
db, dberr := r.modelsStore.GetDatabase(ctx)
if dberr == nil {
for _, p := range db.Providers {
if v, ok := p.Models[summaryModel.BaseConfig().ModelConfig.Model]; ok {
if m == nil || v.Limit.Context < m.Limit.Context {
m = &v
err = nil
}
}
}
}
}
if err != nil || m == nil {
return 0
}
return int64(m.Limit.Context)
}- Add tests — the fallback is non-trivial logic; unit tests for
compactionContextLimitwith a mock store that triggersErrProviderNotFoundwould give confidence.
What's good here:
- The
ErrProviderNotFoundsentinel +%wwrapping is the correct Go pattern. - "Use the lowest context limit" is a sensible conservative default.
- The structured logging improvements are a nice touch (though they'll need adapting to the current code which uses
slog.DebugContext).
|
/review |
| // Find the lowest context limit for this model, regardless of the provider. | ||
| for _, provider := range db.Providers { | ||
| if v, ok := provider.Models[summaryModel.BaseConfig().ModelConfig.Model]; ok { | ||
| if m == nil || v.Limit.Context < m.Limit.Context { |
There was a problem hiding this comment.
[MEDIUM] Zero Limit.Context value in models.dev data causes compaction to strip all messages
The minimum-finding loop uses v.Limit.Context < m.Limit.Context without guarding against zero values. If any provider in the models.dev database carries this model with Context: 0 (unset/unknown), it will win the comparison (0 < any positive number) and set m.Limit.Context = 0.
This zero propagates into extractMessagesToCompact as contextLimit = 0, which computes:
contextAvailable := max(0, 0 - maxSummaryTokens - systemPromptLen - userPromptLen) // = 0…causing compaction to strip all messages rather than triggering conservatively as intended.
Suggested fix — skip entries with a zero or negative context limit:
for _, provider := range db.Providers {
if v, ok := provider.Models[summaryModel.BaseConfig().ModelConfig.Model]; ok {
if v.Limit.Context > 0 && (m == nil || v.Limit.Context < m.Limit.Context) {
m = &v
err = nil
}
}
}
Problem
When the provider is not found in the models store, compaction fails to determine the correct context limit for the model.
Fix
Added a fallback that searches all providers for the model and uses the lowest context limit found.
This ensures compaction triggers conservatively regardless of which provider is actually being used at runtime.