discourse/plugins/discourse-ai/app/jobs/regular/stream_composer_helper.rb
Keegan George 902fd7494b
FEATURE: Hosted LLM credit system (#35162)
## 🔍 Overview
This update adds a credit system under the hood which will be used for
our CDCK Hosted LLM models so we can make our features more accessible
to our hosted customers!

## 📷 Screenshots
<img width="1105" height="268" alt="Screenshot 2025-10-02 at 12 48 58"
src="https://github.com/user-attachments/assets/2a07d89b-7510-4565-82bb-26b46fbcf5c4"
/>

_☝🏽 ` ProblemCheck` notices to inform customers_

<img width="1077" height="472" alt="Screenshot 2025-10-02 at 12 49 41"
src="https://github.com/user-attachments/assets/b72028f7-5df2-45a8-8c71-65cf750755ab"
/>

_☝🏽 AI Usage page for easy monitoring_

<img width="1112" height="1083" alt="Screenshot 2025-10-02 at 18 17 01"
src="https://github.com/user-attachments/assets/a01992d5-15a0-472a-9501-bc3bc9a54ade"
/>

_☝🏽 Credit bars underneath relevant LLM models_

<img width="866" height="267" alt="Screenshot 2025-10-03 at 11 35 19"
src="https://github.com/user-attachments/assets/e7b4c0e7-c93d-4b0f-923d-79ac5d53028b"
/>

_☝🏽 Dialog box when trying to use without available credits_
2025-10-14 07:48:20 -07:00

53 lines
1.4 KiB
Ruby
Vendored

# frozen_string_literal: true
module Jobs
class StreamComposerHelper < ::Jobs::Base
sidekiq_options retry: false
def execute(args)
return unless args[:prompt]
return unless user = User.find_by(id: args[:user_id])
return unless args[:text]
return unless args[:client_id]
return unless args[:progress_channel]
helper_mode = args[:prompt]
begin
DiscourseAi::AiHelper::Assistant.new.stream_prompt(
helper_mode,
args[:text],
user,
args[:progress_channel],
force_default_locale: args[:force_default_locale],
client_id: args[:client_id],
custom_prompt: args[:custom_prompt],
)
rescue LlmCreditAllocation::CreditLimitExceeded => e
publish_error(args[:progress_channel], user, e)
end
end
private
def publish_error(channel, user, exception)
allocation = exception.allocation
details = {}
if allocation
details[:reset_time_relative] = allocation.relative_reset_time
details[:reset_time_absolute] = allocation.formatted_reset_time
end
payload = {
error: true,
error_type: "credit_limit_exceeded",
message: exception.message,
details: details,
done: true,
}
MessageBus.publish(channel, payload, user_ids: [user.id], max_backlog_age: 60)
end
end
end