Skip to content

Commit 7587250

Browse files
committed
LCORE-1570: models/config: add CompactionConfiguration
Add the configuration class that controls when conversation compaction triggers and how much recent context is kept verbatim. Disabled by default so the addition is a pure no-op until an operator opts in. Fields and defaults match the spec doc exactly: * enabled: bool — false. The master switch; everything below is inert when this is off. * threshold_ratio: float — 0.7. Trigger when estimated input tokens exceed this fraction of the configured context window. field_validator clamps the value to [0, 1] inclusive so a misconfiguration surfaces at parse time, not at first request. * token_floor: NonNegativeInt — 4096. Floor below which compaction cannot trigger, regardless of threshold_ratio. Protects very small context windows from spurious triggers. Zero is allowed (caller may want pure ratio-based triggering). * buffer_turns: NonNegativeInt — 4. Initial size of the recent-turns buffer the runtime keeps verbatim. The runtime applies a degrading guard (4 -> 3 -> 2 -> 1 -> 0) when these turns themselves exceed the available budget, so zero is a legitimate fallback state. * buffer_max_ratio: float — 0.3. Hard cap on the fraction of the context window the buffer zone may occupy. The configuration is wired onto the root Configuration class as the `compaction` field with a default_factory, so existing deployments that have not configured a `compaction:` block in YAML continue to parse unchanged and silently inherit the disabled-by-default behavior. Tests cover every field's default, the boundary values for both ratio validators, NonNegativeInt's negative-rejection on token_floor and buffer_turns, the extra='forbid' inherited from ConfigurationBase, and that the root Configuration declares the compaction field with the correct default-factory. Tests verify the default-factory inspection-style to avoid having to construct a full Configuration with all its other required sub-configurations.
1 parent 5fc1cbb commit 7587250

2 files changed

Lines changed: 203 additions & 0 deletions

File tree

src/models/config.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,84 @@ def check_default_model_and_provider(self) -> Self:
14601460
return self
14611461

14621462

1463+
class CompactionConfiguration(ConfigurationBase):
1464+
"""Configuration for conversation history compaction.
1465+
1466+
Compaction summarizes older conversation turns when their estimated
1467+
token count approaches the context window limit, keeping the
1468+
conversation usable instead of failing with HTTP 413. The
1469+
configuration here controls when compaction triggers and how much
1470+
recent context is preserved verbatim.
1471+
1472+
Attributes:
1473+
enabled: Master switch. When False, compaction never triggers
1474+
and other fields are inert.
1475+
threshold_ratio: Trigger compaction when estimated input tokens
1476+
exceed this fraction of the model's context window
1477+
(clamped to 0.0..1.0).
1478+
token_floor: Minimum estimated token count before compaction
1479+
can trigger, regardless of threshold_ratio. Prevents
1480+
triggering on very small context windows.
1481+
buffer_turns: Initial number of recent turns to keep verbatim.
1482+
The runtime applies a degrading guard — if these turns
1483+
exceed the available budget, it reduces buffer_turns by
1484+
one repeatedly until the budget fits, down to zero.
1485+
buffer_max_ratio: Hard cap on the fraction of the context
1486+
window the buffer zone may occupy, regardless of
1487+
buffer_turns.
1488+
"""
1489+
1490+
enabled: bool = Field(
1491+
False,
1492+
title="Enable compaction",
1493+
description="When true, older conversation turns are summarized "
1494+
"when estimated tokens approach the context window limit.",
1495+
)
1496+
threshold_ratio: float = Field(
1497+
0.7,
1498+
title="Threshold ratio",
1499+
description="Trigger compaction when estimated tokens exceed "
1500+
"this fraction of the model's context window (0.0-1.0).",
1501+
)
1502+
token_floor: NonNegativeInt = Field(
1503+
4096,
1504+
title="Token floor",
1505+
description="Minimum token count before compaction can trigger. "
1506+
"Prevents triggering on very small context windows.",
1507+
)
1508+
buffer_turns: NonNegativeInt = Field(
1509+
4,
1510+
title="Buffer turns",
1511+
description="Number of recent turns to keep verbatim.",
1512+
)
1513+
buffer_max_ratio: float = Field(
1514+
0.3,
1515+
title="Buffer max ratio",
1516+
description="Maximum fraction of context window the buffer zone "
1517+
"can occupy, regardless of buffer_turns.",
1518+
)
1519+
1520+
@field_validator("threshold_ratio")
1521+
@classmethod
1522+
def _validate_threshold_ratio(cls, value: float) -> float:
1523+
"""Reject threshold ratios outside the inclusive 0..1 range."""
1524+
if not 0.0 <= value <= 1.0:
1525+
raise ValueError(
1526+
"threshold_ratio must be between 0.0 and 1.0 (inclusive)"
1527+
)
1528+
return value
1529+
1530+
@field_validator("buffer_max_ratio")
1531+
@classmethod
1532+
def _validate_buffer_max_ratio(cls, value: float) -> float:
1533+
"""Reject buffer-max ratios outside the inclusive 0..1 range."""
1534+
if not 0.0 <= value <= 1.0:
1535+
raise ValueError(
1536+
"buffer_max_ratio must be between 0.0 and 1.0 (inclusive)"
1537+
)
1538+
return value
1539+
1540+
14631541
class ConversationHistoryConfiguration(ConfigurationBase):
14641542
"""Conversation history configuration."""
14651543

@@ -1932,6 +2010,15 @@ class Configuration(ConfigurationBase):
19322010
description="Conversation history configuration.",
19332011
)
19342012

2013+
compaction: CompactionConfiguration = Field(
2014+
default_factory=CompactionConfiguration,
2015+
title="Conversation compaction configuration",
2016+
description="Controls when conversation history is summarized "
2017+
"to keep the model's input below the context window limit. "
2018+
"Disabled by default — when disabled, requests that exceed the "
2019+
"window continue to surface as HTTP 413.",
2020+
)
2021+
19352022
byok_rag: list[ByokRag] = Field(
19362023
default_factory=list,
19372024
title="BYOK RAG configuration",
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""Unit tests for CompactionConfiguration and its placement on Configuration."""
2+
3+
import pytest
4+
5+
from models.config import CompactionConfiguration, Configuration
6+
7+
8+
def test_default_values() -> None:
9+
"""Default-construct CompactionConfiguration matches the spec defaults.
10+
11+
Defaults come from the conversation-compaction spec doc — disabled,
12+
70% trigger, 4096 token floor, 4 buffer turns, 30% max buffer.
13+
"""
14+
config = CompactionConfiguration()
15+
assert config.enabled is False
16+
assert config.threshold_ratio == 0.7
17+
assert config.token_floor == 4096
18+
assert config.buffer_turns == 4
19+
assert config.buffer_max_ratio == 0.3
20+
21+
22+
def test_enabled_can_be_turned_on() -> None:
23+
"""The enabled master switch is configurable."""
24+
config = CompactionConfiguration(enabled=True)
25+
assert config.enabled is True
26+
27+
28+
def test_threshold_ratio_accepts_boundary_values() -> None:
29+
"""threshold_ratio accepts 0.0 and 1.0 inclusively."""
30+
assert CompactionConfiguration(threshold_ratio=0.0).threshold_ratio == 0.0
31+
assert CompactionConfiguration(threshold_ratio=1.0).threshold_ratio == 1.0
32+
33+
34+
def test_threshold_ratio_rejects_negative() -> None:
35+
"""threshold_ratio below 0 is rejected."""
36+
with pytest.raises(
37+
ValueError, match="threshold_ratio must be between 0.0 and 1.0"
38+
):
39+
CompactionConfiguration(threshold_ratio=-0.1)
40+
41+
42+
def test_threshold_ratio_rejects_above_one() -> None:
43+
"""threshold_ratio above 1 is rejected."""
44+
with pytest.raises(
45+
ValueError, match="threshold_ratio must be between 0.0 and 1.0"
46+
):
47+
CompactionConfiguration(threshold_ratio=1.1)
48+
49+
50+
def test_buffer_max_ratio_boundary_values() -> None:
51+
"""buffer_max_ratio accepts 0.0 and 1.0 inclusively."""
52+
assert CompactionConfiguration(buffer_max_ratio=0.0).buffer_max_ratio == 0.0
53+
assert CompactionConfiguration(buffer_max_ratio=1.0).buffer_max_ratio == 1.0
54+
55+
56+
def test_buffer_max_ratio_rejects_negative() -> None:
57+
"""buffer_max_ratio below 0 is rejected."""
58+
with pytest.raises(
59+
ValueError, match="buffer_max_ratio must be between 0.0 and 1.0"
60+
):
61+
CompactionConfiguration(buffer_max_ratio=-0.05)
62+
63+
64+
def test_buffer_max_ratio_rejects_above_one() -> None:
65+
"""buffer_max_ratio above 1 is rejected."""
66+
with pytest.raises(
67+
ValueError, match="buffer_max_ratio must be between 0.0 and 1.0"
68+
):
69+
CompactionConfiguration(buffer_max_ratio=1.5)
70+
71+
72+
def test_token_floor_rejects_negative() -> None:
73+
"""token_floor is NonNegativeInt and rejects negatives."""
74+
with pytest.raises(ValueError):
75+
CompactionConfiguration(token_floor=-1)
76+
77+
78+
def test_buffer_turns_rejects_negative() -> None:
79+
"""buffer_turns is NonNegativeInt and rejects negatives."""
80+
with pytest.raises(ValueError):
81+
CompactionConfiguration(buffer_turns=-1)
82+
83+
84+
def test_token_floor_zero_is_allowed() -> None:
85+
"""A zero token floor is allowed — caller may want pure ratio trigger."""
86+
assert CompactionConfiguration(token_floor=0).token_floor == 0
87+
88+
89+
def test_buffer_turns_zero_is_allowed() -> None:
90+
"""Zero buffer turns are allowed — caller may want full summarization."""
91+
assert CompactionConfiguration(buffer_turns=0).buffer_turns == 0
92+
93+
94+
def test_rejects_unknown_field() -> None:
95+
"""Unknown fields are forbidden via ConfigurationBase's extra='forbid'."""
96+
with pytest.raises(ValueError):
97+
CompactionConfiguration(unknown_field=True) # type: ignore[call-arg]
98+
99+
100+
def test_root_configuration_has_compaction_field() -> None:
101+
"""The root Configuration declares a `compaction` field typed as
102+
CompactionConfiguration with a default-factory that produces a
103+
fresh CompactionConfiguration with the spec defaults."""
104+
fields = Configuration.model_fields
105+
assert "compaction" in fields, "Configuration must declare a compaction field"
106+
field_info = fields["compaction"]
107+
assert field_info.annotation is CompactionConfiguration
108+
109+
# default_factory must produce a CompactionConfiguration with disabled
110+
# state — sanity check that the wiring isn't accidentally swapping in
111+
# an enabled-by-default instance.
112+
factory = field_info.default_factory
113+
assert factory is not None
114+
default = factory() # type: ignore[call-arg]
115+
assert isinstance(default, CompactionConfiguration)
116+
assert default.enabled is False

0 commit comments

Comments
 (0)