From 7b4c0996730f153e134e87178ccb50ecb3c8d55e Mon Sep 17 00:00:00 2001 From: Roman Rizzi Date: Tue, 6 Aug 2024 14:35:35 -0300 Subject: [PATCH] FIX: LlmModel validations. (#742) - Validate fields to reduce the chance of breaking features by a misconfigured model. - Fixed a bug where the URL might get deleted during an update. - Display a warning when a model is currently in use. --- .../discourse_ai/admin/ai_llms_controller.rb | 5 +- app/models/llm_model.rb | 7 +++ app/serializers/llm_model_serializer.rb | 8 +-- .../components/ai-llm-editor-form.gjs | 17 +++++++ config/locales/client.en.yml | 4 ++ spec/fabricators/llm_model_fabricator.rb | 2 + .../requests/admin/ai_llms_controller_spec.rb | 50 ++++++++++++++----- 7 files changed, 76 insertions(+), 17 deletions(-) diff --git a/app/controllers/discourse_ai/admin/ai_llms_controller.rb b/app/controllers/discourse_ai/admin/ai_llms_controller.rb index 4707cb6b..635a4a2f 100644 --- a/app/controllers/discourse_ai/admin/ai_llms_controller.rb +++ b/app/controllers/discourse_ai/admin/ai_llms_controller.rb @@ -97,6 +97,8 @@ module DiscourseAi private def ai_llm_params(updating: nil) + return {} if params[:ai_llm].blank? + permitted = params.require(:ai_llm).permit( :display_name, @@ -112,7 +114,8 @@ module DiscourseAi provider = updating ? updating.provider : permitted[:provider] permit_url = provider != LlmModel::BEDROCK_PROVIDER_NAME - permitted[:url] = params.dig(:ai_llm, :url) if permit_url + new_url = params.dig(:ai_llm, :url) + permitted[:url] = new_url if permit_url && new_url extra_field_names = LlmModel.provider_params.dig(provider&.to_sym, :fields).to_a received_prov_params = params.dig(:ai_llm, :provider_params) diff --git a/app/models/llm_model.rb b/app/models/llm_model.rb index 8e6049b4..d3e92854 100644 --- a/app/models/llm_model.rb +++ b/app/models/llm_model.rb @@ -6,6 +6,13 @@ class LlmModel < ActiveRecord::Base belongs_to :user + validates :display_name, presence: true, length: { maximum: 100 } + validates :tokenizer, presence: true, inclusion: DiscourseAi::Completions::Llm.tokenizer_names + validates :provider, presence: true, inclusion: DiscourseAi::Completions::Llm.provider_names + validates :url, presence: true, unless: -> { provider == BEDROCK_PROVIDER_NAME } + validates_presence_of :name, :api_key + validates :max_prompt_tokens, numericality: { greater_than: 0 } + def self.provider_params { aws_bedrock: { diff --git a/app/serializers/llm_model_serializer.rb b/app/serializers/llm_model_serializer.rb index 2ef1b28b..ca852409 100644 --- a/app/serializers/llm_model_serializer.rb +++ b/app/serializers/llm_model_serializer.rb @@ -12,13 +12,13 @@ class LlmModelSerializer < ApplicationSerializer :api_key, :url, :enabled_chat_bot, - :shadowed_by_srv, :provider_params, - :vision_enabled + :vision_enabled, + :used_by has_one :user, serializer: BasicUserSerializer, embed: :object - def shadowed_by_srv - object.url.to_s.starts_with?("srv://") + def used_by + DiscourseAi::Configuration::LlmValidator.new.modules_using(object) end end diff --git a/assets/javascripts/discourse/components/ai-llm-editor-form.gjs b/assets/javascripts/discourse/components/ai-llm-editor-form.gjs index 48d239a5..172ba0c7 100644 --- a/assets/javascripts/discourse/components/ai-llm-editor-form.gjs +++ b/assets/javascripts/discourse/components/ai-llm-editor-form.gjs @@ -65,6 +65,17 @@ export default class AiLlmEditorForm extends Component { return this.metaProviderParams.url_editable !== false; } + get modulesUsingModel() { + return this.args.model.used_by?.join(", "); + } + + get inUseWarning() { + return I18n.t("discourse_ai.llms.in_use_warning", { + settings: this.modulesUsingModel, + count: this.args.model.used_by.length, + }); + } + @computed("args.model.provider") get metaProviderParams() { return ( @@ -166,6 +177,12 @@ export default class AiLlmEditorForm extends Component { }