never executed always true always false
1 module PureClaw.Agent.ContextTracker
2 ( -- * Context status snapshot
3 ContextStatus (..)
4 , contextStatus
5 -- * Model context windows
6 , contextWindowForModel
7 , defaultContextWindow
8 -- * Utilization queries
9 , isContextHigh
10 , highUtilizationThreshold
11 -- * Formatting
12 , formatContextStatus
13 ) where
14
15 import Data.Text (Text)
16 import Data.Text qualified as T
17
18 import PureClaw.Agent.Context
19 import PureClaw.Core.Types
20
21 -- | Snapshot of current context tracking state. Produced from a
22 -- 'Context' and a 'ModelId' — purely computed, no IO.
23 data ContextStatus = ContextStatus
24 { _cs_estimatedTokens :: !Int
25 -- ^ Heuristic estimate of tokens in the current context window.
26 , _cs_contextWindow :: !Int
27 -- ^ Maximum context window size for the active model.
28 , _cs_messageCount :: !Int
29 -- ^ Number of messages in the conversation.
30 , _cs_utilizationPct :: !Double
31 -- ^ Estimated tokens / context window, as a fraction (0.0–1.0).
32 , _cs_totalInputTokens :: !Int
33 -- ^ Cumulative input tokens reported by the provider.
34 , _cs_totalOutputTokens :: !Int
35 -- ^ Cumulative output tokens reported by the provider.
36 }
37 deriving stock (Show, Eq)
38
39 -- | Compute a context status snapshot from the current model and context.
40 contextStatus :: ModelId -> Context -> ContextStatus
41 contextStatus model ctx =
42 let window = contextWindowForModel model
43 estimated = contextTokenEstimate ctx
44 pct = if window > 0
45 then fromIntegral estimated / fromIntegral window
46 else 0.0
47 in ContextStatus
48 { _cs_estimatedTokens = estimated
49 , _cs_contextWindow = window
50 , _cs_messageCount = contextMessageCount ctx
51 , _cs_utilizationPct = pct
52 , _cs_totalInputTokens = contextTotalInputTokens ctx
53 , _cs_totalOutputTokens = contextTotalOutputTokens ctx
54 }
55
56 -- | Look up the context window size for a model. Uses prefix matching
57 -- against known model families, falling back to 'defaultContextWindow'.
58 contextWindowForModel :: ModelId -> Int
59 contextWindowForModel (ModelId mid) = go knownModels
60 where
61 go [] = defaultContextWindow
62 go ((prefix, window):rest)
63 | prefix `T.isPrefixOf` mid = window
64 | otherwise = go rest
65
66 -- | Default context window for unknown models (128k — conservative).
67 defaultContextWindow :: Int
68 defaultContextWindow = 128000
69
70 -- | Known model families and their context window sizes.
71 knownModels :: [(Text, Int)]
72 knownModels =
73 -- Anthropic Claude
74 [ ("claude-opus-4", 200000)
75 , ("claude-sonnet-4", 200000)
76 , ("claude-3-7-sonnet", 200000)
77 , ("claude-3-5-sonnet", 200000)
78 , ("claude-3-5-haiku", 200000)
79 , ("claude-3-opus", 200000)
80 , ("claude-3-sonnet", 200000)
81 , ("claude-3-haiku", 200000)
82 -- OpenAI
83 , ("gpt-4o", 128000)
84 , ("gpt-4-turbo", 128000)
85 , ("gpt-4-0125", 128000)
86 , ("gpt-4-1106", 128000)
87 , ("gpt-4", 8192)
88 , ("gpt-3.5-turbo", 16385)
89 , ("o1", 200000)
90 , ("o3", 200000)
91 -- Ollama common defaults
92 , ("llama3", 8192)
93 , ("mistral", 32768)
94 , ("mixtral", 32768)
95 , ("deepseek", 128000)
96 ]
97
98 -- | Threshold above which context utilization is considered high (80%).
99 highUtilizationThreshold :: Double
100 highUtilizationThreshold = 0.80
101
102 -- | Check whether context utilization exceeds the high threshold.
103 isContextHigh :: ModelId -> Context -> Bool
104 isContextHigh model ctx =
105 let status = contextStatus model ctx
106 in _cs_utilizationPct status >= highUtilizationThreshold
107
108 -- | Format a context status snapshot as human-readable text for display.
109 formatContextStatus :: ContextStatus -> Text
110 formatContextStatus status = T.intercalate "\n"
111 [ "Context window: " <> T.pack (show (_cs_estimatedTokens status))
112 <> " / " <> T.pack (show (_cs_contextWindow status))
113 <> " tokens (" <> T.pack (show (round (100 * _cs_utilizationPct status) :: Int)) <> "%)"
114 , "Messages: " <> T.pack (show (_cs_messageCount status))
115 , "Total usage: " <> T.pack (show (_cs_totalInputTokens status)) <> " in / "
116 <> T.pack (show (_cs_totalOutputTokens status)) <> " out"
117 ]