Class: Tryouts::CLI::TokenBudget

Inherits:
Object
  • Object
show all
Defined in:
lib/tryouts/cli/formatters/token_budget.rb

Overview

Token budget tracking for agent-optimized output

Constant Summary collapse

DEFAULT_LIMIT =
5000
BUFFER_PERCENT =

5% buffer to avoid going over

0.05

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(limit = DEFAULT_LIMIT) ⇒ TokenBudget

Returns a new instance of TokenBudget.



14
15
16
17
18
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 14

def initialize(limit = DEFAULT_LIMIT)
  @limit = limit
  @used = 0
  @buffer_size = (@limit * BUFFER_PERCENT).to_i
end

Instance Attribute Details

#limitObject (readonly)

Returns the value of attribute limit.



12
13
14
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 12

def limit
  @limit
end

#remainingObject (readonly)

Get remaining budget



48
49
50
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 48

def remaining
  @remaining
end

#usedObject (readonly)

Returns the value of attribute used.



12
13
14
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 12

def used
  @used
end

Instance Method Details

#allocate_budgetObject

Distribution strategy for budget allocation



140
141
142
143
144
145
146
147
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 140

def allocate_budget
  {
    summary: (@limit * 0.20).to_i,      # 20% for file summaries
    failures: (@limit * 0.60).to_i,     # 60% for failure details
    context: (@limit * 0.15).to_i,      # 15% for additional context
    buffer: (@limit * 0.05).to_i        # 5% buffer
  }
end

#consume(text) ⇒ Object

Add text to budget if within limits



34
35
36
37
38
39
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 34

def consume(text)
  return false if would_exceed?(text)

  @used += estimate_tokens(text)
  true
end

#estimate_tokens(text) ⇒ Object

Estimate tokens in text (rough approximation: 1 token ≈ 4 characters)



21
22
23
24
25
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 21

def estimate_tokens(text)
  return 0 if text.nil? || text.empty?

  (text.length / 4.0).ceil
end

#fit_text(text, preserve_suffix: nil) ⇒ Object

Try to fit text within remaining budget by truncating



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 63

def fit_text(text, preserve_suffix: nil)
  token_count = estimate_tokens(text)

  return text if token_count <= remaining
  return '' unless has_budget?

  # Calculate how many characters we can fit
  max_chars = remaining * 4

  if preserve_suffix
    suffix_chars = preserve_suffix.length
    return preserve_suffix if max_chars <= suffix_chars

    available_chars = max_chars - suffix_chars - 3  # 3 for "..."
    return "#{text[0, available_chars]}...#{preserve_suffix}"
  else
    return text[0, max_chars - 3] + '...' if max_chars > 3
    return ''
  end
end

#force_consume(text) ⇒ Object

Force consume (for critical information that must be included)



42
43
44
45
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 42

def force_consume(text)
  @used += estimate_tokens(text)
  true
end

#has_budget?Boolean

Check if we have budget remaining

Returns:

  • (Boolean)


53
54
55
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 53

def has_budget?
  remaining > 0
end

#resetObject

Reset budget



150
151
152
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 150

def reset
  @used = 0
end

#smart_truncate(value, max_tokens: nil) ⇒ Object

Smart truncate for different data types



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 85

def smart_truncate(value, max_tokens: nil)
  max_tokens ||= [remaining / 2, 50].min  # Use half remaining or 50, whichever is smaller
  max_chars = [max_tokens.to_i * 4, 0].max

  case value
  when String
    return value if value.length <= max_chars
    return '...' if max_chars <= 3
    "#{value[0, max_chars - 3]}..."
  when Array
    if estimate_tokens(value.inspect) <= max_tokens
      value.inspect
    else
      # Show first few elements
      truncated = []
      char_count = 2  # for "[]"

      value.each do |item|
        item_str = item.inspect
        if char_count + item_str.length + 2 <= max_chars - 10  # 10 chars for ", ..."
          truncated << item
          char_count += item_str.length + 2  # +2 for ", "
        else
          break
        end
      end

      "[#{truncated.map(&:inspect).join(', ')}, ...#{value.size - truncated.size} more]"
    end
  when Hash
    if estimate_tokens(value.inspect) <= max_tokens
      value.inspect
    else
      # Show first few key-value pairs
      truncated = {}
      char_count = 2  # for "{}"

      value.each do |key, val|
        pair_str = "#{key.inspect}=>#{val.inspect}"
        if char_count + pair_str.length + 2 <= max_chars - 10
          truncated[key] = val
          char_count += pair_str.length + 2
        else
          break
        end
      end

      "{#{truncated.map { |k, v| "#{k.inspect}=>#{v.inspect}" }.join(', ')}, ...#{value.size - truncated.size} more}"
    end
  else
    smart_truncate(value.to_s, max_tokens: max_tokens)
  end
end

#to_sObject



154
155
156
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 154

def to_s
  "TokenBudget[#{@used}/#{@limit} tokens (#{utilization}%)]"
end

#utilizationObject

Get utilization percentage



58
59
60
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 58

def utilization
  (@used.to_f / @limit * 100).round(1)
end

#would_exceed?(text) ⇒ Boolean

Check if text would exceed budget

Returns:

  • (Boolean)


28
29
30
31
# File 'lib/tryouts/cli/formatters/token_budget.rb', line 28

def would_exceed?(text)
  token_count = estimate_tokens(text)
  (@used + token_count) > (@limit - @buffer_size)
end