diff --git a/docs/context-compact.md b/docs/context-compact.md index af991d91..86c20a63 100644 --- a/docs/context-compact.md +++ b/docs/context-compact.md @@ -47,7 +47,7 @@ context: - `auto_compact.enabled` 控制是否启用基于 token 阈值的自动压缩;默认关闭。 - `auto_compact.input_token_threshold` - 当会话累计输入 token 数达到此阈值时触发自动压缩;默认 100000。 + 当会话累计输入 token 数达到此阈值时触发自动压缩;默认 `0`(自动推导),推导失败时回退到 `fallback_input_token_threshold`(默认 `100000`)。 ## 自动压缩 diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md index 4a0191e1..c03f3209 100644 --- a/docs/guides/configuration.md +++ b/docs/guides/configuration.md @@ -83,6 +83,8 @@ context: | `context.compact.micro_compact_disabled` | 是否关闭默认启用的 micro compact | | `context.auto_compact.enabled` | 是否启用自动压缩 | | `context.auto_compact.input_token_threshold` | 自动压缩输入 token 阈值 | +| `context.auto_compact.reserve_tokens` | 自动阈值推导时预留 token 缓冲(`resolved_threshold = context_window - reserve_tokens`) | +| `context.auto_compact.fallback_input_token_threshold` | 自动推导失败时使用的保底阈值 | ### `runtime` 字段 diff --git a/internal/runtime/run.go b/internal/runtime/run.go index 93415b36..031eb092 100644 --- a/internal/runtime/run.go +++ b/internal/runtime/run.go @@ -359,7 +359,7 @@ func (s *Service) autoCompactThreshold(ctx context.Context, cfg config.Config) i } if s != nil && s.autoCompactThresholdResolver != nil { threshold, err := s.autoCompactThresholdResolver.ResolveAutoCompactThreshold(ctx, cfg) - if err == nil { + if err == nil && threshold > 0 { return threshold } } diff --git a/internal/runtime/runtime_test.go b/internal/runtime/runtime_test.go index 8ad229d7..a67f70de 100644 --- a/internal/runtime/runtime_test.go +++ b/internal/runtime/runtime_test.go @@ -4297,6 +4297,58 @@ func TestAutoCompactThresholdFallsBackWhenResolverErrors(t *testing.T) { } } +func TestAutoCompactThresholdFallsBackWhenResolverReturnsZeroWithoutError(t *testing.T) { + t.Parallel() + + service := &Service{} + service.SetAutoCompactThresholdResolver(autoCompactThresholdResolverFunc( + func(ctx context.Context, cfg config.Config) (int, error) { + return 0, nil + }, + )) + + cfg := config.Config{ + Context: config.ContextConfig{ + AutoCompact: config.AutoCompactConfig{ + Enabled: true, + InputTokenThreshold: 0, + FallbackInputTokenThreshold: 88000, + }, + }, + } + + threshold := service.autoCompactThreshold(context.Background(), cfg) + if threshold != 88000 { + t.Fatalf("expected fallback threshold == 88000, got %d", threshold) + } +} + +func TestAutoCompactThresholdFallsBackWhenResolverReturnsNegativeWithoutError(t *testing.T) { + t.Parallel() + + service := &Service{} + service.SetAutoCompactThresholdResolver(autoCompactThresholdResolverFunc( + func(ctx context.Context, cfg config.Config) (int, error) { + return -1, nil + }, + )) + + cfg := config.Config{ + Context: config.ContextConfig{ + AutoCompact: config.AutoCompactConfig{ + Enabled: true, + InputTokenThreshold: 0, + FallbackInputTokenThreshold: 88000, + }, + }, + } + + threshold := service.autoCompactThreshold(context.Background(), cfg) + if threshold != 88000 { + t.Fatalf("expected fallback threshold == 88000, got %d", threshold) + } +} + func TestAutoCompactThresholdImplicitModeWithoutResolverUsesFallback(t *testing.T) { t.Parallel()