diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index e109ba2e..b70b072c 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -63,8 +63,9 @@ en: ai_bot_enable_chat_warning: "Display a warning when PM chat is initiated. Can be overriden by editing the translation string: discourse_ai.ai_bot.pm_warning" ai_bot_allowed_groups: "When the GPT Bot has access to the PM, it will reply to members of these groups." ai_bot_enabled_chat_bots: "Available models to act as an AI Bot" - ai_bot_enabled_chat_commands: "Available GPT integrations used to provide external functionality to the model." + ai_bot_enabled_chat_commands: "Available GPT integrations used to provide external functionality to the Forum Helper bot, keep in mind that certain commands may only be available if appropriate API keys are added." ai_bot_add_to_header: "Display a button in the header to start a PM with a AI Bot" + ai_bot_enabled_personas: "List of personas available for the AI Bot" ai_stability_api_key: "API key for the stability.ai API" ai_stability_engine: "Image generation engine to use for the stability.ai API" @@ -103,6 +104,9 @@ en: sql_helper: name: SQL Helper description: "AI Bot specialized in helping craft SQL queries on this Discourse instance" + settings_explorer: + name: Settings Explorer + description: "AI Bot specialized in helping explore Discourse site settings" default_pm_prefix: "[Untitled AI bot PM]" topic_not_found: "Summary unavailable, topic not found!" command_summary: diff --git a/config/settings.yml b/config/settings.yml index 0f15ea6c..1a0c876f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -216,7 +216,14 @@ discourse_ai: - read - tags - time - - setting_context + ai_bot_enabled_personas: + type: list + default: "general|artist|sql_helper|settings_explorer" + choices: + - general + - artist + - sql_helper + - settings_explorer ai_bot_add_to_header: default: true client: true diff --git a/lib/modules/ai_bot/entry_point.rb b/lib/modules/ai_bot/entry_point.rb index e0ce6f0b..da31adb4 100644 --- a/lib/modules/ai_bot/entry_point.rb +++ b/lib/modules/ai_bot/entry_point.rb @@ -44,6 +44,7 @@ module DiscourseAi require_relative "personas/artist" require_relative "personas/general" require_relative "personas/sql_helper" + require_relative "personas/settings_explorer" end def inject_into(plugin) diff --git a/lib/modules/ai_bot/personas/persona.rb b/lib/modules/ai_bot/personas/persona.rb index 693159d3..37cd6b9c 100644 --- a/lib/modules/ai_bot/personas/persona.rb +++ b/lib/modules/ai_bot/personas/persona.rb @@ -6,7 +6,10 @@ module DiscourseAi def self.all personas = [Personas::General, Personas::SqlHelper] personas << Personas::Artist if SiteSetting.ai_stability_api_key.present? - personas + personas << Personas::SettingsExplorer + + personas_allowed = SiteSetting.ai_bot_enabled_personas.split("|") + personas.filter { |persona| personas_allowed.include?(persona.to_s.demodulize.underscore) } end class Persona @@ -91,7 +94,6 @@ module DiscourseAi Commands::SearchCommand, Commands::SummarizeCommand, Commands::ReadCommand, - Commands::SettingContextCommand, ] all_commands << Commands::TagsCommand if SiteSetting.tagging_enabled diff --git a/lib/modules/ai_bot/personas/settings_explorer.rb b/lib/modules/ai_bot/personas/settings_explorer.rb new file mode 100644 index 00000000..5dff3b57 --- /dev/null +++ b/lib/modules/ai_bot/personas/settings_explorer.rb @@ -0,0 +1,37 @@ +#frozen_string_literal: true + +module DiscourseAi + module AiBot + module Personas + class SettingsExplorer < Persona + def commands + all_available_commands + end + + def all_available_commands + [DiscourseAi::AiBot::Commands::SettingContextCommand] + end + + def system_prompt + <<~PROMPT + You are Discourse Site settings bot. + + - You know the full list of all the site settings. + - You are able to request context for a specific setting. + - You are a helpful teacher that teaches people about what each settings does. + + Current time is: {time} + + Full list of all the site settings: + {{ + #{SiteSetting.all_settings.map { |setting| setting[:setting].to_s }.join("\n")} + }} + + {commands} + + PROMPT + end + end + end + end +end diff --git a/lib/modules/ai_bot/personas/sql_helper.rb b/lib/modules/ai_bot/personas/sql_helper.rb index 321a4e2d..3900afa2 100644 --- a/lib/modules/ai_bot/personas/sql_helper.rb +++ b/lib/modules/ai_bot/personas/sql_helper.rb @@ -8,7 +8,7 @@ module DiscourseAi return @schema if defined?(@schema) tables = Hash.new - priority_tables = %w[posts topics notifications users user_actions] + priority_tables = %w[posts topics notifications users user_actions user_emails] DB.query(<<~SQL).each { |row| (tables[row.table_name] ||= []) << row.column_name } select table_name, column_name from information_schema.columns diff --git a/spec/lib/modules/ai_bot/personas/persona_spec.rb b/spec/lib/modules/ai_bot/personas/persona_spec.rb index 0bd8fa8e..376184e4 100644 --- a/spec/lib/modules/ai_bot/personas/persona_spec.rb +++ b/spec/lib/modules/ai_bot/personas/persona_spec.rb @@ -22,40 +22,62 @@ class TestPersona < DiscourseAi::AiBot::Personas::Persona end end -RSpec.describe DiscourseAi::AiBot::Personas::Persona do - let :persona do - TestPersona.new - end +module DiscourseAi::AiBot::Personas + RSpec.describe Persona do + let :persona do + TestPersona.new + end - let :topic_with_users do - topic = Topic.new - topic.allowed_users = [User.new(username: "joe"), User.new(username: "jane")] - topic - end + let :topic_with_users do + topic = Topic.new + topic.allowed_users = [User.new(username: "joe"), User.new(username: "jane")] + topic + end - it "renders the system prompt" do - freeze_time + it "renders the system prompt" do + freeze_time - SiteSetting.title = "test site title" - SiteSetting.site_description = "test site description" + SiteSetting.title = "test site title" + SiteSetting.site_description = "test site description" - rendered = - persona.render_system_prompt(topic: topic_with_users, render_function_instructions: true) + rendered = + persona.render_system_prompt(topic: topic_with_users, render_function_instructions: true) - expect(rendered).to include(Discourse.base_url) - expect(rendered).to include("test site title") - expect(rendered).to include("test site description") - expect(rendered).to include("joe, jane") - expect(rendered).to include(Time.zone.now.to_s) - expect(rendered).to include("!search") - expect(rendered).to include("!tags") - # needs to be configured so it is not available - expect(rendered).not_to include("!image") + expect(rendered).to include(Discourse.base_url) + expect(rendered).to include("test site title") + expect(rendered).to include("test site description") + expect(rendered).to include("joe, jane") + expect(rendered).to include(Time.zone.now.to_s) + expect(rendered).to include("!search") + expect(rendered).to include("!tags") + # needs to be configured so it is not available + expect(rendered).not_to include("!image") - rendered = - persona.render_system_prompt(topic: topic_with_users, render_function_instructions: false) + rendered = + persona.render_system_prompt(topic: topic_with_users, render_function_instructions: false) - expect(rendered).not_to include("!search") - expect(rendered).not_to include("!tags") + expect(rendered).not_to include("!search") + expect(rendered).not_to include("!tags") + end + + describe "available personas" do + it "includes all personas by default" do + # must be enabled to see it + SiteSetting.ai_stability_api_key = "abc" + + expect(DiscourseAi::AiBot::Personas.all).to contain_exactly( + General, + SqlHelper, + Artist, + SettingsExplorer, + ) + end + + it "can be modified via site settings" do + SiteSetting.ai_bot_enabled_personas = "general|sql_helper" + + expect(DiscourseAi::AiBot::Personas.all).to contain_exactly(General, SqlHelper) + end + end end end diff --git a/spec/lib/modules/ai_bot/personas/settings_explorer_spec.rb b/spec/lib/modules/ai_bot/personas/settings_explorer_spec.rb new file mode 100644 index 00000000..68a7beae --- /dev/null +++ b/spec/lib/modules/ai_bot/personas/settings_explorer_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +RSpec.describe DiscourseAi::AiBot::Personas::SettingsExplorer do + let :settings_explorer do + subject + end + + it "renders schema" do + prompt = settings_explorer.render_system_prompt + # check we render settings + expect(prompt).to include("ai_bot_enabled_personas") + + expect(settings_explorer.available_commands).to eq( + [DiscourseAi::AiBot::Commands::SettingContextCommand], + ) + end +end