Coverage for src/lilbee/cli/tui/widgets/context_chip.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-08-14 11:46 +0000

1"""Live context-window usage, and whether a summarize call is in flight. 

2 

3One chip for both: they are the same question from the user's side -- how much 

4can the model still see, and why did it just pause. 

5""" 

6 

7from __future__ import annotations 

8 

9from pathlib import Path 

10from typing import ClassVar 

11 

12from textual.content import Content 

13from textual.reactive import reactive 

14from textual.widget import Widget 

15 

16from lilbee.cli.tui import messages as msg 

17from lilbee.core.config import cfg 

18from lilbee.retrieval.query.compaction import COMPACT_TRIGGER_FRACTION 

19 

20_CSS_FILE = Path(__file__).with_suffix(".tcss") 

21 

22# Below this the chip stays quiet: a half-empty window is not news. 

23_QUIET_BELOW = 0.5 

24# Linked, not duplicated: amber must mean exactly "where compaction fires", 

25# or the gauge lies when the trigger moves. 

26_PRESSURE_AT = COMPACT_TRIGGER_FRACTION 

27 

28 

29class ContextChip(Widget): 

30 """How full the chat's history budget is, and whether it is condensing now.""" 

31 

32 DEFAULT_CSS: ClassVar[str] = _CSS_FILE.read_text(encoding="utf-8") 

33 

34 # layout=True is load-bearing: width is `auto`, and a repaint redraws 

35 # inside the already-measured box. Mounted empty (usage 0 -> no text) the 

36 # chip measures zero columns and stays invisible without a re-layout. 

37 usage: reactive[float] = reactive(0.0, layout=True) 

38 """Fraction of the history budget the conversation currently occupies.""" 

39 

40 compacting: reactive[bool] = reactive(False, layout=True) 

41 """True while a summarizing model call is in flight and blocking the turn.""" 

42 

43 def on_mount(self) -> None: 

44 self.tooltip = msg.CONTEXT_CHIP_TOOLTIP 

45 

46 def render(self) -> Content: 

47 if self.compacting: 

48 return Content.styled(msg.CONTEXT_CHIP_COMPACTING, "$warning") 

49 if self.usage < _QUIET_BELOW: 

50 return Content("") 

51 percent = min(int(self.usage * 100), 100) 

52 if self.usage < _PRESSURE_AT: 

53 return Content.styled(msg.CONTEXT_CHIP_USAGE.format(percent=percent), "$text-muted") 

54 # Nearly full. With compaction on this resolves itself, so the number is 

55 # enough; with it off, turns are about to leave the model's view and the 

56 # user still has time to do something about it. 

57 template = ( 

58 msg.CONTEXT_CHIP_USAGE if cfg.chat_compaction else msg.CONTEXT_CHIP_USAGE_DROPPING 

59 ) 

60 return Content.styled(template.format(percent=percent), "$warning")