diff --git a/app/models/ai_persona.rb b/app/models/ai_persona.rb index a1ed5f88..c2e97313 100644 --- a/app/models/ai_persona.rb +++ b/app/models/ai_persona.rb @@ -72,6 +72,24 @@ class AiPersona < ActiveRecord::Base end end + def self.message_responder_for(group_id: nil) + return nil if !group_id + + all_responders = + persona_cache[:message_responders] ||= AiPersona + .where(role: "message_responder") + .where(enabled: true) + .pluck(:id, :role_group_ids) + + id, _ = all_responders.find { |id, role_group_ids| role_group_ids.include?(group_id) } + + if id + { id: id } + else + nil + end + end + def self.mentionables(user: nil) all_mentionables = persona_cache[:mentionable_usernames] ||= AiPersona @@ -114,6 +132,7 @@ class AiPersona < ActiveRecord::Base vision_max_pixels = self.vision_max_pixels rag_conversation_chunks = self.rag_conversation_chunks question_consolidator_llm = self.question_consolidator_llm + role_whispers = self.role_whispers persona_class = DiscourseAi::AiBot::Personas::Persona.system_personas_by_id[self.id] if persona_class @@ -161,6 +180,10 @@ class AiPersona < ActiveRecord::Base rag_conversation_chunks end + persona_class.define_singleton_method :role_whispers do + role_whispers + end + return persona_class end @@ -252,6 +275,10 @@ class AiPersona < ActiveRecord::Base question_consolidator_llm end + define_singleton_method :role_whispers do + role_whispers + end + define_singleton_method :to_s do "#" end @@ -343,8 +370,8 @@ class AiPersona < ActiveRecord::Base private def system_persona_unchangeable - if top_p_changed? || temperature_changed? || system_prompt_changed? || commands_changed? || - name_changed? || description_changed? + if role_changed? || top_p_changed? || temperature_changed? || system_prompt_changed? || + commands_changed? || name_changed? || description_changed? errors.add(:base, I18n.t("discourse_ai.ai_bot.personas.cannot_edit_system_persona")) end end @@ -387,6 +414,12 @@ end # rag_chunk_overlap_tokens :integer default(10), not null # rag_conversation_chunks :integer default(10), not null # question_consolidator_llm :text +# role :enum default("bot"), not null +# role_category_ids :integer default([]), not null, is an Array +# role_tags :string default([]), not null, is an Array +# role_group_ids :integer default([]), not null, is an Array +# role_whispers :boolean default(FALSE), not null +# role_max_responses_per_hour :integer default(50), not null # # Indexes # diff --git a/db/migrate/20240422054321_add_role_to_ai_persona.rb b/db/migrate/20240422054321_add_role_to_ai_persona.rb new file mode 100644 index 00000000..431370ca --- /dev/null +++ b/db/migrate/20240422054321_add_role_to_ai_persona.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class AddRoleToAiPersona < ActiveRecord::Migration[7.0] + def change + create_enum :ai_persona_role, %w[bot topic_responder message_responder summarizer] + add_column :ai_personas, :role, :enum, default: "bot", null: false, enum_type: :ai_persona_role + + add_column :ai_personas, :role_category_ids, :integer, array: true, default: [], null: false + add_column :ai_personas, :role_tags, :string, array: true, default: [], null: false + add_column :ai_personas, :role_group_ids, :integer, array: true, default: [], null: false + add_column :ai_personas, :role_whispers, :boolean, default: false, null: false + add_column :ai_personas, :role_max_responses_per_hour, :integer, default: 50, null: false + end +end diff --git a/spec/models/ai_persona_spec.rb b/spec/models/ai_persona_spec.rb index 00f86f37..07374a2d 100644 --- a/spec/models/ai_persona_spec.rb +++ b/spec/models/ai_persona_spec.rb @@ -128,4 +128,40 @@ RSpec.describe AiPersona do RailsMultisite::ConnectionManagement.stubs(:current_db) { "abc" } expect(AiPersona.persona_cache[:value]).to eq(nil) end + + it "does not allow role change for system personas" do + persona = AiPersona.find_by(name: "Forum Helper") + expect(persona.update(role: "message_responder")).to eq(false) + end + + describe "#message_responder_for" do + fab!(:group) + fab!(:responder) do + AiPersona.create!( + name: "responder", + description: "responser", + system_prompt: "responder prompt", + role: "message_responder", + role_group_ids: [group.id], + ) + end + + it "properly sets role_whispers on the persona" do + responder.update!(role_whispers: true) + + expect(responder.class_instance.role_whispers).to eq(true) + end + + it "returns can respond to group pms with a custom persona" do + expect(AiPersona.message_responder_for(group_id: group.id)[:id]).to eq(responder.id) + + responder.update!(enabled: false) + + expect(AiPersona.message_responder_for(group_id: group.id)).to eq(nil) + + responder.update!(enabled: true, role: "bot") + + expect(AiPersona.message_responder_for(group_id: group.id)).to eq(nil) + end + end end