FIX: system persona state leaking between sites (#1304)

System personas leaned on reused classes, this was a problem
in a multisite environement cause state, such as "enabled"
ended up being reused between sites.

New implementation ensures state is pristine between sites in
a multisite

* more handling for new superclass story

* small oversight, display name should be used for display
This commit is contained in:
Sam 2025-05-01 13:24:53 +10:00 committed by GitHub
parent 8b1b6811f4
commit 491dac298f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 45 additions and 13 deletions

View File

@ -226,12 +226,19 @@ class AiPersona < ActiveRecord::Base
persona_class = DiscourseAi::Personas::Persona.system_personas_by_id[self.id]
if persona_class
return(
# we need a new copy so we don't leak information
# across sites
Class.new(persona_class) do
# required for localization
define_singleton_method(:to_s) { persona_class.to_s }
instance_attributes.each do |key, value|
# description/name are localized
persona_class.define_singleton_method(key) { value } if key != :description && key != :name
define_singleton_method(key) { value } if key != :description && key != :name
end
persona_class.define_method(:options) { options }
return persona_class
define_method(:options) { options }
end
)
end
ai_persona_id = self.id

View File

@ -459,7 +459,7 @@ module DiscourseAi
post_type: post_type,
skip_guardian: true,
custom_fields: {
DiscourseAi::AiBot::POST_AI_LLM_NAME_FIELD => bot.llm.llm_model.name,
DiscourseAi::AiBot::POST_AI_LLM_NAME_FIELD => bot.llm.llm_model.display_name,
},
)

View File

@ -128,7 +128,8 @@ module DiscourseAi
end
def id
@ai_persona&.id || self.class.system_personas[self.class]
@ai_persona&.id || self.class.system_personas[self.class.superclass] ||
self.class.system_personas[self.class]
end
def tools

View File

@ -785,7 +785,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do
expect(last_post.user_id).to eq(persona.user_id)
expect(last_post.custom_fields[DiscourseAi::AiBot::POST_AI_LLM_NAME_FIELD]).to eq(
gpt_35_turbo.name,
gpt_35_turbo.display_name,
)
end

View File

@ -212,7 +212,7 @@ RSpec.describe DiscourseAi::Personas::Persona do
SiteSetting.ai_google_custom_search_cx = "abc123"
# should be ordered by priority and then alpha
expect(DiscourseAi::Personas::Persona.all(user: user)).to eq(
expect(DiscourseAi::Personas::Persona.all(user: user).map(&:superclass)).to eq(
[
DiscourseAi::Personas::General,
DiscourseAi::Personas::Artist,
@ -226,7 +226,7 @@ RSpec.describe DiscourseAi::Personas::Persona do
)
# it should allow staff access to WebArtifactCreator
expect(DiscourseAi::Personas::Persona.all(user: admin)).to eq(
expect(DiscourseAi::Personas::Persona.all(user: admin).map(&:superclass)).to eq(
[
DiscourseAi::Personas::General,
DiscourseAi::Personas::Artist,
@ -245,7 +245,7 @@ RSpec.describe DiscourseAi::Personas::Persona do
SiteSetting.ai_google_custom_search_api_key = ""
SiteSetting.ai_artifact_security = "disabled"
expect(DiscourseAi::Personas::Persona.all(user: admin)).to contain_exactly(
expect(DiscourseAi::Personas::Persona.all(user: admin).map(&:superclass)).to contain_exactly(
DiscourseAi::Personas::General,
DiscourseAi::Personas::SqlHelper,
DiscourseAi::Personas::SettingsExplorer,
@ -258,7 +258,7 @@ RSpec.describe DiscourseAi::Personas::Persona do
DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::General],
).update!(enabled: false)
expect(DiscourseAi::Personas::Persona.all(user: user)).to contain_exactly(
expect(DiscourseAi::Personas::Persona.all(user: user).map(&:superclass)).to contain_exactly(
DiscourseAi::Personas::SqlHelper,
DiscourseAi::Personas::SettingsExplorer,
DiscourseAi::Personas::Creative,

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
RSpec.describe AiPersona, type: :multisite do
it "is able to amend settings on system personas on multisite" do
persona = AiPersona.find_by(name: "Designer")
expect(persona.allow_personal_messages).to eq(true)
persona.update!(allow_personal_messages: false)
instance = persona.class_instance
expect(instance.allow_personal_messages).to eq(false)
test_multisite_connection("second") do
persona = AiPersona.find_by(name: "Designer")
expect(persona.allow_personal_messages).to eq(true)
instance = persona.class_instance
expect(instance.name).to eq("Designer")
expect(instance.allow_personal_messages).to eq(true)
end
persona = AiPersona.find_by(name: "Designer")
instance = persona.class_instance
expect(instance.allow_personal_messages).to eq(false)
end
end