1"""Single source of truth for model → context-window size mapping.

2

3 Precedence (highest to lowest):

4 1. Explicit ``override`` argument (non-zero)

5 2. ``LL_CONTEXT_LIMIT`` environment variable (non-zero integer)

6 3. ``[1m]`` suffix on model id → 1_000_000

7 4. Exact model-id lookup in MODEL_CONTEXT_WINDOW

8 5. 200_000 conservative floor

9

10# keep in sync with hooks/scripts/context-monitor.sh:get_context_limit()

11"""

12

13 from __future__ import annotations

14

15 import os

16

17# Known model context windows in tokens.

18# Models not listed here fall back to the 200_000 floor.

19 MODEL_CONTEXT_WINDOW: dict[str, int] = {

20 # Claude 4.x — 200k base context

21 "claude-opus-4-8": 200_000,

22 "claude-opus-4-7": 200_000,

23 "claude-opus-4-6": 200_000,

24 "claude-opus-4-5": 200_000,

25 "claude-sonnet-4-6": 200_000,

26 "claude-sonnet-4-5": 200_000,

27 "claude-haiku-4-5": 200_000,

28 "claude-haiku-4-5-20251001": 200_000,

29 # Claude 3.x (legacy)

30 "claude-opus-3-7": 200_000,

31 "claude-sonnet-3-7": 200_000,

32 "claude-haiku-3-5": 200_000,

33}

34

35_DEFAULT_CONTEXT_WINDOW = 200_000

36_1M_CONTEXT_WINDOW = 1_000_000

37

38

39 def context_window_for(model: str | None, override: int | None = None) -> int:

40 """Resolve context-window size for a model id.

41

42 Args:

43 model: Model identifier string (e.g. ``"claude-opus-4-8[1m]"``),

44 or ``None`` to use env-var / floor.

45 override: Explicit token count; takes top precedence when non-zero.

46

47 Returns:

48 Context window size in tokens (always a positive int).

49 """

50 # 1. Explicit override argument

51 if override:

52 return int(override)

53

54 # 2. LL_CONTEXT_LIMIT environment variable

55 env_val = os.environ.get("LL_CONTEXT_LIMIT", "")

56 if env_val:

57 try:

58 parsed = int(env_val)

59 if parsed > 0:

60 return parsed

61 except ValueError:

62 pass

63

64 if model is None:

65 return _DEFAULT_CONTEXT_WINDOW

66

67 # 3. [1m] suffix → 1M context

68 if model.endswith("[1m]"):

69 return _1M_CONTEXT_WINDOW

70

71 # 4. Exact model-id lookup

72 window = MODEL_CONTEXT_WINDOW.get(model)

73 if window is not None:

74 return window

75

76 # 5. Conservative floor

77 return _DEFAULT_CONTEXT_WINDOW