Module: RCrewAI::ContextWindow
- Defined in:
- lib/rcrewai/context_window.rb
Overview
Keeps a conversation within a model's context window by dropping the oldest non-system messages when it would overflow. Token counts use a cheap chars/4 heuristic (no tokenizer dependency); the goal is to avoid hard context-length errors, not exact accounting.
Constant Summary collapse
- CHARS_PER_TOKEN =
4- DEFAULT_WINDOW =
8_192- WINDOWS =
Approximate context window sizes (in tokens) by model.
{ 'gpt-4o' => 128_000, 'gpt-4o-mini' => 128_000, 'gpt-4-turbo' => 128_000, 'gpt-4' => 8_192, 'gpt-3.5-turbo' => 16_385, 'claude-opus-4-7' => 200_000, 'claude-sonnet-4-6' => 200_000, 'claude-haiku-4-5' => 200_000, 'claude-3-5-sonnet-20241022' => 200_000, 'claude-3-haiku-20240307' => 200_000, 'gemini-1.5-pro' => 1_000_000, 'gemini-1.5-flash' => 1_000_000 }.freeze
Class Method Summary collapse
- .estimate_tokens(input) ⇒ Object
-
.fit(messages, limit:, reserve: 0) ⇒ Object
Returns a copy of
messagestrimmed to fit within (limit - reserve) tokens. -
.fits?(system, middle, last, budget) ⇒ Boolean
-- helpers --------------------------------------------------------------.
- .rebuild(original, system, middle, last) ⇒ Object
- .window_for(model) ⇒ Object
Class Method Details
.estimate_tokens(input) ⇒ Object
30 31 32 33 |
# File 'lib/rcrewai/context_window.rb', line 30 def estimate_tokens(input) text = input.is_a?(Array) ? input.map { |m| m[:content].to_s }.join : input.to_s (text.length / CHARS_PER_TOKEN.to_f).ceil end |
.fit(messages, limit:, reserve: 0) ⇒ Object
Returns a copy of messages trimmed to fit within (limit - reserve)
tokens. System messages are always kept, as is the final message. The
oldest non-system, non-final messages are dropped first.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/rcrewai/context_window.rb', line 42 def fit(, limit:, reserve: 0) budget = limit - reserve return if estimate_tokens() <= budget system = .select { |m| m[:role] == 'system' } last = .last # Candidates for dropping: everything that isn't a system message or the # final message, oldest first. middle = .reject { |m| m[:role] == 'system' || m.equal?(last) } kept_middle = middle.dup until fits?(system, kept_middle, last, budget) || kept_middle.empty? kept_middle.shift # drop the oldest end rebuild(, system, kept_middle, last) end |
.fits?(system, middle, last, budget) ⇒ Boolean
-- helpers --------------------------------------------------------------
62 63 64 65 66 |
# File 'lib/rcrewai/context_window.rb', line 62 def fits?(system, middle, last, budget) parts = system + middle parts << last unless system.include?(last) || middle.include?(last) estimate_tokens(parts) <= budget end |
.rebuild(original, system, middle, last) ⇒ Object
68 69 70 71 72 73 |
# File 'lib/rcrewai/context_window.rb', line 68 def rebuild(original, system, middle, last) keep = (system + middle) keep << last unless keep.include?(last) # Preserve original ordering. original.select { |m| keep.include?(m) } end |
.window_for(model) ⇒ Object
35 36 37 |
# File 'lib/rcrewai/context_window.rb', line 35 def window_for(model) WINDOWS[model] || DEFAULT_WINDOW end |