Module: LittleGhost::Support::OutputTruncation

Defined in:
lib/little_ghost/support/output_truncation.rb

Overview

OutputTruncation keeps large tool results within a predictable context budget without breaking UTF-8. Its byte-to-token estimate is deliberately approximate; use a provider tokenizer when exact accounting is required.

Constant Summary collapse

APPROX_BYTES_PER_TOKEN =

Byte estimate used when no provider tokenizer is available.

4

Class Method Summary collapse

Class Method Details

.approx_bytes_for_tokens(tokens) ⇒ Object

Converts a token budget to its approximate byte budget.



20
21
22
# File 'lib/little_ghost/support/output_truncation.rb', line 20

def approx_bytes_for_tokens(tokens)
  Integer(tokens) * APPROX_BYTES_PER_TOKEN
end

.approx_token_count(text) ⇒ Object

Estimates tokens from the UTF-8 byte length of text.



15
16
17
# File 'lib/little_ghost/support/output_truncation.rb', line 15

def approx_token_count(text)
  approx_tokens_from_byte_count(String(text).bytesize)
end

.approx_tokens_from_byte_count(bytes) ⇒ Object

Converts bytes to an approximate token count, rounded up.



25
26
27
# File 'lib/little_ghost/support/output_truncation.rb', line 25

def approx_tokens_from_byte_count(bytes)
  (Integer(bytes) + APPROX_BYTES_PER_TOKEN - 1) / APPROX_BYTES_PER_TOKEN
end

.truncate_middle_with_token_budget(text, max_tokens, framework_prompts: nil, invocation_paths: [], agent_path: nil) ⇒ Object

Keeps text within budget or produces a middle-truncated UTF-8 string and the original approximate token count.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/little_ghost/support/output_truncation.rb', line 31

def truncate_middle_with_token_budget(
  text,
  max_tokens,
  framework_prompts: nil,
  invocation_paths: [],
  agent_path: nil
)
  content = utf8_content(text)
  max_tokens = Integer(max_tokens)
  max_bytes = approx_bytes_for_tokens(max_tokens)
  return [content, nil] if max_tokens.positive? && content.bytesize <= max_bytes
  framework_prompts ||= LittleGhost::FrameworkPrompts.new

  marker = ""
  available_bytes = max_bytes
  3.times do
    removed_tokens = approx_tokens_from_byte_count([content.bytesize - available_bytes, 0].max)
    marker = framework_prompts.render(
      "output/truncation/marker",
      locals: {removed_tokens:},
      invocation_paths:,
      agent_path:
    )
    available_bytes = [max_bytes - marker.bytesize, 0].max
  end
  if marker.bytesize >= max_bytes
    marker = split_string(marker, [max_bytes, 0].max, 0).first
    return [marker, approx_token_count(content)]
  end

  prefix_bytes = available_bytes / 2
  prefix, suffix = split_string(content, prefix_bytes, available_bytes - prefix_bytes)
  truncated = "#{prefix}#{marker}#{suffix}"
  [truncated, approx_token_count(content)]
end