Class: Phronomy::LlmContextWindow::Assembler
- Inherits:
-
Object
- Object
- Phronomy::LlmContextWindow::Assembler
- Defined in:
- lib/phronomy/llm_context_window/assembler.rb
Overview
Assembler collects all four context regions and produces the final messages:, tool_classes: hash consumed by Agent::Base.
Regions:
- Instruction — system prompt text set via #add_instruction
- Capability — tool classes registered via #add_capability
- Knowledge — external facts injected via #add_knowledge (generates XML tags)
- Conversation — historical messages added via #add_messages
Token budgeting: When a budget is given, conversation messages are trimmed from oldest to newest until they fit. Capability token cost is estimated and deducted from the budget before conversation trimming so the reserve is accurate. Knowledge chunks are always included in full (they are assumed to be pre-screened by the caller). When no budget is given all messages are passed through unchanged.
Class Method Summary collapse
-
.xml_tag(text, type:, trusted: false) ⇒ String
private
Builds a single XML context tag string.
Instance Method Summary collapse
-
#add_capability(tool_classes) ⇒ self
private
Register tool classes (Region 2).
-
#add_instruction(text) ⇒ self
private
Set the system instruction text (Region 1).
-
#add_knowledge(text, type:, trusted: false, source: nil) ⇒ self
private
Append a knowledge chunk (Region 3).
-
#add_messages(messages) ⇒ self
private
Set conversation messages (Region 4).
-
#available_for_messages ⇒ Integer?
private
Returns the number of tokens available for conversation messages after accounting for instruction, knowledge, and capability overhead.
-
#build ⇒ Hash{Symbol => Object}
private
Assemble the context.
-
#initialize(budget: nil) ⇒ Assembler
constructor
private
mutant:disable - @instruction = nil deletion is a genuine equivalent (uninitialized Ruby instance variables return nil).
Constructor Details
#initialize(budget: nil) ⇒ Assembler
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
mutant:disable - @instruction = nil deletion is a genuine equivalent (uninitialized Ruby instance variables return nil)
50 51 52 53 54 55 56 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 50 def initialize(budget: nil) @budget = budget @instruction = nil @tool_classes = [] @knowledge_chunks = [] @messages = [] end |
Class Method Details
.xml_tag(text, type:, trusted: false) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Builds a single XML context tag string. Exposed as a class method so callers (e.g. Agent::Base) can build static knowledge XML tags independently of an Assembler instance.
mutant:disable - text.to_str and plain text (no to_s) are genuine equivalents when text is a String; type.to_str is genuine equivalent when type is a String
42 43 44 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 42 def self.xml_tag(text, type:, trusted: false) "<context type=\"#{CGI.escapeHTML(type.to_s)}\" trusted=\"#{trusted}\">\n#{CGI.escapeHTML(text.to_s)}\n</context>" end |
Instance Method Details
#add_capability(tool_classes) ⇒ self
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Register tool classes (Region 2). Estimates their token cost and deducts it from the budget so that conversation trimming accounts for tool definition overhead.
65 66 67 68 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 65 def add_capability(tool_classes) @tool_classes = Array(tool_classes) self end |
#add_instruction(text) ⇒ self
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Set the system instruction text (Region 1). Calling this multiple times replaces the previous value.
mutant:disable - text.to_str and plain text (no .to_s) are genuine equivalents when callers always pass a String
77 78 79 80 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 77 def add_instruction(text) @instruction = text.to_s self end |
#add_knowledge(text, type:, trusted: false, source: nil) ⇒ self
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Append a knowledge chunk (Region 3). The chunk is wrapped in an XML context tag automatically.
mutant:disable - text: (shorthand, no .to_s) and text.to_str are genuine equivalents when text is a String; type: shorthand is genuine equivalent because xml_context_tag always calls .to_s on chunk[:type]
93 94 95 96 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 93 def add_knowledge(text, type:, trusted: false, source: nil) @knowledge_chunks << {text: text.to_s, type: type.to_s, trusted: trusted, source: source} self end |
#add_messages(messages) ⇒ self
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Set conversation messages (Region 4). Replaces any previously set messages.
mutant:disable - @messages = messages (no Array()) is a genuine equivalent when callers always pass an Array
104 105 106 107 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 104 def () @messages = Array() self end |
#available_for_messages ⇒ Integer?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the number of tokens available for conversation messages after accounting for instruction, knowledge, and capability overhead. Returns +nil+ when no budget is configured.
115 116 117 118 119 120 121 122 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 115 def return nil unless @budget knowledge_text = @knowledge_chunks.map { |c| xml_context_tag(c) }.join("\n\n") system_parts = [@instruction, knowledge_text.empty? ? nil : knowledge_text].compact system_text = system_parts.join("\n\n") used = TokenEstimator.estimate(system_text) + estimate_capability_tokens @budget.available(used: used) end |
#build ⇒ Hash{Symbol => Object}
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Assemble the context.
Raises ContextLengthError when a budget is set and the conversation messages do not fit within the remaining token allowance. No automatic trimming is performed — callers must pre-process messages (e.g. via Agent::Base#trim_messages or #compact_messages) before passing them to the Assembler.
mutant:disable - multiple genuine equivalent mutations: map{}.join("\n\n") → map{} is genuine; unless knowledge_text.empty? vs ternary is genuine; { system: unless system_text.empty? } vs ternary is genuine; messages: shorthand vs messages: messages is genuine
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/phronomy/llm_context_window/assembler.rb', line 138 def build knowledge_text = @knowledge_chunks.map { |c| xml_context_tag(c) }.join("\n\n") system_parts = [@instruction, knowledge_text.empty? ? nil : knowledge_text].compact system_text = system_parts.join("\n\n") if @budget && @messages.any? capability_tokens = estimate_capability_tokens used = TokenEstimator.estimate(system_text) + capability_tokens remaining = @budget.available(used: used) msg_tokens = @messages.sum { |m| TokenEstimator.estimate(m.content.to_s) } if msg_tokens > remaining raise Phronomy::ContextLengthError, "Context exceeds token budget: messages require #{msg_tokens} tokens but " \ "only #{remaining} available (context_window=#{@budget.context_window}, " \ "used_by_system=#{used}). Override build_context to trim or compact messages." end end { system: system_text.empty? ? nil : system_text, messages: @messages, tool_classes: @tool_classes } end |