mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-26 01:04:55 +08:00
This commit introduces `DiscourseAi.register_feature` as the public API for other plugins to register AI-powered features. Previously, plugins had to manually register with `DiscoursePluginRegistry`, define their own agent site setting in `settings.yml`, create their own `EnumSiteSetting` subclass for the agent enum, register the site setting area, and assign settings to that area. This was a lot of boilerplate that every plugin would need to duplicate. This commit consolidates all of that into a single `DiscourseAi.register_feature` call that handles: 1. Auto-defining the `<module>_<feature>_agent` site setting with the correct enum, `depends_on`, and `depends_behavior` configuration 2. Registering the `ai-features/<module_name>` site setting area so the admin edit page can load the settings 3. Assigning both the generated agent setting and the `enabled_by_setting` to that area 4. Registering the feature in the `DiscoursePluginRegistry` external AI features registry The data-explorer plugin is updated to use the new API, which lets it drop its custom `AiAgentEnumerator` class, the manual `data_explorer_query_generation_agent` setting definition in `settings.yml`, and the manual area registration and assignment code.
94 lines
3 KiB
Ruby
Vendored
94 lines
3 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module DiscourseAi
|
|
class Engine < ::Rails::Engine
|
|
engine_name PLUGIN_NAME
|
|
isolate_namespace DiscourseAi
|
|
end
|
|
|
|
# Register an AI-powered feature from another plugin. The feature will
|
|
# appear in the AI features admin page, where admins can select which agent
|
|
# backs it.
|
|
#
|
|
# The agent_klass must be a subclass of {DiscourseAi::Agents::Agent} and
|
|
# implement at minimum `#tools` (returning an array of tool classes) and
|
|
# `#system_prompt` (returning the prompt string).
|
|
#
|
|
# Each tool class must be a subclass of {DiscourseAi::Agents::Tools::Tool}
|
|
# and implement:
|
|
# - `.signature` - returns a hash with `name`, `description`, and `parameters`
|
|
# - `.name` - returns the tool name string
|
|
# - `#invoke` - executes the tool and returns a result hash
|
|
#
|
|
# @example Defining a tool, agent, and registering the feature
|
|
# class MyPlugin::Tools::RunQuery < DiscourseAi::Agents::Tools::Tool
|
|
# def self.signature
|
|
# { name: "run_query", description: "Runs a query", parameters: [] }
|
|
# end
|
|
#
|
|
# def self.name
|
|
# "run_query"
|
|
# end
|
|
#
|
|
# def invoke
|
|
# { result: "ok" }
|
|
# end
|
|
# end
|
|
#
|
|
# class MyPlugin::QueryAgent < DiscourseAi::Agents::Agent
|
|
# def tools
|
|
# [MyPlugin::Tools::RunQuery]
|
|
# end
|
|
#
|
|
# def system_prompt
|
|
# "You are a query expert."
|
|
# end
|
|
# end
|
|
#
|
|
# DiscourseAi.register_feature(
|
|
# module_name: :my_plugin,
|
|
# feature: :query_generation,
|
|
# agent_klass: MyPlugin::QueryAgent,
|
|
# enabled_by_setting: "my_plugin_ai_enabled",
|
|
# plugin: self,
|
|
# )
|
|
#
|
|
# @param module_name [Symbol] groups features under a module in the admin UI
|
|
# @param feature [Symbol] the feature name within the module
|
|
# @param agent_klass [Class] a subclass of {DiscourseAi::Agents::Agent}
|
|
# @param enabled_by_setting [String, nil] site setting that gates this feature
|
|
# @param plugin [Plugin::Instance] the plugin instance registering the feature
|
|
def self.register_feature(module_name:, feature:, agent_klass:, enabled_by_setting: nil, plugin:)
|
|
area = "ai-features/#{module_name}"
|
|
plugin.register_site_setting_area(area)
|
|
|
|
setting_name = "#{module_name}_#{feature}_agent"
|
|
SiteSetting.send(
|
|
:setting,
|
|
setting_name.to_sym,
|
|
DiscourseAi::Agents::Agent.external_agent_id(agent_klass).to_s,
|
|
type: "enum",
|
|
enum: "DiscourseAi::Configuration::AgentEnumerator",
|
|
depends_on: ["discourse_ai_enabled", enabled_by_setting].compact,
|
|
depends_behavior: "hidden",
|
|
area: area,
|
|
)
|
|
|
|
if enabled_by_setting.present?
|
|
key = enabled_by_setting.to_sym
|
|
existing = SiteSetting.areas[key]
|
|
SiteSetting.areas[key] = existing ? (Array.wrap(existing) | [area]) : area
|
|
end
|
|
|
|
DiscoursePluginRegistry.register_external_ai_feature(
|
|
{
|
|
module_name: module_name,
|
|
feature: feature,
|
|
agent_klass: agent_klass,
|
|
enabled_by_setting: enabled_by_setting,
|
|
visible: true,
|
|
},
|
|
plugin,
|
|
)
|
|
end
|
|
end
|