diff --git a/app/controllers/discourse_ai/admin/ai_personas_controller.rb b/app/controllers/discourse_ai/admin/ai_personas_controller.rb index 120b7c32..1a994aa2 100644 --- a/app/controllers/discourse_ai/admin/ai_personas_controller.rb +++ b/app/controllers/discourse_ai/admin/ai_personas_controller.rb @@ -62,6 +62,8 @@ module DiscourseAi :enabled, :system_prompt, :priority, + :top_p, + :temperature, allowed_group_ids: [], ) diff --git a/app/models/ai_persona.rb b/app/models/ai_persona.rb index e49aff62..54e54020 100644 --- a/app/models/ai_persona.rb +++ b/app/models/ai_persona.rb @@ -157,6 +157,14 @@ class AiPersona < ActiveRecord::Base options end + define_method :temperature do + @ai_persona&.temperature + end + + define_method :top_p do + @ai_persona&.top_p + end + define_method :system_prompt do @ai_persona&.system_prompt || "You are a helpful bot." end @@ -166,7 +174,8 @@ class AiPersona < ActiveRecord::Base private def system_persona_unchangeable - if system_prompt_changed? || commands_changed? || name_changed? || description_changed? + if 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 @@ -186,7 +195,7 @@ end # id :bigint not null, primary key # name :string(100) not null # description :string(2000) not null -# commands :string default([]), not null, is an Array +# commands :json not null # system_prompt :string(10000000) not null # allowed_group_ids :integer default([]), not null, is an Array # created_by_id :integer @@ -194,7 +203,9 @@ end # created_at :datetime not null # updated_at :datetime not null # system :boolean default(FALSE), not null -# priority :integer default(0), not null +# priority :boolean default(FALSE), not null +# temperature :float +# top_p :float # # Indexes # diff --git a/app/serializers/localized_ai_persona_serializer.rb b/app/serializers/localized_ai_persona_serializer.rb index 1ecd3239..fca5f6d2 100644 --- a/app/serializers/localized_ai_persona_serializer.rb +++ b/app/serializers/localized_ai_persona_serializer.rb @@ -11,7 +11,9 @@ class LocalizedAiPersonaSerializer < ApplicationSerializer :priority, :commands, :system_prompt, - :allowed_group_ids + :allowed_group_ids, + :temperature, + :top_p def name object.class_instance.name diff --git a/assets/javascripts/discourse/admin/models/ai-persona.js b/assets/javascripts/discourse/admin/models/ai-persona.js index d9152cd9..8d7c4171 100644 --- a/assets/javascripts/discourse/admin/models/ai-persona.js +++ b/assets/javascripts/discourse/admin/models/ai-persona.js @@ -10,6 +10,8 @@ const ATTRIBUTES = [ "enabled", "system", "priority", + "top_p", + "temperature", ]; class CommandOption { diff --git a/assets/javascripts/discourse/components/ai-persona-editor.gjs b/assets/javascripts/discourse/components/ai-persona-editor.gjs index be922cdb..b172d0de 100644 --- a/assets/javascripts/discourse/components/ai-persona-editor.gjs +++ b/assets/javascripts/discourse/components/ai-persona-editor.gjs @@ -73,6 +73,14 @@ export default class PersonaEditor extends Component { } } + get showTemperature() { + return this.editingModel?.temperature || !this.editingModel?.system; + } + + get showTopP() { + return this.editingModel?.top_p || !this.editingModel?.system; + } + @action delete() { return this.dialog.confirm({ @@ -213,6 +221,36 @@ export default class PersonaEditor extends Component { disabled={{this.editingModel.system}} /> + {{#if this.showTemperature}} +
+ + + +
+ {{/if}} + {{#if this.showTopP}} +
+ + + +
+ {{/if}}
"test" }]], + top_p: 0.1, + temperature: 0.5, } end @@ -127,8 +129,17 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do "CONTENT_TYPE" => "application/json", } expect(response).to be_successful - persona = AiPersona.find(response.parsed_body["ai_persona"]["id"]) + persona_json = response.parsed_body["ai_persona"] + + expect(persona_json["name"]).to eq("superbot") + expect(persona_json["top_p"]).to eq(0.1) + expect(persona_json["temperature"]).to eq(0.5) + + persona = AiPersona.find(persona_json["id"]) + expect(persona.commands).to eq([["search", { "base_query" => "test" }]]) + expect(persona.top_p).to eq(0.1) + expect(persona.temperature).to eq(0.5) }.to change(AiPersona, :count).by(1) end end @@ -143,6 +154,36 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end describe "PUT #update" do + it "allows us to trivially clear top_p and temperature" do + persona = Fabricate(:ai_persona, name: "test_bot2", top_p: 0.5, temperature: 0.1) + + put "/admin/plugins/discourse-ai/ai_personas/#{persona.id}.json", + params: { + ai_persona: { + top_p: "", + temperature: "", + }, + } + + expect(response).to have_http_status(:ok) + persona.reload + + expect(persona.top_p).to eq(nil) + expect(persona.temperature).to eq(nil) + end + + it "does not allow temperature and top p changes on stock personas" do + put "/admin/plugins/discourse-ai/ai_personas/#{DiscourseAi::AiBot::Personas::Persona.system_personas.values.first}.json", + params: { + ai_persona: { + top_p: 0.5, + temperature: 0.1, + }, + } + + expect(response).to have_http_status(:unprocessable_entity) + end + context "with valid params" do it "updates the requested ai_persona" do put "/admin/plugins/discourse-ai/ai_personas/#{ai_persona.id}.json", diff --git a/test/javascripts/unit/models/ai-persona-test.js b/test/javascripts/unit/models/ai-persona-test.js index 0df91379..84ba05af 100644 --- a/test/javascripts/unit/models/ai-persona-test.js +++ b/test/javascripts/unit/models/ai-persona-test.js @@ -39,6 +39,8 @@ module("Discourse AI | Unit | Model | ai-persona", function () { system_prompt: "System Prompt", priority: false, description: "Description", + top_p: 0.8, + temperature: 0.7, }; const aiPersona = AiPersona.create({ ...properties }); @@ -63,6 +65,8 @@ module("Discourse AI | Unit | Model | ai-persona", function () { system_prompt: "System Prompt", priority: false, description: "Description", + top_p: 0.8, + temperature: 0.7, }; const aiPersona = AiPersona.create({ ...properties });