discourse-ai/lib/ai_bot/tools/setting_context.rb

161 lines
4.2 KiB
Ruby

#frozen_string_literal: true
module DiscourseAi
module AiBot
module Tools
class SettingContext < Tool
MAX_CONTEXT_TOKENS = 2000
CODE_FILE_EXTENSIONS = "rb,js,gjs,hbs"
class << self
def rg_installed?
if defined?(@rg_installed)
@rg_installed
else
@rg_installed =
begin
Discourse::Utils.execute_command("which", "rg")
true
rescue Discourse::Utils::CommandError
false
end
end
end
def signature
{
name: name,
description:
"Will provide you with full context regarding a particular site setting in Discourse",
parameters: [
{
name: "setting_name",
description: "The name of the site setting we need context for",
type: "string",
required: true,
},
],
}
end
def name
"setting_context"
end
end
def setting_name
parameters[:setting_name]
end
def invoke(_bot_user, llm)
if !self.class.rg_installed?
return(
{
setting_name: setting_name,
context:
"This command requires the rg command line tool to be installed on the server",
}
)
end
if !SiteSetting.has_setting?(setting_name)
{ setting_name: setting_name, context: "This setting does not exist" }
else
description = SiteSetting.description(setting_name)
result = +"# #{setting_name}\n#{description}\n\n"
setting_info =
find_setting_info(setting_name, [Rails.root.join("config", "site_settings.yml").to_s])
if !setting_info
setting_info =
find_setting_info(setting_name, Dir[Rails.root.join("plugins/**/settings.yml")])
end
result << setting_info
result << "\n\n"
%w[lib app plugins].each do |dir|
path = Rails.root.join(dir).to_s
result << Discourse::Utils.execute_command(
"rg",
setting_name,
path,
"-g",
"!**/spec/**",
"-g",
"!**/dist/**",
"-g",
"*.{#{CODE_FILE_EXTENSIONS}}",
"-C",
"10",
"--color",
"never",
"--heading",
"--no-ignore",
chdir: path,
success_status_codes: [0, 1],
)
end
result.gsub!(/^#{Regexp.escape(Rails.root.to_s)}/, "")
result = llm.tokenizer.truncate(result, MAX_CONTEXT_TOKENS)
{ setting_name: setting_name, context: result }
end
end
private
def find_setting_info(name, paths)
path, result = nil
paths.each do |search_path|
result =
Discourse::Utils.execute_command(
"rg",
name,
search_path,
"-g",
"*.{#{CODE_FILE_EXTENSIONS}}",
"-A",
"10",
"--color",
"never",
"--heading",
success_status_codes: [0, 1],
)
if !result.blank?
path = search_path
break
end
end
if result.blank?
nil
else
rows = result.split("\n")
leading_spaces = rows[0].match(/^\s*/)[0].length
filtered = []
rows.each do |row|
if !filtered.blank?
break if row.match(/^\s*/)[0].length <= leading_spaces
end
filtered << row
end
filtered.unshift("#{path}")
filtered.join("\n")
end
end
def description_args
parameters.slice(:setting_name)
end
end
end
end
end