diff --git a/app/controllers/discourse_ai/admin/ai_llms_controller.rb b/app/controllers/discourse_ai/admin/ai_llms_controller.rb index 3a007037..0c5a5f6d 100644 --- a/app/controllers/discourse_ai/admin/ai_llms_controller.rb +++ b/app/controllers/discourse_ai/admin/ai_llms_controller.rb @@ -107,6 +107,7 @@ def ai_llm_params :url, :api_key, :bot_username, + :enabled_chat_bot, ) end end diff --git a/app/models/ai_persona.rb b/app/models/ai_persona.rb index 0d979ed8..a18499be 100644 --- a/app/models/ai_persona.rb +++ b/app/models/ai_persona.rb @@ -252,40 +252,32 @@ def ensure_not_system # # Table name: ai_personas # -# id :bigint not null, primary key -# name :string(100) not null -# description :string(2000) not null -# tools :json not null -# system_prompt :string(10000000) not null -# allowed_group_ids :integer default([]), not null, is an Array -# created_by_id :integer -# enabled :boolean default(TRUE), not null -# created_at :datetime not null -# updated_at :datetime not null -# system :boolean default(FALSE), not null -# priority :boolean default(FALSE), not null -# temperature :float -# top_p :float -# user_id :integer -# mentionable :boolean default(FALSE), not null -# default_llm :text -# max_context_posts :integer -# max_post_context_tokens :integer -# max_context_tokens :integer -# vision_enabled :boolean default(FALSE), not null -# vision_max_pixels :integer default(1048576), not null -# rag_chunk_tokens :integer default(374), not null -# rag_chunk_overlap_tokens :integer default(10), not null -# rag_conversation_chunks :integer default(10), not null -# 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 -# question_consolidator_llm :text -# allow_chat :boolean default(FALSE), not null -# tool_details :boolean default(TRUE), not null +# id :bigint not null, primary key +# name :string(100) not null +# description :string(2000) not null +# system_prompt :string(10000000) not null +# allowed_group_ids :integer default([]), not null, is an Array +# created_by_id :integer +# enabled :boolean default(TRUE), not null +# created_at :datetime not null +# updated_at :datetime not null +# system :boolean default(FALSE), not null +# priority :boolean default(FALSE), not null +# temperature :float +# top_p :float +# user_id :integer +# mentionable :boolean default(FALSE), not null +# default_llm :text +# max_context_posts :integer +# vision_enabled :boolean default(FALSE), not null +# vision_max_pixels :integer default(1048576), not null +# rag_chunk_tokens :integer default(374), not null +# rag_chunk_overlap_tokens :integer default(10), not null +# rag_conversation_chunks :integer default(10), not null +# question_consolidator_llm :text +# allow_chat :boolean default(FALSE), not null +# tool_details :boolean default(TRUE), not null +# tools :json not null # # Indexes # diff --git a/app/models/chat_message_custom_prompt.rb b/app/models/chat_message_custom_prompt.rb index 30b28533..6b36b24b 100644 --- a/app/models/chat_message_custom_prompt.rb +++ b/app/models/chat_message_custom_prompt.rb @@ -6,7 +6,7 @@ class ChatMessageCustomPrompt < ActiveRecord::Base # == Schema Information # -# Table name: message_custom_prompts +# Table name: chat_message_custom_prompts # # id :bigint not null, primary key # message_id :bigint not null @@ -16,5 +16,5 @@ class ChatMessageCustomPrompt < ActiveRecord::Base # # Indexes # -# index_message_custom_prompts_on_message_id (message_id) UNIQUE +# index_chat_message_custom_prompts_on_message_id (message_id) UNIQUE # diff --git a/app/models/llm_model.rb b/app/models/llm_model.rb index d90309b0..b0d861bf 100644 --- a/app/models/llm_model.rb +++ b/app/models/llm_model.rb @@ -1,8 +1,51 @@ # frozen_string_literal: true class LlmModel < ActiveRecord::Base + FIRST_BOT_USER_ID = -1200 + belongs_to :user + def toggle_companion_user + return if bot_username == "fake" && Rails.env.production? + + enable_check = SiteSetting.ai_bot_enabled && enabled_chat_bot + + if enable_check + if !user + next_id = DB.query_single(<<~SQL).first + SELECT min(id) - 1 FROM users + SQL + + new_user = + User.new( + id: [FIRST_BOT_USER_ID, next_id].min, + email: "no_email_#{bot_username}", + name: bot_username.titleize, + username: UserNameSuggester.suggest(bot_username), + active: true, + approved: true, + admin: true, + moderator: true, + trust_level: TrustLevel[4], + ) + new_user.save!(validate: false) + self.update!(user: new_user) + else + user.update!(active: true) + end + elsif user + # will include deleted + has_posts = DB.query_single("SELECT 1 FROM posts WHERE user_id = #{user.id} LIMIT 1").present? + + if has_posts + user.update!(active: false) if user.active + else + user.destroy! + self.update!(user: nil) + end + end + end + def tokenizer_class tokenizer.constantize end @@ -22,4 +65,7 @@ def tokenizer_class # updated_at :datetime not null # url :string # api_key :string +# bot_username :string +# user_id :integer +# enabled_chat_bot :boolean default(FALSE), not null # diff --git a/assets/javascripts/discourse/admin/models/ai-llm.js b/assets/javascripts/discourse/admin/models/ai-llm.js index f40d0649..bf9dc1be 100644 --- a/assets/javascripts/discourse/admin/models/ai-llm.js +++ b/assets/javascripts/discourse/admin/models/ai-llm.js @@ -12,7 +12,8 @@ export default class AiLlm extends RestModel { "max_prompt_tokens", "url", "api_key", - "bot_username" + "bot_username", + "enabled_chat_bot" ); } diff --git a/assets/javascripts/discourse/components/ai-bot-header-icon.gjs b/assets/javascripts/discourse/components/ai-bot-header-icon.gjs index a95dfe18..d6bb6abe 100644 --- a/assets/javascripts/discourse/components/ai-bot-header-icon.gjs +++ b/assets/javascripts/discourse/components/ai-bot-header-icon.gjs @@ -7,13 +7,15 @@ import i18n from "discourse-common/helpers/i18n"; import { composeAiBotMessage } from "../lib/ai-bot-helper"; export default class AiBotHeaderIcon extends Component { - @service siteSettings; + @service currentUser; @service composer; get bots() { - return this.siteSettings.ai_bot_add_to_header - ? this.siteSettings.ai_bot_enabled_chat_bots.split("|").filter(Boolean) - : []; + const availableBots = this.currentUser.ai_enabled_chat_bots + .filter((bot) => !bot.is_persosna) + .filter(Boolean); + + return availableBots ? availableBots.map((bot) => bot.model_name) : []; } @action diff --git a/assets/javascripts/discourse/components/ai-llm-editor.gjs b/assets/javascripts/discourse/components/ai-llm-editor.gjs index 7c64857c..11730ee9 100644 --- a/assets/javascripts/discourse/components/ai-llm-editor.gjs +++ b/assets/javascripts/discourse/components/ai-llm-editor.gjs @@ -1,11 +1,13 @@ import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; import { Input } from "@ember/component"; +import { on } from "@ember/modifier"; import { action } from "@ember/object"; import { later } from "@ember/runloop"; import { inject as service } from "@ember/service"; import BackButton from "discourse/components/back-button"; import DButton from "discourse/components/d-button"; +import DToggleSwitch from "discourse/components/d-toggle-switch"; import { popupAjaxError } from "discourse/lib/ajax-error"; import icon from "discourse-common/helpers/d-icon"; import i18n from "discourse-common/helpers/i18n"; @@ -110,6 +112,21 @@ export default class AiLlmEditor extends Component { }); } + @action + async toggleEnabledChatBot() { + this.args.model.set("enabled_chat_bot", !this.args.model.enabled_chat_bot); + if (!this.args.model.isNew) { + try { + await this.args.model.update({ + enabled_chat_bot: this.args.model.enabled_chat_bot, + }); + } catch (e) { + popupAjaxError(e); + } + } + await this.toggleField("enabled_chat_bot", true); + } +