DEV: Update rate limits for image captioning (#816)

This PR updates the rate limits for AI helper so that image caption follows a specific rate limit of 20 requests per minute. This should help when uploading multiple files that need to be captioned. This PR also updates the UI so that it shows toast message with the extracted error message instead of having a blocking `popupAjaxError` error dialog.
---------

Co-authored-by: Rafael dos Santos Silva <xfalcox@gmail.com>
Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
Keegan George 2024-10-03 02:36:35 +09:00 committed by GitHub
parent 7ae6c17236
commit 110a1629aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 64 additions and 4 deletions

View File

@ -6,10 +6,21 @@ module DiscourseAi
requires_plugin ::DiscourseAi::PLUGIN_NAME requires_plugin ::DiscourseAi::PLUGIN_NAME
requires_login requires_login
before_action :ensure_can_request_suggestions before_action :ensure_can_request_suggestions
before_action :rate_limiter_performed!, except: %i[prompts] before_action :rate_limiter_performed!
include SecureUploadEndpointHelpers include SecureUploadEndpointHelpers
RATE_LIMITS = {
"default" => {
amount: 6,
interval: 3.minutes,
},
"caption_image" => {
amount: 20,
interval: 1.minute,
},
}.freeze
def suggest def suggest
input = get_text_param! input = get_text_param!
force_default_locale = params[:force_default_locale] || false force_default_locale = params[:force_default_locale] || false
@ -161,7 +172,13 @@ module DiscourseAi
end end
def rate_limiter_performed! def rate_limiter_performed!
RateLimiter.new(current_user, "ai_assistant", 6, 3.minutes).performed! action_rate_limit = RATE_LIMITS[action_name] || RATE_LIMITS["default"]
RateLimiter.new(
current_user,
"ai_assistant",
action_rate_limit[:amount],
action_rate_limit[:interval],
).performed!
end end
def ensure_can_request_suggestions def ensure_can_request_suggestions

View File

@ -1,5 +1,5 @@
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { extractError, popupAjaxError } from "discourse/lib/ajax-error";
import { apiInitializer } from "discourse/lib/api"; import { apiInitializer } from "discourse/lib/api";
import { getUploadMarkdown, isImage } from "discourse/lib/uploads"; import { getUploadMarkdown, isImage } from "discourse/lib/uploads";
import I18n from "discourse-i18n"; import I18n from "discourse-i18n";
@ -111,7 +111,13 @@ export default apiInitializer("1.25.0", (api) => {
}); });
return response.caption; return response.caption;
} catch (error) { } catch (error) {
popupAjaxError(error); toasts.error({
class: "ai-image-caption-error-toast",
duration: 3000,
data: {
message: extractError(error),
},
});
} }
} }
@ -129,6 +135,7 @@ export default apiInitializer("1.25.0", (api) => {
return; return;
} }
const toasts = api.container.lookup("service:toasts");
// Automatically caption uploaded images // Automatically caption uploaded images
api.addComposerUploadMarkdownResolver(async (upload) => { api.addComposerUploadMarkdownResolver(async (upload) => {
const autoCaptionEnabled = currentUser.get( const autoCaptionEnabled = currentUser.get(

View File

@ -100,6 +100,23 @@ RSpec.describe DiscourseAi::AiHelper::AssistantController do
expect(response.parsed_body["diff"]).to eq(expected_diff) expect(response.parsed_body["diff"]).to eq(expected_diff)
end end
end end
context "when performing numerous requests" do
it "rate limits" do
RateLimiter.enable
rate_limit = described_class::RATE_LIMITS["default"]
amount = rate_limit[:amount]
amount.times do
post "/discourse-ai/ai-helper/suggest", params: { mode: mode, text: text_to_proofread }
expect(response.status).to eq(200)
end
DiscourseAi::Completions::Llm.with_prepared_responses([proofread_text]) do
post "/discourse-ai/ai-helper/suggest", params: { mode: mode, text: text_to_proofread }
expect(response.status).to eq(429)
end
end
end
end end
end end
@ -258,6 +275,25 @@ RSpec.describe DiscourseAi::AiHelper::AssistantController do
end end
end end
end end
context "when performing numerous requests" do
it "rate limits" do
RateLimiter.enable
rate_limit = described_class::RATE_LIMITS["caption_image"]
amount = rate_limit[:amount]
amount.times do
request_caption({ image_url: image_url, image_url_type: "long_url" }) do |r|
expect(r.status).to eq(200)
end
end
request_caption({ image_url: image_url, image_url_type: "long_url" }) do |r|
expect(r.status).to eq(429)
end
end
end
end end
end end
end end