mirror of
https://github.com/discourse/discourse.git
synced 2026-08-07 13:19:19 +08:00
Follow up to https://github.com/discourse/discourse/pull/41586 1. **Bulk of the PR diff** -- there's a mix of usages for "description" and "caption", we'll stick to V1 terminology and keep "AI caption" instead of bringing in "description". 2. Move image caption out of AI helper, into its own dedicated "AI Feature" for consolidated settings 3. Update default values for the existing image caption agent - vision_enabled was default off, now on - update description 4. Validations and problem checks - via Site Setting validation - agent / llm - when enabling the feature - or when selecting invalid agent - inform admin when they've enabled the setting but the agent is misconfigured via ProblemCheck
226 lines
6.9 KiB
Ruby
Vendored
226 lines
6.9 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module DiscourseAi
|
|
module Configuration
|
|
class Module
|
|
SUMMARIZATION = "summarization"
|
|
SEARCH = "search"
|
|
DISCORD = "discord"
|
|
INFERENCE = "inference"
|
|
AI_HELPER = "ai_helper"
|
|
IMAGE_CAPTION = "image_caption"
|
|
TRANSLATION = "translation"
|
|
BOT = "bot"
|
|
SPAM = "spam"
|
|
EMBEDDINGS = "embeddings"
|
|
AUTOMATION_REPORTS = "automation_reports"
|
|
AUTOMATION_TRIAGE = "automation_triage"
|
|
ADMIN_DASHBOARD = "admin_dashboard"
|
|
|
|
NAMES = [
|
|
SUMMARIZATION,
|
|
SEARCH,
|
|
DISCORD,
|
|
INFERENCE,
|
|
AI_HELPER,
|
|
IMAGE_CAPTION,
|
|
TRANSLATION,
|
|
BOT,
|
|
SPAM,
|
|
EMBEDDINGS,
|
|
AUTOMATION_REPORTS,
|
|
AUTOMATION_TRIAGE,
|
|
ADMIN_DASHBOARD,
|
|
].freeze
|
|
|
|
SUMMARIZATION_ID = 1
|
|
SEARCH_ID = 2
|
|
DISCORD_ID = 3
|
|
INFERENCE_ID = 4
|
|
AI_HELPER_ID = 5
|
|
TRANSLATION_ID = 6
|
|
BOT_ID = 7
|
|
SPAM_ID = 8
|
|
EMBEDDINGS_ID = 9
|
|
AUTOMATION_REPORTS_ID = 10
|
|
AUTOMATION_TRIAGE_ID = 11
|
|
ADMIN_DASHBOARD_ID = 12
|
|
IMAGE_CAPTION_ID = 13
|
|
|
|
class << self
|
|
def external_module_id(module_name)
|
|
Digest::SHA1.hexdigest(module_name.to_s).to_i(16) % 100_000 + 1000
|
|
end
|
|
|
|
def all
|
|
base_modules = [
|
|
new(
|
|
SUMMARIZATION_ID,
|
|
SUMMARIZATION,
|
|
enabled_by_setting: "ai_summarization_enabled",
|
|
features: DiscourseAi::Configuration::Feature.summarization_features,
|
|
),
|
|
new(
|
|
SEARCH_ID,
|
|
SEARCH,
|
|
enabled_by_setting: "ai_discover_enabled",
|
|
features: DiscourseAi::Configuration::Feature.search_features,
|
|
),
|
|
new(
|
|
DISCORD_ID,
|
|
DISCORD,
|
|
enabled_by_setting: "ai_discord_search_enabled",
|
|
features: DiscourseAi::Configuration::Feature.discord_features,
|
|
),
|
|
new(
|
|
INFERENCE_ID,
|
|
INFERENCE,
|
|
enabled_by_setting: "inferred_concepts_enabled",
|
|
features: DiscourseAi::Configuration::Feature.inference_features,
|
|
),
|
|
new(
|
|
AI_HELPER_ID,
|
|
AI_HELPER,
|
|
enabled_by_setting: "ai_helper_enabled",
|
|
features: DiscourseAi::Configuration::Feature.ai_helper_features,
|
|
),
|
|
new(
|
|
IMAGE_CAPTION_ID,
|
|
IMAGE_CAPTION,
|
|
enabled_by_setting: "ai_post_image_captions_enabled",
|
|
features: DiscourseAi::Configuration::Feature.image_caption_features,
|
|
),
|
|
new(
|
|
TRANSLATION_ID,
|
|
TRANSLATION,
|
|
enabled_by_setting: "ai_translation_enabled",
|
|
features: DiscourseAi::Configuration::Feature.translation_features,
|
|
),
|
|
new(
|
|
BOT_ID,
|
|
BOT,
|
|
enabled_by_setting: "ai_bot_enabled",
|
|
features: DiscourseAi::Configuration::Feature.bot_features,
|
|
),
|
|
new(
|
|
SPAM_ID,
|
|
SPAM,
|
|
enabled_by_setting: "ai_spam_detection_enabled",
|
|
features: DiscourseAi::Configuration::Feature.spam_features,
|
|
),
|
|
new(
|
|
EMBEDDINGS_ID,
|
|
EMBEDDINGS,
|
|
enabled_by_setting: "ai_embeddings_enabled",
|
|
features: DiscourseAi::Configuration::Feature.embeddings_features,
|
|
extra_check: -> { SiteSetting.ai_embeddings_semantic_search_enabled },
|
|
),
|
|
new(
|
|
ADMIN_DASHBOARD_ID,
|
|
ADMIN_DASHBOARD,
|
|
enabled_by_setting: "ai_admin_dashboard_enabled",
|
|
features: DiscourseAi::Configuration::Feature.admin_dashboard_features,
|
|
extra_check: -> do
|
|
DiscourseAi::Configuration::Feature.admin_dashboard_features.any?(&:enabled?)
|
|
end,
|
|
visible: false,
|
|
),
|
|
]
|
|
|
|
if SiteSetting.discourse_automation_enabled
|
|
base_modules << new(
|
|
AUTOMATION_REPORTS_ID,
|
|
AUTOMATION_REPORTS,
|
|
enabled_by_setting: "discourse_automation_enabled",
|
|
features: DiscourseAi::Configuration::Feature.ai_automation_report_scripts,
|
|
extra_check: -> { has_scripts?(["llm_report"]) },
|
|
)
|
|
base_modules << new(
|
|
AUTOMATION_TRIAGE_ID,
|
|
AUTOMATION_TRIAGE,
|
|
enabled_by_setting: "discourse_automation_enabled",
|
|
features: DiscourseAi::Configuration::Feature.ai_automation_triage_scripts,
|
|
extra_check: -> { has_scripts?(%w[llm_triage llm_agent_triage]) },
|
|
)
|
|
end
|
|
|
|
# external modules from plugin registry
|
|
DiscoursePluginRegistry
|
|
.external_ai_features
|
|
.group_by { |e| e[:module_name] }
|
|
.each do |mod_name, entries|
|
|
module_id = external_module_id(mod_name)
|
|
features =
|
|
entries.map do |e|
|
|
setting_name = "#{mod_name}_#{e[:feature]}_agent"
|
|
DiscourseAi::Configuration::Feature.new(
|
|
e[:feature].to_s,
|
|
setting_name,
|
|
module_id,
|
|
mod_name.to_s,
|
|
enabled_by_setting: e[:enabled_by_setting],
|
|
)
|
|
end
|
|
base_modules << new(
|
|
module_id,
|
|
mod_name,
|
|
features:,
|
|
extra_check: -> { features.any?(&:enabled?) },
|
|
visible: entries.any? { |e| e.fetch(:visible, true) },
|
|
)
|
|
end
|
|
|
|
base_modules
|
|
end
|
|
|
|
def has_scripts?(script_names)
|
|
DB
|
|
.query_single(
|
|
"SELECT COUNT(*) FROM discourse_automation_automations WHERE script IN (:names) and enabled",
|
|
names: script_names,
|
|
)
|
|
.first
|
|
.to_i > 0
|
|
end
|
|
|
|
def find_by(id:)
|
|
all.find { |m| m.id == id }
|
|
end
|
|
end
|
|
|
|
def initialize(
|
|
id,
|
|
name,
|
|
enabled_by_setting: nil,
|
|
features: [],
|
|
extra_check: nil,
|
|
visible: true
|
|
)
|
|
@id = id
|
|
@name = name
|
|
@enabled_by_setting = enabled_by_setting
|
|
@features = features
|
|
@extra_check = extra_check
|
|
@visible = visible
|
|
end
|
|
|
|
attr_reader :id, :name, :enabled_by_setting, :features
|
|
|
|
def visible?
|
|
@visible
|
|
end
|
|
|
|
def enabled?
|
|
return @extra_check.present? ? @extra_check.call : true if enabled_by_setting.blank?
|
|
|
|
enabled_setting = SiteSetting.get(enabled_by_setting)
|
|
|
|
if @extra_check
|
|
enabled_setting && @extra_check.call
|
|
else
|
|
enabled_setting
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|