FEATURE: new site setting explorer persona (#178)

Also adds ai_bot_enabled_personas so admins can tweak which stock
personas are enabled.

The new persona has a full listing of all site settings and is
able to get context for each setting.

This means you can ask it to search through settings for something
relevant.

Security wise there is no access to actual configuration of settings
just to the names / description and implementation.

Previously this was part of the forum helper persona however it
just clashes too much with other behaviors, isolating it makes
it far more powerful.

* sneaking this one in, user_emails is a non obvious table in our
structure.

usually one would assume users has emails so the clarifies a bit
better. plus it is a very common table to hit.
This commit is contained in:
Sam 2023-08-31 17:02:03 +10:00 committed by GitHub
parent 8e4347acba
commit 00d69b463e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 123 additions and 33 deletions

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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