diff --git a/app/controllers/api/v1/accounts/integrations/hooks_controller.rb b/app/controllers/api/v1/accounts/integrations/hooks_controller.rb index 087a9b7..7ee3e18 100755 --- a/app/controllers/api/v1/accounts/integrations/hooks_controller.rb +++ b/app/controllers/api/v1/accounts/integrations/hooks_controller.rb @@ -4,10 +4,12 @@ class Api::V1::Accounts::Integrations::HooksController < Api::V1::Accounts::Base def create @hook = Current.account.hooks.create!(permitted_params) + sync_llm_integration_settings(@hook) end def update @hook.update!(permitted_params.slice(:status, :settings)) + sync_llm_integration_settings(@hook) end def process_event @@ -42,4 +44,15 @@ class Api::V1::Accounts::Integrations::HooksController < Api::V1::Accounts::Base def permitted_params params.require(:hook).permit(:app_id, :inbox_id, :status, settings: {}) end + + def sync_llm_integration_settings(hook) + return unless %w[openai gemini].include?(hook.app_id) + + api_key = hook.settings['api_key'].to_s.strip + return if api_key.blank? + + config_key = hook.app_id == 'gemini' ? 'CAPTAIN_GEMINI_API_KEY' : 'CAPTAIN_OPEN_AI_API_KEY' + InstallationConfig.find_or_initialize_by(name: config_key).update!(value: api_key) + Llm::Config.reset! + end end diff --git a/app/controllers/api/v1/accounts/integrations/llm_models_controller.rb b/app/controllers/api/v1/accounts/integrations/llm_models_controller.rb new file mode 100644 index 0000000..1245c4e --- /dev/null +++ b/app/controllers/api/v1/accounts/integrations/llm_models_controller.rb @@ -0,0 +1,59 @@ +class Api::V1::Accounts::Integrations::LlmModelsController < Api::V1::Accounts::BaseController + before_action :check_authorization + + def test + provider = params.require(:provider) + model = params.require(:model) + return render json: { error: 'Unsupported provider' }, status: :unprocessable_entity unless %w[openai gemini].include?(provider) + + hook = Current.account.hooks.find_by(app_id: provider) + + return render json: { error: 'Integration not configured' }, status: :unprocessable_entity if hook.blank? + + api_key = hook.settings['api_key'].to_s.strip + return render json: { error: 'API key is missing' }, status: :unprocessable_entity if api_key.blank? + + result = Llm::ModelTestService.new( + provider: provider, + model: model, + api_key: api_key + ).perform + + update_hook_results(hook, model, result) + + if result[:success] + render json: { success: true } + else + render json: { error: result[:error] || 'Model test failed' }, status: :unprocessable_entity + end + end + + private + + def check_authorization + authorize(:hook, :update?) + end + + def update_hook_results(hook, model, result) + settings = hook.settings.to_h.deep_stringify_keys + model_tests = settings['model_tests'] || {} + model_tests[model] = { + 'success' => result[:success], + 'error' => result[:error], + 'tested_at' => Time.current.iso8601 + } + + validated_models = model_tests.filter_map do |name, entry| + name if entry['success'] + end + + settings['model_tests'] = model_tests + settings['validated_models'] = validated_models + hook.update!(settings: settings) + rescue ActiveRecord::RecordInvalid + Rails.logger.error( + "[LLM][ModelTest] Failed to persist model test results hook_id=#{hook.id} errors=#{hook.errors.full_messages.join(', ')}" + ) + hook.update_columns(settings: settings) + end +end diff --git a/app/javascript/dashboard/api/integrations.js b/app/javascript/dashboard/api/integrations.js index d4ffcbc..e839b19 100755 --- a/app/javascript/dashboard/api/integrations.js +++ b/app/javascript/dashboard/api/integrations.js @@ -33,6 +33,13 @@ class IntegrationsAPI extends ApiClient { return axios.delete(`${this.baseUrl()}/integrations/hooks/${hookId}`); } + testLlmModel({ provider, model }) { + return axios.post(`${this.baseUrl()}/integrations/llm_models/test`, { + provider, + model, + }); + } + connectShopify({ shopDomain }) { return axios.post(`${this.baseUrl()}/integrations/shopify/auth`, { shop_domain: shopDomain, diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/assistant/settings/AssistantBasicSettingsForm.vue b/app/javascript/dashboard/components-next/captain/pageComponents/assistant/settings/AssistantBasicSettingsForm.vue index e398448..7961c98 100755 --- a/app/javascript/dashboard/components-next/captain/pageComponents/assistant/settings/AssistantBasicSettingsForm.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/assistant/settings/AssistantBasicSettingsForm.vue @@ -1,8 +1,10 @@