From 961ecd388c657868d67671f0e0bfa7d2f53262c3 Mon Sep 17 00:00:00 2001 From: Moreminimore Date: Wed, 19 Aug 2026 11:15:28 +0700 Subject: [PATCH] revert(captain): undo OpenAI provider switch (wrong scope) The OpenAI Custom Server request targets the per-account OpenAI integration (/app/accounts/{id}/settings/integrations/openai), not the instance-level Captain config. The CAPTAIN_OPEN_AI_PROVIDER provider-switch added in 46fe70398 was out of the intended scope and is new behavior that did not previously exist, so it is removed. This restores the original Captain behavior (CAPTAIN_OPEN_AI_ENDPOINT remains usable as before, no provider gating). The correct implementation goes in config/integration/apps.yml for the per-account openai integration. --- .../super_admin/app_configs_controller.rb | 2 +- app/models/installation_config.rb | 26 ------------------- config/initializers/ai_agents.rb | 2 +- config/installation_config.yml | 9 ------- lib/captain/base_task_service.rb | 2 +- lib/integrations/llm_base_service.rb | 2 +- lib/integrations/openai/key_validator.rb | 2 +- lib/llm/config.rb | 22 ++++++---------- 8 files changed, 13 insertions(+), 54 deletions(-) diff --git a/app/controllers/super_admin/app_configs_controller.rb b/app/controllers/super_admin/app_configs_controller.rb index d48b97c24..593b40761 100644 --- a/app/controllers/super_admin/app_configs_controller.rb +++ b/app/controllers/super_admin/app_configs_controller.rb @@ -56,7 +56,7 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController 'whatsapp_embedded' => %w[WHATSAPP_APP_ID WHATSAPP_APP_SECRET WHATSAPP_CONFIGURATION_ID WHATSAPP_API_VERSION], 'notion' => %w[NOTION_CLIENT_ID NOTION_CLIENT_SECRET], 'google' => %w[GOOGLE_OAUTH_CLIENT_ID GOOGLE_OAUTH_CLIENT_SECRET GOOGLE_OAUTH_REDIRECT_URI ENABLE_GOOGLE_OAUTH_LOGIN], - 'captain' => %w[CAPTAIN_OPEN_AI_PROVIDER CAPTAIN_OPEN_AI_API_KEY CAPTAIN_OPEN_AI_MODEL CAPTAIN_OPEN_AI_ENDPOINT] + 'captain' => %w[CAPTAIN_OPEN_AI_API_KEY CAPTAIN_OPEN_AI_MODEL CAPTAIN_OPEN_AI_ENDPOINT] } @allowed_configs = mapping.fetch(@config, general_configs) diff --git a/app/models/installation_config.rb b/app/models/installation_config.rb index ab36af040..ef349799f 100644 --- a/app/models/installation_config.rb +++ b/app/models/installation_config.rb @@ -37,8 +37,6 @@ class InstallationConfig < ApplicationRecord before_validation :set_lock validates :name, presence: true validate :saml_sso_users_check, if: -> { name == 'ENABLE_SAML_SSO_LOGIN' } - validate :openai_custom_provider_check, if: -> { name == 'CAPTAIN_OPEN_AI_PROVIDER' } - validate :openai_custom_endpoint_check, if: -> { name == 'CAPTAIN_OPEN_AI_ENDPOINT' } # TODO: Get rid of default scope # https://stackoverflow.com/a/1834250/939299 @@ -73,28 +71,4 @@ class InstallationConfig < ApplicationRecord errors.add(:base, 'Cannot disable SAML SSO login while users are using SAML authentication') end - - # Only allow provider values the platform understands ('openai' default or 'custom') - # and ensure the "custom" provider actually requires an endpoint. - def openai_custom_provider_check - return if %w[openai custom].include?(value.to_s) - - errors.add(:base, 'CAPTAIN_OPEN_AI_PROVIDER must be "openai" or "custom"') - end - - # Fail-closed: when the provider is "custom", CAPTAIN_OPEN_AI_ENDPOINT must be - # present AND a secure https URL (OpenAI-compatible base). - def openai_custom_endpoint_check - provider = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_PROVIDER')&.value.to_s - return unless provider == 'custom' - - if value.blank? - errors.add(:base, 'CAPTAIN_OPEN_AI_ENDPOINT is required when using the custom provider') - return - end - - return if value.to_s.match?(%r{\Ahttps://\S+}) - - errors.add(:base, 'CAPTAIN_OPEN_AI_ENDPOINT must be a valid https URL when using the custom provider') - end end diff --git a/config/initializers/ai_agents.rb b/config/initializers/ai_agents.rb index a12483d44..be8a8a9cc 100644 --- a/config/initializers/ai_agents.rb +++ b/config/initializers/ai_agents.rb @@ -5,7 +5,7 @@ require 'agents' Rails.application.config.after_initialize do api_key = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_API_KEY')&.value model = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_MODEL')&.value.presence || LlmConstants::DEFAULT_MODEL - api_endpoint = Llm::Config.openai_endpoint.presence || LlmConstants::OPENAI_API_ENDPOINT + api_endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value || LlmConstants::OPENAI_API_ENDPOINT if api_key.present? Agents.configure do |config| diff --git a/config/installation_config.yml b/config/installation_config.yml index 03e300824..f659a80d0 100644 --- a/config/installation_config.yml +++ b/config/installation_config.yml @@ -191,15 +191,6 @@ display_title: 'OpenAI API Endpoint (optional)' description: 'The OpenAI endpoint configured for use in Captain AI. Default: https://api.openai.com/' locked: false -- name: CAPTAIN_OPEN_AI_PROVIDER - display_title: 'OpenAI Provider' - description: 'Select the OpenAI-compatible provider: "openai" uses the default api.openai.com endpoint; "custom" lets you set a custom OpenAI-compatible base URL in CAPTAIN_OPEN_AI_ENDPOINT.' - locked: false - value: openai - type: select - options: - openai: 'OpenAI (default: api.openai.com)' - custom: 'OpenAI Custom Server' - name: CAPTAIN_EMBEDDING_MODEL display_title: 'Embedding Model (optional)' description: 'The embedding model configured for use in Captain AI. Default: text-embedding-3-small' diff --git a/lib/captain/base_task_service.rb b/lib/captain/base_task_service.rb index 6abaff4f7..cfeb4e427 100644 --- a/lib/captain/base_task_service.rb +++ b/lib/captain/base_task_service.rb @@ -32,7 +32,7 @@ class Captain::BaseTaskService end def api_base - endpoint = Llm::Config.openai_endpoint.presence || 'https://api.openai.com/' + endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value.presence || 'https://api.openai.com/' endpoint = endpoint.chomp('/') "#{endpoint}/v1" end diff --git a/lib/integrations/llm_base_service.rb b/lib/integrations/llm_base_service.rb index 4e93ada95..8410130ee 100644 --- a/lib/integrations/llm_base_service.rb +++ b/lib/integrations/llm_base_service.rb @@ -84,7 +84,7 @@ class Integrations::LlmBaseService end def api_base - endpoint = Llm::Config.openai_endpoint.presence || 'https://api.openai.com/' + endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value.presence || 'https://api.openai.com/' endpoint = endpoint.chomp('/') "#{endpoint}/v1" end diff --git a/lib/integrations/openai/key_validator.rb b/lib/integrations/openai/key_validator.rb index 0551ad928..dad464f7d 100644 --- a/lib/integrations/openai/key_validator.rb +++ b/lib/integrations/openai/key_validator.rb @@ -20,7 +20,7 @@ module Integrations::Openai::KeyValidator end def self.api_base - endpoint = Llm::Config.openai_endpoint.presence || 'https://api.openai.com/' + endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value.presence || 'https://api.openai.com/' "#{endpoint.chomp('/')}/v1" end end diff --git a/lib/llm/config.rb b/lib/llm/config.rb index 5faae0435..6528e90c8 100644 --- a/lib/llm/config.rb +++ b/lib/llm/config.rb @@ -29,20 +29,6 @@ module Llm::Config yield context end - # Returns the OpenAI-compatible base URL for Captain AI. - # - provider == 'custom' -> use the operator-configured CAPTAIN_OPEN_AI_ENDPOINT - # - provider == 'openai' -> nil (RubyLLM uses the default https://api.openai.com/) - def openai_endpoint - provider = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_PROVIDER')&.value.presence || 'openai' - return nil unless provider == 'custom' - - InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value - end - - def system_api_key - InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_API_KEY')&.value - end - private def configure_ruby_llm @@ -53,5 +39,13 @@ module Llm::Config config.logger = Rails.logger end end + + def system_api_key + InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_API_KEY')&.value + end + + def openai_endpoint + InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value + end end end