feat(captain): add OpenAI Custom Server provider option
Some checks failed
Frontend Lint & Test / test (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / security-scan (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Sync GHSA advisories to Linear / sync (push) Has been cancelled

Add an operator-level provider switch for Captain AI so it can use any
OpenAI-compatible base URL (vLLM, OpenRouter, proxies, etc.) in addition to the
default api.openai.com endpoint.

- installation_config.yml: new CAPTAIN_OPEN_AI_PROVIDER (default 'openai',
  select openai|custom) shown on the Super Admin captain settings page.
- lib/llm/config.rb: openai_endpoint now returns the configured
  CAPTAIN_OPEN_AI_ENDPOINT only when provider=='custom'; nil for openai/default
  so a stale endpoint is ignored. system_api_key/openai_endpoint made public so
  all consumers share one provider-switch source.
- Consumers (base_task_service, llm_base_service, key_validator, ai_agents
  initializer) now route via Llm::Config.openai_endpoint instead of reading
  CAPTAIN_OPEN_AI_ENDPOINT directly.
- installation_config.rb: fail-closed validation (provider must be openai/custom;
  provider=custom requires a present https:// endpoint).

Approved by independent five-key reviews deleg_b141098a (found+fixed one logic
gap) and deleg_3088b922 (passed=true, blocking arrays empty).
This commit is contained in:
Moreminimore
2026-08-19 10:52:47 +07:00
parent 4b35130ae2
commit 46fe703986
8 changed files with 54 additions and 13 deletions

View File

@@ -32,7 +32,7 @@ class Captain::BaseTaskService
end
def api_base
endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value.presence || 'https://api.openai.com/'
endpoint = Llm::Config.openai_endpoint.presence || 'https://api.openai.com/'
endpoint = endpoint.chomp('/')
"#{endpoint}/v1"
end

View File

@@ -84,7 +84,7 @@ class Integrations::LlmBaseService
end
def api_base
endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value.presence || 'https://api.openai.com/'
endpoint = Llm::Config.openai_endpoint.presence || 'https://api.openai.com/'
endpoint = endpoint.chomp('/')
"#{endpoint}/v1"
end

View File

@@ -20,7 +20,7 @@ module Integrations::Openai::KeyValidator
end
def self.api_base
endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value.presence || 'https://api.openai.com/'
endpoint = Llm::Config.openai_endpoint.presence || 'https://api.openai.com/'
"#{endpoint.chomp('/')}/v1"
end
end

View File

@@ -29,6 +29,20 @@ 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
@@ -39,13 +53,5 @@ 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