Class: Aireview::ContextBuilder
- Inherits:
-
Object
- Object
- Aireview::ContextBuilder
- Defined in:
- lib/aireview/context_builder.rb
Defined Under Namespace
Classes: Context
Constant Summary collapse
- GENERATE_PROMPT_TEMPLATE =
File.read(File.('prompts/generate.txt', __dir__)).strip.freeze
- CRITIQUE_PROMPT_TEMPLATE =
File.read(File.('prompts/critique.txt', __dir__)).strip.freeze
- LANGUAGE_NAMES =
{ 'ru' => 'Russian', 'en' => 'English' }.freeze
- CHANGES_HEADER =
"Changes:\n"- CANDIDATES_HEADER =
"\n\nCandidates JSON from Generate:\n"- CANDIDATES_RESERVE_CHARS =
Room for the candidates in the Critique prompt: three candidates of ~1,500 characters. An estimate, not a guarantee; the actual size is checked before sending.
4_500
Instance Method Summary collapse
- #build_critique_prompt(context, candidates_json:) ⇒ Object
- #build_generate_prompt(context) ⇒ Object
-
#check_stage_size!(stage, system, user) ⇒ Object
A check before sending: when the candidates exceed the reserve and the request does not fit, that is an error, not a reason to silently cut the context Generate has already seen.
-
#initialize(config:, logger: Logger.new($stderr)) ⇒ ContextBuilder
constructor
A new instance of ContextBuilder.
- #prepare(merge_request:, changes:, jira_issue: nil, critique: true) ⇒ Object
- #scrub_text(text) ⇒ Object
- #system_prompt(stage) ⇒ Object
Constructor Details
#initialize(config:, logger: Logger.new($stderr)) ⇒ ContextBuilder
Returns a new instance of ContextBuilder.
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/aireview/context_builder.rb', line 29 def initialize(config:, logger: Logger.new($stderr)) @config = config @logger = logger @secret_scrubber = SecretScrubber.new( secret_patterns: config.secret_patterns, secret_files: config.secret_files, logger: logger ) @diff_fetcher = DiffFetcher.new(ignore_paths: [], logger: logger) end |
Instance Method Details
#build_critique_prompt(context, candidates_json:) ⇒ Object
61 62 63 64 |
# File 'lib/aireview/context_builder.rb', line 61 def build_critique_prompt(context, candidates_json:) user = "#{context.user_prompt}#{CANDIDATES_HEADER}#{scrub_text(candidates_json)}" check_stage_size!('critique', system_prompt('critique'), user) end |
#build_generate_prompt(context) ⇒ Object
57 58 59 |
# File 'lib/aireview/context_builder.rb', line 57 def build_generate_prompt(context) check_stage_size!('generate', system_prompt('generate'), context.user_prompt) end |
#check_stage_size!(stage, system, user) ⇒ Object
A check before sending: when the candidates exceed the reserve and the request does not fit, that is an error, not a reason to silently cut the context Generate has already seen.
80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/aireview/context_builder.rb', line 80 def check_stage_size!(stage, system, user) stage = stage.to_s limit = @config.max_prompt_chars(stage) total = system.length + user.length if total > limit raise ContextBudgetError, "#{stage.capitalize} request is #{total} chars, over llm.#{stage}.max_prompt_chars=#{limit} " \ "(system prompt #{system.length}, context #{user.length})" end {system_prompt: system, user_prompt: user} end |
#prepare(merge_request:, changes:, jira_issue: nil, critique: true) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/aireview/context_builder.rb', line 40 def prepare(merge_request:, changes:, jira_issue: nil, critique: true) coverage = ContextBudget::Coverage.empty budget = context_budget(critique: critique) sections = merge_request_sections(merge_request, coverage: coverage) sections << jira_section(jira_issue, coverage: coverage) if jira_issue fixed = "#{sections.join("\n\n")}\n\n#{CHANGES_HEADER}" diff_budget = [budget - fixed.length, @config.max_diff_chars].min entries = @diff_fetcher.entries(@secret_scrubber.scrub_changes(changes)) packed = ContextBudget.pack_entries(entries, budget: diff_budget, coverage: coverage) sizes = context_sizes(fixed: fixed, packed: packed, budget: budget, diff_budget: diff_budget, critique: critique) log_sizes(sizes) Context.new(user_prompt: fixed + packed.text, sections: sections.join("\n\n"), diff_text: packed.text, coverage: coverage, sizes: sizes) end |
#scrub_text(text) ⇒ Object
93 94 95 |
# File 'lib/aireview/context_builder.rb', line 93 def scrub_text(text) @secret_scrubber.scrub_text(text.to_s) end |
#system_prompt(stage) ⇒ Object
66 67 68 69 70 71 72 73 74 75 |
# File 'lib/aireview/context_builder.rb', line 66 def system_prompt(stage) template = stage.to_s == 'critique' ? CRITIQUE_PROMPT_TEMPLATE : GENERATE_PROMPT_TEMPLATE extras = [] if Aireview::Utils.present?(@config.review_instructions) extras << "Additional project instructions:\n#{scrub_text(@config.review_instructions.strip)}" end extras << "Response language: #{language_name(@config.review_language)}." [template, *extras].join("\n\n") end |