# frozen_string_literal: true module DiscourseAi module Configuration class Feature class << self def feature_cache @feature_cache ||= ::DiscourseAi::MultisiteHash.new("feature_cache") end def summarization_features feature_cache[:summarization] ||= [ new( "topic_summaries", "ai_summarization_persona", DiscourseAi::Configuration::Module::SUMMARIZATION_ID, DiscourseAi::Configuration::Module::SUMMARIZATION, ), new( "gists", "ai_summary_gists_persona", DiscourseAi::Configuration::Module::SUMMARIZATION_ID, DiscourseAi::Configuration::Module::SUMMARIZATION, enabled_by_setting: "ai_summary_gists_enabled", ), ] end def search_features feature_cache[:search] ||= [ new( "discoveries", "ai_bot_discover_persona", DiscourseAi::Configuration::Module::SEARCH_ID, DiscourseAi::Configuration::Module::SEARCH, ), ] end def discord_features feature_cache[:discord] ||= [ new( "search", "ai_discord_search_persona", DiscourseAi::Configuration::Module::DISCORD_ID, DiscourseAi::Configuration::Module::DISCORD, ), ] end def inference_features feature_cache[:inference] ||= [ new( "generate_concepts", "inferred_concepts_generate_persona", DiscourseAi::Configuration::Module::INFERENCE_ID, DiscourseAi::Configuration::Module::INFERENCE, ), new( "match_concepts", "inferred_concepts_match_persona", DiscourseAi::Configuration::Module::INFERENCE_ID, DiscourseAi::Configuration::Module::INFERENCE, ), new( "deduplicate_concepts", "inferred_concepts_deduplicate_persona", DiscourseAi::Configuration::Module::INFERENCE_ID, DiscourseAi::Configuration::Module::INFERENCE, ), ] end def ai_helper_features feature_cache[:ai_helper] ||= [ new( "proofread", "ai_helper_proofreader_persona", DiscourseAi::Configuration::Module::AI_HELPER_ID, DiscourseAi::Configuration::Module::AI_HELPER, ), new( "title_suggestions", "ai_helper_title_suggestions_persona", DiscourseAi::Configuration::Module::AI_HELPER_ID, DiscourseAi::Configuration::Module::AI_HELPER, ), new( "explain", "ai_helper_explain_persona", DiscourseAi::Configuration::Module::AI_HELPER_ID, DiscourseAi::Configuration::Module::AI_HELPER, ), new( "smart_dates", "ai_helper_smart_dates_persona", DiscourseAi::Configuration::Module::AI_HELPER_ID, DiscourseAi::Configuration::Module::AI_HELPER, ), new( "markdown_tables", "ai_helper_markdown_tables_persona", DiscourseAi::Configuration::Module::AI_HELPER_ID, DiscourseAi::Configuration::Module::AI_HELPER, ), new( "custom_prompt", "ai_helper_custom_prompt_persona", DiscourseAi::Configuration::Module::AI_HELPER_ID, DiscourseAi::Configuration::Module::AI_HELPER, ), new( "image_caption", "ai_helper_image_caption_persona", DiscourseAi::Configuration::Module::AI_HELPER_ID, DiscourseAi::Configuration::Module::AI_HELPER, ), ] end def bot_features feature_cache[:bot] ||= [ new( "bot", nil, DiscourseAi::Configuration::Module::BOT_ID, DiscourseAi::Configuration::Module::BOT, persona_ids_lookup: -> { lookup_bot_persona_ids }, llm_models_lookup: -> { lookup_bot_llms }, ), ] end def spam_features feature_cache[:spam] ||= [ new( "inspect_posts", nil, DiscourseAi::Configuration::Module::SPAM_ID, DiscourseAi::Configuration::Module::SPAM, persona_ids_lookup: -> { [AiModerationSetting.spam&.ai_persona_id].compact }, llm_models_lookup: -> { [AiModerationSetting.spam&.llm_model].compact }, ), ] end def lookup_bot_persona_ids AiPersona .where(enabled: true) .where( "allow_chat_channel_mentions OR allow_chat_direct_messages OR allow_topic_mentions OR allow_personal_messages", ) .pluck(:id) end def lookup_bot_llms LlmModel.where(enabled_chat_bot: true).to_a end def translation_features feature_cache[:translation] ||= [ new( "locale_detector", "ai_translation_locale_detector_persona", DiscourseAi::Configuration::Module::TRANSLATION_ID, DiscourseAi::Configuration::Module::TRANSLATION, ), new( "post_raw_translator", "ai_translation_post_raw_translator_persona", DiscourseAi::Configuration::Module::TRANSLATION_ID, DiscourseAi::Configuration::Module::TRANSLATION, ), new( "topic_title_translator", "ai_translation_topic_title_translator_persona", DiscourseAi::Configuration::Module::TRANSLATION_ID, DiscourseAi::Configuration::Module::TRANSLATION, ), new( "short_text_translator", "ai_translation_short_text_translator_persona", DiscourseAi::Configuration::Module::TRANSLATION_ID, DiscourseAi::Configuration::Module::TRANSLATION, ), ] end def all [ summarization_features, search_features, discord_features, inference_features, ai_helper_features, translation_features, bot_features, spam_features, ].flatten end def find_features_using(persona_id:) all.select { |feature| feature.persona_ids.include?(persona_id) } end end def initialize( name, persona_setting, module_id, module_name, enabled_by_setting: "", persona_ids_lookup: nil, llm_models_lookup: nil ) @name = name @persona_setting = persona_setting @module_id = module_id @module_name = module_name @enabled_by_setting = enabled_by_setting @persona_ids_lookup = persona_ids_lookup @llm_models_lookup = llm_models_lookup end def llm_models return @llm_models_lookup.call if @llm_models_lookup return if !persona_ids llm_models = [] personas = AiPersona.where(id: persona_ids) personas.each do |persona| next if persona.blank? persona_klass = persona.class_instance llm_model = case module_name when DiscourseAi::Configuration::Module::SUMMARIZATION DiscourseAi::Summarization.find_summarization_model(persona_klass) when DiscourseAi::Configuration::Module::AI_HELPER DiscourseAi::AiHelper::Assistant.find_ai_helper_model(name, persona_klass) when DiscourseAi::Configuration::Module::TRANSLATION DiscourseAi::Translation::BaseTranslator.preferred_llm_model(persona_klass) end if llm_model.blank? && persona.default_llm_id llm_model = LlmModel.find_by(id: persona.default_llm_id) end llm_models << llm_model if llm_model end llm_models.compact.uniq end attr_reader :name, :persona_setting, :module_id, :module_name def enabled? @enabled_by_setting.blank? || SiteSetting.get(@enabled_by_setting) end def persona_ids if @persona_ids_lookup @persona_ids_lookup.call else id = SiteSetting.get(persona_setting).to_i if id != 0 [id] else [] end end end end end end