2023-03-15 16:02:20 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module DiscourseAi
|
|
|
|
module AiHelper
|
|
|
|
class AssistantController < ::ApplicationController
|
|
|
|
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
|
|
|
requires_login
|
|
|
|
before_action :ensure_can_request_suggestions
|
2023-09-14 11:53:44 -04:00
|
|
|
before_action :rate_limiter_performed!, except: %i[prompts]
|
2023-03-15 16:02:20 -04:00
|
|
|
|
2024-02-19 21:43:39 -05:00
|
|
|
include SecureUploadEndpointHelpers
|
|
|
|
|
2023-03-15 16:02:20 -04:00
|
|
|
def suggest
|
2023-09-14 11:53:44 -04:00
|
|
|
input = get_text_param!
|
2024-07-04 11:23:37 -04:00
|
|
|
force_default_locale = params[:force_default_locale] || false
|
2023-03-15 16:02:20 -04:00
|
|
|
|
2023-04-10 10:04:42 -04:00
|
|
|
prompt = CompletionPrompt.find_by(id: params[:mode])
|
2023-09-25 14:12:54 -04:00
|
|
|
|
2023-03-17 14:14:19 -04:00
|
|
|
raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled?
|
2023-12-11 17:26:56 -05:00
|
|
|
|
|
|
|
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
|
|
|
|
raise Discourse::InvalidParameters.new(:custom_prompt) if params[:custom_prompt].blank?
|
|
|
|
|
|
|
|
prompt.custom_instruction = params[:custom_prompt]
|
2023-09-25 14:12:54 -04:00
|
|
|
end
|
2023-03-15 16:02:20 -04:00
|
|
|
|
2023-12-19 14:17:34 -05:00
|
|
|
suggest_thumbnails(input) if prompt.id == CompletionPrompt::ILLUSTRATE_POST
|
|
|
|
|
2023-03-15 16:02:20 -04:00
|
|
|
hijack do
|
2023-09-25 14:12:54 -04:00
|
|
|
render json:
|
2023-11-27 07:33:31 -05:00
|
|
|
DiscourseAi::AiHelper::Assistant.new.generate_and_send_prompt(
|
|
|
|
prompt,
|
|
|
|
input,
|
|
|
|
current_user,
|
2024-07-04 11:23:37 -04:00
|
|
|
force_default_locale,
|
2023-11-27 07:33:31 -05:00
|
|
|
),
|
2023-03-15 16:02:20 -04:00
|
|
|
status: 200
|
|
|
|
end
|
2024-01-04 07:53:47 -05:00
|
|
|
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed
|
2023-03-22 15:00:28 -04:00
|
|
|
render_json_error I18n.t("discourse_ai.ai_helper.errors.completion_request_failed"),
|
|
|
|
status: 502
|
2023-03-15 16:02:20 -04:00
|
|
|
end
|
|
|
|
|
2023-09-01 20:10:58 -04:00
|
|
|
def suggest_title
|
2023-09-14 11:53:44 -04:00
|
|
|
input = get_text_param!
|
2023-09-01 20:10:58 -04:00
|
|
|
|
2023-11-27 07:33:31 -05:00
|
|
|
prompt = CompletionPrompt.enabled_by_name("generate_titles")
|
|
|
|
raise Discourse::InvalidParameters.new(:mode) if !prompt
|
2023-09-01 20:10:58 -04:00
|
|
|
|
|
|
|
hijack do
|
2023-09-25 14:12:54 -04:00
|
|
|
render json:
|
2023-11-27 07:33:31 -05:00
|
|
|
DiscourseAi::AiHelper::Assistant.new.generate_and_send_prompt(
|
2023-09-25 14:12:54 -04:00
|
|
|
prompt,
|
2023-11-27 07:33:31 -05:00
|
|
|
input,
|
|
|
|
current_user,
|
2023-09-25 14:12:54 -04:00
|
|
|
),
|
2023-09-01 20:10:58 -04:00
|
|
|
status: 200
|
|
|
|
end
|
2024-01-04 07:53:47 -05:00
|
|
|
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed
|
2023-09-01 20:10:58 -04:00
|
|
|
render_json_error I18n.t("discourse_ai.ai_helper.errors.completion_request_failed"),
|
|
|
|
status: 502
|
|
|
|
end
|
|
|
|
|
|
|
|
def suggest_category
|
2023-09-14 11:53:44 -04:00
|
|
|
input = get_text_param!
|
2023-09-25 14:12:54 -04:00
|
|
|
input_hash = { text: input }
|
2023-09-01 20:10:58 -04:00
|
|
|
|
2023-09-25 14:12:54 -04:00
|
|
|
render json:
|
|
|
|
DiscourseAi::AiHelper::SemanticCategorizer.new(
|
|
|
|
input_hash,
|
|
|
|
current_user,
|
|
|
|
).categories,
|
2023-09-01 20:10:58 -04:00
|
|
|
status: 200
|
|
|
|
end
|
|
|
|
|
|
|
|
def suggest_tags
|
2023-09-14 11:53:44 -04:00
|
|
|
input = get_text_param!
|
2023-09-25 14:12:54 -04:00
|
|
|
input_hash = { text: input }
|
2023-09-01 20:10:58 -04:00
|
|
|
|
2023-09-25 14:12:54 -04:00
|
|
|
render json: DiscourseAi::AiHelper::SemanticCategorizer.new(input_hash, current_user).tags,
|
2023-09-04 13:30:33 -04:00
|
|
|
status: 200
|
2023-09-01 20:10:58 -04:00
|
|
|
end
|
|
|
|
|
2023-12-19 14:17:34 -05:00
|
|
|
def suggest_thumbnails(input)
|
2023-09-14 11:53:44 -04:00
|
|
|
hijack do
|
|
|
|
thumbnails = DiscourseAi::AiHelper::Painter.new.commission_thumbnails(input, current_user)
|
|
|
|
|
|
|
|
render json: { thumbnails: thumbnails }, status: 200
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-08-08 14:32:39 -04:00
|
|
|
def stream_suggestion
|
2023-10-23 10:41:36 -04:00
|
|
|
post_id = get_post_param!
|
2024-08-08 14:32:39 -04:00
|
|
|
text = get_text_param!
|
2023-11-27 07:33:31 -05:00
|
|
|
post = Post.includes(:topic).find_by(id: post_id)
|
2024-08-08 14:32:39 -04:00
|
|
|
prompt = CompletionPrompt.find_by(id: params[:mode])
|
2023-10-23 10:41:36 -04:00
|
|
|
|
2024-08-08 14:32:39 -04:00
|
|
|
raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled?
|
2023-10-23 10:41:36 -04:00
|
|
|
raise Discourse::InvalidParameters.new(:post_id) unless post
|
|
|
|
|
2024-08-08 14:32:39 -04:00
|
|
|
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
|
|
|
|
raise Discourse::InvalidParameters.new(:custom_prompt) if params[:custom_prompt].blank?
|
|
|
|
end
|
|
|
|
|
2023-12-12 12:28:39 -05:00
|
|
|
Jobs.enqueue(
|
|
|
|
:stream_post_helper,
|
|
|
|
post_id: post.id,
|
|
|
|
user_id: current_user.id,
|
2024-08-08 14:32:39 -04:00
|
|
|
text: text,
|
|
|
|
prompt: prompt.name,
|
|
|
|
custom_prompt: params[:custom_prompt],
|
2023-12-12 12:28:39 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
render json: { success: true }, status: 200
|
2024-01-04 07:53:47 -05:00
|
|
|
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed
|
2023-10-23 10:41:36 -04:00
|
|
|
render_json_error I18n.t("discourse_ai.ai_helper.errors.completion_request_failed"),
|
|
|
|
status: 502
|
|
|
|
end
|
|
|
|
|
2024-02-19 12:56:28 -05:00
|
|
|
def caption_image
|
|
|
|
image_url = params[:image_url]
|
2024-05-27 13:49:24 -04:00
|
|
|
image_url_type = params[:image_url_type]
|
|
|
|
|
2024-02-19 12:56:28 -05:00
|
|
|
raise Discourse::InvalidParameters.new(:image_url) if !image_url
|
2024-05-27 13:49:24 -04:00
|
|
|
raise Discourse::InvalidParameters.new(:image_url) if !image_url_type
|
|
|
|
|
|
|
|
if image_url_type == "short_path"
|
|
|
|
image = Upload.find_by(sha1: Upload.sha1_from_short_path(image_url))
|
|
|
|
elsif image_url_type == "short_url"
|
|
|
|
image = Upload.find_by(sha1: Upload.sha1_from_short_url(image_url))
|
|
|
|
else
|
|
|
|
image = upload_from_full_url(image_url)
|
|
|
|
end
|
2024-02-19 12:56:28 -05:00
|
|
|
|
2024-02-19 21:43:39 -05:00
|
|
|
raise Discourse::NotFound if image.blank?
|
2024-05-28 09:31:15 -04:00
|
|
|
|
|
|
|
check_secure_upload_permission(image) if image.secure?
|
|
|
|
user = current_user
|
2024-02-19 12:56:28 -05:00
|
|
|
|
|
|
|
hijack do
|
2024-05-28 09:31:15 -04:00
|
|
|
caption = DiscourseAi::AiHelper::Assistant.new.generate_image_caption(image, user)
|
2024-02-21 13:10:22 -05:00
|
|
|
render json: {
|
|
|
|
caption:
|
|
|
|
"#{caption} (#{I18n.t("discourse_ai.ai_helper.image_caption.attribution")})",
|
|
|
|
},
|
|
|
|
status: 200
|
2024-02-19 12:56:28 -05:00
|
|
|
end
|
|
|
|
rescue DiscourseAi::Completions::Endpoints::Base::CompletionFailed, Net::HTTPBadResponse
|
|
|
|
render_json_error I18n.t("discourse_ai.ai_helper.errors.completion_request_failed"),
|
|
|
|
status: 502
|
|
|
|
end
|
|
|
|
|
2023-03-15 16:02:20 -04:00
|
|
|
private
|
|
|
|
|
2023-09-14 11:53:44 -04:00
|
|
|
def get_text_param!
|
|
|
|
params[:text].tap { |t| raise Discourse::InvalidParameters.new(:text) if t.blank? }
|
|
|
|
end
|
|
|
|
|
2023-10-23 10:41:36 -04:00
|
|
|
def get_post_param!
|
|
|
|
params[:post_id].tap { |t| raise Discourse::InvalidParameters.new(:post_id) if t.blank? }
|
|
|
|
end
|
|
|
|
|
2023-09-14 11:53:44 -04:00
|
|
|
def rate_limiter_performed!
|
|
|
|
RateLimiter.new(current_user, "ai_assistant", 6, 3.minutes).performed!
|
|
|
|
end
|
|
|
|
|
2023-03-15 16:02:20 -04:00
|
|
|
def ensure_can_request_suggestions
|
2024-08-12 18:40:23 -04:00
|
|
|
allowed_groups =
|
|
|
|
(
|
|
|
|
SiteSetting.composer_ai_helper_allowed_groups_map |
|
|
|
|
SiteSetting.post_ai_helper_allowed_groups_map
|
|
|
|
)
|
|
|
|
|
|
|
|
raise Discourse::InvalidAccess if !current_user.in_any_groups?(allowed_groups)
|
2024-02-19 21:43:39 -05:00
|
|
|
end
|
2023-03-15 16:02:20 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|